
前几天复盘手头一个 AI 客服项目查了近三十天的日志我对着屏幕愣了挺久。系统日志里请求成功率 99.2%机器人应答率 97.8%看起来一片全绿。但同期的业务报表摆在那儿用户最终成功解决问题的比例只有 22%。换句话说78% 的失败日志里全标着“成功”。这 78% 哪儿去了是被谁吃掉了我翻遍了日志、链路追踪、甚至是数据库的慢查询记录最后才反应过来——问题根本不在哪一行日志丢了而在于我们记录的“成功”从一开始就不是用户眼里的“成功”。1. 先说结论这 78% 不是被吞了而是被“定义”掉了1.1 日志里的“成功”和用户眼里的“成功”不是一回事我先说说我们当时的日志埋点逻辑。机器人服务有个核心接口叫 answer前端用户发一句话后端跑完意图识别、检索、大模型生成之后返回一段文本。当时埋点在接口末尾只要接口正常返回 code200就记一条 INFO 日志标记为“机器人应答成功”。如果返回非 200就记 ERROR算一次失败。这套逻辑在传统 API 场景里没什么问题接口能通、有响应就是成功。但放在 AI 客服上就完全错位了。用户不是来调用接口的用户是来解决问题的。接口返回了一段文本这段文本是不是回答了用户的问题是不是让用户满意用户有没有因此挂断对话、转人工全都没有被记录。日志只记录了“系统做了什么”完全没有记录“系统做得好不好”。打个比方你给朋友指路朋友拿着导航走了你的逻辑是“我已经指了路任务完成”。但朋友走错了方向绕了半小时到了你会觉得这是你的问题吗用户不会这么想用户只会觉得这客服不靠谱。我在日志里看到的情况就是机器人回复了但回的是答非所问的话触发不了任何告警因为系统认为“本次交互完成”。日志全绿体验全黑。1.2 为什么我们团队一直没发现三个隐蔽的系统性原因第一个原因是埋点惯性。团队里做客服系统的同学多数有后端接口开发背景天然用接口成功率的思路来定义成功率。没有人一开始就提出来“成功应该按用户的问题是否被解决来算。” 这个惯性让我们在指标设计阶段就踩了坑而且这个坑被前面几个月看起来不错的数字掩盖了。第二个原因是缺少会话级标签。我们当时只记录单轮请求日志没有把同一会话的多轮上下文串起来做整体评估。单看某一轮机器人确实回了把整个会话拉通看用户问了三次同样的问题机器人给了三种不同的说法最终用户丢下一句“转人工”走了。以会话为维度这个对话是失败的以请求为维度三次都记成成功。同一个事实两种截然不同的统计数据。第三个原因是日志容量和成本的妥协。一开始我们也想给每轮回复都做质量评估但一条会话日志几十 KB包含完整上下文和检索结果一天几百万条请求存储成本一下子就上去了。后来妥协成只记摘要字段把“机器人说了什么”简化为“机器人有没有说”。这是低价方案也直接杀死了后续分析的可能性。这一段经历给我的最大教训是任何客服系统的成功指标必须从用户视角定义而不是从系统视角定义。日志里写的“成功”如果不代表用户满意那这个成功就是数字幻觉。2. 日志采集链路上的黑洞你以为记了其实没记2.1 日志级别和采集环节的漏记除了定义问题我在排查过程中还发现日志采集本身有大量黑洞。首先是日志级别配置问题。当时线上有个模块用 logbackroot level 设的是 INFO。听起来没问题问题在于这个模块里大量关键的中间过程日志用的是 DEBUG。比如意图识别的置信度分数、知识库检索命中的文档 ID、候选答案的得分排名——这些恰恰是判断回复质量的核心数据全部被过滤掉了。线上出了问题时想查某条回复为什么是那个答案翻日志全是空的。我后来是把线上日志级别临时调到 DEBUG再找低峰期跑了几轮话单回放才看到关键信息。但这样操作一次日志量暴涨filebeat 采集端吞吐跟不上又出现新的丢日志问题。filebeat 在队列积压时会主动丢弃新日志默认配置下没有配置较长的背压缓冲网络抖动三五秒就能造成秒级的日志缺失。排查的时候对不上时间线以为是业务异常其实是采集端丢了。处理方式也简单分两步。第一步把关键中间过程日志的级别从 DEBUG 提升到 INFO用结构化字段输出不靠人肉翻文本。第二步给 filebeat 调了 queue.mem.events、flush.timeout、output 端的 worker 数量让它能扛住短时峰值。再给日志采集做了一层校验比对业务请求总条数和日志落盘条数对不上就告警。把“日志本身有没有丢”变成一条可监控的指标而不是事后靠人肉排查。2.2 链路 ID 断裂根本拼不出完整会话另一个让我排查时极其痛苦的问题是链路 ID 断裂。客服会话从用户前端进来经过网关、对话管理服务、知识库检索、大模型调用、再到日志采集一条链路穿过五六个服务。前端的会话 ID 和服务端的 trace ID 之间没有做映射导致日志平台里搜不到某一通完整对话的上下文。你想排查一条失败会话进入日志平台搜用户 ID只看到入口的一两条请求后续服务内部的处理过程散落在不同 trace 下没有任何关联字段。结果是会话级、话单级的数据完全拼不出来更别提去定位失败原因。我们后来是把前端生成的 session_id 以节点透传的方式传进所有服务内部作为统一的日志关联键。这个动作本身不复杂但需要每个服务都愿意配合改一行 header 解析逻辑。在多个团队协作的项目里这类最基础的日志规范反而最难推动。除了链路 ID会话内的消息序号也有问题。原来消息时间戳只精确到秒用户快速连发两条信息时日志里先后顺序会颠倒导致多轮对话日志回放时错乱。后来把时间戳改成毫秒级并且强制带上 seq 序号按序号排序日志才算能真实还原对话过程。提示日志采集链路本身的可靠性值得用和业务一样的标准去监控。很多 AI 项目里日志只是“顺便记一记”排查问题时才发现日志是最不可信的数据源。3. 能被日志记录的失败都不是最难啃的3.1 大模型“一本正经地胡说”才是最大黑洞把日志采集链路理顺之后我能看到完整会话了此时才真正理解了一个更扎心的事实能被日志记录下来的失败往往是那些“有明确报错”的失败真正难啃的是大模型一本正经地给出错误答案。这种情况在日志里呈现出来的样子是速度正常、耗时正常、代码 200没有任何异常字段。你只看日志会觉得一切顺利但答案是错的用户完全没有获得有效帮助。当时我们系统里接了大模型做话术生成知识库检索到候选答案后让大模型做润色和改写。本来是希望回答更自然结果出了两种问题一是大模型在润色时擅自“补充”了知识库里没有的内容凭空制造了一个事实错误二是它对检索到的多篇文档做“信息融合”时把不相干的内容缝合在一起看着通顺实际上语义已经变了用户照做可能还会造成误导。这类问题不是日志排查能解决的。我后来做了两件事。第一在日志中强制输出 prompt 版本号、检索命中的文档 ID 列表、以及大模型生成结果中每个关键论点的来源切片确保每一次生成都有据可查。第二也是最关键的不再把大模型当“可信答案的来源”而是把它定位成“基于检索结果的表达工具”并且规则上限定知识库里明确有答案的场景由模板直接返回大模型只承担没命中时的兜底表达同时在兜底话术前加上“我帮您转接人工”的选项避免错误答案被当作最终结论。这几条规则下来答非所问的比例降得非常明显。3.2 检索命中不等于答案正确知识库检索这个环节也是“日志显示成功但实际失败”的重灾区。我们用向量检索加关键词检索的混合召回方案后面接一个排序层。日志里的成功定义为检索接口返回了 top5 结果。这个定义听着没毛病但有一类经典问题用户问的是“怎么退订需要扣费吗”向量检索召回的是“退订流程说明”语义相近但文档里根本没有扣费政策。系统认为命中成功用户看到的是答非所问。这里暴露的是检索评估指标的问题。我们用 Recall5、MRR 这类离线指标来衡量检索质量线上却没有跟踪“检索结果是否解决问题”。离线指标全绿线上体验全黑。针对这种情况我在日志里增加了两个关键字段一个是意图识别置信度分数一个是检索结果的得分排布。当意图置信度低于 0.6 或者 top1 与 top2 得分差距小于阈值时强制转人工而不是硬答。这条兜底逻辑单看会降低机器人“答了”的比例但用户实际满意度反而涨了因为我们把“答错”这种最伤害体验的情况给拦掉了。还有一个容易忽略的问题知识库里的相似问题收敛。用户问“退款”“退钱”“钱什么时候回来”是同一个意图但在日志里是三路不同的 query检索结果可能都命中不同文档。我们离线做了问题聚类把高频相似问法收敛到同一标准问法上日志里用标准问法 ID 来做统计数据一下子就干净了。4. 我是怎么把真实失败率捞出来的4.1 临时加三张表把话单变成可分析的样本要破除“日志全绿、体验全黑”的困境最直接的办法就是不看日志系统里的结果老老实实回到话单本身做人工评估。我当时的做法是抽了 2000 条完整会话让三个运营同学分头打分。规则很简单每条会话回答三个问题第一用户的问题有没有被回答这个问题的标准不是“机器人有没有回复”而是“机器人给出的内容能不能解决用户问的问题”。第二用户是主动结束的还是被动结束的如果用户最后一条消息是“好的明白了”算主动且满意如果用户最后说“算了”“转人工”算被动且不满意。第三整段会话里用户有没有重复过问题重复意味着第一次回答实际没有命中用户需求。这三个问题打完了再把数据按不同维度聚合。比如按意图聚合看哪些意图的失败率最高按渠道聚合看是 H5 上的体验差还是小程序差按时段聚合看晚高峰是不是因为响应变慢导致转人工。因为人工打分有时间成本所以样本量不会特别大但已经足够暴露问题。打分的产出之一是找到了日志系统里完全看不到的现象有 4% 的会话用户发“在吗”机器人回复“您好我是智能助手请问有什么可以帮您”用户又说“在吗”机器人又回一遍介绍用户第三次说“在吗”机器人第三次回复同样的话。三次日志全部标记“成功”但这通会话显然是失败的。任何基于接口状态的监控都发现不了这类问题只有回到话单本身才看得见。4.2 一个月数据复盘78% 的真实构成根据抽样的评估结果和日志字段回填我粗略拆了一下这 78% 的真实失败构成。46% 属于“语义错位型失败”机器人回复了但回答的不是用户问的问题。这里面一半是检索命中错误文档一半是意图识别跑偏。18% 属于“信息缺失型失败”机器人说的话对但只给了部分答案用户还得再问好几轮才能凑齐信息用户在第三轮时已经失去耐心转人工。13% 属于“流程断点型失败”机器人回答了“怎么操作”却给不了“操作链接”或“下一步入口”用户在系统里找不到那个功能只能放弃。剩下的大概是表达生硬、回答过长、多轮纠缠等等。这个拆解最大的价值是把模糊的“体验不好”变成了具体的、可定位的工程问题。语义错位型就调检索和意图信息缺失型就是知识库里没有覆盖完整流程得补内容流程断点型则是知识库答案里缺少操作入口需要专门处理。有一个数字让我印象特别深信息缺失型失败的会话里平均要 6.2 轮对话才能完成信息收集而语义错位型的平均轮次只有 2.3 轮。轮次越低、失败率越高——说明用户问了一次、发现答非所问后就不太愿意继续问了直接走人。轮次高但失败的说明用户在给机器人机会系统却没有抓住。两种失败需要完全不同的处理策略如果不拆这个数据我根本不可能想到要按这个维度去分。4.3 改进动作核心是改“成功的定义”有了数据之后我做的第一件事不是去优化模型而是改日志里“成功”的定义。从“接口返回 200 就算成功”改成“用户没有在后续三轮内重复同样问题、没有转人工、会话满意度不低于三颗星三者同时满足才算成功”。这个定义一改日志里的成功率瞬间从 97.8% 掉到 60% 多接近真实水平。告警也随之开始发挥作用因为真正的问题终于能在指标上暴露出来了。改完成功定义之后再把日志里输出的核心字段补齐意图置信度、检索 top5 得分、命中的文档 ID、大模型生成版本号、用户是否在三轮内重复问题、是否转人工。这些都是结构化字段方便后续做聚合查询。最后一步是把之前人工打标的三问逻辑做成一个持续的运营抽检流程每周抽 200 条会话做质量跟踪而不是等项目出大问题再来一次“大排查”。这一套做完之后效果很直接真实会话解决率从 22% 涨到了 45% 左右转人工率从 68% 降到 52%。离完美还很远但至少日志终于能反映真实情况了下面做优化才有方向。5. 常见问题速查表与最终体会5.1 常见问题速查表我把这次排查过程中踩过的坑和对应的处理方式整理成了一张速查表方便后续同类项目直接参考现象日志里的表现真实原因处理方案接口全绿用户疯狂投诉请求成功率 99%无异常日志成功指标定义错误接口成功不等于问题解决把“解决用户问题”纳入成功定义如无重复提问、无转人工想排查会话上下文日志拼不出来同一会话分散在多个 trace无关联 ID链路 ID 断裂前后端未透传用 session_id 做统一关联键全链路透传时间戳精确到毫秒关键中间过程查不到日志平台里只有接口出入口中间过程日志用了 DEBUG 级别被过滤把关键过程日志提升到 INFO并结构化输出高峰期日志缺失时间线对不上日志出现秒级断档业务日志突然跳变filebeat 采集队列积压背压不足导致丢弃调大采集缓冲、增加 worker 数并校验日志条数和请求条数检索命中但回答与问题无关检索返回 top5日志无异常离线指标全绿但线上无人跟踪回答质量日志输出检索得分分布低置信度强制转人工大模型一本正经胡说无报错、速度正常、状态码 200大模型篡改了知识库内容或缝合了无关信息限制大模型定位为“表达工具”强制输出来源切片、加版本号这张表本质上就是提醒一句话日志系统的核心不是记录“系统做了什么”而是支持回答“业务做得怎么样”。AI 项目里的日志尤其如此。5.2 一点个人经验这个项目做完之后我最大的体会是AI 客服的排障很多时候真正的问题不是“不会查日志”而是“不知道哪些日志才是判断成功的关键”。你问一句“日志里怎么看失败”绝大多数人都会从接口状态、异常堆栈入手。但 AI 客服这类生成式系统它的失败往往藏在“没有异常”的角落里——回答错了但状态码是 200没帮助但耗时很正常体验很差但日志很规整。如果你一开始就把成功指标定义错了后面所有的监控、告警、看板都是在为一个错误的指标服务。如果让我给做 AI 客服项目的同学一条最直接的建议上线第一天就把“用户问题是否被解决”设计进日志字段和成功判定标准里而不是等出了问题再去翻日志。这个动作只要晚一天就会积攒一天的假数据等你想回头清理时成本已经很高了。日志不是事后诸葛亮的工具它应该是一开始就参与定义“什么是好结果”的那个裁判。