大概从 2024 年下半年开始,我们团队最核心的工作变成了一个用 LLM 支撑的智能客服助理。功能跑通后第一周就被打脸:线上监控面板一片绿,CPU、内存、QPS 全部正常,可用户投诉"机器人答非所问"的比例居高不下,更可怕的是我们连两个最基础的问题都答不上来——这个回答到底花了多少钱?同一个问题为什么昨天和今天的答案完全不一样?那段时间我几乎天天在看模型 Provider 的控制台,但总控台上只有聚合请求量,根本定位不到具体是哪个用户、哪一轮对话、哪一段 Prompt 出了问题。这让我下定决心,必须把 AgentOps 这套运维体系搭起来。
这篇东西就是一次完整的实战复盘:我们怎样基于观测云,从零开始搭建面向 LLM 和 AI Agent 的运维体系,覆盖链路追踪、指标监控、告警、成本治理、质量评估。适合正在做 LLM 应用、AI Agent、RAG 知识库的工程团队参考,也适合那些早就被"模型输出不稳定、token 费用不可控"折磨得不轻的团队。我不会绕弯子,全程都是踩过的坑和验证过的方案。
1. AgentOps 要管住的新问题:为什么传统监控面板在这次事故里失灵
1.1 那次 429 事故:面板全绿,业务却崩了
先说说那次让我彻底改变认知的 429 事故。某天下午,客服助理的失败率突然飙升到 30%,用户侧开始大量超时重试。我打开观测云的基础设施和 APM 看板,主机 CPU 正常、内存正常、服务 QPS 也在预期范围内,就 HTTP 错误率有点波动——但后台是异步任务,前端有兜底,看起来不至于崩。
实际原因直到半小时后才查到:模型 Provider 侧的并发配额被打满,所有经过网关的请求被批量 429。传统监控盯着的是"你自己的系统跑得怎么样",而 LLM 应用的问题是,你的系统跑得再好,模型 Provider 一个限流策略就能让业务瞬间劣化。更麻烦的是,这个 Provider 的配额是按分钟动态计算的,控制台指标有延迟,等你在网页上看到曲线已经晚了至少十分钟。
从那之后我明白了一件事:LLM 应用的运维对象,已经从"进程和接口"变成了"一次 LLM 调用的质量、成本、依赖状态"。这就是 AgentOps 要解决的问题。
1.2 LLM 应用与传统服务的四个本质差异
传统可观测的三大支柱:指标(Metrics)、日志(Logs)、链路(Traces),对应的是可预测的代码路径。一段代码输入确定、输出确定,监控它的 QPS、延迟、错误码就够了。但 LLM 应用不是这样,我总结下来有四个本质差异:
第一个差异是确定性变成了概率性。同样的 Prompt、同样的参数,模型可能这次答对、下次答错,改一个标点都可能改变行为。传统监控无法感知"答非所问",因为系统层面一切正常,这需要新的观测维度——质量评估。
第二个差异是每次调用都有真实的费用。传统服务调用内部接口,成本是服务器折旧和电费,边际成本很低。LLM 调用是按 token 计费的,一个复杂的 Agent 任务可能内部循环调用十几次模型,成本甚至比调用次数更敏感。如果不观测 token,月底对账才发现预算超了,那是真金白银的教训。
第三个差异是外部依赖成了一个黑盒模型提供商。模型 API 不稳定、限流、延迟毛刺、版本悄然更新,都会影响业务。你不能控制它,只能观测它,而且必须把"依赖状态"纳入自己的告警体系,而不是等用户投诉。
第四个差异是安全与合规风险。用户问题、知识库内容、Prompt 模板都会流经外部模型 API,传统日志只记录 HTTP 请求,不记录语义内容,出了泄漏事件都不知道怎么追踪。
这四个差异决定了 AgentOps 不是传统可观测的换皮,而是一套新的数据模型、埋点策略和告警体系。
1.3 观测云在 AgentOps 体系里的定位
选型的时候我们评估过 LangSmith、Langfuse、Phoenix 这类专用 LLM 可观测工具,也考虑过自建 Elastic + Prometheus 的组合。最后选择基于观测云来做,原因很实际:第一,公司已经有基础设施和 APM 监控在用,统一平台意味着排障时不用在三个系统间反复跳转;第二,专用 LLM 可观测工具部署复杂,数据出网合规和私有化都是问题;第三,观测云底座提供了 DataKit 采集器、统一存储、看板、告警、日志检索这些能力,AgentOps 需要的数据模型完全可以通过 OpenTelemetry 标准协议自己定义。
换句话说,观测云在这个体系里的定位是数据底座和工作台:负责把基础设施指标、应用链路、日志、自定义业务指标统一接入、存储、关联和展示。AgentOps 的业务逻辑——比如 LLM 调用的 Span 结构、token 成本计算、评估分数回传——由我们的应用层埋点来实现。这跟我们自己从零搭一套监控平台的差别是:平台能力和告警基础设施不用重复造轮子,可以专心处理 LLM 特有的语义。
2. 设计与埋点:先定义清楚"一次 Agent 任务"的模型
2.1 链路模型:从用户请求到 LLM 到工具再到评估
我们搭 AgentOps 的第一步不是写代码,而是画数据模型。如果连"一条完整链路长什么样"都没定义清楚,后面所有看板和告警都是空中楼阁。
我给一次客服任务定义的链路层次是这样的:最外层是用户会话(Session/Conversation),中间是 Agent 的一次完整决策循环(Workflow/Span),往内是具体的一次 LLM 调用、一次工具调用(Function Call / HTTP请求)、一次 RAG 检索(Embedding + 向量库查询 + 重排序),旁边还要挂上异步的评估结果(Eval)。
用 OpenTelemetry 的术语来说,就是一个根 Span 代表一次完整的用户请求或者 Agent 任务,下面挂着若干个子 Span,分别表示 LLM 调用、工具执行、检索操作,最后评估 Span 作为独立事件或旁路节点关联到根 Trace 上。Trace ID 贯穿所有环节。
这里最容易被忽略的是 Session 维度的建模。很多同学做 LLM 埋点只给单次 API 调用建一个 Span,结果就是一个用户在一段对话里产生了几十条互不关联的 Trace,复盘的视角完全碎片化。你在告警里看到一个高耗时 Trace,点进去是某一次模型调用,根本不知道这是哪场对话的第几轮、前面上下文是什么。所以我们强制要求:一切从 Session 开始,Session 是根,每次用户输入产生一个 Workflow Span,Workflow 内再拆 LLM Span、Tool Span、Retrieval Span。
2.2 每条 Span 上挂什么属性
Span 的属性设计决定了数据进到观测云之后能不能被切片、聚合、下钻。我们直接沿用了 OpenTelemetry 社区正在标准化的 GenAI 语义约定,也就是gen_ai.*前缀的一组属性,再叠加自己定义的业务标签。
一次 LLM 调用 Span 我建议至少带这几类属性:
| 类别 | 属性示例 | 用途 |
|---|---|---|
| 模型标识 | gen_ai.system(openai/azure/ollama)、gen_ai.request.model(gpt-4o-mini) | 按模型聚合成本与性能 |
| 请求参数 | gen_ai.request.max_tokens、gen_ai.request.temperature | 判断参数调整对质量的影响 |
| 用量 | gen_ai.usage.input_tokens、gen_ai.usage.output_tokens | token 统计与成本计算的唯一来源 |
| 业务标签 | app、env、conversation_id、user_id、feature | 从业务视角切片 |
| Prompt 信息 | gen_ai.prompt(脱敏后的摘要)、agent.tool.name | 复现问题、回溯 Prompt 版本 |
为什么要把业务标签看得和模型属性一样重?因为后续所有成本分摊、用户粒度排障、功能对比都依赖这些维度。没有user_id,财务问"哪些高价值用户消耗了最多 token"你答不上来;没有feature,产品问"知识库检索这个功能是不是比直接问答贵很多"你也只能干瞪眼。这些标签在埋点设计阶段就得定好,后面补会异常痛苦。
2.3 指标设计:Token、成本、延迟、质量
Span 描述的是"发生了什么",指标回答的是"整体态势是什么"。我们的指标设计分四组,几乎可以看成 LLM 场景的黄金信号变形:
Token 与成本指标:token_input_total、token_output_total、token_cost_total。Cost 不建议做成绝对值,每次模型改价就要改代码,更合理的做法是把 input/output token 数上报,成本在观测云侧用自定义指标或者看板公式计算,改价只改公式。这点后面踩坑部分还会展开。
性能指标:端到端延迟、首 Token 延迟(TTFT)、Token 吞吐(tokens/s)。TTFT 在流式场景里比总延迟更重要,因为它直接影响用户感知的"机器开始说话了"的等待时间。对客服场景我们的经验是 P95 TTFT 超过 2 秒,体感就会明显变差。
质量指标:LLM-as-Judge 的评分均值、RAG 检索命中率、答案相关性打分。这类指标不是机器自动产生的,需要评估服务算出来之后上报,我会在第三章详细说怎么回写。
业务指标:会话数、Agent 任务完成率、平均步数、工具调用成功率。平均步数是个很有意思的指标,它反映了 Agent 的"聪明程度"——同一个任务,步数越多通常意味着模型在无效探索上花了越多 token。
2.4 日志与事件:把对话样本接进可观测体系
链路给了骨架,日志给了血肉。我们的做法是保留一份"对话级日志",但不是把所有 prompt 和 response 都刷进去,那样成本太高。我们记录的是:一条消息的摘要、模型返回的摘要、关键标签、关联的 Trace ID。真正的完整对话内容只在出 bad case 时异步补采,存到离线存储里。
在观测云里,日志和 Trace 是通过trace_id关联的。你打开一条 Trace,右侧能看到当时打印的日志;反过来检索日志,也能跳转到对应的完整链路。这个能力在排障的时候非常有用——比如看到一个报错日志,直接跳到链路看是哪次模型调用触发的上下文。我们初期没有把日志和 Trace 关联起来,结果每次排障都要手动去两个系统里对时间戳,效率极低,这个关联的功夫一定要花。
3. 基于观测云接入:DataKit + OpenTelemetry 打通第一条 LLM Trace
3.1 接入准备:工作空间、DataKit 与 SDK
说完了设计,下面是接入的实操。前提是你已经有一个观测云账号并创建工作空间。接着在你的工作空间里找到"集成/DataKit"页面,会有一段安装脚本,一般是带 Token 的 curl 命令,贴到服务器上执行就行。DataKit 相当于一个数据采集入口,它把我们应用上报的 OpenTelemetry Trace、自定义指标、日志统一接收并写入观测云后端存储,我们不需要关心后端怎么存、怎么建索引。
我建议 DataKit 部署在和业务应用同一台主机或者同一内网的节点上,尽量减少上报链路的额外网络损耗。DataKit 默认开启了很多采集器,包括主机指标、容器、Nginx 等,但这些跟 AgentOps 没直接关系,按需开启就行,重点确认 OpenTelemetry 采集器端口是否正常监听。Observability 方案里最怕的就是采集器本身变成一个新的故障点,所以 DataKit 进程挂了要有自己的告警,我们当时忽略了,结果有一天链路数据全没了还浑然不觉。
应用侧我们用的是各语言的 OpenTelemetry SDK。这里有个选型判断:我们用 Python 做服务,所以选了opentelemetry-python,配置 exporter 指向 DataKit 的 OTLP 地址。下面的步骤对其他语言也成立,只是 SDK 初始化方式略有差异。
3.2 为 LLM 调用埋点:一段最小可运行的代码
以一次最简单的 OpenAI 调用为例,埋点代码大概长这样:
from opentelemetry import trace from opentelemetry.trace import SpanKind tracer = trace.get_tracer("agentops.llm") def chat_with_llm(messages, model="gpt-4o-mini", **kwargs): # 创建 LLM 调用 Span with tracer.start_as_current_span("llm.call", kind=SpanKind.CLIENT) as span: span.set_attribute("gen_ai.system", "openai") span.set_attribute("gen_ai.request.model", model) span.set_attribute("conversation_id", kwargs.get("conversation_id", "")) span.set_attribute("user_id", kwargs.get("user_id", "")) span.set_attribute("feature", kwargs.get("feature", "customer_service")) # 调用模型 resp = openai_client.chat.completions.create( model=model, messages=messages, temperature=kwargs.get("temperature", 0.3), ) # 从响应里取真实用量 span.set_attribute("gen_ai.usage.input_tokens", resp.usage.prompt_tokens) span.set_attribute("gen_ai.usage.output_tokens", resp.usage.completion_tokens) span.end() return resp有一个很常见的错误:只在调用前后包一层耗时记录,但不在 Span 里挂 token 用量和模型 ID。没有 token 用量,成本指标就是零,后面整个成本治理模块直接废掉;没有模型 ID,有一天你把某条线路整体切到更便宜的模型,你根本没法对比前后成本变化。这属于埋点里最不能省的两条属性。
我还习惯把 temperature、max_tokens 这类关键参数也写进 Span。排查输出质量问题时,发现某个 prompt 出现幻觉,看一眼 Span 参数发现 temperature 被配成了 0.9,问题原因一步到位。
3.3 RAG 检索和工具调用的 Span 怎么串
真实场景里 LLM 调用不会单独出现,几乎都伴随着 RAG 检索和工具调用。以我们的客服助理为例,一次回答典型的子链路是:Embedding 向量化用户问题 → 向量库检索 TopK → 拿到知识片段后拼接 Prompt → LLM 生成回答。这里面每一次环节都值得单独建 Span。
Embedding 调用我建一个llm.embeddingSpan,挂gen_ai.operation.name="embed";向量库查询(在观测云里实际是去 ClickHouse 或 Milvus 查)建一个tool.vector_searchSpan,挂集合名、检索召回数;重排序如果用了 Rerank 模型,单独建rerank.callSpan。这些 Span 的父子关系严格按调用顺序来,LLM 生成回答的 Span 是它们共同的下游。
还得注意工具调用的跨系统问题。Agent 调用一个工具(查订单 API、查库存 API),这个工具调用链路上可能还有 HTTP 客户端埋点,会生成一个独立的 HTTP Span。我们要做的不是让它孤零零挂着,而是把工具 Span 和 HTTP Span 通过 context propagation 串起来。具体做法是在调用工具前把当前 Trace Context 注入到 HTTP 头里,让下游服务的链路自动挂到同一个 Trace 上。这个工作如果不在埋点阶段完成,事后排查 Agent 为什么拿到错误工具结果,你看到的链路是断的:Agent 说调用了查询 API,但 API 侧没有任何 trace 上下文。
3.4 评估结果如何异步回写链路
质量评估这种环节,绝不能放进线上主链路同步执行。我们最初用 GPT-4o 给客服回答质量实时打分,结果发现一次回答延迟多了 3 秒,直接触发了超时告警。后来改成异步队列:线上只记录 Trace ID 和必要的上下文,调用完成之后把样本丢进队列,由独立的评估服务消费,调用一个便宜的小模型或者可用的判断模型给回答打分,最后把分数写回。
写回的方式有两种,我们结合使用。一种是在评估 Span 上打标,评估服务同样用 OpenTelemetry 初始化一个 Span,设置trace_id指向原始 Trace,再挂llm.judge.score、llm.judge.aspects这些属性。另一种是直接往原始 Trace 的上下文里再写入一个评估事件,观测云支持链接到已有 Trace。无论哪种,目标都是让"质量"在链路里可见。
有一点需要注意:Span 一旦结束,OTel 规范里是不允许再改属性的,所以别尝试在异步评估结束之后去改原始 LLM Span 的属性,正确做法是创建独立的评估 Span 并与之关联。我们在观测云里看效果:打开任意一条客服 Trace,除了 LLM 调用外,还能看到旁边挂着一个 evaluate 子 Span,上面写着准确性、相关性、幻觉分数,资深工程师扫一眼就知道这条回答为什么出了问题。
4. 从数据到决策:看板布局与告警规则
4.1 看板四块:成本、性能、质量、业务
数据接入只是开始,真正每天要用的是一套能让人快速做决策的看板。我们的主看板划分成四块,布局是有讲究的:最上面是成本区,因为老板每天问;中间是性能和错误区,是值班工程师最关心的;下面是质量与业务区,给产品和算法同事看。
成本区放了三个核心图:按模型分组的每日 token 趋势(输入/输出堆叠)、每日估算费用、按功能模块(feature)分组的费用占比饼图。费用估算在观测云里用看板公式计算,把 input token 数乘以输入单价、output token 数乘以输出单价,再按天聚合。模型改价或者切换模型时,只需要更新公式,不用动采集端。
性能和错误区放的是:端到端延迟和 TTFT 的 P50/P95 趋势、模型错误码分布(429/500/超时)、Agent 任务失败率。质量区放的是:LLM 评估得分的趋势线、RAG 检索召回率和命中率、幻觉检测结果分布。业务区放的是:会话总量、Agent 完成率、平均步数、工具调用成功率。这四个区合在一起,基本覆盖了"花的钱、跑的慢、答得差、业务不OK"四类问题。
4.2 告警规则怎么设计才不至于"狼来了"
LLM 应用的告警设计,我的原则是"指标可以多埋,告警必须少而精"。告警数量失控的结果就是没人看,我们第一批配置了二十多条规则,两周后只剩五条真正生效。
我们保留下来最有效的三条规则值得分享。第一条是费用突增告警:当每五分钟累计估算成本超过过去三小时同时间段均值的 3 倍时触发。成本异常上升通常意味着 Agent 陷入循环调用或者 Prompt 意外膨胀,这是 LLM 应用特有的毁灭性 bug。第二条是 429 占比告警:五分钟内 429 错误数超过总调用数的 5% 就触发。等眼看 Provider 控制台永远是滞后报警,不如在自己这边算占比,更贴近业务体感。第三条是质量分滑落告警:LLM 评估分滑动窗口均值比前一日下降超过 15%。这个规则触发过两次,一次是上游换了模型版本,一次是我们改了 Prompt 模板,都靠它在灰度阶段拦下来了。
除此之外还有一些常规的:端到端延迟 P95 超阈值、错误率超阈值、Agent 任务完成率低于 90%。但这些和普通接口告警逻辑一致,不赘述。
4.3 一次标准排障路径:从告警到 Trace 到 Prompt
告警的价值要落实在排障路径上。我拿一个真实案例演示我们的标准流程:某天收到费用突增告警后,第一反应不是去看模型 Provider 控制台,而是打开观测云看板,点进费用异常的那段时间窗口,按 feature 过滤看哪个功能在烧钱。定位到"订单查询"功能后,点 trace 列表按成本倒序排列,找到最贵的一条 Trace,展开链路发现 Agent 陷入了 18 步循环:不停地调用一个返回空结果的工具,每循环一次都产生一次 LLM 调用 token 消耗。
再往下钻到某一次 LLM Span,看到模型输出的 Thought 部分是"结果为空,我继续尝试另一种查询方式",而工具返回的数据结构因为上游接口字段变更已经变了,模型的解析逻辑全部落空。问题根因是上游 API 返回了不兼容的 JSON 结构,Agent 误判为工具未生效,于是不断重试。这个链路如果没有 Trace 关联、没有 token 成本打标,我们大概率会花半天时间在日志里捞关系,而不是在十分钟内精确定位到一次工具响应格式变化。看板负责告诉你"哪里不对",Trace 负责告诉你"为什么不对",这就是 AgentOps 排障闭环。
5. 生产环境里踩过的六个坑
5.1 Prompt 与敏感信息脱敏
这是底线问题,必须放在最前面。LLM 可观测本质上是把 prompt 和响应数据送进可观测平台,如果不过滤,用户手机号、姓名、地址、内部文档全文就会跟着 Span 属性一起进存储。一旦入库,不管后续能不能删,都属于一次数据事件。
我们的做法分三层:第一层,所有gen_ai.prompt属性在上报前做 PII 脱敏和截断,保留前 200 个 Token,命中手机号、邮箱、身份证等正则直接打码;第二层,Span 上彻底不挂对话原文,只挂摘要和业务 ID,完整文本统一存到单独的脱敏对象存储;第三层,内部文档做权限隔离,知识库内容除非显式标记为可观测,否则绝不进入链路。如果你现在还没做脱敏,我建议立刻停掉 prompt 属性上报,宁可牺牲排查便利,不要冒数据泄漏的险。
5.2 全量采集不如分层采样
初期我们天真地认为全量保留所有 trace 最稳妥,结果一周后存储成本和查询性能都崩了——客服系统每天几十万次调用,全量 Span 是天文数字。后来我们用分层采样方案:所有错误和慢调用(延迟超过 P95 的)全量保留;带评估结果的优质样本保留 20%;普通成功调用保留 5%~10%;指标数据永远是全量聚合的。
采样维度一定要挂在 Session 而不是单条 Span 上,否则会出现同一场对话一半有 trace 一半没有的尴尬局面。观测云本身支持在写入前做过滤,也可以用 OTel SDK 的 Sampler 实现,我们用的是基于 Trace ID 的尾部采样策略:先把 Span 全量上报,由观测云端做采样决策。这样一个复杂 Agent 任务的整条链路只要进入采样池,就是从根到叶子完整保留。
5.3 Session ID 没打通导致链路断裂
这是一个非常隐蔽的坑。我们在接入消息队列后,用户请求先从 HTTP 入口进来,然后扔进 Kafka 异步处理,消费端再发起 LLM 调用。由于没有把 Trace Context 传播到消息头,HTTP 入口的 Trace 和消费端产生的 Trace 成了两条完全不相干的链路。结果就是用户在 App 上等的这段时间,从可观测视角看是"业务结束"了,其实真正的 Agent 工作才刚刚开始。
解决办法有两个,任选其一都行:一是用 OpenTelemetry 的消息传播扩展,生产者在发送消息时把 trace context 序列化进消息头,消费者在接收时反序列化并恢复 parent span;二是更简单粗暴,在消息体里手动带 conversation_id 和 trace_id,消费端收到后创建一个新 Span 并把 trace_id 指向原始 Trace。我们最后选了后者,改动小、逻辑直观,排障时一条完整链路从用户点击贯穿到模型返回。
5.4 评估回传放异步,别拖垮主链路
这一点在第三章提过,但值得单独列为教训。第一次尝试把正确答案照进评估里的时候,我们在主请求里同步调用了另一个更贵的模型做 LLM-as-Judge。测试环境没暴露问题,上线后 P95 延迟从 2 秒飙到 5 秒,用户体验直接受损。后来评估服务全面异步化,可靠性提升,还意外获得了另一个好处:评估队列本身成了一个缓冲,模型 Provider 限流时不会连带核心业务。
异步评估最大难点是"怎么在评估结果出来之后准确关联到原链路"。我们依赖的是把 trace_id、conversation_id、message_id 三件套一起作为消息内容传给评估服务,评估结束时回写。这里一定要留意消息丢失的场景,评估样本丢了不可怕,怕的是丢了不知道,可以在评估服务侧加一个成功处理数指标,与线上样本总数对账。
5.5 成本标签缺失导致无法分摊
我对这个坑的印象深,因为实在被坑得太狠。初版埋点里我们没有给 LLM Span 打user_id和feature标签,结果上线一个月后财务要求"成本分摊到业务线",我们对着观测云看板干瞪眼——只有模型维度和总 token 数,根本分不出哪个业务线花了多少。最后只能靠临时在日志里按关键字解析出一个大概,又慢又准。
成本标签的设计建议是:至少在 Span 上带app、feat、env、user_id、conversation_id五个维度。如果你们的 Agent 服务由多个上游系统调用,再加一个channel表示来源渠道。这些标签是成本报表的切片维度,也是后续做用户级异常检测的依据,越早设计越省事。
5.6 多 Agent 场景的链路拼装
当我们从单 Agent 演进到多 Agent 协作(比如一个主管 Agent 分配任务、多个子 Agent 并行处理)后,链路开始变得复杂:一个任务的 Span 树变得很深很宽,子 Agent 的模型调用散落在不同进程甚至不同机器上。我们现在的做法是在框架层统一创建一个 Workflow 级别的根 Span,子 Agent 的全部调用通过 context 拼接上去,同时给每个子 Agent 打一个agent.name和agent.role的标签,方便按角色聚合表现。
多 Agent 场景里尤其要注意循环引用和扇出爆炸:一个子 Agent 回调主管 Agent,主管再给他派新任务,如果不像上面那样统一建 Workflow Span,最终链路会乱成一团无法阅读。我们在观测云里看到过一次 40 多个子 Agent 并行任务的 Trace,没有 Workflow 根,展开就是灾难。
6. 从可观测到可治理:我们正在做的三件事
6.1 评估集进入发布准入
可观测解决的是"线上发生了什么",但光知道不够,我们开始把观测数据反向接入研发流程。现在每一次模型版本切换、Prompt 模板更新,都要先跑一遍固定的评估集,把新的评估得分和线上历史得分对比,低于基线就不允许发布。这个评估集的构建不是拍脑袋写的,而是从观测云里持续沉淀的 bad case 里挑出来的,覆盖了客服、检索、工具调用等高频场景。
观测数据在这里的角色是"裁判的数据来源":发布前的评估集样本、发布后的线上质量分、两者对照。我们用脚本定时从观测云的质量看板拉取打分趋势,自动生成回归报告,出了问题可以在代码评审阶段就把新 Prompt 挡回去。这个过程让可观测从被动的排障工具变成了主动的质量门禁。
6.2 Bad Case 回归机制
我们专门建了一个"金矿"流程:每周从观测云里筛选评分最低、成本最高、用户投诉最多的若干条 Trace,自动导出完整对话记录(已脱敏)进离线评估集。下个迭代开始前,先在离线环境验证这些 bad case 是否被新 Prompt 或新模型修复,修复率低于 60% 就要重新调整方案。
这套机制最值钱的地方在于它把偶发的、概率性的模型问题变成了可追踪的工程问题。同一类幻觉、同一个 RAG 检索失败被解决了多少次都是有据可查的,而不是靠记忆力"感觉好像好了一点"。Bad case 回归和发布准入两块合起来,构成了我们的质量闭环:线上问题 → 观测云发现 → 样本沉淀 → 离线回归 → 新版本发布 → 再上观测云验证。
6.3 反馈与观测数据的飞轮
最后一件事我们还在验证中,但方向已经确定:把用户的显式反馈(点赞/点踩/追加问题)和隐式反馈(是否放弃追问、是否转人工)写回链路,和 LLM 调用、评估分数放在同一个 Trace 里。有了这个飞轮,AgentOps 就不仅能回答"哪里出了问题",还能回答"哪些问题对用户最重要"——转人工的那一轮对话、用户反复重试的那类问题,就是整个系统最值得优化的部分。
现在每次周会上,我们的核心决策依据已经不再是谁的直觉,而是观测云看板里的几个关键数字:每万次会话成本、质量得分趋势、bad case 修复率。如果让我重新搭一遍这套体系,我会把成本标签设计和评估异步回传放在第一天就做,而不是先跑通功能再补观测——补观测的代价,永远比一开始就设计好的代价要高得多。