一个用户、一次对话、一夜之间烧掉41.2美元代币费用。这不是攻击,是AI代理卡在了自己挖的坑里——连续187次调用同一个搜索工具,每次返回空结果,每次又把空结果喂给下一轮当"已有上下文"。

监控大屏上一切正常:日志完整,代币消耗曲线陡峭。唯独缺了一样东西——在凌晨4点12分循环启动的那一刻,没人能看见"同一工具、同一输入"正在疯狂重复。

打开网易新闻 查看精彩图片

这篇文章讲的就是怎么在第一轮迭代就抓住它。

一、账单背后的人:谁在凌晨4点12分值班?

故事的主角不是那位睡梦中的工程师,是支撑这套系统的观测逻辑。

OpenTelemetry的生成式AI语义规范把代理执行拆成两层:agent.turn作为父跨度(一次完整的用户请求到最终回复),下面挂着子跨度——每次大模型调用、每次工具执行。父跨度承载用户可见的工作,子跨度藏着循环的信号。

真正致命的不是"调用了187次",而是这187次在追踪界面里呈现的形状:一个父节点下面,扇形展开一排完全相同的tool.execute子节点。如果UI里看不见这个结构,日志再多也没用——循环会淹没在噪音里。

标准OTel Python配置就能拿到这个形状,不需要花哨方案。

二、代码即证据:三行配置换一张透视图

核心代码出乎意料地轻量。初始化追踪器,指向你的后端,完事。

关键在"turn"的定义:用户消息进,最终助手消息出,中间不管多少工具调用都算一次回合。这个括号就是父跨度的生命周期——消息到达时打开,返回最终答案或触达迭代上限时关闭。

作者给出的代码片段里,run_turn函数用start_as_current_span包裹整个执行流程,属性里塞入对话ID和代理名称。这些标签是让后续查询能定位到"同一用户、同一会话"的关键。

后端无关是刻意设计。OTLP协议把跨度送到哪都行,Jaeger、Grafana、自建平台——形状不变,逻辑不变。

三、从"能看见"到"能警报":重复调用的指纹

技术实现只是前半场。真正省钱的是那条"在账单到来之前触发"的警报规则。

作者没展开写这条规则的具体阈值,但逻辑清晰可推:监控同一父跨度下,同名工具、相同输入参数的重复出现。第一次重复就值得注意,第三次重复就该叫醒值班的人。

320%的月度涨幅发生在周日清晨,说明自动化监控的盲区比想象中大。代币消耗指标是滞后指标——等它报警,循环已经跑了两个半小时。结构化的跨度数据才是领先指标,能在第一次无意义重复时就暴露异常。

这里有个反直觉的点:空结果本身不是问题。代理把空结果当"已有上下文"继续推进,才是循环的燃料。追踪系统需要捕获的不只是"调用了什么",还有"输入输出如何在轮次间传递"。

四、为什么这事值得现在关注

41.2美元是个小数,但乘数效应吓人。一个代理、一个用户、一个周日夜晚。换成企业级部署,百个代理并发,账单数字会换个量级。

更隐蔽的成本在体验侧。那187次调用里,用户可能一直在等一个永远不会来的答案。代币烧光之前,信任已经烧光了。

OpenTelemetry的生成式AI规范还在演进,但"父跨度+子跨度"这个基础结构已经够用来解决最痛的场景。作者强调的"形状可见性"是个产品洞察——工具再强大,UI里呈现不对,人就没法用。

这套方案的真正价值不是技术复杂度低,是它把"调试循环"从事后验尸变成事中拦截。对于正在把代理投入生产的团队,这是从"能跑"到"敢跑"的关键一跃。

毕竟,没人想解释为什么客户的支持机器人在凌晨四点突然开始疯狂查询一个不存在的订单号——更不想解释的是,为什么花了两个半小时才发现。