让一位工程师列出其智能体的失败模式,你会听到幻觉、错误的工具调用和糟糕的 JSON。问到时间呢?你得到的只是一个耸肩。然而,当生产环境中的智能体出错时,最常见的情况并不是大声失败——它只是花费了太长时间。它陷入循环,重试一个不稳定的工具,等待一个永远不会流出第一个 token 的模型调用。而你的评估套件在事后针对最终出现的任何输出运行,却给它打了绿灯。

这就是盲点:我们将延迟视为 SRE 仪表盘的关注点,与正确性脱钩。但对于智能体而言,错过截止期限就是一次正确性失败。晚到 90 秒的摘要往往比没有摘要更糟糕——用户已经离开,下游作业已经超时,重试已经双倍收费。时间属于你的评估,并且它属于堆栈的最底层。

时间是第一层证据

如果你遵循过 agent-eval 背后的层级原则,你就会知道证据是根据独立性轴来排名的——从智能体无法伪造的证据,到它与之共享基质的意见——而不是根据成本轴。三个层级:

  • 第一层——智能体无法伪造的外部可观察证明:有效的 JSON、文件存在、代码已编译、测试通过、它在截止期限内完成、输出非空。
  • 第二层——针对智能体未编写的基线的统计信号:与任务的嵌入相似度、长度与重复性、差异是否实际改变了任何内容。
  • 第三层——模型作为评判者:一种共享基质的意见。一种信号,而非定论。

注意“在超时时间内完成”所处的位置。它是第一层,与“有效的 JSON”并列。这并非偶然。挂钟截止期限是你拥有的最独立的信号——智能体无法与秒表争论,无法通过推理绕过它,无法伪造它。物理定律对此进行评判。在你整个评估套件中,没有比“时钟已超时”更不可腐蚀的地面真相。

至关重要的是,第一层+第二层是你的实时门控:确定性的、成本接近零、速度快到足以处于热路径并阻止运行。超时检查是这种最纯粹的表达——它本身就处于热路径中。第三层,即评判者,则相反:按量计费、缓慢、非确定性、仅限离线。你永远不会将模型作为评判者放在关键路径上,以决定响应是否足够快。评判者甚至看不到时钟。

“被评为绿灯”实际上是什么样子

这就是陷阱。大多数评估工具接收 (input, output) 并对输出进行评分。output 是智能体最终返回的任何内容——因此根据构造,计时信息在评分开始前就已经被丢弃。评估根本看不到运行错过了截止期限,因为它只在运行结束后才存在。

你必须对运行进行评分,而不是对返回值进行评分:

type RunResult<T> =
  | { status: "ok"; value: T; elapsedMs: number }
  | { status: "timeout"; elapsedMs: number; deadlineMs: number }
  | { status: "error"; elapsedMs: number; error: string };

async function withDeadline<T>(
  work: (signal: AbortSignal) => Promise<T>,
  deadlineMs: number,
): Promise<RunResult<T>> {
  const ctrl = new AbortController();
  const started = Date.now();
  const timer = setTimeout(() => ctrl.abort(), deadlineMs);
  try {
    const value = await work(ctrl.signal);
    return { status: "ok", value, elapsedMs: Date.now() - started };
  } catch (err) {
    const elapsedMs = Date.now() - started;
    if (ctrl.signal.aborted) {
      return { status: "timeout", elapsedMs, deadlineMs };
    }
    return { status: "error", elapsedMs, error: String(err) };
  } finally {
    clearTimeout(timer);
  }
}

进入全屏模式 退出全屏模式

现在评估门控变得微不足道且具有确定性——一个在微秒内运行的第一层检查:

function tier1TimingGate(run: RunResult<unknown>): { pass: boolean; reason?: string } {
  if (run.status === "timeout") {
    return { pass: false, reason: `deadline miss: ${run.elapsedMs}ms > ${run.deadlineMs}ms` };
  }
  if (run.status === "error") {
    return { pass: false, reason: `crashed after ${run.elapsedMs}ms` };
  }
  return { pass: true };
}

进入全屏模式 退出全屏模式

超时的运行永远不会到达第二层或第三层。没有什么可嵌入的,没有什么可评判的——太晚到达的正确答案并不是正确答案。你短路,你阻止运行,你回退。没有模型被要求给出其意见,因为不需要任何意见。

截止期限是按预算的,而不是按智能体的

我接下来看到的错误是一个单一的全局超时。截止期限是上下文相关的:一个后台重新索引作业可能需要十分钟;一个交互式聊天回合在用户感觉到停滞前可能只有三秒。截止期限是调用站点的属性,而不是智能体的。将它作为任务契约的一部分传递下去,让每个工具步骤都继承并递减一个共享预算——这样,一个消耗 80% 预算的工具就让模型没有空间实际响应,而也是一个可评分的事件。

你无法对你未记录的时间进行评分

所有这一切都假设你确实捕获了每一步的耗时——而故事的评估部分需要其另一半。agent-eval 对输出进行评分和门控;只有当某物记录了计时,它才能执行第一层计时门控。这就是跟踪捕获的工作。AgentLens 对智能体的轨迹进行检测——每个模型调用和工具步骤,带有已解析的输入、原始输出以及开始/停止时间戳——因此“在截止期限内完成”是一个你可以从智能体未编写的、不可伪造的跟踪中读取的事实,而不是你希望有人记录的数字。

这种配对就是全部要点。跟踪是第一层+第二层进行评分的基质:因为 AgentLens 独立于智能体声称它做了什么,记录了每个步骤何时开始和停止,你的计时门控拥有真正的地面真相。一个智能体可以幻觉它“快速响应”。它无法编辑它未编写的跟踪中的时间戳。这就是评估在第一层+第二层确定性地捕获 80% 无聊失败(陈旧、崩溃、空、太慢)与一个仪表盘上绿色的对号安静地隐藏每一个迟到一分钟才勉强越过终点线的运行之间的区别。

将评判者留给主观的 20%——语气、帮助性、“这是否真正回答了问题”——并将其标记为意见,而非证据。但秒表呢?那是证据。把它放在第一位。