with one click
langgraph-interrupts
人机交互与动态中断和断点:暂停执行以供人工审查和使用 Command 恢复
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
人机交互与动态中断和断点:暂停执行以供人工审查和使用 Command 恢复
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Mac 系统深度清理和优化工具。使用 Mole (mo 命令) 执行系统清理、磁盘分析、应用卸载、系统优化等任务。 触发场景(当用户提到以下任一内容时使用此 skill): - 清理 Mac、清理磁盘、释放空间、清理缓存、清理系统 - 卸载应用、删除应用、移除应用及其残留 - 磁盘分析、查看磁盘占用、大文件查找、空间分析 - 系统优化、系统维护、刷新系统、重建缓存 - 系统状态、系统监控、CPU/内存/磁盘监控 - 清理 node_modules、清理构建产物、清理项目依赖 - 清理安装包、删除 dmg/pkg 文件 - Mac 清理工具、类似 CleanMyMac 的功能 - "我的 Mac 太慢了"、"磁盘空间不足"、"电脑卡顿" - 即使没有明确说 "Mole",只要涉及上述场景就应使用
快速搭建和配置 pnpm monorepo 项目结构,包含 TypeScript、tsup 构建、私有 npm registry 配置。当用户需要"创建 monorepo"、"初始化 monorepo 项目"、"配置 pnpm workspace"、"设置 monorepo 构建"、"monorepo setup"时使用。特别适合需要统一管理多个包、配置构建工具、处理 TypeScript 路径问题的场景。即使用户只是说"帮我搭建项目结构"或"配置构建",如果涉及多包管理也应该使用此 skill。
智能拆分暂存区的代码变更为多个符合 Conventional Commits 规范的逻辑提交。当用户需要将大量变更按逻辑关系分组提交时使用,比如"拆分这些提交"、"把暂存区的变更分成多个 commit"、"按功能分别提交"、"split commits"等场景。特别适合处理包含多个模块、多种类型文件(配置、代码、测试、文档)的复杂变更集。
OKR 优化与质量评估专家。当用户需要:(1) 评估现有 OKR 的质量,(2) 优化模糊或不可量化的关键结果,(3) 检查 OKR 是否符合核心原则(聚焦、可量化、有挑战),(4) 将任务型 KR 转化为结果型 KR,(5) 提供具体的改进建议时使用。触发词包括"帮我优化 OKR"、"检查这个 OKR"、"这个 KR 写得好吗"、"如何量化这个目标"。
基于 git commits 自动生成 CHANGELOG.md 变更日志。支持语义化版本、分类整理、多格式输出。触发场景包括"生成变更日志"、"更新 CHANGELOG"、"版本记录"。
GitHub PR 代码审查技能。检查代码质量、安全性、性能和最佳实践,生成结构化审查报告。触发场景包括"审查 PR"、"代码检查"、"review pull request"。
| name | langgraph-interrupts |
| description | 人机交互与动态中断和断点:暂停执行以供人工审查和使用 Command 恢复 |
| language | js |
中断通过暂停图执行以等待外部输入来实现人机交互模式。LangGraph 保存状态并无限期等待,直到您恢复执行。
关键类型:
interrupt() 函数interruptBefore/interruptAfter| 类型 | 设置时机 | 使用场景 |
|---|---|---|
动态 (interrupt()) | 在节点代码内 | 基于逻辑的条件暂停 |
静态 (interruptBefore) | 在编译时 | 在特定节点之前调试/测试 |
静态 (interruptAfter) | 在编译时 | 在特定节点之后审查输出 |
import { interrupt, Command } from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph";
const reviewNode = async (state) => {
// 有条件地暂停以供审查
if (state.needsReview) {
// 暂停并向用户展示数据
const userResponse = interrupt({
action: "review",
data: state.draft,
question: "Approve this draft?",
});
// userResponse 来自 Command({ resume: ... })
if (userResponse === "reject") {
return { status: "rejected" };
}
}
return { status: "approved" };
};
const checkpointer = new MemorySaver();
const graph = new StateGraph(State)
.addNode("review", reviewNode)
.addEdge(START, "review")
.addEdge("review", END)
.compile({ checkpointer }); // 必需!
// 初始调用 - 将暂停
const config = { configurable: { thread_id: "1" } };
const result = await graph.invoke(
{ needsReview: true, draft: "content" },
config
);
// 检查中断
if ("__interrupt__" in result) {
console.log(result.__interrupt__); // 查看中断负载
}
// 使用用户决策恢复
const finalResult = await graph.invoke(
new Command({ resume: "approve" }), // 用户的响应
config
);
const checkpointer = new MemorySaver();
const graph = new StateGraph(State)
.addNode("step1", step1)
.addNode("step2", step2)
.addNode("step3", step3)
.addEdge(START, "step1")
.addEdge("step1", "step2")
.addEdge("step2", "step3")
.addEdge("step3", END)
.compile({
checkpointer,
interruptBefore: ["step2"], // 在 step2 之前暂停
interruptAfter: ["step3"], // 在 step3 之后暂停
});
const config = { configurable: { thread_id: "1" } };
// 运行到第一个断点
await graph.invoke({ data: "test" }, config);
// 恢复(在下一个断点暂停)
await graph.invoke(null, config); // null = 恢复
// 再次恢复
await graph.invoke(null, config);
import { interrupt, Command } from "@langchain/langgraph";
const toolExecutor = async (state) => {
const toolCalls = state.messages.at(-1)?.tool_calls || [];
const results = [];
for (const toolCall of toolCalls) {
// 为每个工具调用暂停
const userDecision = interrupt({
tool: toolCall.name,
args: toolCall.args,
question: "Execute this tool?",
});
let result;
if (userDecision.type === "approve") {
// 执行工具
result = await executeTool(toolCall);
} else if (userDecision.type === "edit") {
// 使用编辑后的参数
result = await executeTool(userDecision.args);
} else { // reject
result = "Tool execution rejected";
}
// 存储结果
results.push(new ToolMessage({
content: result,
tool_call_id: toolCall.id,
}));
}
return { messages: results };
};
// 使用
const result = await graph.invoke({ messages: [...] }, config);
// 审查并批准
await graph.invoke(new Command({ resume: { type: "approve" } }), config);
// 或编辑参数
await graph.invoke(
new Command({ resume: { type: "edit", args: { query: "modified" } } }),
config
);
// 或拒绝
await graph.invoke(new Command({ resume: { type: "reject" } }), config);
const config = { configurable: { thread_id: "1" } };
// 运行到中断
await graph.invoke({ data: "test" }, config);
// 在恢复之前修改状态
await graph.updateState(config, { data: "manually edited" });
// 使用编辑后的状态恢复
await graph.invoke(null, config);
const config = {
configurable: { thread_id: "1" },
streamMode: ["updates", "messages"] as const,
};
for await (const [mode, chunk] of await graph.stream({ query: "test" }, config)) {
if (mode === "updates") {
if ("__interrupt__" in chunk) {
// 处理中断
const interruptInfo = chunk.__interrupt__[0].value;
const userInput = await getUserInput(interruptInfo);
// 恢复
await graph.invoke(new Command({ resume: userInput }), config);
break;
}
}
}
✅ 在节点中的任何位置调用 interrupt()
✅ 设置编译时断点
✅ 使用 Command({ resume: ... }) 恢复
✅ 在中断期间编辑状态
✅ 在处理中断时流式传输
✅ 条件中断逻辑
❌ 在没有检查点器的情况下中断 ❌ 修改中断机制 ❌ 在没有 thread_id 的情况下恢复
// ❌ 错误 - 没有检查点器
const graph = builder.compile(); // 没有持久化!
await graph.invoke(...); // 中断不起作用
// ✅ 正确
const checkpointer = new MemorySaver();
const graph = builder.compile({ checkpointer });
// ❌ 错误 - 没有 thread_id
await graph.invoke({ data: "test" }); // 无法恢复!
// ✅ 正确
const config = { configurable: { thread_id: "session-1" } };
await graph.invoke({ data: "test" }, config);
// ❌ 错误 - 传递常规对象
await graph.invoke({ resumeData: "approve" }, config); // 重新开始!
// ✅ 正确 - 使用 Command
import { Command } from "@langchain/langgraph";
await graph.invoke(new Command({ resume: "approve" }), config);
// ❌ 错误
const result = graph.invoke({}, config);
console.log(result); // Promise!
// ✅ 正确
const result = await graph.invoke({}, config);
console.log(result);