Skip to content

Human-in-the-loop

HITL 在模型请求敏感工具时暂停 LangGraph 执行,把待执行 action 和允许的决定交给人工界面。恢复 时必须使用同一个 thread config,checkpointer 才能找到暂停前状态。

执行时序

前端类比

它类似可恢复的服务端 transaction approval:前端不是简单弹出 confirm 后继续本地函数,而是把 决定提交给持久化工作流,由服务端从 checkpoint 恢复。

Deep Agents 原生语义interrupt_on/interruptOn 会配置 HumanInTheLoopMiddleware。 Middleware 在普通 tool call 执行前产生 LangGraph interrupt;Command(resume=...) 携带与 action 顺序对应的 decisions 恢复图。

配置审批策略

python
import os

from deepagents import create_deep_agent
from langgraph.checkpoint.memory import MemorySaver

agent = create_deep_agent(
    model=os.environ["DEEPAGENTS_MODEL"],
    checkpointer=MemorySaver(),
    interrupt_on={
        "read_file": False,
        "write_file": {"allowed_decisions": ["approve", "reject"]},
        "delete": {"allowed_decisions": ["approve", "edit", "reject"]},
    },
)
ts
import { createDeepAgent } from 'deepagents'
import { MemorySaver } from '@langchain/langgraph'

const model = process.env.DEEPAGENTS_MODEL
if (!model) throw new Error('DEEPAGENTS_MODEL is required')

const agent = createDeepAgent({
  model,
  checkpointer: new MemorySaver(),
  interruptOn: {
    read_file: false,
    write_file: { allowedDecisions: ['approve', 'reject'] },
  },
})

内存 checkpointer 只适合教程。生产环境需要持久、并发安全并满足数据保留要求的实现。

读取 Interrupt 并恢复

Python v2 invoke 返回带 interruptsvalue 的结果:

python
from uuid import uuid4

from langgraph.types import Command

config = {"configurable": {"thread_id": str(uuid4())}}

result = agent.invoke(
    {"messages": [{"role": "user", "content": "删除临时草稿"}]},
    config=config,
    version="v2",
)

if result.interrupts:
    request = result.interrupts[0].value
    print(request["action_requests"])
    print(request["review_configs"])

    result = agent.invoke(
        Command(
            resume={
                "decisions": [
                    {
                        "type": "reject",
                        "message": "无法确认文件归属,请不要重试删除。",
                    }
                ]
            }
        ),
        config=config,
        version="v2",
    )

决定数量和顺序必须与 action_requests 对齐。edit 需要提供完整 edited_action/ editedAction,审批端必须再次验证修改后的工具名和参数。

TypeScript 使用 new Command({ resume: { decisions } }),并从 invoke 结果的 interrupt 数据读取待 审批 action。字段采用 camelCase,不能直接复制 Python snake_case。

三种决定

决定语义UI 必须展示
approve按原参数执行工具名、完整参数、目标资源、影响
edit用审批人修改后的 action 执行原值与新值 diff、重新校验结果
reject跳过工具并把原因反馈给模型拒绝原因、是否禁止模型重试

Reject message 应明确后续行为。例如“不要重试删除,请改为只列出候选文件”,比“拒绝”更能避免 模型换一种参数重复请求。

Subagent 与 HITL

自定义 subagent 可以配置自己的 interrupt_on/interruptOn,并覆盖继承设置。审批界面必须 展示 action 来源和 subagent 名称,不能把所有请求都标成主 Agent。

Interpreter 内的 PTC 和动态 task 不一定经过普通 tool-call 路径。需要逐次审批的能力不应只靠 interpreter bridge 暴露;应把不可绕过规则放在工具、sandbox 或上层 LangGraph 节点。

HITL 不是安全隔离

  • 审批人可能误判命令或参数。
  • 已批准工具仍可能访问超出预期的数据。
  • Checkpointer 保存状态,不限制工具权限。
  • Prompt injection 可以诱导 Agent 构造看似合理的请求。

HITL 应叠加在最小工具权限、sandbox、认证、审计与幂等设计之上。

先修

下一步

官方参考

学习文档整合站点