深入学 LangChain 官方文档(十五)Human-in-the-loop 与 Guardrails

发布时间:2026/7/21 21:42:06
深入学 LangChain 官方文档(十五)Human-in-the-loop 与 Guardrails 深入学 LangChain 官方文档十五Human-in-the-loop 与 Guardrails本篇对应的官方文档Human-in-the-loop支撑HumanInTheLoopMiddleware、interrupt_on、四类审批决定和暂停恢复生命周期。Guardrails支撑确定性与模型型 Guardrail、运行期检查位置和安全治理边界。LangGraph Interrupts支撑 checkpoint、thread_id、Command(resume...)和节点重放规则。本篇讲解范围本篇用客服 Agent 的退款与改地址场景讲清高风险工具如何按策略暂停、怎样把待审动作交给审批界面、怎样提交决定并恢复同一条运行。重试、降级、PII 处理和 Custom Middleware 留给下一篇业务身份、数据库事务和完整审计仍由应用系统负责。客服 Agent 查到订单后模型生成了一个参数完全合法的refund_order(order_idA-2048, amount699)。schema 校验能证明订单号是字符串、金额是数字却证明不了当前用户就是订单本人也证明不了 699 元符合退款政策。如果执行层看到合法 tool call 就直接退款Structured Output 越准确副作用反而可能越稳定地被执行。真正缺少的不是参数格式而是模型决定与业务执行之间的一道运行期闸门。Tool schema 负责约束名称和参数业务授权负责确认主体、额度与政策人工审批负责处理需要判断的高风险动作。三者处在不同层任何一层通过都不能替代另外两层。Human-in-the-loopHITL人在回路要解决的就是让 Agent 在真正产生副作用前停下来把“准备做什么、允许怎样处理”交给外部审批者。一、Guardrail 不是一句“请注意安全”Guardrail 可以理解为 Agent 运行期的约束策略。它可以检查用户输入、模型输出、工具参数或工具结果也可以在违反策略时修改、阻断、降级或要求人工介入。确定性 Guardrail 使用正则、名单、额度、角色和明确业务规则速度快、结果可预测。模型型 Guardrail 使用分类模型或 LLM 判断语义风险能识别更隐蔽的问题但会增加延迟、成本和不确定性。两者不是替代关系信用卡号脱敏适合确定性规则复杂内容风险可以再交给模型判断。HITL 是 Guardrail 的一种实现。它不负责自动判断所有风险而是在策略命中后把最终决定交给人。外层 Guardrail 决定在哪些运行位置检查确定性规则和模型判断负责识别风险HITL 只接住其中需要人工确认的动作。把所有 Guardrail 都等同于审批会漏掉脱敏、限额和内容过滤等自动策略。因此退款审批不是把“退款要谨慎”写进 system prompt。Prompt 可以影响模型选择却不能阻止执行层运行已经生成的 tool call。真正的闸门必须位于工具执行路径上。二、暂停点位于模型返回之后、工具执行之前HumanInTheLoopMiddleware会检查模型响应中的 tool calls。它通过after_modelhook 运行模型已经提出动作但 ToolNode 还没有执行工具。若调用命中interrupt_on策略middleware 会构造 HITL 请求并调用interrupt()。LangGraph 把当前 State 写入 checkpointer运行返回给调用方等待外部决定。收到决定后原图从保存的线程继续推进批准的动作才会进入工具执行。模型先产生 tool callHITL middleware 再匹配策略命中后保存 State 并输出 interrupt。审批决定通过同一线程返回随后才可能执行工具并生成ToolMessage。暂停发生在副作用之前。这条顺序解释了 HITL 的能力边界。它能阻止一个尚未执行的退款工具却不能撤销 middleware 运行前已经提交到外部系统的副作用。外部 API 的幂等键、事务与补偿机制仍然必须存在。三、审批策略应按风险分级interrupt_on以工具名为 key。值为False时调用直接通过值为True时使用默认审批配置也可以提供allowed_decisions、description与when只在特定参数命中时暂停。客服场景可以分成三层get_order是只读查询正常直通update_shipping_address总是需要人工检查refund_order只有金额超过自动退款上限时才暂停。策略由真实风险决定不由工具名称听起来是否危险决定。只读查询走False直通低额退款由when判定后自动通过高额退款与改地址进入审批。分级能把人工注意力留给真实副作用避免安全工具也被无差别阻塞。when接收ToolCallRequest可以读取本次 tool call 的参数。它适合实现稳定、可审计的条件例如金额阈值或工作区路径如果风险依赖用户角色还要从可信 runtime context 读取身份不能让模型自己在参数里声明“我是管理员”。四、用一条代码链走通暂停与恢复下面的示例只模拟退款执行重点是HumanInTheLoopMiddleware、checkpointer、thread_id与Command的配合。生产环境应把InMemorySaver换成持久化 checkpointer并把退款工具连接到具备鉴权、幂等和审计能力的业务服务。importosfromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportHumanInTheLoopMiddleware,ToolCallRequestfromlangchain.toolsimporttoolfromlangchain_openaiimportChatOpenAIfromlanggraph.checkpoint.memoryimportInMemorySaverfromlanggraph.typesimportCommand# 作用模拟读取订单只返回演示用的非敏感状态。tooldefget_order(order_id:str)-str:returnf订单{order_id}已支付可申请退款。# 作用模拟提交退款真实系统必须在服务端再次鉴权并使用幂等键。tooldefrefund_order(order_id:str,amount:float)-str:returnf订单{order_id}已提交退款{amount:.2f}元。# 作用只让超过自动退款上限的调用进入人工审批。defneeds_refund_review(request:ToolCallRequest)-bool:amountfloat(request.tool_call[args].get(amount,0))returnamount200modelChatOpenAI(modelqwen3.7-plus,api_keyos.environ[MODEL_API_KEY],base_urlos.environ[MODEL_BASE_URL],)agentcreate_agent(modelmodel,tools[get_order,refund_order],middleware[HumanInTheLoopMiddleware(interrupt_on{get_order:False,refund_order:{allowed_decisions:[approve,edit,reject],when:needs_refund_review,description:高额退款需要客服主管审批,},})],checkpointerInMemorySaver(),)config{configurable:{thread_id:refund-A-2048}}first_resultagent.invoke({messages:[{role:user,content:查询订单 A-2048并退款 699 元,}]},configconfig,versionv2,)forpendinginfirst_result.interrupts:print(pending.value[action_requests])print(pending.value[review_configs])final_resultagent.invoke(Command(resume{decisions:[{type:approve}]}),configconfig,versionv2,)print(final_result.value[messages][-1].content)第一次invoke()运行到高额退款时返回interrupts而不是继续调用refund_order。每个 interrupt 的 value 中action_requests描述待执行的工具与参数review_configs描述审批者能够选择的决定。action_requests回答“Agent 想做什么”review_configs回答“审批者能怎样处理”。审批界面应把两者按位置配对展示而不是自行猜测某个工具允许 edit 还是只能 reject。第二次invoke()传入Command(resume...)并复用原来的config。这不是启动一个新的 Agent 请求而是向已经暂停的运行补交决定。五、四类决定有不同的执行语义approve保留原工具名和参数随后执行工具。它适合审批者确认动作完全正确的情况。edit提供新的edited_action可以修改工具参数后再执行。它适合把退款金额从 699 元调整为政策允许的 499 元。修改应保持保守如果把工具名和参数大幅改写模型可能重新评估并产生额外调用。reject跳过工具并把拒绝说明作为反馈交给 Agent。对退款、删库、发邮件等副作用动作拒绝必须走这条分支并明确说明是终止、询问用户还是改走安全方案。respond不执行工具而是把人的文字作为成功的工具结果返回。它适合ask_user这类“工具本身就是向人提问”的占位能力不适合表达拒绝。approve与edit最终进入真实工具reject生成拒绝反馈但不执行respond生成成功语义的人工回复。把respond用于拒绝会让模型误以为工具已经成功完成。决定类型不是界面按钮文案而是会改变 Agent State 和 ToolMessage 语义的运行协议。审批系统必须保存原动作、决定人、决定内容与恢复结果不能只记录“点了确认”。六、恢复靠 checkpoint 和同一个thread_idHITL 能跨时间等待是因为暂停时 State 已被 checkpointer 保存。thread_id是找到这份状态的稳定指针再次使用同一个值运行时才能加载原 checkpoint换成新值只会得到一条空白线程。首次运行把 State 写入thread_idrefund-A-2048对应的 checkpoint。审批决定只有携带同一thread_id才能回到原暂停点新 ID 会创建新线程无法继承待审动作。演示中的InMemorySaver会随进程结束而丢失数据不能支撑跨服务重启的审批。生产环境需要持久化 checkpointer还要定义 thread 的所有权、过期时间、重复恢复保护和审批并发控制。同一个thread_id也不等于同一个用户。服务端仍要验证当前审批者是否有权恢复这条线程不能因为客户端知道 ID 就允许其提交决定。七、多个动作必须逐一、按序决定模型可能一次提出“修改地址”和“发确认短信”两个 tool calls。若两者都命中策略interrupt 会同时包含两个action_requests恢复时也必须提交两个 decisions并保持相同顺序。待审动作 A、B 与决定 1、2 采用位置配对而不是按按钮点击时间或工具名重新排序。缺少决定或顺序错位都可能把审批结论应用到错误动作。审批界面最好为每个动作展示稳定序号、工具名、关键参数、允许决定和风险说明提交时再按原序列组装 decisions。不要把多个动作折叠成一个“全部同意”除非业务明确允许批量授权且后端仍能逐项审计。八、底层 interrupt 会重跑节点LangGraph 的interrupt()不是在 Python 栈帧中原地休眠。它通过特殊异常让运行时暂停恢复后所在节点会从头重新执行再把 resume value 作为interrupt()的返回值。这带来三条必须遵守的工程规则。第一不要用宽泛try/except Exception包住interrupt()否则暂停异常可能被吞掉。第二同一节点内多个 interrupt 的顺序必须稳定因为 resume value 按位置匹配。第三interrupt 之前的副作用可能再次发生必须幂等或移动到暂停之后。恢复时节点从头重跑纯读取可以再次执行外部写操作必须使用幂等键或放到审批之后interrupt 不能被宽泛异常捕获多个 interrupt 也不能在不同运行中改变顺序。HumanInTheLoopMiddleware已经封装了常见 tool-call 审批。只有需要在自定义图节点中收集额外输入或实现特殊工作流时才应直接使用interrupt()此时必须把节点重放当作设计前提而不是异常情况。九、审批不能替代业务系统一套可用的高风险动作链至少有四层。模型与 tool schema 负责形成结构化请求Guardrail 与 HITL 负责决定是否暂停业务服务负责身份、额度、幂等和事务审计系统负责记录谁在何时批准了什么。人工点了approve只表示允许 Agent 继续尝试执行。外部服务仍可能因为订单状态变化、余额不足或权限过期而拒绝操作这类结果要作为真实工具错误返回不能把审批成功写成业务成功。反过来业务服务已经具备严格授权也不代表 HITL 没有价值。授权回答“这个角色能不能做”审批回答“这一次是否应该做”。在高金额退款、生产数据修改和对外发送场景里两者通常同时需要。总结让副作用在可恢复的位置等人Human-in-the-loop 把 Agent 的一次“确认”扩展成可验证的运行协议模型提出动作middleware 按风险策略暂停checkpointer 保存状态审批者对每个动作给出合法决定运行时再用同一thread_id恢复。这条协议最重要的边界同样清楚schema 不能替代授权checkpoint 不能替代幂等respond不能替代reject知道 thread ID 也不能替代身份认证。当审批链路稳定后新的问题会出现PII 脱敏、失败重试、模型降级、调用限额和自定义策略应该怎样组合多个 Middleware 的先后顺序又会怎样改变行为。下一篇将进入 Built-in 与 Custom Middleware把这些治理能力放回完整 Agent 生命周期中。