news 2026/8/20 14:04:33

LangChain / Advanced usage / Human-in-the-loop

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain / Advanced usage / Human-in-the-loop

原文链接:https://docs.langchain.com/oss/python/langchain/human-in-the-loop

高级用法

人机协同

复制页面

人机协同(Human-in-the-Loop,HITL)中间件让您可以为智能体的工具调用添加人工审核环节。当模型提议执行某个可能需要审核的操作时——例如写入文件或执行SQL——该中间件可以暂停执行并等待决策。

它的工作方式是针对配置的策略检查每个工具调用。如果需要人工介入,中间件会发出一个中断来暂停执行。图状态会使用LangGraph的持久化层保存,因此执行可以安全地暂停并在稍后恢复。

随后由人工决策决定下一步操作:可以按原样批准操作(approve)、在运行前修改(edit)、附带反馈拒绝(reject),或直接回复(respond)——适用于“询问用户”类型的工具。

中断决策类型

该中间件定义了四种内置的人工响应中断的方式:

决策类型描述示例用例
✅ 批准按智能体提议的原始参数执行工具。按原样发送邮件草稿
✏️ 编辑在执行前修改工具参数。发送邮件前更改收件人
❌ 拒绝完全跳过该工具调用,并向智能体返回拒绝反馈。拒绝删除文件并说明原因
💬 回复将人工消息直接作为合成工具结果返回,跳过执行,适用于“询问用户”类型的工具。用直接回复回答"ask_user"提示

每个工具可用的决策类型取决于您在interrupt_on中配置的策略。当多个工具调用同时被暂停时,每个操作都需要单独的决策。决策必须按照中断请求中操作出现的顺序提供。

当人工拒绝请求的操作时使用reject。仅当人工充当工具角色时(例如回答ask_user提示)才使用respond。不要使用respond来拒绝有副作用的工具,因为其消息会被视为成功的工具结果。

当编辑工具参数时,请保守地修改。对原始参数进行大幅修改可能导致模型重新评估其方法,并可能多次执行该工具或采取意外操作。

配置中断

要使用HITL,请在创建智能体时将中间件添加到智能体的中间件列表中。

配置时使用工具操作到允许的决策类型的映射。当工具调用匹配映射中的操作时,中间件将中断执行。

fromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportHumanInTheLoopMiddlewarefromlanggraph.checkpoint.memoryimportInMemorySaver agent=create_agent(model="gpt-5.5",tools=[write_file,execute_sql,read_data],middleware=[HumanInTheLoopMiddleware(interrupt_on={"write_file":True,# 允许所有决策(批准、编辑、拒绝、回复)"execute_sql":{"allowed_decisions":["approve","reject"]},# 不允许编辑"read_data":False,# 安全操作,无需批准},# 中断消息前缀 - 与工具名称和参数组合形成完整消息# 例如:"Tool execution pending approval: execute_sql with query='DELETE FROM...'"# 单个工具可以通过在其中断配置中指定"description"来覆盖此前缀description_prefix="Tool execution pending approval",),],# 人机协同需要检查点机制来处理中断。# 生产环境中,请使用持久化检查点器,如AsyncPostgresSaver或MongoDBSaver。checkpointer=InMemorySaver(),)

您必须配置检查点器来跨中断持久化图状态。在生产环境中,请使用持久化检查点器,如AsyncPostgresSaverMongoDBSaver。对于测试或原型开发,可使用InMemorySaver

调用智能体时,传入包含线程ID的配置,以将执行与对话线程关联。详情请参阅LangGraph中断文档。

配置选项

配置选项

interrupt_on
dict,必填
工具名称到审批配置的映射。值可以是True(使用默认配置中断)、False(自动批准),或一个InterruptOnConfig对象。

description_prefix
string,默认值:"Tool execution requires approval"
操作请求描述的前缀

InterruptOnConfig 选项:

allowed_decisions
list[string]
允许的决策列表:'approve'(批准)、'edit'(编辑)、'reject'(拒绝)或'respond'(回复)

description
string | callable
静态字符串或用于自定义描述的可调用函数

when
callable
可选谓词,接收一个ToolCallRequest对象,返回True则中断,返回False则自动批准。用于根据调用的参数进行门控中断。需要langchain>=1.3.3

条件中断

默认情况下,interrupt_on中列出的每个工具调用都会暂停以供审核。要为部分调用添加条件暂停,请为工具的InterruptOnConfig添加when谓词。该谓词接收ToolCallRequest,返回True则中断,返回False则自动批准,因此您可以基于工具参数进行门控。

条件中断需要langchain>=1.3.3

fromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportHumanInTheLoopMiddleware,ToolCallRequestfromlanggraph.checkpoint.memoryimportInMemorySaverdefwrites_outside_workspace(request:ToolCallRequest)->bool:"""暂停写入工作区目录之外路径的操作。"""path=request.tool_call["args"].get("path","")returnnotpath.startswith("/workspace/")defis_write_query(request:ToolCallRequest)->bool:"""暂停非只读SELECT的SQL操作。"""query=request.tool_call["args"].get("query","")returnnotquery.lstrip().upper().startswith("SELECT")agent=create_agent(model="gpt-5.5",tools=[write_file,execute_sql,read_data],middleware=[HumanInTheLoopMiddleware(interrupt_on={"write_file":{"allowed_decisions":["approve","edit","reject"],"when":writes_outside_workspace,},"execute_sql":{"allowed_decisions":["approve","reject"],"when":is_write_query,},},),],checkpointer=InMemorySaver(),)

when谓词返回False时,调用会直接运行而不会中断。当返回True时,或省略when时,调用会照常暂停。返回False的调用永远不会被添加到中断批次中,因此审核者只会看到需要决策的操作。

响应中断

当您调用智能体时,它会一直运行直到完成或触发中断。当工具调用匹配您在interrupt_on中配置的策略时,会触发中断。使用version="v2"时,结果是带有interrupts属性的GraphOutput,其中包含需要审核的操作。然后您可以将这些操作呈现给审核者,并在提供决策后恢复执行。

fromlanggraph.typesimportCommand# 人机协同利用LangGraph的持久化层。# 您必须提供线程ID来将执行与对话线程关联,# 以便对话可以被暂停和恢复(这是人工审核所需要的)。config={"configurable":{"thread_id":"some_id"}}# 运行图直到触发中断。result=agent.invoke({"messages":[{"role":"user","content":"Delete old records from the database",}]},config=config,version="v2",)# result是带有.value和.interrupts的GraphOutputprint(result.interrupts)# > (# > Interrupt(# > value={# > 'action_requests': [# > {# > 'name': 'execute_sql',# > 'arguments': {'query': 'DELETE FROM records WHERE created_at < NOW() - INTERVAL \'30 days\';'},# > 'description': 'Tool execution pending approval\n\nTool: execute_sql\nArgs: {...}'# > }# > ],# > 'review_configs': [# > {# > 'action_name': 'execute_sql',# > 'allowed_decisions': ['approve', 'reject']# > }# > ]# > }# > ),# > )# 使用批准决策恢复agent.invoke(Command(resume={"decisions":[{"type":"approve"}]}# 或 "reject"),config=config,# 使用相同的线程ID恢复暂停的对话version="v2",)

决策类型

✅ 批准
使用approve可按原样批准工具调用,并在不做任何更改的情况下执行它。

agent.invoke(Command(# 决策以列表形式提供,每个待审核操作对应一个决策。# 决策的顺序必须与中断请求中操作的出现顺序匹配。resume={"decisions":[{"type":"approve",}]}),config=config,# 使用相同的线程ID恢复暂停的对话version="v2",)

✏️ 编辑
使用edit在执行前修改工具调用。提供编辑后的操作,包含新的工具名称和参数。

agent.invoke(Command(# 决策以列表形式提供,每个待审核操作对应一个决策。# 决策的顺序必须与中断请求中操作的出现顺序匹配。resume={"decisions":[{"type":"edit",# 编辑后的操作,包含工具名称和参数"edited_action":{# 要调用的工具名称。# 通常与原始操作相同。"name":"new_tool_name",# 传递给工具的参数。"args":{"key1":"new_value","key2":"original_value"},}}]}),config=config,# 使用相同的线程ID恢复暂停的对话version="v2",)

当编辑工具参数时,请保守地修改。对原始参数进行大幅修改可能导致模型重新评估其方法,并可能多次执行该工具或采取意外操作。

❌ 拒绝

使用reject来拒绝工具调用并提供反馈,而不是执行它。该工具不会被执行。

agent.invoke(Command(# 决策以列表形式提供,每个待审核操作对应一个决策。# 决策的顺序必须与中断请求中操作的出现顺序匹配。resume={"decisions":[{"type":"reject",# 可选:解释操作被拒绝的原因# 以及智能体是否应重试不同的方法。"message":"User rejected this action. Do not retry this tool call.",}]}),config=config,# 使用相同的线程ID恢复暂停的对话version="v2",)

该消息会作为反馈添加到对话中,帮助智能体理解操作被拒绝的原因以及它应该采取什么替代方案。当您省略message时,中间件会使用默认的拒绝消息,告诉模型该工具未被执行,并且除非用户要求,否则不要重试相同的工具调用。对于有副作用的工具,请提供领域特定的消息,明确说明智能体应该放弃该操作、提出后续问题,还是尝试更安全的替代方案。

💬 回复

使用respond适用于“询问用户”类型的工具,这类工具的实际实现就是人工的回复。消息内容会直接作为工具结果返回;工具本身不会被执行。

agent.invoke(Command(# 决策以列表形式提供,每个待审核操作对应一个决策。# 决策的顺序必须与中断请求中操作的出现顺序匹配。resume={"decisions":[{"type":"respond",# 人工的回复,直接作为工具结果返回"message":"Blue.",}]}),config=config,# 使用相同的线程ID恢复暂停的对话version="v2",)

该消息会作为成功的ToolMessage返回给智能体。当工具故意作为人工输入的占位符时,请使用respond,例如用于提示澄清的ask_user工具。不要使用respond来拒绝提议的操作,因为它会告诉模型该工具已成功完成。

多个决策

当多个操作正在审核中时,为每个操作按它们在中断中出现的顺序提供决策:

{"decisions":[{"type":"approve"},{"type":"edit","edited_action":{"name":"tool_name","args":{"param":"new_value"}}},{"type":"reject","message":"This action is not allowed"}]}

人机协同流式传输

您可以在智能体运行和处理中断时使用stream_events()流式传输实时更新。使用stream.messages流式传输LLM令牌,使用stream.values检查智能体状态快照以获取中断信息。

fromlanggraph.typesimportCommand config={"configurable":{"thread_id":"some_id"}}# 流式传输智能体进度和LLM令牌直到中断stream=agent.stream_events({"messages":[{"role":"user","content":"Delete old records from the database"}]},config=config,version="v3",)formessageinstream.messages:fortokeninmessage.text:print(token,end="",flush=True)# 检查运行是否暂停等待人工输入ifstream.interrupted:print(f"\n\nInterrupt:{stream.interrupts}")# 人工决策后恢复并流式传输stream=agent.stream_events(Command(resume={"decisions":[{"type":"approve"}]}),config=config,version="v3",)formessageinstream.messages:fortokeninmessage.text:print(token,end="",flush=True)

有关流模式详情,请参阅流式传输指南。

执行生命周期

中间件定义了一个after_model钩子,在模型生成响应之后、任何工具调用执行之前运行:

  1. 智能体调用模型生成响应。
  2. 中间件检查响应中的工具调用。
  3. 如果有任何调用需要人工输入,中间件构建包含action_requestsreview_configsHITLRequest并调用interrupt
  4. 智能体等待人工决策。
  5. 根据HITLResponse决策,中间件执行批准或编辑的调用,为拒绝的调用合成ToolMessage,对respond决策直接将人工回复作为ToolMessage返回,并恢复执行。

自定义HITL逻辑

对于更专业的工作流,您可以直接使用interrupt原语和中间件抽象构建自定义HITL逻辑。

请查看上述执行生命周期以了解如何将中断集成到智能体的操作中。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/20 13:59:17

ClickShow:让鼠标点击“看得见”的Windows鼠标点击特效工具

ClickShow&#xff1a;让鼠标点击“看得见”的Windows鼠标点击特效工具 【免费下载链接】ClickShow 鼠标点击特效 项目地址: https://gitcode.com/gh_mirrors/cl/ClickShow 无论是录制操作教程、在线授课&#xff0c;还是在会议演示中讲解软件流程&#xff0c;观众最常问…

作者头像 李华
网站建设 2026/8/20 13:57:13

赛车电子元器件供应链:从车规级标准到赛道实战的可靠性保障

1. 从一场胜利看电子元器件在赛车运动中的隐形战场最近看到董荷斌在亚洲勒芒系列赛又拿下一个冠军的消息&#xff0c;作为一位长期关注赛车运动&#xff0c;同时也对背后技术支撑感兴趣的从业者&#xff0c;我感触颇深。这不仅仅是一个车手或一个车队的胜利&#xff0c;更是整个…

作者头像 李华
网站建设 2026/8/20 13:55:18

AI Agent开发成本优化:从Token消耗原理到工程实践

1. 先搞清楚“AI Agent烧掉100倍Token”到底在说什么 如果你最近在关注AI应用开发&#xff0c;特别是基于大语言模型的智能体&#xff08;Agent&#xff09;&#xff0c;可能已经听过一个说法&#xff1a;一个AI Agent单次执行消耗的Token数量&#xff0c;可能是普通聊天对话的…

作者头像 李华
网站建设 2026/8/20 13:55:00

智驾车载以太网(14)-switch交换机之port

从本文档开始后续都开始介绍switch交换机的各个模块特性,本文主要介绍一下switch的port口。 switch的port是什么? 我们知道以太网和can,lin这些总线形式的通讯方式是不同的,以太网通讯的2个设备之间是点对点的连接方式,如果想要实现一对多的通讯就需要一个总线设备。swi…

作者头像 李华