编码母题 / P1 / AI 全栈与 Agent 工程 / 03

执行轨迹收集与评测运行器

先读清题意与边界,独立尝试,再展开思路并对照可运行的参考代码。

重要程度岗位专项难度困难预计完成45–60 分钟

难度按 LeetCode 简单/中等/困难三档估计;工程题并非 LeetCode 官方题目。用时指独立编码并检查主要边界的参考时间,因经验而异。

题目是什么

收集 run/step 的耗时、Token、成本和错误;评测器并发运行数据集,单用例失败不影响其他用例,并汇总成功率等指标。

输入输出示例

三个评测用例中一个失败,应保留三条结果而不是中断整个批次。

练习起手式与验证

复制代码后补全空方法,再运行下方三组验证;复杂题所需的辅助类型与输入准备一并放在代码块中。

/*
 * 题目:执行轨迹收集与评测运行器
 * 要求:收集 run/step 的耗时、Token、成本和错误;评测器并发运行数据集,
 * 单用例失败不影响其他用例,并汇总成功率等指标。
 * 示例:三个评测用例中一个失败,应保留三条结果而不是中断整个批次。
 */
class TraceCollector {
  constructor(now = Date.now) {}
  start(runId, metadata = {}) {}
  step(runId, step) {}
  end(runId, status, error = null) {}
}
async function runEval(dataset, { agent, scorer, concurrency = 2 }) {}

(async () => {
  let time = 0;
  const trace = new TraceCollector(() => time);
  trace.start("r");
  time = 5;
  trace.step("r", { name: "tool", tokens: 10, cost: 0.1 });
  time = 9;
  const finished = trace.end("r", "completed");
  console.log(finished.duration); // 预期:9
  console.log(finished.usage.tokens); // 预期:10
  const report = await runEval(
    [
      { id: "1", input: 2, expected: 4 },
      { id: "2", input: 3, expected: 6 },
    ],
    {
      agent: async (n) => ({ output: n * 2, cost: 0.01 }),
      scorer: async (actual, expected) => Number(actual === expected),
    },
  );
  console.log(report.summary.averageScore); // 预期:1
})().catch(console.error);

解题思路

展开思路、不变量与常见边界

关键切入点:每个 case 按输入索引写回;agent、scorer、clock 可注入,保证评测可重复。

核心模型:评测依赖结构化 Trace,而不是日志字符串;agent/scorer/clock 均注入, 保证测试可重复。每个 case 按输入索引写回,结果顺序稳定。生产系统还需版本标签、 数据集快照、置信区间、人工复核及敏感数据脱敏。

参考代码与验证

“关键机制”注释用于解释实现,下载链接提供未经页面注释修改的原始 JS 文件。

// 关键机制:每个 case 按输入索引写回;agent、scorer、clock 可注入,保证评测可重复。
class TraceCollector {
  constructor(now = Date.now) {
    this.now = now;
    this.runs = new Map();
  }
  start(runId, metadata = {}) {
    if (this.runs.has(runId)) throw new Error("duplicate run");
    this.runs.set(runId, {
      runId,
      metadata,
      startedAt: this.now(),
      endedAt: null,
      steps: [],
      usage: { tokens: 0, cost: 0 },
    });
  }
  step(runId, step) {
    const run = this.runs.get(runId);
    if (!run || run.endedAt !== null) throw new Error("run not active");
    run.steps.push(structuredClone(step));
    run.usage.tokens += step.tokens || 0;
    run.usage.cost += step.cost || 0;
  }
  end(runId, status, error = null) {
    const run = this.runs.get(runId);
    if (!run || run.endedAt !== null) throw new Error("run not active");
    run.endedAt = this.now();
    run.status = status;
    run.error = error ? String(error.message || error) : null;
    return structuredClone({ ...run, duration: run.endedAt - run.startedAt });
  }
}
async function runEval(dataset, { agent, scorer, concurrency = 2 }) {
  const results = new Array(dataset.length);
  let next = 0;
  async function worker() {
    while (true) {
      const index = next++;
      if (index >= dataset.length) return;
      const item = dataset[index];
      const started = Date.now();
      try {
        const execution = await agent(item.input, item);
        const score = await scorer(execution.output, item.expected, execution);
        results[index] = {
          id: item.id,
          ok: true,
          score,
          latency: Date.now() - started,
          cost: execution.cost || 0,
        };
      } catch (error) {
        results[index] = {
          id: item.id,
          ok: false,
          score: 0,
          latency: Date.now() - started,
          cost: 0,
          error: String(error.message || error),
        };
      }
    }
  }
  await Promise.all(
    Array.from({ length: Math.min(concurrency, dataset.length) }, worker),
  );
  const totals = results.reduce(
    (a, r) => ({
      score: a.score + r.score,
      latency: a.latency + r.latency,
      cost: a.cost + r.cost,
      success: a.success + Number(r.ok),
    }),
    { score: 0, latency: 0, cost: 0, success: 0 },
  );
  const count = results.length || 1;
  return {
    results,
    summary: {
      cases: results.length,
      successRate: totals.success / count,
      averageScore: totals.score / count,
      averageLatency: totals.latency / count,
      totalCost: totals.cost,
    },
  };
}

// 三组以上可直接运行的验证;异步题使用独立执行块。
(async () => {
  let time = 0;
  const trace = new TraceCollector(() => time);
  trace.start("r");
  time = 5;
  trace.step("r", { name: "tool", tokens: 10, cost: 0.1 });
  time = 9;
  const finished = trace.end("r", "completed");
  console.log(finished.duration); // 预期:9
  console.log(finished.usage.tokens); // 预期:10
  const report = await runEval(
    [
      { id: "1", input: 2, expected: 4 },
      { id: "2", input: 3, expected: 6 },
    ],
    {
      agent: async (n) => ({ output: n * 2, cost: 0.01 }),
      scorer: async (actual, expected) => Number(actual === expected),
    },
  );
  console.log(report.summary.averageScore); // 预期:1
  console.log(report.summary.totalCost); // 预期:0.02


})().catch(console.error);

复杂度分析

时间复杂度:O(n),不计 agent/scorer。调度并汇总 n 条评测用例各一次。

空间复杂度:O(n)。结果和统计输入保存每条用例的输出。

下载原始 JS 文件