Agent 应用上线容易,上线之后怎么盯住它别跑偏,是另一回事。我最近帮一个团队排查生产事故,现象很典型:Agent 稳定运行一周后突然响应变慢,Token 账单翻了三倍,可 CPU、内存、带宽全都正常,日志里也找不到一条报错。他们唯一的“可观测性”工具就是数据库慢查询,慢查询确实变多了——但真正的问题根本不在数据库,而在 Agent 的规划循环:一个工具返回了异常格式,Agent 没有识别出来,进入无限重试,每次重试都重新调用一次大模型,单个会话烧掉了几十万 Token。这种故障,传统监控三板斧根本抓不到。
这正是 Agent 可观测性要解决的问题。把分布式追踪、链路诊断、Token 成本精细化核算合起来看,本质上是在回答三件事:这一次 Agent 任务到底经历了什么?出问题时根因在哪一环?整个过程烧了多少 Token、花在哪些环节?这篇文章写给正在做 Agent 应用、又觉得“上线了但心里没底”的团队,也写给准备搭 Agent 可观测体系的平台工程师。文章里不会给你一堆天花乱坠的概念,都是我在实际项目里验证过的埋点方案、排障路径和成本核算方法,你可以直接拿去抄作业。
1. 为什么通用监控套件在 Agent 场景下会失灵:从确定性请求到动态决策链
1.1 传统监控三板斧的假设条件,在 Agent 这里全都不成立了
在传统 Web 服务里,一次请求的路径是确定的:网关进来,经过认证服务,调业务 A,业务 A 调数据库,再调业务 B,最后返回。链路上每个节点的耗时、错误率、饱和度都可以预先定义好,所以指标(Metrics)、日志(Logs)、链路(Traces)这三板斧能很好工作。你画一张拓扑图,哪个节点红了,问题就在哪。
Agent 应用把这件事彻底打乱了。一次用户请求进来,Agent 要根据当前上下文自己决定下一步做什么。同一个问题,昨天走三步就完成了,今天可能绕了二十步还回不来。链路是模型动态生成的,不是代码静态定义的。拓扑图没法画,因为边和节点都是运行时才出现的。更麻烦的是,LLM 调用本身有随机性,同一段 prompt 两次调用结果可能不同,这导致你很难通过复现来定位问题——日志里能找到的只是大量相似但不完全相同的调用记录。
还有个被多数团队忽略的点:传统监控衡量系统健康度靠“错误率”和“延迟”,但 Agent 场景下,一个任务即使没有报错,也可能已经跑偏了很久。我见过一个数据抓取 Agent,上游接口返回的 JSON 结构变了,它反复尝试用各种 prompt 去解析,结果虽然“成功”了,但多花了 17 倍 token,耗时从 8 秒涨到 130 秒。从监控面板看,延迟变高了,但没有任何错误,没有人知道发生了什么。你说这是故障吗?用户体验变差、成本失控,当然是故障。但传统监控会说:一切正常。
1.2 一个真实事故:无报错的性能劣化与“Token 黑洞”
我复盘一下那个事故的完整过程,你就能直观感受到 Agent 可观测性和传统监控的差异。这个 Agent 的核心功能是帮运营人员批量整理商品信息:给定一批商品 ID,调用商品接口拿原始数据,再让 LLM 总结成结构化文案。
上线第一周一切正常。每个商品平均 2 次工具调用、1 次 LLM 调用,单商品成本可控。第二周开始,响应时间从平均 10 秒涨到 40 秒,运营同事开始投诉。当时团队能看到的指标:服务端 CPU 20%,内存 40%,全部正常;日志里没有 error;数据库慢查询有几条,但都是后台任务,和用户投诉时间对不上。一天后账单出来,token 费用涨了 3 倍,团队才意识到问题出在 LLM 调用上。
后来我帮他们把 LLM 调用日志单独拉出来按天聚合,发现问题出在第 9 天。那天有一条技能发布,改动了“商品摘要”的 prompt,把“如果商品描述缺字段,返回示例格式”改成了“请尝试从商品图片信息中补齐”。效果是:Agent 开始对一个缺字段的商品反复调用图像理解工具,每次工具返回结果它都觉得不够,继续让 LLM“再想想”。单个商品的 LLM 调用次数从 1 次变成 18 次,工具调用从 2 次变成 35 次。没有报错,只有 token 账单在加速狂奔。
这就是我常说的 Token 黑洞:一切指标正常,只有 token 消耗在无声扩张。没有 token 计量,这个事故只能等账单出来才发现;有了 token 计量和链路追踪,你可以在第一小时就看到单商品成本异常,并顺着 trace 找到是哪个 prompt 改动造成的。
1.3 Agent 可观测性要回答的三个新问题
所以我的结论是:Agent 可观测性不是把原来的监控体系延长一截,而是新增三个必须被观测的对象。
第一,执行轨迹。Agent 在运行时调用哪些工具、每次调用的参数和结果、模型做了哪些决策、有没有重试和循环。这需要分布式追踪,但 Span 的语义要重新设计,不能用“HTTP 调用”这种粗粒度概念。
第二,Token 消耗。Token 是 Agent 世界的 CPU 时间,是唯一能统一衡量“思考代价”的指标。一次请求烧了多少输入 token、多少输出 token、缓存命中多少,这个数据必须被埋点捕获,否则你既没法控制成本,也没法判断一条链路是不是高效。这个观点和“AI 应用成本分析”领域的主流思路是一致的:把 token 消耗当作应用的一等监控指标来看待。
第三,决策质量。Agent 输出了什么内容、有没有偏离目标、工具结果是否被正确理解。这部分很难自动化评估,但至少要把工具调用的返回状态、模型输出的摘要记录下来,便于事后复盘。很多团队上线 Agent 之后不看日志,就是因为他们觉得“反正模型会自己处理”,但模型处理得好不好,恰恰是最需要看的。
2. 给 Agent 做分布式追踪:Span 类型、埋点设计与全链路串联
2.1 Trace 与 Span 在 Agent 语义下的重新定义
分布式追踪的基本概念大家应该都听过:Trace 是一次完整请求的端到端执行轨迹,Span 是轨迹里的独立工作单元,Span 之间通过 parent-child 关系串联。这个框架本身没有问题,问题在于 Span 怎么定义。
在传统后端,一个 Span 通常对应一次 HTTP 请求、一次 RPC 调用、一次数据库查询。在 Agent 场景,如果你也这么埋,得到的就只是一堆零散的 HTTP 调用记录,根本看不出 Agent 的决策过程。我习惯把 Span 定义为“Agent 在这次任务中做的一件有语义的事情”,而不是“一次网络请求”。
举例来说,一次用户请求可能是:Agent 先分析意图(这是一个 llm.call Span),决定要查订单(这是一个 agent.plan Span),于是调用订单接口(这是一个 tool.call Span),拿到结果后又让模型总结(这是第二个 llm.call Span)。这四个 Span 串起来,才叫“一次 Agent 任务”。如果只记录 HTTP 调用,你会看到两次 LLM 调用分散在树里,它们之间是什么关系、为什么会有第二次,完全不可见。
2.2 我常用的五类 Span 与关键属性清单
我做了几个 Agent 项目后,沉淀了一套相对固定的 Span 类型,在团队里用下来效果不错。不一定是最佳实践,但至少能覆盖大部分诊断场景。
| Span 类型 | span.name 示例 | 父子关系 | 关键属性 |
|---|---|---|---|
| 任务级 | agent.task | 根 Span | task_id、session_id、user_id、goal摘要、agent_name |
| 决策级 | agent.plan | parent 为 agent.task | 决策内容摘要、选择工具、推理摘要 |
| 模型调用 | llm.call | parent 为 agent.plan 或 agent.task | provider、model_name、prompt_tokens、completion_tokens、cache_read_tokens、temperature、max_tokens |
| 工具调用 | tool.call | parent 为 agent.plan | tool_name、tool_args摘要、tool_result_status、error_type |
| 检索调用 | retrieval.call | parent 为 agent.plan | top_k、命中文档数、检索耗时 |
task_id、session_id、trace_id 这三层关联标识是我特别想强调的。trace_id 关联单次任务执行链路,像一张“任务过程快照”;session_id 关联同一个用户的多轮对话,用来观察上下文如何累积;task_id 则用来关联业务侧的订单号、工单号等业务实体。我在实际项目里踩过坑:一开始只埋了 trace_id,结果用户来投诉说“昨天那个对话回答错了”,我们对着 trace 查了半天,发现查到了用户昨天的另外一次任务,因为系统把 trace_id 重置了。后来强制要求三个 ID 同时落,业务排查效率高了一个量级。
2.3 埋点实现:一个不污染业务代码的 LLM 调用追踪装饰器
很多团队不乐意做埋点,是因为觉得埋点代码会污染业务逻辑。实际上用装饰器或者中间件可以完全解决这个问题,LLM 调用的埋点尤其适合做在统一出口上。下面这个例子是我在一个 Python 项目里实际用过的简化版,核心思路是:所有 LLM 调用都走同一个网关函数,装饰器在这个函数外自动创建 Span 并捕获 token 用量。
from opentelemetry import trace from opentelemetry.trace import SpanKind from functools import wraps tracer = trace.get_tracer("llm-gateway") def trace_llm_call(provider, model): def decorator(func): @wraps(func) def wrapper(messages, **kwargs): span = tracer.start_span("llm.call", kind=SpanKind.CLIENT) span.set_attribute("llm.provider", provider) span.set_attribute("llm.model", model) span.set_attribute("llm.prompt_tokens_estimate", estimate_tokens(messages)) try: resp = func(messages, **kwargs) usage = resp.get("usage", {}) span.set_attribute("llm.prompt_tokens", usage.get("prompt_tokens", 0)) span.set_attribute("llm.completion_tokens", usage.get("completion_tokens", 0)) span.set_attribute("llm.total_tokens", usage.get("total_tokens", 0)) cached = usage.get("prompt_tokens_details", {}).get("cached_tokens", 0) span.set_attribute("llm.cache_read_tokens", cached) return resp except Exception as e: span.set_attribute("error.type", type(e).__name__) trace.get_current_span().record_exception(e) raise finally: span.end() return wrapper return decorator这里面有几个细节值得说。第一,在调用前先估算 prompt_tokens,虽然不准,但能让你在模型真正返回前就看到 token 量级的初步信号,对追踪超长请求特别有用。第二,从响应里读取 usage 字段是标准做法,但要注意不同模型服务商的字段结构略有差异,有的放在顶层,有的放在 response_usage 里,网关层要做好适配。第三,缓存读取 token 这个字段太容易被忽略,但很多服务商对缓存 token 和普通输入 token 分别计价,而且差价很大,不单独记录会导致你的成本核算偏差巨大。
2.4 采样策略:元数据全量采集,内容按需快照
Agent 场景的采样策略和传统后端不一样。传统 APM 通常建议概率采样,比如采样 10% 的请求,因为流量大、低价值。但 Agent 请求往往低频高价值——一个任务可能消耗几千上万 token,而且模型行为随机,如果只采样 10%,出问题的那个任务很可能恰好没被采到,留下的都是正常请求,链路诊断失去意义。
我的做法是分层策略:所有 Span 的元数据全量采集,包括模型名、token 数、工具名、耗时、错误类型;但 prompt 内容和模型输出内容默认不采,因为在生产环境里 prompt 可能包含用户隐私或业务敏感信息,全量落盘会有合规风险。
只有当链路诊断需要时,才针对特定 trace_id 开启内容快照。实现上也不难:在网关层设一个开关,命中采样规则的请求把 prompt 摘要存到专门的存储,设置 7 天过期。我甚至见过更细的做法:只存 prompt 的前 200 个 token 和模型输出的前 200 个 token,对大部分问题诊断已经够了,而且把敏感信息暴露面降到最低。用摘要代替全量,这个思路在 Agent 可观测性里非常重要。
3. 链路诊断实战:Token 交换失败与上下文超限的完整排障路径
3.1 案例一:登录链路“token exchange failed”反复出现,怎么定位
Agent 应用里 token 这个词有两层含义,很多人一开始会被绕晕。一层是模型计费用 token,另一层是认证鉴权用 token(JWT、OAuth token)。我在项目里遇到最多的坑是后者:用户登录时控制台报“sign-in could not be completed, token exchange failed”,或者对接外部 API 时偶发“token exchange failed: 400 bad request”。这类报错排查起来特别容易踩坑,因为问题往往不在报错的那一行代码里。
我总结了一套可复用的排查路径,核心就是让链路追踪告诉你“token 交换到底卡在哪一步”。
第一步,先看错误率趋势。打开认证服务的错误率曲线,区分这是瞬时抖动还是持续故障。瞬时抖动大概率是某个基础组件重启或网络瞬断,持续故障才需要深入链路。第二步,在追踪系统里按 trace_id 拉出登录链路的完整 Span 列表,你会看到认证服务调用身份提供商的 Span 标红,展开后能看到 HTTP 状态码、请求 URL、响应体摘要。第三步,根据状态码分流:
- 400 Bad Request,常见于 refresh_token 为空或格式非法。我在一个项目里遇到过“invalid 'refresh_token': empty string. expected a string with minimum length 1”的报错,最后定位到代码里从本地存储取 token 时,key 拼错了,取到的是 None,不是 token 真的失效。这类问题没有链路追踪的话,只能靠人肉读代码猜。
- 403 Forbidden,常见于账号所属区域、IP 策略不匹配。这个和代码关系不大,问题在账号配置或网关策略。链路里记录的 provider 名和请求目标 IP 能帮你快速确认是这个原因。
- 网络层错误,比如“error sending request”,则是 API 不可达,需要检查网络策略、防火墙、域名解析,和 token 本身没有关系。
这个案例里我用“auth.example.com/oauth/token”这种通用形式来举例,实际项目里换成你的真实认证端点即可。
3.2 案例一背后的共性根因:Token 生命周期与续签机制设计
排查完单次故障之后,我会建议团队不要急着关 Issue,因为你很可能碰到了 Agent 长任务场景下最常见的共性问题——token 过期。JWT 和 OAuth access token 一般都有有效期,短则 30 分钟,长则几小时。Agent 任务往往比这更久,一个复杂的调研任务跑十几分钟很常见,如果中间调用了大量外部 API,很可能任务还没跑完,token 已经过期。热词里“jwt实现token续签”被反复搜索,就是因为这个需求在 Agent 项目里特别普遍。
我的建议是三层防护。第一,接入层用双 token 机制,access_token 短时效 + refresh_token 长时效,网关捕获 401 后自动用 refresh_token 续签,续签动作本身也作为一个 Span 被追踪,这样你随时能看出“这个任务中途续签了几次、失败了几次”。第二,在工具调用 Span 里记录 token 剩余有效期,接近过期时提前刷新,避免请求打到一半突然失败。第三,监控 refresh_token 的失败率,因为 refresh_token 一般是长期有效的,如果它频繁失败,说明本地存储有问题或者被服务端撤销了,那就不只是续签能解决的,而是整个会话体系出问题了。
3.3 案例二:单会话 Token 消耗逐轮上涨,最后撞上上下文窗口
第二个案例是这样的:一个客服 Agent 上线三周后,开始出现“context length exceeded”报错,而且用户反馈对话越来越慢。当时团队的第一反应是调大模型上下文窗口,但成本不允许。后来我帮他们看 Token 计量曲线,发现一个规律:prompt_tokens 是逐轮线性上涨的,每轮对话大约增加 1500 token。
顺着这个特征去查 llm.call Span,果然在属性里看到每一轮都把完整历史消息塞进 prompt,没有做任何压缩。客服对话长一点,prompt 里就塞了几万 token,一来慢,二来贵,三来把上下文挤爆。这个根因用链路追踪很好定位:在 Span 属性里新增两个字段,compression_before_tokens 和 compression_after_tokens,分别记录压缩前后的 token 数,再用时序曲线叠加看,一目了然。
修复方案是“滚动摘要 + 滑动窗口”:每轮对话结束时,用一个小模型把历史对话压缩成 300 token 的摘要,只保留最近 8 轮完整对话,加摘要一起进 prompt。上线后观察 Span 属性,prompt_tokens 从 4 万降到 6000,响应时间从 30 秒降到 8 秒,单会话 token 成本下降 70%。
3.4 Token 用量曲线本身就是最重要的健康信号
这两个案例放在一起看,你会发现一个共同点:真正定位问题靠的不是错误日志,而是 Token 用量曲线和链路属性的组合。Token 用量在这个场景里就是 Agent 的“体温曲线”,你不需要理解业务逻辑,单看曲线形态就能发现异常。
我常用的判断规则有这几条:第一,单会话 token 消耗呈线性增长,多半是上下文没有压缩;第二,单任务 token 消耗出现阶梯式跳升,多半是 prompt 模板改动或工具返回异常;第三,错误率没涨但 token 翻倍,多半是重试循环;第四,缓存命中率突然下降,多半是 prompt 前缀被改动,缓存全部失效。这几条规则看起来简单,但真正把它变成监控告警的团队不多,绝大部分是等到账单爆了才回头查。
4. Token 成本精细化核算:从网关计量埋点到业务维度分账
4.1 为什么常规“看账单”式核算根本不够用
很多团队算 token 成本的方式特别粗犷:月底看模型服务商账单,总额多少就记多少。这种“事后总额”完全支撑不了决策。你没法回答三个问题:钱花在哪个功能上?哪个 Agent 技能成本最高?哪个用户烧掉了最多的 token?
我记得有次和一位做 Agent 产品的朋友聊,他说自己也知道成本高,但说不清高在哪,只能无差别地限制所有用户的使用次数。结果把重度付费用户也限了,客户满意度直线下降。精细化核算本质上不是为了算账,是为了让你能“基于成本做决策”:是这个技能价值高值得保留,还是这个技能设计有问题导致成本虚高?没有分账数据,你只能一刀切。
4.2 计费埋点记录什么:一张审计字段表
我的做法是在 LLM 调用网关统一埋点,每个请求生成一条计费审计记录,字段表大致如下。这个表我调整过好几轮,前四类字段是必须的,后面两类是进阶。
| 类别 | 字段 | 说明 |
|---|---|---|
| 模型信息 | model、provider | 用于按模型单价核算 |
| Token 用量 | prompt_tokens、completion_tokens、total_tokens、cache_read_tokens | 核心计量数据 |
| 业务标签 | user_id、session_id、trace_id、agent_name、skill_name | 用于按业务维度分账 |
| 性能字段 | latency_ms、request_time | 用于联动性能分析 |
| 结果字段 | error_flag、retry_count、finish_reason | 用于异常识别 |
| 进阶字段 | app_version、prompt_template_version | 用于定位模板改动影响 |
特别要强调 skill_name 这个字段,它代表“这次调用是哪个具体技能触发的”。很多团队只记录 agent_name,粒度太粗,一个 Agent 里可能挂了十几个技能,成本差异巨大。按技能分账后你才能真正看到,原来“订单查询”这个技能单次成本是“天气查询”的 20 倍。
4.3 成本如何算:输入、输出、缓存读取要分开计价
计费字段拿到之后,成本计算本身也有一些门道。大多数模型服务商对输入 token 和输出 token 分别计价,而且现在越来越多的服务商支持 prompt 缓存,缓存命中的输入 token 价格远低于普通输入 token。如果核算时不区分这三类,成本会严重失真。
计算公式我建议这样写:
单次调用成本 = 输入token数(未命中缓存) × 输入单价 + 缓存读取token数 × 缓存单价 + 输出token数 × 输出单价下面拿一个假设的模型服务商价目表举个例子,仅用于演示算法,实际单价请以你对接模型服务商的最新合同或官网价格为准。
| 计费项 | 单价示例(每百万 token) | 用量 | 成本 |
|---|---|---|---|
| 普通输入 | 3 美元 | 8,000 | 0.024 美元 |
| 缓存读取 | 0.3 美元 | 20,000 | 0.006 美元 |
| 输出 | 15 美元 | 1,200 | 0.018 美元 |
| 合计 | - | - | 0.048 美元 |
同样的调用如果缓存命中率差,把缓存读取的 2 万 token 记为普通输入,成本就变成 0.06+0.018=0.078 美元,偏差超过 60%。所以缓存命中这个字段必须记录,它是成本核算准确性的基石。
4.4 三个高价值分析维度与一个成本黑洞案例
有了明细数据,我建议优先做三个维度的分析。第一个是按会话聚合,找单会话 Token 特别高的异常会话。正常用户的对话平均几千 token,突然出现一个 50 万 token 的会话,大概率是上下文泄漏或者死循环,这是最重要的成本异常信号。第二个是按 Agent 技能聚合,做成本排行。排行 Top 的技能不一定是坏事,但要计算单位成本对应的业务价值。第三个是错误率和成本的联合分析。高错误率配高成本是最危险的组合,意味着系统在重复做无用功。
我实际做过的一个客服 Agent 案例很典型。按会话维度看,平均每会话成本 0.35 美元,看起来完全正常。但按技能拆分后发现,一个“订单查询”技能的单次成本高达 2.1 美元,占了总成本的 42%。再钻取 trace 找到原因:查询时使用了全文检索,Top-K 设为 20,每次把 20 份文档全塞进 prompt,而真正有用的只有 1 到 2 份。调整后把 Top-K 降到 5,引入缓存,迭代了 prompt 模板,单次成本从 2.1 美元降到 0.8 美元,整体成本下降 37%。同样的业务量,省下来的都是利润。
5. 观测大盘的建设顺序与落地工具建议
5.1 别一上来就铺 OpenTelemetry,按这个顺序三步走
很多团队看别人上了分布式追踪,心痒痒,立刻引入 OpenTelemetry 把 Agent 框架的每一个节点都埋一遍。我劝你先冷静一下。全链路埋点改动量巨大,要动业务代码、要部署 Collector、要选存储后端,通常还没看到效果,业务团队就抗拒了。
我建议倒过来,按成本从低到高分三步走。第一步,先做 LLM 调用网关的计量埋点。这一步不涉及业务代码,只需要在统一网关函数里加日志,当天就能上线,当周就能看到 token 消耗成本全景。这笔投资回报率最高。第二步,统一业务日志格式,强制所有 Agent 任务在日志里输出 trace_id、session_id、task_id,让日志服务能够按 ID 聚合。这一步也不需要引入新组件,只是规范日志输出,但能解决 80% 的“根据用户反馈定位问题”场景。第三步,等团队意识到链路追踪的价值后,再逐步引入 OpenTelemetry 做完整 Span 埋点,覆盖 plan、tool、llm 等核心流程。
这套顺序我在两个团队里都实践过,第一阶段通常一两周就能完成,但带来的感知非常强。相比之下,直接铺 OpenTelemetry 的项目,大部分三个星期后还在折腾 Collector,业务方已经失去耐心。
5.2 看板上的核心指标:一张你抄得走的指标清单
观测大盘不用做得很花哨,关键是放对指标。我经过几轮精简,现在团队看板上固定放下面这组指标,每个指标背后都有具体的告警逻辑。
| 指标 | 计算粒度 | 告警规则示例 |
|---|---|---|
| 请求成功率 | 全任务 | 5 分钟成功率低于 95% |
| P95 端到端延迟 | 全任务 | 环比上升超 30% |
| LLM 调用错误率 | 全任务 | 错误率超过 5% |
| Token 日消耗总量 | 全任务 | 环比上升超 50% |
| 缓存命中率 | 全任务 | 低于 60% 时预警 |
| 工具调用失败率 | 按工具 | 单个工具失败率超 10% |
| 单会话 Token 消耗 P95 | 按会话 | 超过阈值触发检查 |
| 平均单任务 Token 数 | 按 Agent | 环比上升超 30% |
这里面有两个指标是我用很久之后才意识到重要的。一个是缓存命中率,它直接反映 prompt 模板是否稳定,模板频繁改动会导致缓存大量失效,成本上涨但业务量没涨。另一个是单会话 Token 消耗 P95,相比平均值,分位数更能暴露“最差的那批用户”在烧多少钱。
5.3 几个容易踩的坑和我的应对习惯
最后分享几个我在实践里反复踩过的坑,以及现在养成的应对习惯。
第一个坑:埋点本身拖垮了业务。LLM 调用的响应时间本身就慢,如果你在装饰器里做同步写日志、同步上报追踪数据,等于把慢路径又加长了。我现在一律要求异步上报,日志先写本地缓冲区,后台批量推送,绝不在业务线程里做网络 IO。还有就是要给追踪 SDK 加熔断开关,极端情况下观测系统自身故障不能影响业务。
第二个坑:只埋点不设预算熔断。Token 成本不像服务器费用是相对固定的,一个 bug 可以让成本在几小时内翻几倍。我现在会在网关层设置“预算熔断”逻辑:当某个 Agent 技能的日消耗超过设定阈值时,自动降级到更便宜的模型,或者暂停非核心技能,优先保住核心链路。这个和传统的限流不太一样,它的目的不是保护服务,而是保护钱包。
第三个坑:prompt 模板的版本没有纳入观测。改 prompt 是调成本最快的方式,也是最容易翻车的方式。我把 prompt_template_version 作为 Span 属性和计费字段记录之后,就找到过一次很隐蔽的成本上涨:一个运营同学改了 prompt 里的一个词,导致缓存命中率从 85% 掉到 20%,整体成本涨了 40%。如果没记版本号,这种问题只能大海捞针。
第四个坑:过度采集导致存储成本失控。全量采集元数据听起来很好,但一个 Agent 平台一天可能产生上千万条 Span,存储成本也不便宜。我的习惯是 Span 元数据保留 30 天,内容快照保留 7 天,超过时间的自动清理。可观测性本身也是成本,也要被纳入成本核算,别把监控做成了新的账单炸弹。
做 Agent 可观测性这段时间,我最大的体会是:这个领域还没有像 Web 监控那样的成熟标准,很多方案要自己摸索,但核心思路是清晰的——把 Agent 的执行轨迹和 Token 消耗当作第一等公民来观测。你先从网关计量埋点开始,把成本账算清楚,再逐步加链路追踪和诊断能力,这条路是通的。至于要不要上 OpenTelemetry、选 Jaeger 还是 ClickHouse 当后端,这些都是后话,先把最基本的 trace_id 打通,你就已经比大多数团队领先了。