Skip to content
LangChain 文档版本 跟随上游 源快照 b2e0d333 更新于 2026-10-19

人工介入总览

智能体如何在工具调用前暂停以等待人工审查,以及逐调用与批量两种中断方式的区别。

人工介入(HITL)在工具调用执行前暂停智能体,交由人工审查。审核者可以批准该调用、编辑其参数、带反馈地拒绝,或代替工具作答。适用于难以撤销或需要签核的操作,例如删除记录、发送邮件或写入文件。

使用 HumanInTheLoopMiddleware,可在 LangChain 中加入审查;也可在 Deep Agents 中用 interrupt_on 参数加入审查。中间件以两种模式之一暂停:

  • 逐调用:每个需要审查的工具调用各暂停一次。
  • 批量:每个模型轮次暂停一次,覆盖该轮次中的所有工具调用。批量模式是当前默认值。

NOTE

逐调用模式要求 langchain>=1.4.6。Deep Agents 使用批量模式。

从暂停到恢复:一次批准的完整旅程

无论哪种模式,每次批准都遵循相同的步骤:

  1. 模型提出一个或多个工具调用。
  2. 中间件将每个调用与你的 interrupt_on 策略比对。需要审查的调用会以 interrupt 暂停智能体。
  3. 检查点保存器保存智能体状态,因此运行可以停下、稍后再恢复,甚至换到另一个进程也一样。
  4. 你的应用把待处理的调用展示给审核者,并为每个调用收集一个决策。
  5. 你用 Command(resume=...) 恢复智能体。中间件依次应用每个决策,智能体继续执行。

决策共有四种类型:

决策类型描述示例使用场景
✅ approve按智能体提出的原始参数执行该工具。按所写内容原样发送邮件草稿
✏️ edit在执行前修改工具参数。在发送邮件前更改收件人
❌ reject完全跳过执行此工具调用,并向智能体返回拒绝反馈。拒绝删除文件并说明原因
💬 respond将人工的消息直接作为合成的工具结果返回,跳过执行,适用于 "ask user" 风格的工具。用直接回复来回应 "ask_user" 提示

本页的示例使用一个带有三个工具的智能体。write_file 允许所有决策类型。execute_sql 只允许 approve 与 reject。read_data 无需审查即可执行。在同一个轮次里,模型调用了这三个工具。

逐个审查工具调用

在逐调用模式下,每个需要审查的工具调用在开始时都会各自暂停:

sequenceDiagram
    participant App as Your app
    participant Agent
    participant Tools

    App->>Agent: invoke(user message)
    Agent->>Tools: write_file, execute_sql, read_data
    Tools-->>Agent: read_data result (not gated, runs right away)
    Tools-->>App: interrupt for write_file
    Tools-->>App: interrupt for execute_sql
    App->>Agent: resume {write_file ID: approve, execute_sql ID: reject}
    Note over Agent: Each answer is checked against its response_schema before it is saved
    Agent->>Tools: run write_file
    Agent-->>App: final result

每个 interrupt 值描述单个调用,并带有 type: "tool_approval"。execute_sql 的 interrupt 如下所示:

python
{
    "type": "tool_approval",
    "tool_call_id": "call_sql",
    "name": "execute_sql",
    "args": {"query": "DELETE FROM orders WHERE created_at < '2025-01-01'"},
    "description": "Tool execution requires approval\n\nTool: execute_sql\nArgs: {'query': \"DELETE FROM orders WHERE created_at < '2025-01-01'\"}",
}

每个 interrupt 还携带一个 response_schema,即该调用可接受答案的 JSON schema。对 execute_sql,schema 只允许 approve 与 reject。对 write_file,还允许 respond 与 edit。edit 分支包含 write_file 自身的参数 schema。客户端可以根据 schema 渲染表单,而不必硬编码决策类型。LangChain 指南展示了每种决策类型对应的 schema。

你逐个回答 interrupt,每个 interrupt 用一个决策作答,并以该 interrupt 的 ID 为键:

python
Command(resume={
    write_file_interrupt.id: {"type": "approve"},
    execute_sql_interrupt.id: {"type": "reject", "message": "Archive the rows before deleting them."},
})

LangGraph 会在保存前拒绝无效答案,因此审核者可以更正后重新回答。未回答的 interrupt 保持待处理,审核者可以逐个作答。

一次审查整个轮次

在批量模式下,中间件在模型响应之后、任何工具执行之前暂停一次。read_data 虽然没有被拦截,也要等待这次审查。一个 interrupt 在 action_requests 中列出所有被拦截的调用,并在单独的 review_configs 列表中列出每个调用允许的决策:

python
{
    "action_requests": [
        {"name": "write_file", "args": {"path": "orders_archive.csv", "content": "id,total\n1,20\n"}, "description": "..."},
        {"name": "execute_sql", "args": {"query": "DELETE FROM orders WHERE created_at < '2025-01-01'"}, "description": "..."},
    ],
    "review_configs": [
        {"action_name": "write_file", "allowed_decisions": ["approve", "edit", "reject", "respond"]},
        {"action_name": "execute_sql", "allowed_decisions": ["approve", "reject"]},
    ],
}

你用一个决策列表回答,顺序与 action_requests 一致:

python
Command(resume={"decisions": [
    {"type": "approve"},
    {"type": "reject", "message": "Archive the rows before deleting them."},
]})

该 interrupt 没有 response_schema。中间件只在应用每个决策时才校验它,此时 LangGraph 已保存了答案。

对比批量模式的问题

批量模式按位置将答案与调用配对,并且每个答案先保存、后校验。下表每行是示例智能体在批量模式下会遇到的一个问题,以及逐调用模式如何避免它:

在批量模式下会发生什么在逐调用模式下
决策按位置与调用配对审核者本想批准 write_file、拒绝 execute_sql,却发出了 [reject, approve]。DELETE 被执行了。每个决策以其调用的 interrupt ID 为键。
允许的决策放在 review_configs 里客户端按位置配对两个列表,并为每种决策类型硬编码一个按钮。每个 interrupt 的 response_schema 列出其决策与可编辑参数。
答案先保存后校验两个调用只收到一个决策时抛出 ValueError: Number of human decisions (1) does not match number of hanging tool calls (2). 更正后的答案仍以同样的错误失败,线程就此卡住。答案先校验。修正后重新回答即可。
编辑后的参数未经校验对 write_file 的编辑缺少 content 时工具仍会执行,模型为一次自己从未发起的调用收到错误。该编辑被拒绝,报 edit.edited_action.args.content Field required。

选择中断模式

Per-callBatched (default)
When it pausesAs each gated tool call startsAfter the model responds, before any tool runs
Interrupts per turnOne per gated tool callOne for the whole turn
Ungated calls in the turnRun right awayWait for the review
AnswerOne decision per interrupt, keyed by interrupt IDA list of decisions, matched by position
Best forNew agents, one approval card or message per call, forms built from the schemaApproval UIs that already read action_requests

新的智能体请使用逐调用模式。LangChain 计划在下一个大版本中将其设为默认。

如果你的审批 UI 读取批量模式格式(例如基于 useStream 审批模式搭建的),请暂时保留批量模式。

逐调用模式还会改变中间件排序与多次回答恢复的行为。切换前请先阅读为逐调用审查排列中间件顺序与按 ID 逐个恢复调用。

另请参阅


BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。