「AI 应用工程实录」系列 · 02

别让 AI 自己决定"要不要发出去":我给 AI 系统设计的三层风险兜底

「AI 应用工程实录」系列 · 02 当 AI 判断错了,什么东西能拦住它?

一个让客户睡不着的问题

做 AI 客服、AI 审核、AI 排查这类系统时,客户最怕的从来不是"AI 不够聪明",而是------

"万一 AI 判断错了,把一个不该自动发的东西发出去了,怎么办?"

这个担心是对的。LLM 会幻觉,会把错的说得理直气壮。如果你让 AI 既做判断、又做执行,一旦它判断错,没有任何东西拦得住它。

所以我在设计这类系统时守一条线:风险判断和不可逆操作之间,必须有兜底。 在我做的一个客服工单系统里,这条线落地成了三层。下面用真实代码讲每一层在拦什么、为什么这么设计。

背景:这个系统在做什么

工单进来后,流程是:分类 → 路由到专职 Agent 处理 → 生成回复草稿 → 风险审查 → 高危走人工确认、低危自动发送。

关键的分叉点在"风险审查":这条回复到底能不能直接发给客户? 这个判断错了,后果最严重------把一条涉及生产事故的错误回复自动发出去,是不可逆的。三层兜底就是围绕这个判断展开的。

第一层:硬规则兜底,不把高危判断交给 LLM

最关键的认知是:越不可逆的判断,越不敢只信 LLM。

"这条回复要不要人工确认",我没有一上来就问 LLM。而是先用代码硬规则扫一遍关键词:

python 复制代码
def detect_rule_risk(state):
    text = f"{state['ticket']}\n{state.get('reply_draft') or ''}"
    high_risk_keywords = ["500", "生产", "故障", "数据丢失", "权限", "支付", "订单", "合同", "赔偿"]
    matched = [kw for kw in high_risk_keywords if kw in text]
    return {
        "requires_human": bool(matched),
        "risk_level": "high" if matched else "low",
        "matched_keywords": matched,
    }

逻辑很笨:只要工单或回复里出现"生产""500""赔偿""数据丢失"这类词,不管 LLM 怎么想,直接标记高危

为什么要这么笨?因为如果风险判断纯靠 LLM,它完全可能幻觉说"这条没风险",然后系统就把一条涉及生产事故的回复自动发出去了。硬规则不聪明,但它确定、可复现------同样的输入永远给同样的结果,这是 LLM 给不了的保证。它是最后一道不依赖模型的防线。

对比一下同系统里的另一个判断------工单"优先级"(P0~P3)我就纯用 LLM,没加硬规则:

python 复制代码
# 优先级:纯 LLM 判断,代码只校验合法性
priority = parsed.get("priority")
state["priority"] = priority if priority in valid_priorities else "P2"

为什么区别对待?因为出错后果不对等 。优先级判错,顶多排期慢一点,可挽回;高危判错,会把事故回复自动发出去,不可逆。兜底策略应该和"出错代价"挂钩,而不是一刀切。 风险越高、越不可逆的判断,才越值得用代码兜底。

第二层:LLM 判断,但结构化 + 保守失败

第二层才轮到 LLM,让它综合语义做更细的风险评估。但有两个约束。

约束一:强制输出 JSON,代码来决策,不信自由文本。

python 复制代码
# prompt 要求模型只输出 JSON:
# {"requires_human": true, "risk_level": "high", "reasons": [...], "suggestion": "..."}
review_json = safe_json(llm_output)

约束二:解析失败时,保守地走人工。

python 复制代码
if not review_json:
    # LLM 返回的 JSON 解析不了 → 不赌,直接转人工
    review_json = {"requires_human": True, "risk_level": "high",
                   "reasons": ["审查 JSON 解析失败"], "suggestion": "请人工复核"}

最终决策是"硬规则 OR LLM 判断,任一为真就走人工":

python 复制代码
state["requires_human"] = bool(rule_risk["requires_human"] or review_json.get("requires_human"))

保守失败 是这一层的核心:不确定的时候,永远选风险更低的那条路。JSON 解析失败本可以直接报错或默认放行,但我选择默认转人工------宁可多让人看一眼,绝不漏放一个高危。这个"默认往安全方向倒"的原则,比"追求自动化率"重要得多。

三层:Human-in-the-loop,人保留最终控制权

前两层筛出"高危"之后,第三层是真正的人工卡点。高危工单不自动发,而是挂起流程、等人处理:

python 复制代码
def human_review_node(state):
    # 展示草稿和风险原因,等人输入
    choice = input("你的选择 (a 批准 / e 修改后批准 / r 拒绝): ")
    if choice == "e":
        state["final_reply"] = state["reply_draft"] + "\n\n" + input("补充内容:")
    elif choice == "r":
        state["final_reply"] = None   # 拒绝,不发送
    else:
        state["final_reply"] = state["reply_draft"]

这里的关键是流程真的挂起了 ------input() 会阻塞,直到人做出决定。在真实系统里,这一步等价于"把状态存进数据库、前端展示待办、人在后台点击、流程从断点恢复"。所以这类系统的状态必须是可序列化的,能存下来、之后接着跑。

同样的思路我还用在权限设计 上:AI 可以自动执行只读查询,但任何写操作 (改配置、建工单)必须人工确认。道理一样------读是可逆的,写是不可逆的,不可逆的交给人。

值得强调的是:HITL 不是"AI 做不了就找人",而是把人工判断设计成流程的正式一环。它还会产生新数据------审核人、动作、修改内容------这些能反哺 prompt 优化和评估。

三层怎么配合

复制代码
AI 生成回复
   ↓
① 硬规则扫关键词 ──命中──┐
   ↓ 未命中             │
② LLM 风险评估 ──高危──┤(解析失败也算高危)
   ↓ 低危              ↓
自动发送            ③ HITL 人工确认 → 批准 / 修改 / 拒绝

要让一条高危回复被错误地自动发出去,得三层同时失效:硬规则没命中关键词、LLM 判断为低危、且解析没失败。概率极低。这就是分层防御的意义------单层都可能漏,但叠起来就很难穿透。

面对"你能保证不出错吗"

客户问这句,我从不说"能"。任何 AI 系统都有出错概率,说"保证不出错"要么外行、要么不诚实。

我会说:出错概率极低,而且即使出错,我有日志能定位是哪一层失守、能把这个 case 变成新的硬规则关键词,保证不犯第二次。 客户要的从来不是"零险"的空话,而是"你有没有能力管理风险"。

三层兜底,加上"出了错能定位、能补、能防复发",就是这个问题的完整答案。


本文是「AI 应用工程实录」系列第 2 篇。文中的系统设计基于真实项目经验,已做脱敏处理,讲的是可复用的设计思路而非具体实现。

系列其他文章:

  • 01 · 从手写 RAG 到理解 LangChain:为什么我先绕了"远路"
  • 03 · 48h 做一个 RAG 知识库:一个 FDE 的取舍笔记
  • 04 · 微调 embedding 实录:正例拉近、负例推远,以及没学好的那一对
相关推荐
ezreal7 小时前
AI 应用工程实录
llm
猫头_7 小时前
AI 流式传输工程指南:有了 EventSource 为何还要 Fetch?
javascript·http·llm
SyMind8 小时前
如何做好 AI 的挂件
llm·ai编程
GPUStack10 小时前
Day 0 实测|在 GPUStack 上部署 Inkling-BF16:8 卡 H20-141G 推理性能测试
ai·大模型·llm·gpu·vllm·gpu集群·sglang·gpustack
带刺的坐椅11 小时前
Solon TeamAgent 协作协议:从 SEQUENTIAL 流水线到 HIERARCHICAL 主管团队
java·ai·llm·agent·solon
Token炼金师11 小时前
自主的引擎:ReAct、MCP、多 Agent、Workflow 与沙箱护栏 —— Agent 与工具六器
人工智能·深度学习·llm
咪库咪库咪11 小时前
qdrant
llm
xing-xing12 小时前
魔搭社区(ModelScope)下载模型文件
python·jupyter·llm