AI Agent转人工机制设计实战:从降级策略到上下文工单

AI Agent转人工机制设计实战:从降级策略到上下文工单 这次我们聊一个 AI Agent 项目里最容易被低估的设计转人工机制。很多团队把“转人工”当成保险丝模型答不上来就转人工用户连续追问两次就转人工工具调用报错也转人工。最后做出来的不是一个 AI Agent而是一个“人工客服中转站”。这轮实战记录把问题拆开讲AI 为什么不能一遇到问题就转人工转人工的触发条件应该怎么设计以及转人工之后信息怎么交接才能不丢上下文。先说结论转人工不是兜底方案转人工是最后一道防线。如果 Agent 的第一反应是转人工那说明意图识别、知识检索、工具调用和话术兜底这几层都没有发挥作用。转人工每触发一次就相当于把一次本该由系统消化的成本转嫁到人工团队身上。短期看问题解决了长期看模型没有因为“解决不了的问题”发生任何改变转人工率会一直居高不下。这篇内容面向正在做 AI 客服、AI 助手、内部知识问答这类 Agent 项目的同学。你会看到给 Agent 设计一套从重试到澄清再到转人工的分级降级策略如何用置信度决定是否触达人工转人工前如何把上下文打包成工单以及转人工率这个指标怎么压测和优化。核心代码用 Python 伪代码写可以直接按你的项目结构改。1. 为什么说“一遇到问题就转人工”是反面实践1.1 转人工的隐性成本很多项目组把转人工率当作一个“安全指标”只要转人工了就默认 AI 没有乱答至少没闯祸。但转人工的成本并不是“用户被转走”那一瞬间才产生的。用户被转走后需要重新描述问题人工客服需要重新阅读上下文如果在转人工时 AI 又没有提供足够的对话摘要用户会明显感受到服务的断裂感。等待人工接管的几分钟里用户可能已经失去耐心。更要命的是转人工率过高会直接掩盖 Agent 的能力缺陷。设想一个企业内部知识问答 Agent上线第一周转人工率 15%。如果团队把“转人工”当成正常的兜底路径那这个 15% 会被一直保留下去。没有人会去分析这 15% 到底是什么问题、分布在哪些意图、知识库里缺了什么文档。时间一长Agent 能自动处理的问题比例不会增长人工团队的工作量也不会下降这个项目的价值就无法体现。从成本模型上看转人工的代价可以拆成三部分一是人工处理的人力成本二是用户等待和重复描述带来的体验损耗三是问题没有被系统自动吸收带来的长期能力缺失。前两点是显性成本第三点才是转人工率过高的真正风险。Agent 系统如果没有机制从“解决不了的问题”中学习那它永远停留在原地只能做简单问题分流。1.2 什么时候才应该转人工反过来说转人工当然不能完全取消。有些场景是 AI 不该碰的涉及退款、账号安全、法律条款解释、医疗建议、情绪激烈投诉这些场景即使模型有 90% 的置信度也应该走人工审核。这里的原则是“风险优先于效率”宁可多转一个不能漏转一个。另外用户明确要求“转人工”“叫客服”“我要投诉”时Agent 不应该反复挽留。这时候最好的体验是马上转并且在转人工前把已经收集到的信息一并交给人工让用户不需要再重复一遍。这个点非常关键因为很多实现里“转人工”就是把会话切换到另一个队列上下文却断了。所以正确的思路不是“能不能不转人工”而是“在什么条件下允许 AI 继续自救在什么条件下必须转人工”。把这两种情况做成显式规则用测试数据持续迭代。2. AI Agent 自救层级把转人工推到最后转人工之前Agent 至少应该有五级自救手段。每一级都是独立的处理逻辑上一级失败才进入下一级。这样设计的好处是每级都可以单独测试不会出现“一失败就跳到转人工”的黑箱行为。2.1 第一层重试与容错第一层解决的是“偶发故障”。LLM 接口超时、工具 API 返回 500、JSON 解析失败这些都不代表问题本身解决不了很可能是网络抖动或服务临时不可用。此时直接转人工等于把一个临时故障放大了成一次人工工单。建议在 Agent 的工具调用层做统一的失败重试机制。核心原则是区分“可重试错误”和“不可重试错误”。超时、5xx、限流属于可重试参数错误、鉴权失败、业务规则校验失败属于不可重试。不要把两类错误混在一起处理。# 工具调用重试逻辑简化版 import time from tenacity import retry, stop_after_attempt, wait_exponential RETRYABLE_STATUS {500, 502, 503, 504} retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min1, max10)) def call_order_api(order_id: str) - dict: resp http_client.post(/api/order/detail, json{order_id: order_id}) if resp.status_code in RETRYABLE_STATUS: raise RuntimeError(ftemporary error: {resp.status_code}) if resp.status_code ! 200: raise ValueError(fbusiness error: {resp.status_code} {resp.text}) return resp.json()这个例子说明一个关键点工具调用的重试要在 Agent 框架层做而不是在每个业务工具里重复写。重试两到三次仍然失败才开始进入下一级自救而不是转人工。2.2 第二层澄清与反问很多“回答不了”的问题其实是用户没把问题说清楚。比如用户只问“这个怎么弄”不加对象、不加上下文。Agent 这时候直接转人工等于把一个“信息不足”的问题甩给了人工。澄清策略要注意两点。第一澄清要给出可选项不要开放式提问。开放式提问会让用户觉得你在敷衍而给出选项能帮助用户更快确认意图。第二澄清必须有次数上限。如果澄清了两轮用户仍然无法表达清楚那就不该继续追问而应该进入后面的检索或转人工判断避免无限循环。例如用户问“怎么退款”Agent 应该先判断当前会话有没有订单上下文。没有上下文时回复“您是想退哪一笔订单可以给我订单号或者告诉我您在哪个页面看到的入口。”这个追问本身不是转人工而是把问题信息补全。2.3 第三层知识库检索与多路召回澄清之后Agent 需要检索知识库。但很多项目的检索只有一路把用户问题向量化到向量库做相似度检索top5 没有相关结果就直接转人工。这种做法太粗糙。更稳妥的是多路召回。除了向量检索还要做关键词检索、同义词改写检索、文档标题检索。有时候向量检索召回不到是因为问题里的关键词和文档里的用词不一致。比如用户问“怎么开发票”文档里写的是“发票申请流程”向量相似度可能不够高但关键词检索能命中。多路召回之后再做重排把各路结果合并、去重、打分。如果重排后仍然没有超过阈值的答案这时候才认为“知识库真的没有覆盖这个问题”。这一层做完还没有进入转人工因为 Agent 还可以尝试工具调用或者给出替代性建议。2.4 第四层工具调用与外部系统查询知识库没答案不代表系统无法解决问题。很多业务问题需要查询订单系统、库存系统、CRM 系统才能回答。Agent 在这个层级要通过工具调用获取结构化数据再结合知识库内容生成回答。工具调用的失败模式需要细分。最常见的问题是“工具本身正常但数据不存在”。比如用户问“我的订单到哪了”订单系统返回“订单号不存在”。这不是工具故障而是用户给的信息有问题。此时 Agent 的错误处理应该引导用户核对订单号而不是转人工。判断工具结果时要注意工具返回空结果”和“工具调用异常”是两个完全不同的状态。前者说明系统内确实没有这个数据后者说明系统暂时不可用。不要把两者都塞进同一个异常分支里。2.5 第五层让步式回答与替代方案如果知识库和工具都拿不到确切答案Agent 不要直接说“不知道”。更好的做法是给出替代方案或近似答案同时标明不确定性。例如用户问“我们公司能不能报销这个项目费用”知识库里没有这一条具体说明。Agent 可以回复“目前知识库里没有找到该项目费用的明确说明。从现有政策看类似项目通常需要三级审批。建议您查看知识库文档《费用报销细则》第 4.2 节或者直接联系财务同事确认。”这段话虽然没有完全解决用户问题但给了用户下一步动作比一句“不知道”有价值得多也可以降低不必要的转人工频次。3. 转人工的判断核心置信度与边界规则3.1 置信度从哪来转人工不能靠“猜”需要把判断逻辑显式化。常见的置信度信号可以分成三类意图识别的置信度、检索结果的相关度得分、工具调用失败的类型和次数。意图识别置信度来自分类模型或 LLM 结构化输出。可以让 LLM 在输出意图时同时输出一个 0 到 1 的 confidence 字段。这个值不是精确概率但可以作为决策参考。关键是每次判断后要有日志后续用线下数据校准阈值。检索相关度得分需要设置一个下限。低于下限时即使 LLM 强行生成了答案答案也可能是在“编”。这一层要结合模型校准来判断宁可让模型说“没有找到资料”也不要让它顺着用户的话生成一个没有依据的答案。工具调用失败次数是另一个重要信号。如果一个 Agent 在单轮对话里连续失败 3 次继续让它自愈的意义已经不大。它可能遇到了配置错误、权限不足或外部系统持续故障这些都不是“多问一遍”能解决的。3.2 用规则函数控制转人工把上面的信号整合成一个判断函数业务人员可以直接改阈值测试人员可以批量跑测试数据。def should_escalate(conv_state: dict) - bool: 判断当前对话是否需要转人工。 返回 True 表示转人工False 表示 Agent 继续自救。 # 1. 用户明确要求人工直接转 if conv_state.get(user_request_human): return True # 2. 高危业务意图无条件转人工 danger_intents {refund, complaint, legal, medical, account_security} if conv_state.get(intent) in danger_intents: return True # 3. 自救次数已经超过上限 if conv_state.get(retry_count, 0) 3: return True # 4. 置信度低并且问题有一定敏感度 confidence conv_state.get(confidence, 0.0) sensitivity conv_state.get(sensitivity, 0.0) if confidence 0.4 and sensitivity 0.6: return True return False这个函数的逻辑顺序很重要。先判断硬性规则再判断软性置信度。硬性规则是不允许通过调低置信度阈值来绕过的比如高危意图和用户主动要求人工。顺序反过来的话可能出现“用户都要求转人工了系统还因为置信度不够高而继续自救”的荒唐局面。3.3 让 LLM 输出结构化置信度实际工程中可以通过约束 LLM 输出 JSON 来获得 confidence 字段。下面是一个简化示例def predict_intent_with_confidence(dialogue: list[dict]) - dict: prompt ( 根据用户最近的对话内容判断用户的核心意图。\n 输出 JSON{\intent\: \意图名称\, \confidence\: 0-1的小数, \need_clarify\: true/false, \sensitivity\: 0-1的小数}\n sensitivity 表示问题涉及资金、法律、医疗、账号安全等敏感程度越高越敏感。 ) resp llm.chat( messages[ {role: system, content: prompt}, {role: user, content: format_dialogue(dialogue)}, ], response_format{type: json_object}, ) return json.loads(resp.content)有一点要强调LLM 输出的 confidence 不是真正的模型概率它只是模型对自己的自我评估。这个值会受 prompt 影响也可能存在过度自信。所以不能把 confidence 当成唯一判断依据必须和规则、外部工具结果组合使用。上线后要定期抽取样本人工标注“当时该不该转人工”再去校准阈值而不是一次性定死。4. 转人工工单设计不丢上下文不重复描述转人工最伤体验的不是“转走了”而是“用户需要重新说一遍”。要避免这个问题Agent 在转人工前必须生成一份结构化的上下文摘要和会话一起提交给人工客服工作台。4.1 工单应该包含哪些内容一份合格的转人工工单至少要有六个部分用户基本信息和意图、对话摘要、Agent 已尝试的路径、工具调用结果、敏感标记、建议下一步动作。对话摘要是给人工客服快速阅读的不要直接把原始聊天记录全量贴过去。Agent 已尝试的路径可以让客服知道用户已经被问过什么、查过什么避免重复询问。工具调用结果里面如果有订单号、报错信息这类结构化数据也要直接写进去。{ ticket_id: TK20250117001, user_id: u_123456, intent: order_status, summary: 用户询问订单 20250112_088 的物流状态Agent 在订单系统中查到订单已发货但物流接口两次请求超时。, agent_tried: [ {step: retry, result: failed_2_times}, {step: tool_call, tool: order_api, result: success}, {step: tool_call, tool: logistics_api, result: timeout} ], structured_data: { order_id: 20250112_088, status: shipped }, sensitive_flags: [high_value_order], suggested_next_action: 查询物流接口异常原因联系仓库确认包裹当前位置 }这样人工客服一上来就能看到问题全貌可以直接进入处理而不是花两分钟读聊天记录。4.2 转人工后的反馈闭环转人工本身不应该是一个终点。人工客服处理完问题后系统应该记录这个问题最终是如何解决的、参考了哪份文档、调用了哪个系统。这些信息是知识库补充和 Agent 能力优化的关键素材。如果人工解决了问题但知识库中没有对应内容可以考虑把该问题的标准答案沉淀进知识库如果人工发现是 Agent 误判了用户意图那要回去优化意图识别逻辑。转人工率不是越低越好更合理的指标是“转人工后的人工无效处理率”如果人工接手后发现问题其实很常规说明 Agent 转得太早了。5. 状态机与主循环实现把整个流程串起来到此为止各层自救和转人工判断都是分散的逻辑。要把它们组织成一个可运行的 Agent 主循环建议引入简单的状态机。状态包括接收输入、澄清、检索、工具调用、生成回复、转人工六个状态。在实际项目中不需要引入复杂的状态机框架用枚举加条件分支就能实现。关键是每个状态都有明确的进入条件和退出条件方便排查问题。from enum import Enum class AgentState(str, Enum): COLLECT_INPUT collect_input CLARIFY clarify RETRIEVE retrieve TOOL_CALL tool_call RESPOND respond ESCALATE escalate def agent_loop(conv_state: dict) - dict: 简易 Agent 主循环。省略了持久化、日志、鉴权等细节。 state AgentState.COLLECT_INPUT while True: if state AgentState.COLLECT_INPUT: # 接收用户消息进入意图识别 user_msg receive_user_message(conv_state) conv_state[last_user_msg] user_msg intent_info predict_intent_with_confidence(conv_state) conv_state.update(intent_info) if should_escalate(conv_state): state AgentState.ESCALATE elif intent_info.get(need_clarify): state AgentState.CLARIFY else: state AgentState.RETRIEVE elif state AgentState.CLARIFY: clarity_count conv_state.get(clarity_count, 0) if clarity_count 2: state AgentState.ESCALATE else: ask_clarify_question(conv_state) conv_state[clarity_count] clarity_count 1 state AgentState.COLLECT_INPUT elif state AgentState.RETRIEVE: docs multi_recall(conv_state[last_user_msg]) if docs: conv_state[retrieved_docs] docs state AgentState.TOOL_CALL else: # 知识库没有命中尝试工具调用 state AgentState.TOOL_CALL elif state AgentState.TOOL_CALL: tool_result run_tool_with_retry(conv_state) conv_state[tool_result] tool_result if tool_result.get(exhausted_retry): state AgentState.ESCALATE else: state AgentState.RESPOND elif state AgentState.RESPOND: answer generate_answer(conv_state) send_user_message(conv_state[user_id], answer) if should_escalate_after_response(conv_state): state AgentState.ESCALATE else: state AgentState.COLLECT_INPUT elif state AgentState.ESCALATE: ticket build_escalation_ticket(conv_state) handoff_to_human(ticket) return conv_state这类实现的优点是调试起来非常直观。任何一个 Agent 回答异常只要看一眼当前状态是停在哪个分支就能快速定位问题是出在检索、工具调用还是置信度判断。比“遇到问题就转人工”的黑盒方案好排查得多。6. 转人工率的压测与指标观察6.1 准备测试集转人工机制上线后不能只看线上真实流量还要准备一套离线测试集做回归验证。测试集要覆盖五类样本普通问题、模糊问题、边缘问题、情绪化问题、高危敏感问题。普通问题用来验证基础解决率没有退化。模糊问题用来验证澄清机制是否有效比如用户只说“这个怎么弄”而没有指代对象。边缘问题是指知识库里有相关内容但检索容易漏掉的场景。情绪化问题和高危敏感问题用来验证转人工的硬性规则是否触发这类样本必须保证绝对安全。每个样本除了输入对话还要标注期望结果。期望结果可以是“AI 直接解决”“AI 澄清后解决”“转人工”“AI 给出替代建议”四类。标注的时候可以多人交叉评审尽量避免把标准定得太主观。6.2 核心指标组合单看转人工率这一个指标没有意义它必须和解决率、澄清次数、人工处理结果一起看。转人工率衡量的是“多少对话最终被移交给了人工”。如果这个值突然升高要先看是不是意图识别出现了漂移。解决率衡量的是“Agent 能独立解决的问题比例”。澄清次数太频繁会让用户烦躁所以澄清超过三次的会话占比也要监控。还有一个容易被忽视的指标人工客服收到工单后的“无效工单率”。如果转人工工单里大量属于常规问题说明 Agent 的自救层级没有正常生效模型大概率在“偷懒转人工”。6.3 调参方法调整转人工策略时每次只改一个变量。比如先固定危险意图清单然后调整 confidence 阈值跑一遍离线测试集。记录转人工率和解决率的变化曲线。如果 confidence 阈值从 0.5 降到 0.3转人工率下降同时解决率也没有明显下降说明原来的阈值过于保守。但如果解决率也随之下降说明 Agent 在低置信度场景下不具备足够能力这时候不应该继续降阈值而应该补知识库或优化检索。7. 常见问题与排查方法问题现象可能原因排查方式解决方案转人工率突然升高意图识别漂移或新上线功能改变了对话分布对比近三天意图分布和转人工日志重新校准意图分类补充新功能上下文用户被反复转人工转人工后没有反馈闭环知识库未更新检查工单回流流程是否生效建立人工处理后的知识沉淀机制转人工工单缺少上下文构建工单时未收集工具调用结果查看工单构建函数确认是否传入了 conv_state 全量字段在 build_escalation_ticket 中补全关键字段Agent 遇到临时故障直接转人工重试机制未生效或超时时间过短查看工具调用日志中的错误码增加按错误码分类的重试策略澄清问题导致用户流失澄清轮次太多或提问太开放分析澄清后用户是否继续输入限制最多两轮澄清问题改成选择题高危意图没有触发转人工意图识别结果不包含 danger_intents 中定义的词查看该会话的 intent 字段补全意图别名映射增加同义表达8. 最佳实践与项目落地建议转人工机制不是一次性能做完的建议按下面的节奏分阶段落地。第一阶段只做硬性规则用户要求人工立即转接高危意图无条件转人工。这个阶段不需要做置信度判断先把安全底线守住。第二阶段加入置信度判断和自救层级重试、澄清、多路召回、工具调用逐步接入。每接入一个层级跑一遍离线测试集确认转人工率和解决率的变化。第三阶段做转人工后的反馈闭环人工工单处理结果回流到知识库和意图识别模型。这一步是长期降低转人工率的关键。只有让系统从人工处理中学习自动化能力才会持续提升而不是停留在固定的规则阈值上。还要特别注意合规边界。涉及真实用户的退款、账号、投诉等信息时转人工前要做好数据脱敏和权限管控。如果 Agent 处理的是医疗、法律、金融建议系统必须明确标注“AI 生成内容仅供参考最终以人工审核为准”。这类场景宁可多转人工也不能为了指标好看而放宽安全规则。9. 总结与下一步这一期从“AI 为什么不能一遇到问题就转人工”入手把 Agent 的降级链路完整过了一遍。核心就三件事第一给 Agent 配上重试、澄清、知识库检索、工具调用这四级自救手段把转人工推到第五级第二把转人工的触发条件变成显式规则高危意图和用户明确要求是硬性条件置信度阈值是可调参数第三转人工不是甩锅要把上下文摘要、尝试路径、结构化数据打包成工单再把人工处理结果回流到系统。如果你现在正在做 AI 客服或企业助手类 Agent可以从最小的转人工规则开始不要一上来就追求复杂的置信度模型。先记录每次转人工的会话日志跑一个月之后做一次人工分析你会发现大部分转人工样本都集中在几个固定原因上。把这些问题先解决比调模型参数更有效。下一期可以聊一聊这些转人工日志如何做自动化归因把人工处理结果变成 Agent 持续进化的数据燃料。