第八篇:人在回路与内容安全 —— 当 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 对两者要求不同。

相关推荐
2601_9621005412 小时前
python包和模块的内容整理
python·模块·导入·虚拟环境·
二进制漫游记13 小时前
FastAPI项目集成 Qdrant向量数据库+阿里云Embedding完整实战(工具封装+业务调用)
数据库·python·阿里云·embedding
华普微HOPERF13 小时前
数字隔离器,光伏微型逆变系统中的安全核“芯”
安全
DanaAI13 小时前
端云协同的算力分工:AI硬件平台的架构设计与演进
ai agent
DM今天肝到几点?13 小时前
AI 安全进入「攻防同频」:Gemini 3.8 Flash Cyber 上线、Astra 触及关键级、HiddenLayer 融资 1 亿美元
网络·人工智能·深度学习·安全·语言模型·开源·知识图谱
-今昭-14 小时前
《V2V 迁移实战:将 VMware 虚拟机转为 OpenStack 可用 Glance qcow2 镜像》
开发语言·python
2601_9623815814 小时前
【转】Python渗透测试工具:sqlmap
python·渗透测试·sql注入·sqlmap·数据库安全
来让爷抱一个14 小时前
2026 智能体安全实战:把护栏写进SPEC,MonkeyCode 云端跑通
人工智能·安全·机器学习
sungtv14 小时前
大型厂房钢结构施工安全生产动画聚焦哪些风险点
安全·安全生产动画
用户37215742613514 小时前
如何使用 Python 从 Word 文档中提取图片
python