Skip to content

如何添加静态断点

前提条件

本指南假设您熟悉以下概念:

人机交互(HIL)对于代理系统至关重要。断点是一种常见的HIL交互模式,允许图在特定步骤停止并寻求人类批准后再继续执行(例如,对于敏感操作)。

断点是基于LangGraph 检查点构建的,这些检查点在每个节点执行后保存图的状态。检查点保存在线程中,这些线程保留图的状态,并且可以在图执行完成后访问。这使得图可以在特定点暂停,等待人类批准,然后从最后一个检查点恢复执行。

设置

图表代码

在此教程中,我们使用了一个简单的ReAct风格的托管图表(你可以在这里查看其完整代码此处)。重要的是,该图中有两个节点(一个名为agent的节点调用LLM,另一个名为action的节点调用工具),以及从agentaction的路由函数,该函数确定是否调用action节点或结束图运行(action节点在执行后总是调用agent节点)。

SDK初始化

from langgraph_sdk import get_client
client = get_client(url=<DEPLOYMENT_URL>)
# 使用名为"agent"的图部署
assistant_id = "agent"
thread = await client.threads.create()
import { Client } from "@langchain/langgraph-sdk";

const client = new Client({ apiUrl: <DEPLOYMENT_URL> });
// 使用名为"agent"的图部署
const assistantId = "agent";
const thread = await client.threads.create();
curl --request POST \
  --url <DEPLOYMENT_URL>/threads \
  --header 'Content-Type: application/json' \
  --data '{}'

添加断点

我们现在希望在图运行中添加一个断点,在调用某个工具之前插入该断点。 我们可以通过添加interrupt_before=["action"]来实现这一点,这表示在调用动作节点之前中断执行。 我们可以在编译图时或启动运行时添加这个断点。 这里我们将它添加在启动运行时,如果你希望在编译时添加,则需要编辑定义图的Python文件,并在调用.compile时添加interrupt_before参数。

首先让我们通过SDK访问托管的LangGraph实例:

然后,现在让我们在工具节点之前编译带有断点的图:

input = {"messages": [{"role": "user", "content": "what's the weather in sf"}]}
async for chunk in client.runs.stream(
    thread["thread_id"],
    assistant_id,
    input=input,
    stream_mode="updates",
    interrupt_before=["action"],
):
    print(f"接收新的事件类型:{chunk.event}...")
    print(chunk.data)
    print("\n\n")
const input = { messages: [{ role: "human", content: "what's the weather in sf" }] };

const streamResponse = client.runs.stream(
  thread["thread_id"],
  assistantId,
  {
    input: input,
    streamMode: "updates",
    interruptBefore: ["action"]
  }
);

for await (const chunk of streamResponse) {
  console.log(`接收新的事件类型:${chunk.event}...`);
  console.log(chunk.data);
  console.log("\n\n");
}
curl --request POST \
 --url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
 --header 'Content-Type: application/json' \
 --data "{
   \"assistant_id\": \"agent\",
   \"input\": {\"messages\": [{\"role\": \"human\", \"content\": \"what's the weather in sf\"}]},
   \"interrupt_before\": [\"action\"],
   \"stream_mode\": [
     \"messages\"
   ]
 }" | \
 sed 's/\r$//' | \
 awk '
 /^event:/ {
     if (data_content != "") {
         print data_content "\n"
     }
     sub(/^event: /, "接收事件类型:", $0)
     printf "%s...\n", $0
     data_content = ""
 }
 /^data:/ {
     sub(/^data: /, "", $0)
     data_content = $0
 }
 END {
     if (data_content != "") {
         print data_content "\n"
     }
 }
 '

输出:

接收新的事件类型:metadata...
{'run_id': '3b77ef83-687a-4840-8858-0371f91a92c3'}



接收新的事件类型:data...
{'agent': {'messages': [{'content': [{'id': 'toolu_01HwZqM1ptX6E15A5LAmyZTB', 'input': {'query': 'weather in san francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-e5d17791-4d37-4ad2-815f-a0c4cba62585', 'example': False, 'tool_calls': [{'name': 'tavily_search_results_json', 'args': {'query': 'weather in san francisco'}, 'id': 'toolu_01HwZqM1ptX6E15A5LAmyZTB'}], 'invalid_tool_calls': []}]}}



接收新的事件类型:end...
None

Comments