GitHub PR #1192
工具化评测环境 judge
这个 PR 把评测从“看最终答案是否匹配”推进到“在受控业务环境里验证结果”。worker 只通过公开工具完成任务,judge 则作为独立 agent session 调用审计工具,从最终状态、动作日志、状态差异和最终回复多个角度判断任务是否真的完成。
一句话结论
PR #1192 新增了一套可编译、可托管、可审计的工具化评测环境,让复杂业务任务的评测可以检查“状态是否正确改变、是否有错误副作用、worker 是否按受控工具办事”,而不再主要依赖静态字段或最终文本匹配。
影响范围
- 新增 Go CLI 命令:
blade eval-env和blade eval-package。 - 新增 Python EnvSpec compiler 和 runtime 模板。
- 新增 server route:注册 runtime、注销 runtime、代理 tool-call。
- 新增 rehearsal runner、prompt、archive、RunSpec 支持。
- 新增 easy / medium / hard 三个示例评测环境 IR。
- 更新 Docker / Makefile,把 CLI tools 一并打包到产物。
工作流
env_spec.yaml,描述业务实体、工具、case、目标状态和禁止副作用。
blade eval-env compile 生成 env_runtime、worker package 和 judge 材料。
call_tool.sh 调业务工具,不能直接接触 SQL、hidden goal 或 verifier。
/api/eval-env/tool-call 代理到隔离环境。
主要新增能力
受控环境编译
cli/tools/evalenv/compiler.py 将 env_spec.yaml 编译成可执行环境,包含 SQLite 初始状态、工具 controller、worker 可见包和 judge 审计材料。
工具化调用协议
worker 面向的是业务工具,而不是数据库或验证器。工具前置条件失败会以业务错误返回,协议错误会被统一包装,便于 judge 区分“业务失败”和“调用方式错误”。
独立 judge runner
rehearsal runner 支持 worker 完成后启动独立 judge session。judge 不复用 worker 上下文,而是基于环境证据进行复核。
安全与隔离设计
- server 只允许注册受控的本地 runtime root,并通过 token 校验 tool-call。
- runtime 注册记录按 user 和 run_id 隔离,并有 TTL 清理。
- 后续修复加入 runtime map 锁,避免并发注册、清理、调用时的数据竞争。
- case_id 增加路径穿越校验,避免通过
../访问其他 case 或 runtime 文件。 .gitignore排除env_runtime/、work/、judge/等生成产物。
是否可以改成非侵入 BA 的方案?
可以外置的部分
| 当前 PR 内容 | 外置方式 | 是否需要改 BA |
|---|---|---|
| EnvSpec compiler 与 runtime 模板 | 放到独立 blade-eval-env 包或 eval 仓库,生成 work.zip、judge briefing、审计材料。 |
不需要 |
| rehearsal runner / RunSpec | 用 blade-agent-kit Python SDK 调 BA:创建 worker session、上传输入、发送任务、订阅结束事件、下载输出。 |
不需要 |
| judge agent session | runner 再创建一个 judge session,把 audit JSON、state diff、action log 摘要和 worker 输出作为上下文输入。 | 不需要 |
| 示例 IR 与评测包构建 skill | 放在评测仓库或 SDK 示例目录,作为外部评测资产维护。 | 不需要 |
| TypeScript REST 类型更新 | 如果不新增 BA eval-env route,就不需要污染前端 SDK OpenAPI 类型。 | 不需要 |
真正卡住外置化的点
当前 PR 最侵入 BA 的部分是 /api/eval-env/tool-call:它让 worker 在 BA 沙盒里调用一个由 runner 托管、worker 看不到实现细节的受控 runtime。这个能力的价值是“worker 只能通过业务工具行动,不能读 hidden goal、verifier、SQLite 或 reference trajectory”。如果完全不改 BA,也不提供等价回调通道,就只能二选一:
- 把 runtime 放进 worker 包里:实现简单,但 worker 有机会看到工具实现和隐藏材料,隔离性下降。
- 让 worker 调外部服务:可以隔离 runtime,但需要沙盒能访问外部 eval service,并要处理网络、鉴权、生命周期和审计归档。
更推荐的拆分方案
| 层次 | 建议 | 原因 |
|---|---|---|
| 外部 eval harness | 保留 EnvSpec、compiler、runner、judge orchestration、样例 IR 和审计逻辑。 | 这些是评测产品能力,不是 BA 的通用运行时能力。 |
| BA SDK | 复用现有 Python SDK 的 session、upload、chat、download、headless 能力;缺少的只在 SDK 补薄封装。 | runner 作为第三方调用方可以完整 dogfood BA,而不是把评测逻辑塞进 server。 |
| BA 主体 | 如果必须支持隐藏 runtime 的实时工具调用,只新增通用能力:session-scoped external tool bridge 或 MCP/tool-provider 注册,而不是 eval-env 专用 route。 |
通用扩展点可服务评测、业务插件、外部系统工具,不会把评测概念固化进 BA server。 |
| 短期 MVP | 先做纯 SDK runner:worker 包含公开工具 wrapper,runner 在外部完成审计;需要强隔离的 case 暂走外部服务回调。 | 能快速验证工具化 judge 的价值,同时把 BA 改动压到最小。 |
blade-agent-kit 调 BA;第二部分如果确有必要,再单独设计一个通用 external tool bridge。这样可以保留工具化 judge 的核心价值,同时避免在 BA server、CLI、OpenAPI 类型和镜像构建里引入大量评测专用代码。
参考 agent-octagon 后的细化判断
[本地路径已隐藏] 已经验证了一个更低侵入的形态:评测平台独立运行,BA 只是被 SDK 调用的 agent runtime。环境状态、工具鉴权、trace、scorer、对比视图都在 Octagon 自己的进程和数据目录中,BA 不需要新增 eval 专用 API。
Octagon 的关键设计
| 设计点 | Octagon 做法 | 对 PR #1192 的启发 |
|---|---|---|
| 环境是外部黑盒 | Env Attempt Server 暴露 /attempts/{id}/tools/{tool},鉴权后调用 env 的 core.py。 |
eval-env server route 不应放进 BA;应放在评测 harness 进程中。 |
| BA 工具只是传送门 | blade_skill/tools.py 不 import env core、不写 DB,只读取 .octagon/attempt.json 后 HTTP 回调 Octagon。 |
PR 的 call_tool.sh / tool proxy 可以变成外部生成的 skill 薄壳,不污染 BA CLI/server。 |
| 多 agent 入口统一 | Blade 走 skill HTTP 回调;Claude/Codex 走 MCP stdio;最后都汇聚到同一个 env attempt server。 | 评测环境入口应面向多 agent 设计,不应绑定 BA 的专用 route。 |
| trace 在环境层落盘 | @env_tool wrapper 自动记录 trace.jsonl,adapter 不负责写环境轨迹。 |
PR 的 action log / state diff 应由 eval harness 统一生成,BA 只提供 agent 事件和 workspace 产物。 |
| runner 通过 SDK 调 BA | BladeAgentClient 创建 session、上传 attempt 配置、stream chat、读 history、删除 session。 |
PR runner 应优先使用 blade-agent-kit,而不是把 runner 逻辑推进 BA 内部。 |
| scorer 独立于 agent | scorer 读取 env DB、trace、final_state,不读 agent thinking。 | 工具化 judge 可以作为 scorer 后的解释层,而不是替代确定性审计。 |
推荐的目标架构
blade-agent-kit 创建 headless session,上传 attempt 配置和任务物料。
PR #1192 可拆分清单
| 当前变更 | 建议归属 | 处理方式 |
|---|---|---|
server/routes/eval_env.py |
移出 BA | 改为外部 harness 的 Env Attempt Server。BA 不需要注册 runtime,也不需要保存 _RUNTIMES。 |
cli/internal/cmd/eval_env.go |
移出 BA 或降级 | 作为外部 blade-eval CLI;BA CLI 只保留通用能力,不打包评测 compiler。 |
cli/tools/evalenv/compiler.py |
外部 eval 包 | 保留价值很高,但应由 eval harness 发布和版本化。 |
scripts/eval_rehearsal_runner.py |
外部 eval runner | 重写为 SDK adapter 模式:create_session、upload_file、chat、get_history、download_file。 |
evals/thinking_envs/* |
外部评测资产 | 保留为 eval repo 的 fixtures;BA 仓库只可放轻量示例或链接。 |
web/packages/agent-kit/src/client/types/rest.ts |
不应变化 | 删除 eval-env 专用 OpenAPI 类型。若 SDK 缺方法,只补通用 session/file/history 方法。 |
core/builtin_skills/.../eval-package-builder |
谨慎外置 | 如果这是评测作者工具,应作为外部 skill 或模板包,不进入 BA builtin skills。 |
BA 侧真正值得补的通用能力
- Session 级外部工具入口:如果不想依赖预同步 skill,可以设计通用 external tool bridge,让外部系统按 session 注册工具 schema 和回调地址。这个能力应服务所有插件,而不是叫 eval-env。
- 更完整的 Python SDK:Octagon 目标设计里已经提到不应手写 Socket.IO。BA SDK 可以补
get_messages、递归文件下载、primary_skill_id/solution_id 类型化参数、事件解析 helper。 - 会话技能动态加载:Octagon 目前需要把
blade_skill同步到 skills path,并依赖 BA 重启加载。若 BA 支持 session-scope skill upload/enable,外部 eval 会更干净。 - headless 过程事件稳定契约:评测需要完整 events、thinking、tool calls、usage。SDK 应明确
turn:end/chat:end的稳定字段,而不让外部平台猜 raw 结构。 - 安全的 env 注入:外部 harness 需要把 attempt_id、token、base_url 注入 session。应继续走 workspace 文件或 session env,不应让 BA server 持有评测 runtime path。
和当前 PR 方案的核心差异
| 维度 | PR #1192 当前方案 | Octagon 式方案 |
|---|---|---|
| BA 侵入度 | 新增 server route、CLI、OpenAPI 类型、Docker 打包、builtin skill。 | BA 基本不变,只使用 SDK 和已有 skill/session 能力。 |
| 环境生命周期 | BA server 临时注册 runtime root,并代理 tool-call。 | 外部 harness 自己管理 attempt、token、DB、trace、terminal status。 |
| 工具协议 | worker 调 blade eval-env call 或 server proxy。 |
worker 调 BA skill;skill 薄壳 HTTP 回调外部 env server。 |
| 多 agent 扩展 | 主要围绕 BA worker/judge 设计。 | Blade、Claude、Codex 可通过不同 adapter 接同一个环境核心。 |
| 审计权威性 | audit、state diff、judge runner 混在 PR 新增代码里。 | trace 和 scorer 在环境层统一生成,judge 只解释证据。 |
文件分布
| 区域 | 代表文件 | 作用 |
|---|---|---|
| CLI | cli/internal/cmd/eval_env.gocli/internal/cmd/eval_package.go |
新增评测环境编译、审计、托管调用,以及评测包脚手架和规则检查入口。 |
| Env compiler | cli/tools/evalenv/compiler.pyruntime_templates.py |
把 EnvSpec IR 编译为 runtime、worker 包、judge 材料和审计器。 |
| Server | server/src/blade_agent/server/routes/eval_env.py |
提供 runtime 注册、注销和工具调用代理 API。 |
| Runner | scripts/eval_rehearsal_runner.pyscripts/eval_rehearsal_runspec.py |
支持 IR 自动编译、worker/judge 编排、归档和 RunSpec 执行。 |
| 示例场景 | evals/thinking_envs/*/env/env_spec.yaml |
提供 easy、medium、hard 三档业务评测环境样例,只提交 IR,不提交生成产物。 |
| 测试 | tests/test_eval_env_tool_proxy.pycli/internal/cmd/*_test.go |
覆盖工具代理、路径校验、编译审计、CLI 行为和 eval package 构建规则。 |
验证情况
- 运行 EnvSpec IR 自动编译检查:
ensure_existing_package_compiled(...)。 - 对新增 Python 文件执行
py_compile。 - 对 CLI compiler、runner、server route 和测试执行
ruff check。 - 运行 Python 测试:
tests/test_eval_env_tool_proxy.py、tests/test_builtin_eval_package_builder_skill.py。 - 运行 Go 测试:
go test ./internal/cmd -run 'TestEvalEnv|TestEvalPackage'。
需要关注
这是一个横跨 CLI、server、runner、文档、示例 IR 和生成器的大型新增能力,代码体量超过一万行。后续合入前最值得关注的是端到端 rehearsal 是否在真实 runner 环境稳定,以及 server runtime 托管的权限边界是否符合部署预期。
复审重点
- 确保生成产物不会被误提交,尤其是
env_runtime/、work/、judge/。 - 确认 Docker / release 镜像内确实包含
cli/tools,否则 CLI wrapper 无法找到 Python compiler。 - 关注 eval-env route 的本地路径白名单与 token 传递方式,避免扩大可访问面。
提交脉络
| 提交 | 含义 |
|---|---|
54fb0c4 |
主功能提交:新增工具化评测环境 judge。 |
c6e0fc7 |
同步 main。 |
b442526 / f4b168e |
围绕生成产物清理进行调整与回滚。 |
9171774 |
处理 review 问题:并发锁、路径穿越防护、structured initial values 序列化、worker 解压提示修正等。 |