跳到主内容
创建时间:最后更新:作者:白川

评估最终结果,也检查关键执行行为

同时验证结果、实际工具证据和输入约束,避免把唯一工具顺序写成正确答案。

评估最终结果,也检查关键执行行为

一个报告可以写对,但没有读取实际文件;一条工具轨迹也可以看起来合理,却输出错误结论。结果与行为是两组证据,不能用其中一组代替另一组。

本文为前面的四个任务编写确定性评分器:最终结果准确、必需检查真正发生、教学输入未被改动。模型评委只留给难以写成断言的解释质量,不先给客观问题增加不确定性。

独立示例位于仓库 columns/coding-agent/examples/evaluation-optimization/,固定 Pi 相关依赖为 1.5.0。先在该目录执行 npm install,下文命令也从此处运行。离线样本不需要密钥;真实模型入口另行配置,不会由测试自动触发。

分别计算结果与行为

src/grading.ts 的完整评分逻辑如下:

ts
import type { Report, Task } from "./tasks.js"

export interface ToolEvidence {
  name: string
  file: string
  result?: Report
  isError: boolean
}
export interface Artifact {
  response: string
  tools: ToolEvidence[]
  unchanged: boolean
}

export function parseReport(text: string): Report | undefined {
  let value: unknown
  try {
    value = JSON.parse(text)
  } catch {
    return undefined
  }
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
    return undefined
  }
  const data = value as Record<string, unknown>
  if (
    Object.keys(data).sort().join(",") !== "file,names,status" ||
    typeof data.file !== "string" ||
    !["ok", "missing"].includes(String(data.status)) ||
    !Array.isArray(data.names) ||
    !data.names.every((name) => typeof name === "string")
  ) {
    return undefined
  }
  return data as unknown as Report
}

export function sameReport(left: Report | undefined, right: Report) {
  return (
    left !== undefined &&
    left.file === right.file &&
    left.status === right.status &&
    JSON.stringify([...left.names].sort()) === JSON.stringify([...right.names].sort())
  )
}

export function grade(task: Task, artifact: Artifact) {
  const expected = task.expected
  const outcome =
    expected === "ready"
      ? artifact.response.trim() === "ready"
      : sameReport(parseReport(artifact.response), expected)
  const behavior =
    expected === "ready"
      ? artifact.tools.length === 0
      : artifact.tools.some(
          (tool) =>
            tool.name === "inspect_file" &&
            tool.file === task.file &&
            !tool.isError &&
            sameReport(tool.result, expected),
        )
  const reasons = [
    ...(!outcome ? ["最终结果不符合任务"] : []),
    ...(!behavior ? ["缺少必需的执行证据,或负向任务误用工具"] : []),
    ...(!artifact.unchanged ? ["教学输入文件发生修改"] : []),
  ]
  return { outcome, behavior, unchanged: artifact.unchanged, passed: reasons.length === 0, reasons }
}

JSON 合法只说明语法通过,字段、枚举、文件名、名称集合仍要核对。这里要求三个固定字段,不接受额外字段或代码围栏,因为任务明确约定纯 JSON 报告。若真实产品允许自然语言解释,就应修改契约,不能沿用这一规则误伤合法回答。

工具证据来自实际循环的执行结果;离线夹具可以手写同样结构,但两者的数据来源必须分别标记。只有最终文本正确而 tools 为空,outcome 为 true、behavior 为 false,整个任务仍不通过。

检查必要行为,而非唯一轨迹

需要读取真实文件时,要求存在匹配文件的成功工具结果;不检查文件的负向任务则要求零次工具调用。任务没有规定其他动作顺序,评分器也不强制每条轨迹与参考序列完全一致。

AgentEvals 的轨迹评估提供不同严格程度的匹配方式,可帮助理解必要调用与严格顺序的区别。本例保持自定义简单规则,不依赖该框架。

额外工具是否允许由任务决定。本例不会仅因存在额外轨迹记录就失败;越权操作应另列硬约束,浪费调用则交给预算或效率维度,不能用“跟参考不一样”统一否决。

硬性约束不要被平均分稀释

如果报告正确但输入文件被改,不能靠格式和语言两项高分把结果拉回“合格”。本例采用三个条件全部满足的规则,reasons 保留具体失败原因。

unchanged 只对两份教学输入比较前后内容,不是整个文件系统审计。真实修复任务需要检查允许修改范围、隐藏测试和其他副作用;不要把这个布尔值描述成“操作系统未发生任何改变”。

用错误样本检验评分器

测试包括:正确答案缺少工具证据、工具 isError、重复名称、额外字段、无效状态、输入修改和负向任务误用工具。另有一条合法替代轨迹,确保评分器没有过度限制。

任务低分表示一次有结果的行为没有达到标准;Provider 请求异常、运行器损坏或评分器抛错则是不同状态。不能 catch 所有异常然后返回 false,否则模型质量、服务可用性和测试质量就混在同一个成功率里。

官方扩展评测同时检查扩展加载、真实工具结果及最终回复,说明多层证据可以共同约束一个任务。具体权重与聚合方式仍应按任务设计,不照抄某个示例的阈值。

依据与版本

官方本地快照为 8efd314fa,evals 包标记 1.3.0;本系列示例另按发布包 1.5.0 核验。官方 evals 是 private 工作区包,教程使用自定义教学协议,不冒充稳定公共评测 SDK。