如何等待用户输入¶
人工参与 (HIL) 交互对于 agentic 系统 至关重要。等待人工输入是一种常见的人工参与交互模式,允许 agent 向用户询问澄清问题并在继续之前等待输入。
我们可以在 LangGraph 中使用 interrupt()
函数来实现这些。interrupt
允许我们停止图执行以收集用户输入,并使用收集到的输入继续执行。
设置¶
首先我们需要安装所需的软件包
接下来,我们需要为 Anthropic(我们将使用的 LLM)设置 API 密钥
可选地,我们可以为 LangSmith 追踪 设置 API 密钥,这将为我们提供一流的可观察性。
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_CALLBACKS_BACKGROUND="true"
export LANGCHAIN_API_KEY=your-api-key
简单用法¶
让我们探索使用人工反馈的基本示例。一种直接的方法是创建一个节点,human_feedback
,专门用于收集用户输入。这使我们能够在图中的特定选定点收集反馈。
步骤
- 在
human_feedback
节点内调用interrupt()
。 - 我们设置一个 检查点,以保存图的状态,直到此节点。
- 使用
new Command({ resume: ... })
向human_feedback
节点提供请求的值并恢复执行。
import { StateGraph, Annotation, START, END, interrupt, MemorySaver } from "@langchain/langgraph";
const StateAnnotation = Annotation.Root({
input: Annotation<string>,
userFeedback: Annotation<string>
});
const step1 = (_state: typeof StateAnnotation.State) => {
console.log("---Step 1---");
return {};
}
const humanFeedback = (_state: typeof StateAnnotation.State) => {
console.log("--- humanFeedback ---");
const feedback: string = interrupt("Please provide feedback");
return {
userFeedback: feedback
};
}
const step3 = (_state: typeof StateAnnotation.State) => {
console.log("---Step 3---");
return {};
}
const builder = new StateGraph(StateAnnotation)
.addNode("step1", step1)
.addNode("humanFeedback", humanFeedback)
.addNode("step3", step3)
.addEdge(START, "step1")
.addEdge("step1", "humanFeedback")
.addEdge("humanFeedback", "step3")
.addEdge("step3", END);
// Set up memory
const memory = new MemorySaver()
// Add
const graph = builder.compile({
checkpointer: memory,
});
import * as tslab from "tslab";
const drawableGraph = graph.getGraph();
const image = await drawableGraph.drawMermaidPng();
const arrayBuffer = await image.arrayBuffer();
await tslab.display.png(new Uint8Array(arrayBuffer));
运行直到 humanFeedback
节点中的断点
// Input
const initialInput = { input: "hello world" };
// Thread
const config = { configurable: { thread_id: "1" } };
// Run the graph until the first interruption
for await (const event of await graph.stream(initialInput, config)) {
console.log(event);
}
// Will log when the graph is interrupted, after step 2.
console.log("--- GRAPH INTERRUPTED ---");
---Step 1---
{}
--- humanFeedback ---
{
__interrupt__: [
{
value: 'Please provide feedback',
when: 'during',
resumable: true,
ns: [Array]
}
]
}
--- GRAPH INTERRUPTED ---
import { Command } from "@langchain/langgraph";
// Continue the graph execution
for await (const event of await graph.stream(
new Command({ resume: "go to step 3! "}),
config,
)) {
console.log(event);
console.log("\n====\n");
}
--- humanFeedback ---
{ humanFeedback: { userFeedback: 'go to step 3! ' } }
====
---Step 3---
{}
====
Agent¶
在 agent 的上下文中,等待用户反馈对于询问澄清问题很有用。
为了展示这一点,我们将构建一个相对简单的 ReAct 风格的 agent,它进行工具调用。
我们将使用 Anthropic 的模型和一个模拟工具(仅用于演示目的)。
// Set up the tool
import { ChatAnthropic } from "@langchain/anthropic";
import { tool } from "@langchain/core/tools";
import { StateGraph, MessagesAnnotation, START, END, MemorySaver } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { AIMessage, ToolMessage } from "@langchain/core/messages";
import { z } from "zod";
const search = tool((_) => {
return "It's sunny in San Francisco, but you better look out if you're a Gemini 😈.";
}, {
name: "search",
description: "Call to surf the web.",
schema: z.string(),
})
const tools = [search]
const toolNode = new ToolNode<typeof MessagesAnnotation.State>(tools)
// Set up the model
const model = new ChatAnthropic({ model: "claude-3-5-sonnet-20240620" })
const askHumanTool = tool((_) => {
return "The human said XYZ";
}, {
name: "askHuman",
description: "Ask the human for input.",
schema: z.string(),
});
const modelWithTools = model.bindTools([...tools, askHumanTool])
// Define nodes and conditional edges
// Define the function that determines whether to continue or not
function shouldContinue(state: typeof MessagesAnnotation.State): "action" | "askHuman" | typeof END {
const lastMessage = state.messages[state.messages.length - 1] as AIMessage;
// If there is no function call, then we finish
if (lastMessage && !lastMessage.tool_calls?.length) {
return END;
}
// If tool call is askHuman, we return that node
// You could also add logic here to let some system know that there's something that requires Human input
// For example, send a slack message, etc
if (lastMessage.tool_calls?.[0]?.name === "askHuman") {
console.log("--- ASKING HUMAN ---")
return "askHuman";
}
// Otherwise if it isn't, we continue with the action node
return "action";
}
// Define the function that calls the model
async function callModel(state: typeof MessagesAnnotation.State): Promise<Partial<typeof MessagesAnnotation.State>> {
const messages = state.messages;
const response = await modelWithTools.invoke(messages);
// We return an object with a messages property, because this will get added to the existing list
return { messages: [response] };
}
// We define a fake node to ask the human
function askHuman(state: typeof MessagesAnnotation.State): Partial<typeof MessagesAnnotation.State> {
const lastMessage = state.messages[state.messages.length - 1] as AIMessage;
const toolCallId = lastMessage.tool_calls?.[0].id;
const location: string = interrupt("Please provide your location:");
const newToolMessage = new ToolMessage({
tool_call_id: toolCallId!,
content: location,
})
return { messages: [newToolMessage] };
}
// Define a new graph
const messagesWorkflow = new StateGraph(MessagesAnnotation)
// Define the two nodes we will cycle between
.addNode("agent", callModel)
.addNode("action", toolNode)
.addNode("askHuman", askHuman)
// We now add a conditional edge
.addConditionalEdges(
// First, we define the start node. We use `agent`.
// This means these are the edges taken after the `agent` node is called.
"agent",
// Next, we pass in the function that will determine which node is called next.
shouldContinue
)
// We now add a normal edge from `action` to `agent`.
// This means that after `action` is called, `agent` node is called next.
.addEdge("action", "agent")
// After we get back the human response, we go back to the agent
.addEdge("askHuman", "agent")
// Set the entrypoint as `agent`
// This means that this node is the first one called
.addEdge(START, "agent");
// Setup memory
const messagesMemory = new MemorySaver();
// Finally, we compile it!
// This compiles it into a LangChain Runnable,
// meaning you can use it as you would any other runnable
const messagesApp = messagesWorkflow.compile({
checkpointer: messagesMemory,
});
import * as tslab from "tslab";
const drawableGraph2 = messagesApp.getGraph();
const image2 = await drawableGraph2.drawMermaidPng();
const arrayBuffer2 = await image2.arrayBuffer();
await tslab.display.png(new Uint8Array(arrayBuffer2));
与 Agent 交互¶
我们现在可以与 agent 交互。让我们要求它询问用户他们在哪里,然后告诉他们天气。
这应该使其首先使用 askHuman
工具,然后使用正常工具。请注意,我们切换为使用 streamMode: "values"
以仅返回每个点的更新消息
// Input
const input = {
role: "user",
content: "Use the search tool to ask the user where they are, then look up the weather there",
}
// Thread
const config2 = { configurable: { thread_id: "3" }, streamMode: "values" as const };
for await (const event of await messagesApp.stream({
messages: [input]
}, config2)) {
const recentMsg = event.messages[event.messages.length - 1];
console.log(`================================ ${recentMsg.getType()} Message (1) =================================`)
console.log(recentMsg.content);
}
================================ human Message (1) =================================
Use the search tool to ask the user where they are, then look up the weather there
--- ASKING HUMAN ---
================================ ai Message (1) =================================
[
{
type: 'text',
text: "Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are."
},
{
type: 'tool_use',
id: 'toolu_015UDVFoXcMV7KjRqPY78Umk',
name: 'askHuman',
input: {
input: 'Where are you currently located? Please provide a city and country or region.'
}
}
]
askHuman
节点内被中断,现在正在等待提供 location
。我们可以通过使用 new Command({ resume: "<location>" })
输入调用图来提供此值
import { Command } from "@langchain/langgraph";
// Continue the graph execution
for await (const event of await messagesApp.stream(
new Command({ resume: "San Francisco" }),
config2,
)) {
console.log(event);
console.log("\n====\n");
}
{
messages: [
HumanMessage {
"id": "cfb461cb-0da1-48b4-acef-e3bf0d3a4e6c",
"content": "Use the search tool to ask the user where they are, then look up the weather there",
"additional_kwargs": {},
"response_metadata": {}
},
AIMessage {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"content": [
{
"type": "text",
"text": "Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are."
},
{
"type": "tool_use",
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"name": "askHuman",
"input": {
"input": "Where are you currently located? Please provide a city and country or region."
}
}
],
"additional_kwargs": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
}
},
"response_metadata": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "askHuman",
"args": {
"input": "Where are you currently located? Please provide a city and country or region."
},
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"type": "tool_call"
}
],
"invalid_tool_calls": []
}
]
}
====
{
messages: [
HumanMessage {
"id": "cfb461cb-0da1-48b4-acef-e3bf0d3a4e6c",
"content": "Use the search tool to ask the user where they are, then look up the weather there",
"additional_kwargs": {},
"response_metadata": {}
},
AIMessage {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"content": [
{
"type": "text",
"text": "Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are."
},
{
"type": "tool_use",
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"name": "askHuman",
"input": {
"input": "Where are you currently located? Please provide a city and country or region."
}
}
],
"additional_kwargs": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
}
},
"response_metadata": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "askHuman",
"args": {
"input": "Where are you currently located? Please provide a city and country or region."
},
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"type": "tool_call"
}
],
"invalid_tool_calls": []
},
ToolMessage {
"id": "0225d971-1756-468e-996d-9af93e608a95",
"content": "San Francisco",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "toolu_015UDVFoXcMV7KjRqPY78Umk"
}
]
}
====
{
messages: [
HumanMessage {
"id": "cfb461cb-0da1-48b4-acef-e3bf0d3a4e6c",
"content": "Use the search tool to ask the user where they are, then look up the weather there",
"additional_kwargs": {},
"response_metadata": {}
},
AIMessage {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"content": [
{
"type": "text",
"text": "Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are."
},
{
"type": "tool_use",
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"name": "askHuman",
"input": {
"input": "Where are you currently located? Please provide a city and country or region."
}
}
],
"additional_kwargs": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
}
},
"response_metadata": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "askHuman",
"args": {
"input": "Where are you currently located? Please provide a city and country or region."
},
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"type": "tool_call"
}
],
"invalid_tool_calls": []
},
ToolMessage {
"id": "0225d971-1756-468e-996d-9af93e608a95",
"content": "San Francisco",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "toolu_015UDVFoXcMV7KjRqPY78Umk"
},
AIMessage {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"content": [
{
"type": "text",
"text": "Thank you for providing your location. Now, I'll use the search tool to look up the weather in San Francisco."
},
{
"type": "tool_use",
"id": "toolu_01AzffiwYncLArNb9SbKHK2a",
"name": "search",
"input": {
"input": "current weather in San Francisco"
}
}
],
"additional_kwargs": {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 590,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 81
}
},
"response_metadata": {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 590,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 81
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "search",
"args": {
"input": "current weather in San Francisco"
},
"id": "toolu_01AzffiwYncLArNb9SbKHK2a",
"type": "tool_call"
}
],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 590,
"output_tokens": 81,
"total_tokens": 671
}
}
]
}
====
{
messages: [
HumanMessage {
"id": "cfb461cb-0da1-48b4-acef-e3bf0d3a4e6c",
"content": "Use the search tool to ask the user where they are, then look up the weather there",
"additional_kwargs": {},
"response_metadata": {}
},
AIMessage {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"content": [
{
"type": "text",
"text": "Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are."
},
{
"type": "tool_use",
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"name": "askHuman",
"input": {
"input": "Where are you currently located? Please provide a city and country or region."
}
}
],
"additional_kwargs": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
}
},
"response_metadata": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "askHuman",
"args": {
"input": "Where are you currently located? Please provide a city and country or region."
},
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"type": "tool_call"
}
],
"invalid_tool_calls": []
},
ToolMessage {
"id": "0225d971-1756-468e-996d-9af93e608a95",
"content": "San Francisco",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "toolu_015UDVFoXcMV7KjRqPY78Umk"
},
AIMessage {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"content": [
{
"type": "text",
"text": "Thank you for providing your location. Now, I'll use the search tool to look up the weather in San Francisco."
},
{
"type": "tool_use",
"id": "toolu_01AzffiwYncLArNb9SbKHK2a",
"name": "search",
"input": {
"input": "current weather in San Francisco"
}
}
],
"additional_kwargs": {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 590,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 81
}
},
"response_metadata": {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 590,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 81
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "search",
"args": {
"input": "current weather in San Francisco"
},
"id": "toolu_01AzffiwYncLArNb9SbKHK2a",
"type": "tool_call"
}
],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 590,
"output_tokens": 81,
"total_tokens": 671
}
},
ToolMessage {
"id": "1c52e9b5-1d0b-4889-abd0-10572053e1d8",
"content": "It's sunny in San Francisco, but you better look out if you're a Gemini 😈.",
"name": "search",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "toolu_01AzffiwYncLArNb9SbKHK2a"
}
]
}
====
{
messages: [
HumanMessage {
"id": "cfb461cb-0da1-48b4-acef-e3bf0d3a4e6c",
"content": "Use the search tool to ask the user where they are, then look up the weather there",
"additional_kwargs": {},
"response_metadata": {}
},
AIMessage {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"content": [
{
"type": "text",
"text": "Certainly! I'll use the askHuman tool to ask the user about their location, and then use the search tool to look up the weather for that location. Let's start by asking the user where they are."
},
{
"type": "tool_use",
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"name": "askHuman",
"input": {
"input": "Where are you currently located? Please provide a city and country or region."
}
}
],
"additional_kwargs": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
}
},
"response_metadata": {
"id": "msg_01TA2zHbbrenm7KXSUdcFdXD",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 465,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 112
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "askHuman",
"args": {
"input": "Where are you currently located? Please provide a city and country or region."
},
"id": "toolu_015UDVFoXcMV7KjRqPY78Umk",
"type": "tool_call"
}
],
"invalid_tool_calls": []
},
ToolMessage {
"id": "0225d971-1756-468e-996d-9af93e608a95",
"content": "San Francisco",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "toolu_015UDVFoXcMV7KjRqPY78Umk"
},
AIMessage {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"content": [
{
"type": "text",
"text": "Thank you for providing your location. Now, I'll use the search tool to look up the weather in San Francisco."
},
{
"type": "tool_use",
"id": "toolu_01AzffiwYncLArNb9SbKHK2a",
"name": "search",
"input": {
"input": "current weather in San Francisco"
}
}
],
"additional_kwargs": {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 590,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 81
}
},
"response_metadata": {
"id": "msg_01QDw6TTXvFXKPEX4mYjzU8Y",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 590,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 81
},
"type": "message",
"role": "assistant"
},
"tool_calls": [
{
"name": "search",
"args": {
"input": "current weather in San Francisco"
},
"id": "toolu_01AzffiwYncLArNb9SbKHK2a",
"type": "tool_call"
}
],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 590,
"output_tokens": 81,
"total_tokens": 671
}
},
ToolMessage {
"id": "1c52e9b5-1d0b-4889-abd0-10572053e1d8",
"content": "It's sunny in San Francisco, but you better look out if you're a Gemini 😈.",
"name": "search",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "toolu_01AzffiwYncLArNb9SbKHK2a"
},
AIMessage {
"id": "msg_01UAbWbZ9RVV3L6ABgQWsy9f",
"content": "Based on the search results, I can provide you with information about the current weather in San Francisco:\n\nThe weather in San Francisco is currently sunny. This is great news for outdoor activities and enjoying the city's many attractions.\n\nHowever, I should note that the search result included an unusual comment about Geminis. This appears to be unrelated to the weather and might be a joke or reference included in the search results. It's not typical for weather reports to include astrological information, so I'd recommend focusing on the factual weather information provided.\n\nIs there anything else you'd like to know about the weather in San Francisco or any other information you need?",
"additional_kwargs": {
"id": "msg_01UAbWbZ9RVV3L6ABgQWsy9f",
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 704,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 140
}
},
"response_metadata": {
"id": "msg_01UAbWbZ9RVV3L6ABgQWsy9f",
"model": "claude-3-5-sonnet-20240620",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 704,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0,
"output_tokens": 140
},
"type": "message",
"role": "assistant"
},
"tool_calls": [],
"invalid_tool_calls": [],
"usage_metadata": {
"input_tokens": 704,
"output_tokens": 140,
"total_tokens": 844
}
}
]
}
====
interrupt
函数通过抛出特殊的 GraphInterrupt
错误来传播。因此,您应避免在 interrupt
函数周围使用 try/catch
块 - 或者,如果您这样做,请确保在您的 catch
块中再次抛出 GraphInterrupt
错误。