第八篇:人在回路与内容安全 —— 当 AI 说“让我请示一下“

Human-in-the-loop 看起来只是一个"审批按钮",背后是一整套状态机、超时策略和框架 hack。

LangGraph 内置方案的致命限制

LangGraph 的 HumanInTheLoopMiddleware 基于 interrupt()------序列化图状态、挂起、等待外部信号。文档上的示例干净利落:

python 复制代码
# 文档里的理想用法
def my_node(state):
    result = interrupt("请管理员审批")  # ← 等待外部审批信号
    return result

问题出在我们实际接入时。我们的退款审批逻辑在 request-return tool 内部------tool 的执行上下文不是 graph node ,是 LangChain 的 ToolMessage 回调。当我们在 tool 函数内调用 interrupt(),整个图状态机挂起了,但 tool 的执行栈没有正确序列化------ToolInvocation 对象卡在 pending 状态,没有任何恢复机制能正确重建 tool 执行上下文。

具体表现:ReAct Agent 执行到 request-return tool → tool 调用 interrupt("确认退款 ¥199 → 用户 138xxx") → Agent 线程 hang 住,不抛异常,不超时,Langfuse trace 停在 tool_call start 再也没有后续。我们等了 30 分钟,trace 还是停在原地。

翻 LangGraph 源码确认了根因:checkpoint() 序列化的是 graph node 级别的 Channel 状态,不包含 tool 执行帧。当 interrupt() 在 tool 内部被调用时,恢复后 graph 从 checkpoint 恢复 node 的输入状态重新执行------但 tool 的中间产出的数据(已生成的退款明细)已经被丢掉了。

退款审批恰好发生在 tool 内部(request-return tool 里的"生成退款明细→等待确认→执行退款")。框架给了 80% 的路径------node 间审批------但你业务长在另外 20% 上------tool 内部审批。而这个 20% 恰恰是 LangGraph 的架构边界。


自研方案:一个标志位 + Redis

Tool 正常返回(不抛异常),返回内容带 _pending_approval 标志 + 退款明细摘要。Orchestrator 检测到标志位 → 摘要存入 Redis _INTERRUPT_STORE → Agent 暂停 → 审批员 API 同意/拒绝 → 恢复 Agent。

三个魔鬼细节:

  1. 退款明细随中断一起返回 → 审批者无需额外查询
  2. 审批超时 30 分钟自动拒绝 → 避免挂死
  3. 工具正常返回而非抛异常 → 避免 Langfuse 被 "ApprovalRequiredError" 刷屏

什么情况该换工作流引擎? 标志位+Redis 适合审批路径简单(同意/拒绝,2-3 个状态)的场景。如果你遇到这些信号就该升级了:

  • 多级流转:主管→经理→财务,审批链随金额/类型动态路由
  • 会签或签:多人并行审批,需全部通过或任意一人通过
  • SLA 告警升级:审批超时后不是直接拒绝,而是逐级上升
  • 驳回回退:审批者退回上一级修改,不是简单的"同意→执行→完成"

这时别再在 agent 里手写状态机了。两种选择:

  • Temporal:代码即工作流,Saga 模式原生支持长活事务的补偿与重试。适合「审批跨越几小时甚至几天」的分布式场景。缺点是没有可视化流程建模,审批规则改一次就要改代码、发版。
  • Conductor(Netflix 开源):微服务编排引擎,独立部署,官方 Python SDK 完善。WAIT 节点天然支持「暂停→等待外部回调→继续」的人工审批模式。缺点是流程定义不走 BPMN 标准,需要手写 JSON DSL(自带 UI 可以可视化查看和监控流程图,但不能像 BPMN 设计器那样拖拽建模),如果要业务人员拖拽方式创建审批流,可以考虑使用flowable通过API集成的方式。

核心原则:把审批流程从 agent 中剥离成独立的工作流服务,agent 只负责发起和接收回调。 HITL 的复杂度在于"流程",不在于"AI"------不要让 agent 承担它不该承担的编排责任。


HITL 暂停期间新消息的处理

用户挂了退款审批,又去问"那我的优惠券还能用吗"。推荐"轻量分支":只合并历史、不合并意图,两个请求独立处理。


内容安全:四层纵深防御

90% 的拦截在规则层(L1,<1ms)就结束了:

  • L1 输入规则过滤(正则+敏感词库)→ 直接拒绝
  • L2 本地 LLM 安全审查 → RAG Step2 与问题改写并行
  • L3 云端 LLM 复核 → 可选,仅边界模糊 case
  • L4 输出规则过滤 → 生成后硬阻断

让贵的组件做少的事。


隐私合规要点

三存储体系(Milvus + Redis + DB)全链路清理。假名化 ≠ 匿名化------hash 替代手机号是可逆的,GDPR 对两者要求不同。

相关推荐
Scott9999HH21 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
AI云海1 天前
python 列表、元组、集合和字典
开发语言·python
二十雨辰1 天前
[爬虫]-Urllib
爬虫·python
玉鸯1 天前
Agent Hook:在概率推理之上,为 Agent 叠加确定性控制
python·langchain·agent
2601_960567961 天前
电商套图自动化效率的工程量化分析——从逐张生成到批量套图的架构演进
运维·架构·自动化
shiyi.十一1 天前
第2章:应用层 — 知识要点与架构
网络·计算机网络·架构
weixin_446260851 天前
HACO:面向动态部署环境的对冲式智能计算可靠多智能体调度框架
后端·python·flask
我的xiaodoujiao1 天前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
张忠琳1 天前
【NVIDIA】 NVIDIA Container Toolkit v1.19.1 — OCI 模块超深度分析之三
云原生·容器·架构·kubernetes·nvidia