如何编辑已部署图的状态¶
在创建LangGraph代理时,通常希望添加一个人机交互组件。这在向它们提供工具访问权限时特别有用。在这种情况下,您可能希望在继续之前编辑图的状态(例如,编辑正在调用的工具或调用方式)。
这可以通过多种方式实现,但主要支持的方式是在一个节点执行之前添加一个“中断”。这会在该节点处中断执行。然后,您可以使用update_state更新状态,然后从该位置继续执行。
设置¶
我们不会展示托管图的完整代码,但如果你想查看的话,可以在这里查看:这里。一旦该图被托管,我们就可以调用它并等待用户输入。
SDK初始化¶
首先,我们需要设置客户端,以便能够与托管的图进行通信:
编辑状态¶
初始调用¶
现在让我们调用我们的图,确保在action
节点之前中断。
const input = { messages: [{ role: "human", content: "搜索旧金山的天气" }] };
const streamResponse = client.runs.stream(
thread["thread_id"],
assistantId,
{
input: input,
streamMode: "updates",
interruptBefore: ["action"],
}
);
for await (const chunk of streamResponse) {
if (chunk.data && chunk.event !== "metadata") {
console.log(chunk.data);
}
}
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\": \"搜索旧金山的天气\"}]},
\"interrupt_before\": [\"action\"],
\"stream_mode\": [
\"updates\"
]
}" | \
sed 's/\r$//' | \
awk '
/^event:/ {
if (data_content != "" && event_type != "metadata") {
print data_content "\n"
}
sub(/^event: /, "", $0)
event_type = $0
data_content = ""
}
/^data:/ {
sub(/^data: /, "", $0)
data_content = $0
}
END {
if (data_content != "" && event_type != "metadata") {
print data_content "\n"
}
}
'
输出:
{'agent': {'messages': [{'content': [{'text': "当然!我将为您搜索旧金山当前的天气。这是我的操作步骤:", 'type': 'text'}, {'id': 'toolu_01KEJMBFozSiZoS4mAcPZeqQ', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-6dbb0167-f8f6-4e2a-ab68-229b2d1fbb64', 'example': False, 'tool_calls': [{'name': 'search', 'args': {'query': 'current weather in San Francisco'}, 'id': 'toolu_01KEJMBFozSiZoS4mAcPZeqQ'}], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
编辑状态¶
现在,假设我们实际上想搜索塞迪弗雷(另一个以SF为缩写的城镇)的天气。我们可以编辑状态以正确反映这一点:
# 首先,让我们获取当前状态
current_state = await client.threads.get_state(thread['thread_id'])
# 现在获取状态中的最后一条消息
# 这是包含我们想要更新的工具调用的消息
last_message = current_state['values']['messages'][-1]
# 现在更新该工具调用的参数
last_message['tool_calls'][0]['args'] = {'query': 'current weather in Sidi Frej'}
# 现在调用`update_state`以在`messages`键中传递此消息
# 这将被视为对状态的任何其他更新
# 它将传递给`messages`键的reducer函数
# 该reducer函数将使用消息的ID进行更新
# 重要的是它具有正确的ID!否则它会被添加为一条新消息
await client.threads.update_state(thread['thread_id'], {"messages": last_message})
// 首先,让我们获取当前状态
const currentState = await client.threads.getState(thread["thread_id"]);
// 现在获取状态中的最后一条消息
// 这是包含我们想要更新的工具调用的消息
let lastMessage = currentState.values.messages.slice(-1)[0];
// 现在更新该工具调用的参数
lastMessage.tool_calls[0].args = { query: "current weather in Sidi Frej" };
// 现在调用`update_state`以在`messages`键中传递此消息
// 这将被视为对状态的任何其他更新
// 它将传递给`messages`键的reducer函数
// 该reducer函数将使用消息的ID进行更新
// 重要的是它具有正确的ID!否则它会被添加为一条新消息
await client.threads.updateState(thread["thread_id"], { values: { messages: lastMessage } });
输出:
{'configurable': {'thread_id': '9c8f1a43-9dd8-4017-9271-2c53e57cf66a',
'checkpoint_ns': '',
'checkpoint_id': '1ef58e7e-3641-649f-8002-8b4305a64858'}}
恢复调用¶
现在我们可以恢复图的运行,但使用更新后的状态:
curl --request POST \
--url <DEPLOYMENT_URL>/threads/<THREAD_ID>/runs/stream \
--header 'Content-Type: application/json' \
--data "{
\"assistant_id\": \"agent\",
\"stream_mode\": [
\"updates\"
]
}"| \
sed 's/\r$//' | \
awk '
/^event:/ {
if (data_content != "" && event_type != "metadata") {
print data_content "\n"
}
sub(/^event: /, "", $0)
event_type = $0
data_content = ""
}
/^data:/ {
sub(/^data: /, "", $0)
data_content = $0
}
END {
if (data_content != "" && event_type != "metadata") {
print data_content "\n"
}
}
'
输出:
{'action': {'messages': [{'content': '["我查看了:当前塞迪弗雷的天气。结果:旧金山阳光明媚,但如果你是双子座,你最好小心。 😈."]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': '1161b8d1-bee4-4188-9be8-698aecb69f10', 'tool_call_id': 'toolu_01KEJMBFozSiZoS4mAcPZeqQ'}]}}
{'agent': {'messages': [{'content': [{'text': '我为我的搜索查询中的混淆道歉。似乎搜索功能将“SF”解释为“塞迪弗雷”而不是我们所期望的“旧金山”。让我再次使用完整的城市名称来获取正确的信息:', 'type': 'text'}, {'id': 'toolu_0111rrwgfAcmurHZn55qjqTR', 'input': {'query': 'current weather in San Francisco'}, 'name': 'search', 'type': 'tool_use'}], 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-b8c25779-cfb4-46fc-a421-48553551242f', 'example': False, 'tool_calls': [{'name': 'search', 'args': {'query': 'current weather in San Francisco'}, 'id': 'toolu_0111rrwgfAcmurHZn55qjqTR'}], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
{'action': {'messages': [{'content': '["我查看了:当前旧金山的天气。结果:旧金山阳光明媚,但如果你是双子座,你最好小心。 😈."]', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'search', 'id': '6bc632ae-5ee6-4d01-9532-79c524a2d443', 'tool_call_id': 'toolu_0111rrwgfAcmurHZn55qjqTR'}]}}
{'agent': {'messages': [{'content': "现在,根据搜索结果,我可以为您提供旧金山当前天气的信息:\n\n旧金山目前天气晴好。\n\n值得注意的是,搜索结果中包含了一个关于双子座的不寻常评论,这似乎与天气无关。这可能是由于搜索引擎在结果中包含了某些占星信息或笑话。然而,为了天气信息,我们可以专注于目前旧金山阳光明媚。\n\n您是否还想了解旧金山或其他地方的天气信息?", 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'ai', 'name': None, 'id': 'run-227a042b-dd97-476e-af32-76a3703af5d8', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}]}}
如您所见,它现在搜索了塞迪弗雷的当前天气(尽管我们的虚拟搜索节点仍然返回旧金山的天气结果,因为我们在这个示例中实际上并没有进行搜索,而是每次都返回相同的“旧金山阳光明媚...”结果)。