多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

客服Agent从Demo到生产:Tool、RAG、MCP、Eval四层48关实战避坑指南

客服Agent从Demo到生产:Tool、RAG、MCP、Eval四层48关实战避坑指南 1. 客服 Agent 的 48 关到底在渡什么劫做客服 Agent 这个方向快两年了从最早拿 LangChain 拼一个能查订单状态的玩具到后来带团队做日均百万级会话的生产系统中间踩的坑如果写成文档大概能装满一个 48 关的副本。这个标题里的渡劫 48 关不是夸张修辞是我自己梳理下来一个客服 Agent 从 Demo 走到生产可用至少要跨过的 48 个关键决策点。这些决策点分布在 Tool、RAG、MCP、Eval 四个大方向上每一个方向都有它自己的天劫。先说清楚这个项目是什么。它是一个面向电商和 SaaS 场景的客服 Agent 系统核心能力包括多轮对话理解、订单与工单查询、知识库检索、工具调用、人工兜底转接。适合谁来参考如果你正在做 Agent 开发尤其是客服、售前、售后这类需要查数据答问题执行动作的场景这篇内容基本可以当路线图用。如果你只是刚接触 Agent 概念也没关系我会把每个技术点用生活化的方式讲清楚保证你能看懂为什么要这么做。为什么是 48 关因为客服 Agent 和通用聊天机器人的最大区别在于它必须说对话且做对事。说对话靠 RAG 和 Prompt做对事靠 Tool 和 MCP而保证它一直说对做对靠 Eval。这四个环节任何一个出问题用户感受到的就是这个客服是个智障。我见过太多团队在 Demo 阶段效果惊艳一上生产就崩根本原因就是只渡了前 10 关后面 38 关全跳过了。这篇文章我会按四个大方向拆解先讲整体设计思路和方案选型再讲 Tool 和 RAG 的核心细节然后是 MCP 的接入实操最后是 Eval 体系怎么搭。每一部分都会给出我实际用过的参数、配置和避坑经验不是纸上谈兵。2. 整体架构设计与方案选型思路2.1 为什么客服 Agent 不能只靠一个大模型很多人第一反应是客服嘛把知识库塞进 Prompt让大模型直接答不就行了我早期也这么干过结果就是三个字贵、慢、不准。贵是因为每次请求都要带上几万 token 的知识库内容慢是因为长上下文推理延迟高不准是因为模型在长文本里找答案的能力远不如专门的检索系统。所以客服 Agent 的架构必须是分层的。我的方案是四层接入层负责多渠道消息归一化编排层负责意图识别和路由能力层负责 Tool 调用和 RAG 检索评估层负责线上质量监控。这四层里编排层和能力层是核心也是 48 关里占坑最多的部分。选型上编排层我用的是状态机LLM 混合模式而不是纯 ReAct。原因很简单客服场景有明确的业务流程比如查订单→确认问题→给出方案→执行退款这些步骤是确定的用状态机保证流程不走偏用 LLM 处理每一步的自然语言理解和生成。纯 ReAct 的自由度太高在生产环境里容易自由发挥比如用户问退款它可能先去查了物流浪费一轮交互。2.2 Tool、RAG、MCP、Eval 四者的关系这四个词经常被混在一起讲但它们的职责边界其实很清楚。Tool 是 Agent 的手负责执行具体动作比如查订单、改地址、发工单。RAG 是 Agent 的记忆负责从知识库里找到相关信息。MCP 是 Agent 的神经接口负责标准化地连接外部工具和数据源。Eval 是 Agent 的体检报告负责告诉你它哪里病了。我见过最常见的错误是把 RAG 当 Tool 用或者把 Tool 当 RAG 用。比如用户问我的订单到哪了这是 Tool 该干的事因为需要实时查数据库用户问退货政策是什么这是 RAG 该干的事因为答案是静态知识。如果搞混了要么查不到实时数据要么把静态知识硬编码成工具维护成本爆炸。MCP 的价值在于它让 Tool 的接入标准化了。以前每接一个外部系统就要写一套适配代码有了 MCP只要对方提供 MCP Server我这边就能直接挂载。Eval 则是贯穿始终的从 Tool 调用的准确率到 RAG 的召回率再到最终回答的满意度都需要量化。2.3 48 关的分布与优先级我把 48 关按四个方向做了分布Tool 相关 14 关RAG 相关 16 关MCP 相关 8 关Eval 相关 10 关。优先级上Tool 和 RAG 是必须最先渡的因为它们是 Agent 的核心能力MCP 可以后置等 Tool 多了再上Eval 要尽早搭但可以先用简单指标后面再完善。这里给一个我实际用的优先级表供你参考阶段重点关卡目标建议周期第一阶段Tool 定义、RAG 基础检索Demo 可跑通2 周第二阶段Tool 错误处理、RAG 重排生产可用4 周第三阶段MCP 接入、Eval 基础指标可扩展3 周第四阶段Eval 全链路、RAG 优化持续迭代长期这个表不是死的但大方向是这样。我见过有团队第一阶段就花两个月结果 Tool 还没定义清楚这就是没抓住重点。3. Tool 层从定义到错误处理的 14 关3.1 Tool 定义的三要素与常见坑定义一个 Tool看起来简单其实有三个要素必须想清楚名称、描述、参数 schema。名称要语义明确比如query_order_status就比get_info好得多因为 LLM 是靠名称和描述来判断该不该调用的。描述要写清楚什么时候用和什么时候不用比如当用户询问订单物流状态时使用不要用于查询历史订单列表。参数 schema 是最容易出坑的地方。我踩过的坑包括参数类型不明确导致 LLM 传字符串给数字字段参数没有枚举约束导致 LLM 传了不存在的状态值参数没有必填标记导致调用时缺参。后来我定了一个规矩所有参数必须有类型、有描述、有示例枚举字段必须列出所有合法值。# 一个规范的 Tool 定义示例 { name: query_order_status, description: 查询指定订单的当前物流状态。当用户询问某个订单的配送进度时使用。不要用于查询订单列表或历史订单。, parameters: { type: object, properties: { order_id: { type: string, description: 订单编号通常是 16 位数字字符串, example: 2024051712345678 }, query_type: { type: string, enum: [status, detail, estimate], description: 查询类型status 查状态detail 查详情estimate 查预计送达 } }, required: [order_id] } }这个定义看起来啰嗦但实测下来LLM 的调用准确率能提升 30% 以上。原因很简单LLM 不是人它没有常识你写得越明确它越不容易猜错。3.2 Tool 调用的错误处理与重试策略Tool 调用失败是常态不是异常。网络超时、参数错误、权限不足、下游系统故障这些都会发生。我的处理策略是分三级可重试错误、可降级错误、不可恢复错误。可重试错误包括超时、限流、临时故障这类错误我会重试 2 次间隔 500ms 和 1500ms指数退避。可降级错误包括下游系统返回空数据、部分字段缺失这类错误我会让 Agent 用兜底话术回复比如暂时查不到物流信息我帮您转人工确认。不可恢复错误包括参数格式错误、订单不存在这类错误直接返回明确提示不重试。这里有个坑重试不能无脑重试。我见过有团队对所有错误都重试 3 次结果下游系统已经挂了重试只是雪上加霜。我的经验是只有明确是临时性的错误才重试而且重试次数不要超过 2 次否则用户等待时间太长。提示Tool 调用的超时时间建议设置在 3-5 秒。太短容易误判超时太长用户等不及。如果是查询类 Tool可以放宽到 8 秒如果是写操作类 Tool建议 3 秒内必须返回否则走异步。3.3 Tool 编排与并行调用客服场景里很多问题需要多个 Tool 配合。比如用户说我上周买的鞋还没到帮我看看这需要先查订单列表再查具体订单的物流。如果串行调用延迟就是两次调用之和如果并行调用延迟就是最慢的那次。我的做法是能并行的必须并行。比如查订单状态和查用户等级这两个没有依赖关系可以同时调。但查订单详情和查物流这两个有依赖关系必须先拿到订单号才能查物流只能串行。并行调用的实现上我用的是 asyncio.gather但要注意异常处理。如果并行调用中有一个失败不能让整个流程崩掉而是要让成功的部分继续失败的部分走降级。这个细节很多教程不讲但生产环境里非常重要。3.4 Tool 权限与安全边界Tool 是 Agent 的手但这只手不能乱伸。我定了几条硬规矩写操作类 Tool 必须二次确认比如退款、改地址Agent 必须先问用户确认要退款吗用户确认后才能调用敏感数据类 Tool 必须脱敏比如查手机号返回时只显示后四位高频操作类 Tool 必须限流比如查订单单个用户每分钟最多查 5 次。这些规矩不是技术问题是产品问题但必须在 Tool 层实现。我见过有 Agent 被用户诱导直接把别人的订单信息返回了这就是权限没做好。安全边界这件事宁可严一点不能松。4. RAG 层从检索到重排的 16 关4.1 RAG 的核心瓶颈到底在哪RAG 的瓶颈从来不是能不能检索到而是检索到的是不是对的。我做过统计在客服场景里RAG 出问题的情况中70% 是召回阶段就没找到正确文档20% 是找到了但排序不对只有 10% 是生成阶段的问题。所以优化 RAG重点要放在召回和重排上而不是天天调 Prompt。召回阶段的核心是切分和向量化。切分粒度太粗一个 chunk 里混了多个主题检索时容易召回不相关的切分粒度太细一个完整答案被拆成好几段检索时可能只召回一半。我的经验是客服知识库的 chunk 大小控制在 300-500 字并且要按语义切分不能按固定字数硬切。向量化模型的选择上我试过 OpenAI 的 embedding、BGE、M3E最后在中文客服场景里用的是 BGE-large-zh。原因是对中文语义的捕捉更准尤其是退货和退款这种近义词BGE 的区分度更好。当然如果你的知识库以英文为主OpenAI 的 embedding 也是好选择。4.2 混合检索向量关键词的必要性纯向量检索有个致命问题对专有名词和数字不敏感。比如用户问订单 2024051712345678 的物流向量检索可能召回一堆订单物流相关的文档但就是找不到这个具体订单。这时候就需要关键词检索来兜底。我的方案是混合检索向量检索召回 Top 20关键词检索召回 Top 20然后用 RRF 算法融合取 Top 10 进入重排。RRF 的公式很简单score sum(1 / (k rank))k 通常取 60。这个方案实测下来召回率比纯向量提升了 25% 左右。# RRF 融合的简化实现 def rrf_fusion(vector_results, keyword_results, k60): scores {} for rank, doc in enumerate(vector_results): scores[doc.id] scores.get(doc.id, 0) 1 / (k rank 1) for rank, doc in enumerate(keyword_results): scores[doc.id] scores.get(doc.id, 0) 1 / (k rank 1) return sorted(scores.items(), keylambda x: x[1], reverseTrue)这个实现很简单但效果很稳。我试过用加权求和代替 RRF结果发现权重很难调RRF 的好处是不需要调权重对异常值也更鲁棒。4.3 重排模型的选择与部署重排是 RAG 的第二道关。召回阶段追求的是不漏重排阶段追求的是精准。我用的重排模型是 BGE-reranker-large部署在本地延迟大概 50ms 左右可以接受。重排的输入是 query 和候选文档输出是相关性分数。我的做法是取 Top 10 候选重排后取 Top 3 进入生成。为什么是 Top 3因为客服场景的答案通常很聚焦Top 3 足够覆盖再多反而会引入噪声。这里有个坑重排模型和向量模型最好用同一个系列的比如都用 BGE因为它们的语义空间更一致。我试过向量用 OpenAI、重排用 BGE效果明显不如都用 BGE。4.4 RAG 的评估与迭代RAG 不做评估就是盲人摸象。我用的评估指标有三个召回率、准确率、MRR。召回率看的是正确文档有没有被召回准确率看的是召回的文档里有多少是相关的MRR 看的是正确文档的排名。评估数据的构建上我建议从线上真实问题里采样人工标注正确答案然后定期跑评估。我一般每周跑一次每次 200 条左右能覆盖主要场景。如果召回率低于 80%就要检查切分和向量模型如果准确率低于 70%就要检查重排和阈值。注意RAG 的评估不能只看整体指标要分场景看。比如退货政策类问题的召回率可能是 95%但订单物流类可能只有 60%因为后者需要实时数据RAG 本身就不擅长。分场景看指标才能找到真正的瓶颈。5. MCP 层标准化接入的 8 关5.1 MCP 到底解决了什么问题MCP 刚出来的时候我以为它就是个工具协议后来用下来发现它解决的是工具接入的标准化问题。以前每接一个外部系统就要写一套适配代码A 系统的订单查询和 B 系统的订单查询接口格式完全不一样。有了 MCP只要对方提供 MCP Server我这边就能用统一的方式挂载。MCP 的核心概念是 Server 和 Client。Server 提供工具和资源Client 负责调用。在客服 Agent 里Agent 就是 Client各个业务系统就是 Server。这个架构的好处是解耦业务系统只需要实现 MCP Server不需要关心 Agent 怎么用Agent 只需要知道 MCP 协议不需要关心业务系统的具体实现。5.2 MCP Server 的接入实操接入一个 MCP Server步骤其实不复杂但有几个细节容易出问题。第一步是确认 Server 的传输方式MCP 支持 stdio 和 SSE 两种本地工具用 stdio远程服务用 SSE。第二步是配置连接参数比如命令、参数、环境变量。第三步是测试连接确认工具列表能正常拉取。// MCP Server 配置示例 { mcpServers: { order-service: { command: node, args: [/path/to/order-mcp-server.js], env: { API_KEY: your-api-key } }, knowledge-base: { url: https://internal.example.com/mcp, transport: sse } } }这个配置看起来简单但坑不少。比如 stdio 模式下Server 的日志不能输出到 stdout否则会干扰协议通信必须输出到 stderr。这个细节很多文档不写但实际接入时一定会遇到。5.3 MCP 工具的动态发现与缓存MCP 的一个好处是工具可以动态发现。Agent 启动时会向每个 Server 请求工具列表然后把这些工具注册到自己的工具池里。但这里有个性能问题如果每次请求都去拉工具列表延迟会很高。我的做法是启动时拉一次缓存起来定期刷新比如每 5 分钟。缓存带来的问题是如果 Server 更新了工具Agent 可能不知道。我的解决方案是加一个版本号机制Server 的工具列表带版本号Agent 定期检查版本号变了就刷新。这个机制不复杂但能避免很多工具明明更新了但 Agent 还在用旧版的问题。5.4 MCP 的安全与权限控制MCP 让工具接入变简单了但也带来了安全风险。如果任意 Server 都能注册工具那恶意 Server 可能注册一个删除订单的工具Agent 一旦调用就出大事。所以我的做法是MCP Server 必须白名单只有经过审核的 Server 才能接入工具注册时必须声明权限等级写操作类工具需要额外审批。权限控制上我用的是最小权限原则Agent 只挂载当前场景需要的工具不需要的不挂。比如售前场景只挂查商品和查库存不挂退款和改地址。这样即使 Agent 被诱导也做不了危险操作。6. Eval 层从指标到闭环的 10 关6.1 Eval 的三个层次Eval 不是单一指标而是三个层次组件级、链路级、业务级。组件级评估的是单个 Tool 或 RAG 的准确率链路级评估的是整个对话流程的完成率业务级评估的是用户满意度和问题解决率。组件级评估最容易做也最应该先做。比如 Tool 调用的准确率我可以构造 100 个测试用例看 Agent 有没有调对工具、传对参数。RAG 的召回率我可以标注 200 个问答对看正确文档有没有被召回。这些指标能快速定位问题。链路级评估需要模拟完整对话。我用的方法是脚本回放把线上真实对话录下来去掉 Agent 的回复让新版本 Agent 重新跑一遍对比结果。这个方法能发现很多组件级评估发现不了的问题比如多轮对话中的上下文丢失。业务级评估最直接就是看用户满意度。我用的指标是一次解决率和转人工率。一次解决率越高说明 Agent 越能干转人工率越高说明 Agent 越无能。这两个指标是最终检验标准。6.2 自动化评估流水线的搭建Eval 不能靠人工跑必须自动化。我的流水线是这样的代码提交后自动触发评估任务评估任务从测试集里采样跑一遍 Agent跑完后生成报告对比基线如果指标下降超过阈值就阻断发布。这个流水线听起来复杂其实用 GitHub Actions 就能搭。核心是测试集的管理和指标的计算。测试集我放在版本控制里每次更新都记录变更指标计算用 Python 脚本输出 JSON 格式的报告。# 评估指标计算的简化示例 def evaluate_agent(test_cases, agent): results { tool_accuracy: 0, rag_recall: 0, task_completion: 0 } for case in test_cases: response agent.run(case.input) if case.expected_tool: results[tool_accuracy] int(response.tool case.expected_tool) if case.expected_doc: results[rag_recall] int(case.expected_doc in response.docs) results[task_completion] int(response.completed) n len(test_cases) return {k: v / n for k, v in results.items()}这个脚本很简单但能跑起来就是胜利。我见过有团队评估全靠人工结果一周只能跑一次迭代速度极慢。6.3 线上监控与告警Eval 不只是离线评估线上监控同样重要。我监控的指标包括Tool 调用失败率、RAG 空召回率、对话轮次异常率、用户负面反馈率。这些指标一旦超过阈值就触发告警。告警的阈值设置上我的经验是Tool 失败率超过 5% 告警RAG 空召回率超过 10% 告警对话轮次超过 10 轮告警负面反馈率超过 3% 告警。这些阈值不是绝对的要根据业务调整但大方向是这样。线上监控的另一个价值是发现未知问题。离线评估只能发现已知问题线上监控能发现未知问题。比如某天突然 Tool 失败率飙升查下来发现是下游系统改了接口这种问题离线评估永远发现不了。6.4 评估驱动的迭代闭环Eval 的最终目的是驱动迭代。我的做法是每周跑一次全量评估生成报告报告里列出 Top 5 问题每个问题指派负责人下周评估时看问题有没有解决。这个闭环看起来简单但坚持下来效果很好。迭代的优先级上我一般按影响面×严重度排序。影响面大且严重度高的问题先修比如 Tool 调用错误导致用户被误导影响面小但严重度高的问题也要修比如 RAG 召回错误导致答案完全不对影响面大但严重度低的问题可以缓比如回复语气不够友好。7. 实操中的常见问题与排查技巧7.1 Tool 调用失败的排查路径Tool 调用失败是最常见的问题排查路径我总结成三步先看参数再看网络最后看下游。参数问题占 50%通常是 LLM 传错了类型或漏了必填字段网络问题占 30%通常是超时或限流下游问题占 20%通常是下游系统故障或返回格式变了。排查参数问题时我会把 LLM 的原始输出和 Tool 的 schema 对比看哪里不匹配。排查网络问题时我会看超时日志和限流日志。排查下游问题时我会直接调下游接口看返回什么。7.2 RAG 召回不准的优化手段RAG 召回不准优化手段有四个调切分、换模型、加关键词、加重排。调切分是最容易的把 chunk 大小从 500 调到 300或者按标题切分换模型成本高但效果可能明显加关键词能解决专有名词问题加重排能解决排序问题。我的经验是先调切分和加关键词这两个成本低见效快如果还不行再考虑换模型和加重排。换模型不是万能的我试过换了好几个模型效果提升有限最后还是靠混合检索和重排解决的。7.3 MCP 连接不上的排查MCP 连接不上常见原因有三个配置错误、网络不通、Server 没启动。配置错误包括命令写错、参数写错、环境变量没设网络不通包括防火墙拦截、端口不对Server 没启动就是字面意思。排查时我会先用命令行手动启动 Server看能不能跑起来然后用 MCP Client 测试连接看能不能拉取工具列表最后看日志找具体报错。这个流程能解决 90% 的连接问题。7.4 Eval 指标波动的归因Eval 指标波动归因要看是真波动还是假波动。真波动是 Agent 真的变差了假波动是测试集或评估方法变了。我一般先看测试集有没有变再看评估代码有没有变最后才看 Agent 代码。如果确认是 Agent 变差了我会用二分法定位把最近的代码变更列出来逐个回滚测试找到导致变差的那个变更。这个方法笨但有效我靠它定位过好几次看起来无关但实际有关的变更。8. 一些踩坑之后的个人体会做客服 Agent 这两年最大的体会是技术不是最难的难的是定义清楚问题。Tool 该定义成什么样RAG 该召回什么Eval 该评估什么这些问题想清楚了技术实现反而简单。我见过太多团队技术很强但问题没定义清楚做出来的东西没人用。第二个体会是不要追求一步到位。48 关不是一天渡完的我自己的系统也是迭代了十几版才稳定。先渡最关键的几关让系统能跑起来然后再逐步优化。完美主义在 Agent 开发里是毒药因为 Agent 本身就有不确定性你永远等不到完美的那天。第三个体会是Eval 要尽早做。我早期不做 Eval全靠人工看结果就是感觉还行但实际一堆问题。后来搭了 Eval 流水线才发现问题比想象的多。Eval 不是负担是眼睛没有眼睛就是盲人摸象。最后分享一个小技巧客服 Agent 的 Prompt 里一定要加一句如果不确定就说不知道并转人工。这句话能避免 80% 的胡编乱造。Agent 最大的风险不是答不上来而是答错了还理直气壮。宁可转人工不可瞎回答。
返回列表