人机协作 (Human-in-the-loop)¶
为了在代理中审查、编辑和批准工具调用,你可以使用 LangGraph 内置的人工干预(human-in-the-loop)功能,特别是interrupt()
原语。
LangGraph 允许你无限期地暂停执行——可以暂停数分钟、数小时乃至数天——直到收到人工输入为止。
这是可能的,因为代理状态被检查点化到数据库中,这允许系统持久化执行上下文,并在之后恢复工作流,从中断的地方继续执行。
要更深入地了解人工干预(human-in-the-loop)概念,请参阅概念指南。
审查工具调用¶
为工具添加人工审批步骤
- 在工具中使用
interrupt()
来暂停执行。 - 使用
Command({ resume: ... })
恢复执行,以根据人工输入继续。
import { MemorySaver } from "@langchain/langgraph-checkpoint";
import { interrupt } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { initChatModel } from "langchain/chat_models/universal";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
// An example of a sensitive tool that requires human review / approval
const bookHotel = tool(
async (input: { hotelName: string; }) => {
let hotelName = input.hotelName;
const response = interrupt( // (1)!
`Trying to call \`book_hotel\` with args {'hotel_name': ${hotelName}}. ` +
`Please approve or suggest edits.`
)
if (response.type === "accept") {
// proceed to execute the tool logic
} else if (response.type === "edit") {
hotelName = response.args["hotel_name"]
} else {
throw new Error(`Unknown response type: ${response.type}`)
}
return `Successfully booked a stay at ${hotelName}.`;
},
{
name: "bookHotel",
schema: z.object({
hotelName: z.string().describe("Hotel to book"),
}),
description: "Book a hotel.",
}
);
const checkpointer = new MemorySaver(); // (2)!
const llm = await initChatModel("anthropic:claude-3-7-sonnet-latest");
const agent = createReactAgent({
llm,
tools: [bookHotel],
checkpointer // (3)!
});
interrupt 函数
会在特定节点暂停代理图的执行。在这种情况下,我们在工具函数的开头调用interrupt()
,它会在执行该工具的节点处暂停图。interrupt()
内部的信息(例如工具调用)可以呈现给人工,并且图可以根据用户输入(工具调用批准、编辑或反馈)恢复执行。InMemorySaver
用于在工具调用循环的每一步存储代理状态。这实现了短期记忆和人工干预(human-in-the-loop)功能。在此示例中,我们使用InMemorySaver
将代理状态存储在内存中。在生产应用中,代理状态将存储在数据库中。- 使用
checkpointer
初始化代理。
使用 stream()
方法运行代理,并传递 config
对象以指定线程 ID。这使得代理能够在未来的调用中恢复相同的对话。
const config = {
configurable: {
"thread_id": "1"
}
}
for await (const chunk of await agent.stream(
{ messages: "book a stay at McKittrick hotel" },
config
)) {
console.log(chunk);
console.log("\n");
};
你应该会看到代理运行直到它到达
interrupt()
调用,此时它会暂停并等待人工输入。
使用 Command({ resume: ... })
恢复代理,以根据人工输入继续。
import { Command } from "@langchain/langgraph";
for await (const chunk of await agent.stream(
new Command({ resume: { type: "accept" } }), // (1)!
// new Command({ resume: { type: "edit", args: { "hotel_name": "McKittrick Hotel" } } }),
config
)) {
console.log(chunk);
console.log("\n");
};
interrupt 函数
与Command
对象结合使用,以人工提供的值恢复图。