度量智能体延迟。
对智能体来说,按次调用的中位数是错的仪器:一条十五步的轨迹会把"百分之一的慢调用"变成"七分之一的慢任务"——一旦串成链,尾部就成了典型体验。请度量轨迹、按尾部做预算,并把"用户真正感受到的等待"与"你碰巧记进日志的墙钟时间"分开,否则你优化的会是一个没人体验得到的数字。
单位是轨迹。调用只是其中一个部件。
智能体栈里几乎每一块延迟看板,都是从 API 思维里继承来的:请求进去、毫秒出来、按端点分位。这套仪器对单次补全是对的,对智能体则在结构上有误导性——因为用户等的不是一次调用,而是一个任务,而这个任务是一条长度不定的链,链长本身还是个随机变量。
- 按已完成任务报告时长,以用户实际提出的诉求为键,并把步数一并记下。同一任务的两次运行都是 40 秒——一次四步、一次十九步——那是两套系统穿着同一个数字。
- 步数是延迟指标,不是质量指标。它通常是轨迹时长最大的单一驱动因素,也是对提示词改动最敏感的那个;所以当 p95 在基础设施毫无变化的情况下移动时,它是第一个该拉出来看的图。
- 失败与撞上限的运行属于这份分布。把它们剔掉,恰恰是在用户最难受的地方美化数字:一次在 300 秒撞上步数上限、什么也没返回的运行,是你系统里最糟糕的延迟事件;把它当错误丢掉,会让你的延迟图随着产品退化而变好看。
- 重试是时长的一部分。如果编排层静默地把一次失败的工具调用重试了三遍,用户等的是四次尝试。记总和,不要只记成功的那一次——见幂等与重试。
串成链之后,尾部就成了中位数。
这是撑起整页的那道算术,值得明明白白算一遍,因为直觉在这里是反的。如果单步有 1% 的概率落进它的慢尾,那么一条十五步的轨迹里至少含一个这样的步骤的概率是 1 − 0.99¹⁵ ≈ 14%。调用层面的百里挑一,到了任务层面就是七里挑一。
- 优化中位步骤几乎挪不动任务。把一个本就很快的 p50 再削掉 15%,对总和的改变微乎其微;而消掉 p99 那根尖峰,改变的是七分之一用户的体验。预算该往尾部投。
- 在共享的无服务器算力上,你的尾部是别的租户。这是支持专用算力的一条分量很重、却常被忽视的论证——可预测性才是产品,成本只是事后写上的理由。见预置吞吐与用量承诺。
- 长程智能体是极端情形。到了四十步,每步 1% 的尾部会变成每任务 33% 的概率。轨迹长度与尾延迟是相乘的,这也正是"给步数设上限"既是成本控制、也是延迟控制的原因。
- 分位数不能相加。你无法把各步的 p95 加起来得到轨迹的 p95。请端到端度量轨迹,让分布自己说话,而不是拿部件分位数拼出来。
如果这一页你只带走一个数字:单步的 p99 比单步的 p50 更能预测你用户的体验;而它恰恰是几乎没有厂商公布、也几乎没有团队画图的那个数。
先把轨迹拆开,再去怪模型。
团队总是先假设慢在模型上,等真正埋点之后又总是发现慢在别处。一条轨迹的墙钟时间会分成四个桶,各自需要不同的修法与不同的负责人。
- 模型时间——首 token 时延加上生成。它对上下文长度敏感,所以会随轨迹增长而增长;同一个模型上,第十二步的调用就是比第二步慢,而原因与厂商毫无关系。
- 工具时间——智能体调用的外部系统。经常是最大的那个桶,也几乎总是尾部最糟的那个,因为它继承了背后那套遗留 API 的延迟特征。
- 排队与调度时间——限流退避、并发上限、冷启动、等一个沙箱。在朴素的追踪里它是隐形的,因为那会儿什么都没在执行;而在高负载下它常常是主导项——限流与厂商容量。
- 你自己的编排——序列化、检索、护栏检查、上下文拼装。每步都不大,但要乘上步数——这就是一次 200 毫秒的检索调用,如何变成一条轨迹里的八秒钟。
把这四项作为独立 span、共用一个轨迹 ID 记下来,归因问题就自己回答了。这正是追踪与可观测性里的结构性论证;而 span 的类型早已标准化——请用它们,不要自己发明标签,见 OpenTelemetry GenAI 语义约定。
"首个有用输出"与"做完"是两个不同的 SLO。
"什么时候出现了东西"与"任务什么时候完成"是两个不同的问题,答案不同、负责人不同、用户也不同;把它们塌缩成一个指标,是这个领域里最常见的度量错误。
- 对交互式智能体,被感受到的延迟是"到首个有用输出的时间"。不是首个 token——把转圈换成一个 token 不算进展。而是用户第一次得知某件他原本不知道的事:一份计划、一个找到的文件、一段部分答案。
- 对后台智能体,首个输出的时间几乎无关紧要,完成时间就是一切。用同一套方式给两者埋点,会把你推去为一个没人在看的负载优化流式输出——这一区分见后台编码智能体。
- 被感知的时长不等于被测量的时长。一个可见且诚实的进度信号会显著改变容忍度,这意味着有一部分延迟工作其实是界面工作而不是基础设施工作——等待与延迟体验。
- 当心那种"多流式、晚完成"就会变好看的指标。如果你只画首输出,你就会精确地奖励这笔交换。永远两张图并排画。
每步记够信息,好让这个数字日后还重建得出来。
一个没有上下文的时长,是六周之后你没法据以行动的数字——那时 p95 已经挪了位,而没人记得改过什么。下面这些字段几乎不花成本,却是"诊断出一次回归"与"靠猜"之间的分水岭。
- 每次调用的模型 ID 与版本。厂商会在稳定的别名底下换模型;你这边没有任何发布却出现延迟位移,通常就是这个。请钉住并记录,见模型下线与迁移。
- 输入 token 数与命中缓存的 token 数。首 token 时延与提示词长度贴得很紧;而缓存命中率的下滑,看上去和厂商变慢一模一样,实际上完全是你自己的路由问题。这是提示词缓存的运维一半。
- 步序号与终止原因。第几步、总共几步、以及运行为何停下——答完了、放弃了、撞上限了、报错了。终止构成对延迟与质量都是先导指标。
- 把排队等待记成它自己的 span。如果它被折进模型调用里,你会花一个季度去谈一场找错了对象的厂商沟通。
- 把慢的运行整条留下。朴素的头部采样恰好丢掉你想研究的那条尾巴;请缓冲到运行结束,并把每一条慢的、撞上限的、失败的轨迹整条保留——轨迹采样与保留。
写一个熬得过模型更换的 SLO。
智能体的延迟不是你代码的固有属性;厂商重路由时它会动,提示词变长时它会动,某个工具的负责人上线一条更慢的查询时它也会动。一个假定稳定的 SLO,要么永远处于违约,要么永远毫无意义。
- 把目标定在轨迹上、定在某个分位上、并按任务类别分开定。"95% 的工单分诊任务在 45 秒内完成"是可行动的。"模型延迟 p50 低于 800 毫秒"是一个部件指标假扮成了对用户的承诺。
- 把类别分开。一次单工具查询与一个十二步的调研任务不属于同一份分布;把它们平均起来,得到的数字既描述不了这个也描述不了那个,还把两边的回归都藏了起来。
- 在评测集里给延迟一份显式预算。一次为了半个点质量而多加三步的提示词改动就是一次回归,而它能通过任何只给正确性打分的评测——评估驱动的智能体开发。
- 在需要之前就定义好降级模式。预算被打穿时砍什么——换更小的模型、少跑几轮检索、还是把步数上限调低?提前定一次,见优雅降级与兜底。
- 对步数与终止构成的位移告警,不要只对时长告警。这两者都会先于时长分位数动起来,而这买到的正是告警唯一能买到的有用东西:时间。
先做这件事:取一周的生产轨迹,把轨迹时长对步数画出来,并按终止原因上色。几乎每支团队都会发现同样的两件事——一条完全由"撞上限、且从来没人统计过"的运行构成的长尾,以及某一个工具的 p99 比它的 p50 高出一个数量级。这两项发现的价值,通常抵得过一个季度的模型优化。
相关:SLO 与错误预算看外围实践,并发与扩缩容看这些数字在高负载下会怎样,以及成本、质量与延迟看它所处的那个三方权衡。