中断允许您在特定点暂停图执行并等待外部输入后再继续。这实现了人机协作模式,在此模式下您需要外部输入才能继续。当触发中断时,LangGraph 使用其 持久化 层保存图状态,并无限期等待直到您恢复执行。
中断通过调用 interrupt() 函数来实现,您可以在图的任意节点中调用它。该函数接受任何可序列化为 JSON 的值,并将其暴露给调用者。当您准备继续时,使用 Command重新调用图来恢复执行,这将成为 interrupt() 从节点内部调用的返回值。
与静态断点(在特定节点之前或之后暂停)不同,中断是 **动态的**:它们可以放置在代码的任何位置,并且可以根据应用逻辑有条件地触发。
- 检查点保存您的位置: 检查点写入器会精确保存图的状态,以便您稍后可以恢复,即使处于错误状态。
- - **
thread_id是指针:** 使用{ configurable: { thread_id: ... } }作为invoke方法的选项来指定检查点要加载的状态。 - - **中断负载作为
__interrupt__:** 传递给interrupt()的值通过__interrupt__字段返回给调用者,以便您知道图正在等待什么。
您选择的 thread_id 实际上就是您的持久游标。重用它会从同一检查点恢复;使用新值会启动一个具有空状态的新线程。
使用以下方式暂停 interrupt
interrupt 函数会暂停图执行并向调用者返回值。当您调用 interrupt 时,LangGraph 会保存当前图状态并等待您恢复执行并提供输入。
要使用 interrupt,您需要: 1. A **检查点** 持久保存图状态(生产环境中使用持久化检查点) 2. A **线程 ID** 在配置中,以便运行时知道从哪个状态恢复 3. 在您想暂停的位置调用 interrupt() (负载必须可序列化为 JSON)
async function approvalNode(state: State) {
// Pause and ask for approval
const approved = interrupt("Do you approve this action?");
// Command({ resume: ... }) provides the value returned into this variable
return { approved };
}
当您调用 interrupt, 发生的事情如下:
- **图执行被挂起** 在恰好调用
interrupt的位置 - **状态被保存** 使用检查点器,以便稍后可以恢复执行,在生产环境中,这应该是一个持久化检查点器(例如,由数据库支持)
- **值被返回** 给调用方,
__interrupt__;它可以是任何 JSON 可序列化值(字符串、对象、数组等)
- **图无限期等待** 直到你使用响应恢复执行
- **响应在恢复时被传递回** 节点,成为
interrupt()调用的返回值
恢复会中断
在中断暂停执行后,你可以通过使用包含恢复值的 Command 再次调用图来恢复它。恢复值被传递回 interrupt 调用,使节点能够使用外部输入继续执行。
// Initial run - hits the interrupt and pauses
// thread_id is the durable pointer back to the saved checkpoint
const config = { configurable: { thread_id: "thread-1" } };
const result = await graph.invoke({ input: "data" }, config);
// Check what was interrupted
// __interrupt__ mirrors every payload you passed to interrupt()
console.log(result.__interrupt__);
// [{ value: 'Do you approve this action?', ... }]
// Resume with the human's response
// Command({ resume }) returns that value from interrupt() in the node
await graph.invoke(new Command({ resume: true }), config);
关于恢复的关键要点:
- - 你必须使用 **相同的线程 ID** 恢复时必须使用中断发生时所使用的
- - 传递给
new Command({ resume: ... })的值将成为interrupt调用的返回值 - - 节点从
interrupt被调用的节点开始处重新启动,因此interrupt之前的任何代码都会再次运行 - - 你可以传递任何可 JSON 序列化的值作为恢复值
常见模式
中断解锁的关键在于能够暂停执行并等待外部输入。这对于多种用例非常有用,包括:
- - 审批工作流:在执行关键操作(API 调用、数据库更改、金融交易)之前暂停
- - 处理多个中断:在单次调用中恢复多个中断时,将中断 ID 与恢复值配对
- - 审查和编辑:让人类在继续之前审查和修改 LLM 输出或工具调用
- - 中断工具调用:在执行工具调用之前暂停,以审查和编辑工具调用
- - 验证人工输入:在继续下一步之前暂停以验证人工输入
使用人在回路 (HITL) 中断进行流式传输
在构建具有人在回路工作流的交互式代理时,你可以使用 事件流式传输 在处理中断的同时并发消费消息块和状态快照。
在循环中使用 graph.stream_events(..., version="v3") 返回的类型化投影,直到运行完成: - 通过 stream.messages - 按 token 流式传输 AI 响应 stream.values - 通过 stream.interrupted 观察每步状态快照 stream.interrupts - 通过 stream_events 检测中断 Command(resume=...) 并从 stream.interrupted 读取其负载
- - **
stream.messages**: 聊天模型输出为内容块;迭代message.text用于令牌增量。对于嵌套子图,从...读取消息块stream.subgraphs[*].messages. - - **
stream.values**: 每步后的完整状态快照 - - **
stream.interrupted/stream.interrupts**: 每次运行后,检查图是否暂停;从...读取负载stream.interrupts - - **
Command(resume=...)**: 传递为下一个streamEvents输入以恢复;循环直到运行完成而不中断
处理多个中断
当并行分支同时中断时(例如,扇出到多个节点,每个节点调用 interrupt()),您可能需要在一次调用中恢复多个中断。 使用单次调用恢复多个中断时,请将每个中断ID映射到其恢复值。 这确保了每个响应在运行时与正确的中断配对。
Annotation,
Command,
END,
INTERRUPT,
MemorySaver,
START,
StateGraph,
interrupt,
isInterrupted,
} from "@langchain/langgraph";
const State = Annotation.Root({
vals: Annotation<string[]>({
reducer: (left, right) =>
left.concat(Array.isArray(right) ? right : [right]),
default: () => [],
}),
});
function nodeA(_state: typeof State.State) {
const answer = interrupt("question_a") as string;
return { vals: [`a:${answer}`] };
}
function nodeB(_state: typeof State.State) {
const answer = interrupt("question_b") as string;
return { vals: [`b:${answer}`] };
}
const graph = new StateGraph(State)
.addNode("a", nodeA)
.addNode("b", nodeB)
.addEdge(START, "a")
.addEdge(START, "b")
.addEdge("a", END)
.addEdge("b", END)
.compile({ checkpointer: new MemorySaver() });
const config = { configurable: { thread_id: "1" } };
async function main() {
// Step 1: invoke - both parallel nodes hit interrupt() and pause
const interruptedResult = await graph.invoke({ vals: [] }, config);
console.log(interruptedResult);
/*
{
vals: [],
__interrupt__: [
{ id: '...', value: 'question_a' },
{ id: '...', value: 'question_b' }
]
}
*/
// Step 2: resume all pending interrupts at once
const resumeMap: Record<string, string> = {};
if (isInterrupted(interruptedResult)) {
for (const i of interruptedResult[INTERRUPT]) {
if (i.id != null) {
resumeMap[i.id] = `answer for ${i.value}`;
}
}
}
const result = await graph.invoke(new Command({ resume: resumeMap }), config);
console.log("Final state:", result);
//> Final state: { vals: ['a:answer for question_a', 'b:answer for question_b'] }
}
main().catch(console.error);
批准或拒绝
中断最常见的用途之一是在关键操作之前暂停并请求批准。例如,您可能希望让人类批准API调用、数据库更改或任何其他重要决策。
const approvalNode: typeof State.Node = (state) => {
// Pause execution; payload surfaces in result.__interrupt__
const isApproved = interrupt({
question: "Do you want to proceed?",
details: state.actionDetails
});
// Route based on the response
if (isApproved) {
return new Command({ goto: "proceed" }); // Runs after the resume payload is provided
} else {
return new Command({ goto: "cancel" });
}
}
当您恢复图时,传递 true 以批准,或 false 以拒绝:
// To approve
await graph.invoke(new Command({ resume: true }), config);
// To reject
await graph.invoke(new Command({ resume: false }), config);
Full example
Command,
MemorySaver,
START,
END,
StateGraph,
StateSchema,
interrupt,
} from "@langchain/langgraph";
const State = new StateSchema({
actionDetails: z.string(),
status: z.enum(["pending", "approved", "rejected"]).nullable(),
});
const graphBuilder = new StateGraph(State)
.addNode("approval", async (state) => {
// Expose details so the caller can render them in a UI
const decision = interrupt({
question: "Approve this action?",
details: state.actionDetails,
});
return new Command({ goto: decision ? "proceed" : "cancel" });
}, { ends: ['proceed', 'cancel'] })
.addNode("proceed", () => ({ status: "approved" }))
.addNode("cancel", () => ({ status: "rejected" }))
.addEdge(START, "approval")
.addEdge("proceed", END)
.addEdge("cancel", END);
// Use a more durable checkpointer in production
const checkpointer = new MemorySaver();
const graph = graphBuilder.compile({ checkpointer });
const config = { configurable: { thread_id: "approval-123" } };
const initial = await graph.invoke(
{ actionDetails: "Transfer $500", status: "pending" },
config,
);
console.log(initial.__interrupt__);
// [{ value: { question: ..., details: ... } }]
// Resume with the decision; true routes to proceed, false to cancel
const resumed = await graph.invoke(new Command({ resume: true }), config);
console.log(resumed.status); // -> "approved"
审阅和编辑状态
有时您希望让人类在继续之前审阅和编辑图状态的部分内容。这对于纠正LLM、添加缺失信息或进行调整非常有用。
const reviewNode: typeof State.Node = (state) => {
// Pause and show the current content for review (surfaces in result.__interrupt__)
const editedContent = interrupt({
instruction: "Review and edit this content",
content: state.generatedText
});
// Update the state with the edited version
return { generatedText: editedContent };
}
恢复时,提供编辑的内容:
await graph.invoke(
new Command({ resume: "The edited and improved text" }), // Value becomes the return from interrupt()
config
);
Full example
Command,
MemorySaver,
START,
END,
StateGraph,
StateSchema,
interrupt,
} from "@langchain/langgraph";
const State = new StateSchema({
generatedText: z.string(),
});
const builder = new StateGraph(State)
.addNode("review", async (state) => {
// Ask a reviewer to edit the generated content
const updated = interrupt({
instruction: "Review and edit this content",
content: state.generatedText,
});
return { generatedText: updated };
})
.addEdge(START, "review")
.addEdge("review", END);
const checkpointer = new MemorySaver();
const graph = builder.compile({ checkpointer });
const config = { configurable: { thread_id: "review-42" } };
const initial = await graph.invoke({ generatedText: "Initial draft" }, config);
console.log(initial.__interrupt__);
// [{ value: { instruction: ..., content: ... } }]
// Resume with the edited text from the reviewer
const finalState = await graph.invoke(
new Command({ resume: "Improved draft after review" }),
config,
);
console.log(finalState.generatedText); // -> "Improved draft after review"
工具中的中断
您也可以直接在工具函数中放置中断。这使得工具本身在每次被调用时暂停以等待批准,并允许在执行前对工具调用进行人工审阅和编辑。
首先,定义一个使用interrupt:
const sendEmailTool = tool(
async ({ to, subject, body }) => {
// Pause before sending; payload surfaces in result.__interrupt__
const response = interrupt({
action: "send_email",
to,
subject,
body,
message: "Approve sending this email?",
});
if (response?.action === "approve") {
// Resume value can override inputs before executing
const finalTo = response.to ?? to;
const finalSubject = response.subject ?? subject;
const finalBody = response.body ?? body;
return `Email sent to ${finalTo} with subject '${finalSubject}'`;
}
return "Email cancelled by user";
},
{
name: "send_email",
description: "Send an email to a recipient",
schema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
},
);
当您希望审批逻辑与工具本身绑定,使其可在图的不同部分重复使用时,这种方法特别有用。LLM可以自然地调用工具,而中断会在工具被触发时暂停执行,让您可以批准、编辑或取消该操作。
Full example
Command,
MemorySaver,
START,
END,
StateGraph,
StateSchema,
MessagesValue,
GraphNode,
interrupt,
} from "@langchain/langgraph";
const sendEmailTool = tool(
async ({ to, subject, body }) => {
// Pause before sending; payload surfaces in result.__interrupt__
const response = interrupt({
action: "send_email",
to,
subject,
body,
message: "Approve sending this email?",
});
if (response?.action === "approve") {
const finalTo = response.to ?? to;
const finalSubject = response.subject ?? subject;
const finalBody = response.body ?? body;
console.log("[sendEmailTool]", finalTo, finalSubject, finalBody);
return `Email sent to ${finalTo}`;
}
return "Email cancelled by user";
},
{
name: "send_email",
description: "Send an email to a recipient",
schema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
},
);
const model = new ChatAnthropic({ model: "claude-sonnet-4-6" }).bindTools([sendEmailTool]);
const State = new StateSchema({
messages: MessagesValue,
});
const agent: typeof State.Node = async (state) => {
// LLM may decide to call the tool; interrupt pauses before sending
const response = await model.invoke(state.messages);
return { messages: [response] };
};
const graphBuilder = new StateGraph(State)
.addNode("agent", agent)
.addEdge(START, "agent")
.addEdge("agent", END);
const checkpointer = new MemorySaver();
const graph = graphBuilder.compile({ checkpointer });
const config = { configurable: { thread_id: "email-workflow" } };
const initial = await graph.invoke(
{
messages: [
{ role: "user", content: "Send an email to alice@example.com about the meeting" },
],
},
config,
);
console.log(initial.__interrupt__); // -> [{ value: { action: 'send_email', ... } }]
// Resume with approval and optionally edited arguments
const resumed = await graph.invoke(
new Command({
resume: { action: "approve", subject: "Updated subject" },
}),
config,
);
console.log(resumed.messages.at(-1)); // -> Tool result returned by send_email
验证人工输入
有时您需要验证人工输入的值,若无效则重新提示。建议的方法是调用 interrupt() **每节点调用一次**,从节点返回并将错误信息存储在状态中,然后使用 **条件边** 循环回到节点,直到提供有效值。
正确的模式:
- 将重新提示的问题存储在状态中(例如
pendingQuestion). - 在节点中,调用
interrupt()**恰好一次**,传递状态中的当前问题。 - 如果答案无效,返回更新后的
pendingQuestion以便下次调用时重新提示。 - 使用
addConditionalEdges路由回节点,直到收集到有效值。
每次恢复调用 getAgeNode 恰好一次,运行 interrupt() 调用一次,然后退出。当答案无效时,条件边会循环返回,下次中断会用更新后的问题重新提示。
Full example
Command,
MemorySaver,
START,
END,
StateGraph,
StateSchema,
interrupt,
} from "@langchain/langgraph";
const State = new StateSchema({
age: z.number().nullable(),
pendingQuestion: z.string().nullable(),
});
const builder = new StateGraph(State)
.addNode("collectAge", (state) => {
const question = state.pendingQuestion ?? "What is your age?";
const answer = interrupt(question); // called exactly once per invocation
if (typeof answer === "number" && answer > 0) {
return { age: answer, pendingQuestion: null };
}
return { pendingQuestion: `'${answer}' is not a valid age. Please enter a positive number.` };
})
.addEdge(START, "collectAge")
.addConditionalEdges("collectAge", (state) =>
state.age !== null ? END : "collectAge"
);
const checkpointer = new MemorySaver();
const graph = builder.compile({ checkpointer });
const config = { configurable: { thread_id: "form-1" } };
const first = await graph.invoke({ age: null, pendingQuestion: null }, config);
console.log(first.__interrupt__); // -> [{ value: "What is your age?", ... }]
// Provide invalid data; the node re-prompts via the conditional edge
const retry = await graph.invoke(new Command({ resume: "thirty" }), config);
console.log(retry.__interrupt__); // -> [{ value: "'thirty' is not a valid age...", ... }]
// Provide valid data; route returns END and the graph finishes
const final = await graph.invoke(new Command({ resume: 30 }), config);
console.log(final.age); // -> 30
中断规则
当你在节点内调用 interrupt 时,LangGraph 通过抛出一个特殊异常来暂停执行,该异常向运行时发出暂停信号。这个异常会向上传播通过调用栈,被运行时捕获,运行时通知图保存当前状态并等待外部输入。
当执行恢复时(在你提供请求的输入之后),运行时从头重新启动整个节点——它不会从调用 interrupt 的确切行继续。这意味着在 interrupt 之前运行的任何代码都会再次执行。正因为如此,在使用中断时需要遵循一些重要规则以确保它们按预期行为。
不要包装 interrupt calls in try/catch
interrupt 通过抛出一个特殊异常来在调用点暂停执行。如果你用 try-catch 包装 interrupt call in a try/catch block, you will catch this exception and the interrupt will not be passed back to the graph.
- * ✅ 将
interrupt调用与易出错的代码分开 - * ✅ 必要时可以有条件地捕获错误
const nodeA: GraphNode<typeof State> = async (state) => {
// ✅ Good: interrupting first, then handling error conditions separately
const name = interrupt("What's your name?");
try {
await fetchData(); // This can fail
} catch (err) {
console.error(error);
}
return state;
}
const nodeA: GraphNode<typeof State> = async (state) => {
// ✅ Good: re-throwing the exception will
// allow the interrupt to be passed back to
// the graph
try {
const name = interrupt("What's your name?");
await fetchData(); // This can fail
} catch (err) {
if (error instanceof NetworkError) {
console.error(error);
}
throw error;
}
return state;
}
- * 🔴 不要包装
interruptcalls in bare try/catch blocks
async function nodeA(state: State) {
// ❌ Bad: wrapping interrupt in bare try/catch will catch the interrupt exception
try {
const name = interrupt("What's your name?");
} catch (err) {
console.error(error);
}
return state;
}
不要重新排序 interrupt 节点内的调用
在单个节点中使用多个中断很常见,但如果处理不当可能会导致意外行为。
当节点包含多个 interrupt 调用时,LangGraph 会为执行该节点的任务维护一个特定的恢复值列表。每当执行恢复时,它会从节点的开头开始。对于遇到的每个 interrupt,LangGraph 会检查任务的恢复列表中是否存在匹配的值。匹配是 **严格基于索引的**,因此节点内 interrupt 调用的顺序很重要。
- * ✅ 保持
interrupt调用在节点执行过程中保持一致
async function nodeA(state: State) {
// ✅ Good: interrupt calls happen in the same order every time
const name = interrupt("What's your name?");
const age = interrupt("What's your age?");
const city = interrupt("What's your city?");
return {
name,
age,
city
};
}
- * 🔴 不要有条件地跳过
interrupt在节点内的调用 - * 🔴 不要循环
interrupt使用在执行过程中不确定的逻辑进行调用,包括while True验证循环。请改用条件边(请参阅 验证人工输入)
const nodeA: GraphNode<typeof State> = async (state) => {
// ❌ Bad: conditionally skipping interrupts changes the order
const name = interrupt("What's your name?");
// On first run, this might skip the interrupt
// On resume, it might not skip it - causing index mismatch
if (state.needsAge) {
const age = interrupt("What's your age?");
}
const city = interrupt("What's your city?");
return { name, city };
}
const nodeA: GraphNode<typeof State> = async (state) => {
// ❌ Bad: looping based on non-deterministic data
// The number of interrupts changes between executions
const results = [];
for (const item of state.dynamicList || []) { // List might change between runs
const result = interrupt(`Approve ${item}?`);
results.push(result);
}
return { results };
}
不要在 interrupt 调用中返回复杂值
根据所使用的检查点不同,复杂值可能无法序列化(例如,您无法序列化函数)。为了使图能够适应任何部署环境,最佳实践是仅使用可以合理序列化的值。
- * ✅ 传递简单的、JSON 可序列化的类型到
interrupt - * ✅ Pass dictionaries/objects with simple values
const nodeA: GraphNode<typeof State> = async (state) => {
// ✅ Good: passing simple types that are serializable
const name = interrupt("What's your name?");
const count = interrupt(42);
const approved = interrupt(true);
return { name, count, approved };
}
const nodeA: GraphNode<typeof State> = async (state) => {
// ✅ Good: passing objects with simple values
const response = interrupt({
question: "Enter user details",
fields: ["name", "email", "age"],
currentValues: state.user || {}
});
return { user: response };
}
- * 🔴 不要将函数、类实例或其他复杂对象传递给
interrupt
function validateInput(value: string): boolean {
return value.length > 0;
}
const nodeA: GraphNode<typeof State> = async (state) => {
// ❌ Bad: passing a function to interrupt
// The function cannot be serialized
const response = interrupt({
question: "What's your name?",
validator: validateInput // This will fail
});
return { name: response };
}
class DataProcessor {
constructor(private config: any) {}
}
const nodeA: GraphNode<typeof State> = async (state) => {
const processor = new DataProcessor({ mode: "strict" });
// ❌ Bad: passing a class instance to interrupt
// The instance cannot be serialized
const response = interrupt({
question: "Enter data to process",
processor: processor // This will fail
});
return { result: response };
}
在 interrupt 之前调用的副作用必须是幂等的
由于 interrupt 的工作方式是通过重新运行调用它们的节点,因此在 interrupt 之前调用的副作用(理想情况下)应该是幂等的。从上下文来看,幂等性意味着同一操作可以多次应用,而不会改变初次执行之后的结果。
例如,您可能在节点内部有一个 API 调用来更新记录。如果 interrupt 在该调用之后被调用,那么当节点恢复时它会被重新运行多次,可能会覆盖初始更新或创建重复记录。
const nodeA: GraphNode<typeof State> = async (state) => {
// ✅ Good: using upsert operation which is idempotent
// Running this multiple times will have the same result
await db.upsertUser({
userId: state.userId,
status: "pending_approval"
});
const approved = interrupt("Approve this change?");
return { approved };
}
const nodeA: GraphNode<typeof State> = async (state) => {
// ✅ Good: placing side effect after the interrupt
// This ensures it only runs once after approval is received
const approved = interrupt("Approve this change?");
if (approved) {
await db.createAuditLog({
userId: state.userId,
action: "approved"
});
}
return { approved };
}
const approvalNode: GraphNode<typeof State> = async (state) => {
// ✅ Good: only handling the interrupt in this node
const approved = interrupt("Approve this change?");
return { approved };
}
const notificationNode: GraphNode<typeof State> = async (state) => {
// ✅ Good: side effect happens in a separate node
// This runs after approval, so it only executes once
if (state.approved) {
await sendNotification({
userId: state.userId,
status: "approved",
});
}
return state;
}
- * 🔴 不要在
interrupt - * 🔴 不要在创建新记录前不检查其是否存在
const nodeA: GraphNode<typeof State> = async (state) => {
// ❌ Bad: creating a new record before interrupt
// This will create duplicate records on each resume
const auditId = await db.createAuditLog({
userId: state.userId,
action: "pending_approval",
timestamp: new Date()
});
const approved = interrupt("Approve this change?");
return { approved, auditId };
}
const nodeA: GraphNode<typeof State> = async (state) => {
// ❌ Bad: appending to an array before interrupt
// This will add duplicate entries on each resume
await db.appendToHistory(state.userId, "approval_requested");
const approved = interrupt("Approve this change?");
return { approved };
}
将 interrupt 与作为函数调用的子图结合使用
在节点内调用子图时,父图将从 **节点的开头** (子图被调用的位置)恢复执行,并且 interrupt 被触发时也会发生类似情况 **子图** 也将从节点的开头恢复执行interrupt被调用的位置)
async function nodeInParentGraph(state: State) {
someCode(); // <-- This will re-execute when resumed
// Invoke a subgraph as a function.
// The subgraph contains an `interrupt` call.
const subgraphResult = await subgraph.invoke(someInput);
// ...
}
async function nodeInSubgraph(state: State) {
someOtherCode(); // <-- This will also re-execute when resumed
const result = interrupt("What's your name?");
// ...
}
使用 interrupt 进行调试
要调试和测试图,您可以使用静态 interrupt 作为断点来逐步执行图,每次一个节点。静态 interrupt 在定义的点触发,要么在节点执行之前,要么在节点执行之后。您可以通过指定 interruptBefore 和 interruptAfter 编译图时。
At compile time
const graph = builder.compile({
interruptBefore: ["node_a"], // [!code highlight]
interruptAfter: ["node_b", "node_c"], // [!code highlight]
checkpointer,
});
// Pass a thread ID to the graph
const config = {
configurable: {
thread_id: "some_thread"
}
};
// Run the graph until the breakpoint
await graph.invoke(inputs, config);# [!code highlight]
await graph.invoke(null, config); # [!code highlight]
- 断点在
compiletime. interruptBefore指定执行应在节点执行之前暂停的节点。interruptAfter指定执行应在节点执行之后暂停的节点。- 需要检查点器来启用断点。
- 图运行直到命中第一个断点。
- 通过传入
null作为输入来恢复图。这将运行图直到命中下一个断点。
At run time
// Run the graph until the breakpoint
graph.invoke(inputs, {
interruptBefore: ["node_a"], // [!code highlight]
interruptAfter: ["node_b", "node_c"], // [!code highlight]
configurable: {
thread_id: "some_thread"
}
});
// Resume the graph
await graph.invoke(null, config); // [!code highlight]
graph.invoke使用interruptBefore和interruptAfter参数调用。这是一种运行时配置,可以在每次调用时更改。interruptBefore指定执行应在节点执行之前暂停的节点。interruptAfter指定执行应在节点执行之后暂停的节点。- 图运行直到命中第一个断点。
- 通过传入
null作为输入来恢复图。这将运行图直到命中下一个断点。
使用 LangSmith Studio
您可以使用 LangSmith Studio 在 UI 中运行图之前设置静态中断。您还可以使用 UI 检查执行过程中任何点的图状态。
!图片