跳到主要内容

15 篇博文 含有标签「opentelemetry」

查看所有标签

OpenTelemetry 尾部采样器丢弃了复盘最需要的 LLM Span

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户联系支持:“助手让我注销服务来更新地址,这太疯狂了。”你的团队开启了故障处理程序,询问对话 ID,将其放入追踪(tracing)UI,结果得到了一句礼貌的 “未发现此 trace 的 spans”。24 小时的保留窗口在一小时前刚刚关闭。尾部采样器(tail sampler)判定这次对话是常规成功,因为响应是一个语法正确的 JSON 对象,返回状态码 200,耗时 1.4 秒。根据你的采集器所能理解的所有信号,什么都没发生。

模型返回的一句话毁掉了一段客户关系,而你的可观测性流水线却将其归类为无事发生。这不是采样器的 bug。采样器完全按照你的配置执行。问题在于,你编写的策略是为“请求-响应”的世界设计的,在那个世界里,“成功”与“值得保留”几乎是同一回事,而你未经修改就将其移植到了一个并非如此的系统中。

你的网关在 LLM 调用与工具执行之间丢失的 traceparent 请求头

· 阅读需 13 分钟
Tian Pan
Software Engineer

一名用户反馈 Agent 回答正确,但数据库从未更新。你打开可观测性工具,搜索用户端对话中标记的 trace ID,发现了一个清晰的树状结构——五次 LLM 调用,四次工具决策,一个最终回答。没有任何错误。接着你搜索负责数据库写入的工具服务,发现了另一个 trace,虽然墙钟时间窗口相同,但 trace ID 不同,根 span 不同,且没有关联回溯。你搜索网关日志。又发现了三个孤立追踪(orphan traces)。在聊天 UI 中看起来像是单次连贯交互的 Agent 运行,在你的追踪后端却分裂成了一片森林。

本应将这一切串联起来的请求头是 traceparent。它是一个 55 字节的 W3C 标准字符串,分布式系统中的每个 span 都用它来识别其父节点。然而,在大多数生产环境的 LLM Agent 技术栈中,它在用户请求与用户真正想要的副作用(side effect)之间,至少会被丢弃一次。

由客户端时钟而非网关标记时间戳的链路追踪时间线

· 阅读需 10 分钟
Tian Pan
Software Engineer

你打开了一个运行缓慢的对话追踪。模型调用竟然在用户点击发送前的 800 毫秒就开始了。你责怪了用户的笔记本电脑,关闭了标签页,继续处理其他事情。

这不仅仅是一个用户的时钟出了问题。这涉及大约三分之一的流量,而且每一个跨越客户端边界的调试会话都在读取一个根本不存在的时间线。浏览器时钟是用户可设置的,经常不同步,偶尔甚至会偏差好几天。大多数可观测性技术栈附带的检测 SDK 会使用设备报告的任何时间来标记客户端 span,通过 traceparent ID 将它们与同步服务器时钟标记的服务器 span 链接成一棵树,然后将结果交给你的值班工程师,就好像这两半具有可比性一样。它们并不具有可比性。

止于供应商边界的链路追踪

· 阅读需 12 分钟
Tian Pan
Software Engineer

你做了追踪(tracing)工作。检索有 span。工具调用有 span。编排循环有 span。Trace ID 贯穿每一个内部跳跃,记录在 W3C 的 traceparent 请求头中,正如 SRE 手册所说。然后请求到达 messages.create,SDK 记录了一个名为 llm.call 的单一 span,接着你流水线中接下来的 2.8 秒在火焰图上变成了一个没有任何内部结构的黑色矩形。首个 token 出现前的 800 毫秒:不透明。之后的 2 秒解码过程:不透明。你的追踪无法得知网络、队列等待、Prefill 或单 token 解码在总耗时中所占的比例。

当客户报告“今天助手感觉很慢”时,你的仪表板可以证实这种缓慢,但无法定位它。你流水线中最昂贵的一分钟——以美元、p95 以及用户感知的延迟来衡量——发生在供应商的数据中心内部,而你签约时接受的合同几乎没有给你任何可见性。你正在为一个黑盒值班(on call)。

在智能体交接处中断的分布式链路追踪

· 阅读需 12 分钟
Tian Pan
Software Engineer

你打开一个失败运行的追踪(trace)。Span 树非常漂亮:用户请求、规划者 Agent 的推理、三次工具调用、Token 计数、延迟,所有这些都整齐地嵌套在一起。然后规划者交接给一个专家 Agent —— 追踪到此结束。并不是出现了错误 Span。它只是停止了。接下来的内容是来自专家 Agent 的另一个、无根的追踪,它从思考的中途开始,没有父级,没有可见的输入,也与导致它的请求没有任何联系。

Bug 就存在于那个间隙中。一直以来都是如此。交接是一个 Agent 的假设与另一个 Agent 的理解相遇的地方,也是你的追踪无法跟随的唯一地方。

这不是日志记录的问题。你的 Agent 可能在两端都正确地发出了 Span。问题在于追踪上下文(trace context)—— 将 Span 缝合成一个故事的线程 ID —— 没能在从调用者到被调用者的跳转中幸存下来。你技术栈中的每个 HTTP 客户端和 gRPC 存根都会免费传播该上下文。但你的 Agent 交接没有这样做,因为没有人告诉它去这样做。

思维标记(Thinking Tokens)在你的日志中隐身,但在账单上却震耳欲聋

· 阅读需 10 分钟
Tian Pan
Software Engineer

第一个注意到你推理模型回退的人,几乎永远不会是工程团队的成员。而是财务分析师,在周二下午联系你的经理,因为上个月的 Anthropic 账单比前一个月高了 2.4 倍,而且“我们并没有发布任何会导致这种结果的东西”。你打开仪表板,查看请求量——平稳。p99 延迟——平稳。每个响应的输出标记——平稳。错误率——平稳。你六个月前配置的每一个面板都显示系统运行健康。财务人员看的是另一个数字,而且他们是对的。

他们看的数字是推理标记(reasoning tokens),而大多数可观测性栈是在这个领域出现之前构建的。

流式响应追踪模式鸿沟:为什么你的 APM 在 LLM 延迟上撒了谎

· 阅读需 12 分钟
Tian Pan
Software Engineer

凌晨 02:14,报警器响了:客户反馈助手在回答长问题时“话说到一半就卡住了”。你打开追踪(trace)。LLM 调用的 span 显示为 8.4 秒 —— 绿色,在 SLO 范围内,没有错误属性,结束原因(finish reason)为 stop。仪表板上该端点的 p95 延迟聚合组件显示为 9.1s,与过去一个月的情况完全一致。根据 APM 显示的所有信号,该请求都成功了。

用户看到前 200 毫秒表现完美,接下来的四秒钟生成了一个连贯的段落,然后眼睁睁看着同样的三句话片段在剩下的四秒钟里不断重复,直到连接结束。这种卡住的内容循环(stuck content loop)是真实的故障,但追踪系统对此一无所知 —— 因为追踪系统是为“返回即结束”的系统设计的,而不是为了这种行为表现为生成过程中产生的中间状态之墙的系统。

智能体可识别性:当 Trace 无法分辨哪个智能体执行了哪些操作时

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户报告说助手在上午 9:47 给了他们一个错误答案。你打开 Trace。这里有 340 个 Span。它们几乎都被命名为 agent.runllm.invoketool.call。有些有父节点,有些是兄弟节点。其中三个进行了重试,一个重试后被取消了。没有一个能告诉你错误的输出是来自 Planner、Worker、Critic、Reflection 过程,还是在 Critic 标记后 Worker 的第二次重试。

在接下来的一个小时里,你根据截图中的 UUID 前缀搜索日志行,比对 Slack 通知的时间戳,并根据 Trace 查看器中的缩进模式在脑海中重建 Agent 拓扑。最终,你猜想第三次 Worker 调用使用的模型别名在昨晚被静默切换到了另一个快照。但你无法仅凭 Trace 证明这一点。

Agent 正常运行,Trace 完整无缺。杂乱无章的 Trace 团(Hairball)本身就是 Bug。

Agent 追踪采样:当 “记录所有内容” 耗费 8 万美元却依然漏掉性能退化时

· 阅读需 11 分钟
Tian Pan
Software Engineer

账单在 3 月份寄达。仅追踪(traces)一项就花费了 8.1 万美元,而 11 月时这一数字仅为 1.2 万美元。团队在 10 月份启用了全量 Agent 追踪,理由是可见性越高越好。到了第一季度,可观测性成本的增速已经超过了推理成本——而当生产环境真正出现性能回归(regression)时,包含故障的追踪记录却被淹没在两千万个无人问津的成功 span 中。

错误并不在于决定进行埋点。错误在于将请求追踪(request-tracing)的心智模型引入了一个行为完全不像传统请求的工作负载中。

一个典型的 Web 请求会生成一个包含少量子节点的 span 树:处理器、数据库调用、缓存查找、下游服务。而一个 Agent 请求生成的树包含 5 个 LLM 调用、3 个工具调用、2 个向量查找、中间草稿(scratchpads),以及一个重新审视其中 3 个步骤的规划器。同样适用于 API 网关的采样策略——头部采样(head-sample)1%,保持其余部分的代表性——在 Agent 场景下会产生一个追踪存储库,其中中位数追踪是拥有 200 个 span 的怪物,长尾效应才是唯一关键的部分,而你发现故障的频率与你花钱的频率完全无关。

你的 Span 名称是未记录的 API:Agent 团队之间的遥测契约

· 阅读需 11 分钟
Tian Pan
Software Engineer

凌晨 3 点让财务部门收到告警的成本飙升其实并不是真正的成本飙升。那只是一个 Span 重命名。Agent 平台团队的某个人觉得 llm.completion.synthesis 应该改为 llm.generate.answer,因为这样读起来更自然。他们提交了一个小的 PR,运行了测试,然后发布了。三天后,财务的月度 Token 消耗仪表盘显示下降了 60%。没有人削减支出。聚合规则仍然按旧名称分组,而新的 Span 流向了一个仪表盘甚至没有渲染的 “其他” 桶中。账单没有变。仪表盘变了。

这是我一直看到团队在重复经历的一类事故。Span 名称和属性键并不是为了让人在追踪 UI 中阅读而存在的标签。它们是一个未公开 API 的公开 Schema,其消费者是生产团队从未谋面的——过滤它们的评估流水线、按它们分组的成本仪表盘、根据其持续时间触发的 SLO 告警、汇总其 Token 属性的 FinOps 报告。一个团队内部 “无害的重命名”,对于另外四个从未看过该 PR 的团队来说,就是一个网络协议破坏。

你的 LLM Span 在撒谎:APM 工具没告诉你的推理延迟真相

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的 LLM 调用耗时 2,340 毫秒。你的 APM Span 是这么记录的。这个数字是你可观测性堆栈中最昂贵的谎言,因为四种完全不同的故障模式都被渲染成了同一个不透明的紫色条块。长提示词下的 Prefill(预填充)浪涌。一个一小时没访问的租户导致的冷 KV 缓存。提供商连续批处理(continuous batching)中的“吵闹邻居”。一次无声的路由变更将你的流量导向了不同的区域。同样的 Span。同样的耗时。同样的 p99 告警。却是四个截然不同的复盘分析。

适用于微服务的分布式追踪准则 —— 每个网络跳数一个 Span、一个时长、几个标签 —— 在面对托管推理时失效了。LLM 调用并非单一实体。它是一个由多个阶段组成的流水线,每个阶段都有截然不同的扩展特性,运行在行为取决于队列中其他人的共享硬件上。将其视为一个单一且不透明的 Span,会导致你花费三天时间去调试“模型变慢了”,而实际上模型根本没变。

跨 Agent 服务边界的分布式追踪:上下文传播的断裂

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数分布式追踪方案在引入 Agent 之前都运作良好。一旦系统中出现 Agent A 跨微服务边界调用 Agent B——Agent B 调用工具服务器、工具服务器再查询向量数据库——原本连贯的端到端视图就会碎片化为互不相连的片段。追踪后端展示的是一个个孤立的操作,而你失去的是因果链:为什么某件事发生了,哪个用户请求触发了它,以及那 800 毫秒究竟消耗在了哪里

这不是监控配置问题,而是上下文传播架构问题。它有着特定的技术形态,大多数团队都是在付出代价后才意识到这一点。