Upsonic 应用科学家预置智能体 Evaluate 技能实战:用机器可读 JSON 完成基准对比与实验裁决
【免费下载链接】gpt-computer-assistantBuild autonomous AI agents in Python.项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-computer-assistant
导读
Evaluate 是 Upsonic 预置的「应用科学家」(Applied Scientist)自主智能体实验流水线(Phase 0 → Phase 5)的收官阶段,负责把 Phase 3 的基线指标与 Phase 4 的新方法指标汇总成一份机器可读的最终裁决报告result.json,并同步更新实验登记表experiments.json、跨实验对比表comparison.json与分阶段日志log.json。读完本文,你将掌握四类裁决(BETTER / WORSE / INCONCLUSIVE / FAILED)的判定逻辑、result.json的逐字段 schema 规范、三个配套 JSON 文件的更新协议,以及仓库源码中ExperimentResult、ExperimentRecord、ExperimentRegistry对这份报告的读取与渲染机制,可以直接在你的研究型 Agent 工作流中复刻这套"诚实、可审计、可程序化消费"的实验评价体系。
一、Evaluate 技能在实验流水线中的位置:为什么实验的产出是一份 JSON 裁决
在展开 Evaluate 的细节之前,先看它所在的上下文。应用科学家智能体的核心职责在 system_prompt.md 中被概括为一句话:take what we have, take what's new, try the new thing, and tell us if it's better(把现有的拿走,把新的拿来,试试新东西,然后告诉我们它是否更好)。
一次完整实验被拆成 6 个阶段,每阶段对应一个技能目录(SKILL.md):
| 阶段 | 名称 | 对应技能文档 |
|---|---|---|
| Phase 0 | Setup(环境搭建) | experiment_management/SKILL.md |
| Phase 1 | Analyze Current(分析基线) | analyze_current/SKILL.md |
| Phase 2 | Research(研究新方法) | research/SKILL.md |
| Phase 3 | Benchmark(定义对比框架) | benchmark/SKILL.md |
| Phase 4 | Implement(实现并运行新方法) | implement/SKILL.md |
| Phase 5 | Evaluate(对比、裁决、出报告) | evaluate/SKILL.md |
Evaluate 就是最后一步"盖棺定论"的环节。它的 Purpose 在 evaluate/SKILL.md 中写得很明确:
Compare baseline and new implementation results. Produce the machine-readable final report
result.json, updateexperiments.json, and append a row tocomparison.json.
这里有一个贯穿全系统的设计哲学:实验的产出不是代码,而是一个决策。整条流水线中所有簿记文件(progress.json、log.json、result.json、experiments.json、comparison.json)全部是合法 JSON,绝不使用 Markdown 报告。这样做的目的是让仪表盘、CLI 和 Jupyter notebook 都能直接轮询和渲染实验结果,实现"人机双读"。
二、输入参数:Evaluate 需要什么
Evaluate 技能接受两个输入参数(见原文档 Input 表):
| 参数 | 类型 | 说明 |
|---|---|---|
experiment_path | path | 实验目录,即experiments/{research_name}/ |
research_name | string | 本次实验的名称,必须逐字使用调用方给定的名字,不得改名、不得加后缀、不得从论文标题重新推导 |
research_name的"逐字使用"约束是全系统规则(CRITICAL RULES 第 9 条):实验文件夹名与所有 JSON 文件中的"name"字段都必须精确一致。在源码层,agent.py 中的Experiment类保存了这份 name,并通过ExperimentRecord/ExperimentRegistry以该名字为键去定位磁盘上的result.json、progress.json、log.json。
另外要注意:Evaluate 的所有输入都只来自磁盘上的既有文件——指标来自log.json(Phase 3 的基线项 + Phase 4 的新方法项),实验目录在 Phase 0 已经创建完毕。因此 Evaluate 阶段不再需要接触原始 notebook 或数据,这也符合系统"NEVER modify original files"的第一铁律。
三、六步行动总览
原文档将 Evaluate 拆为 6 个动作,先建立全局视图:
- 收集全部指标:从
log.json读取 Phase 3 基线条目与 Phase 4 新方法条目。 - 判定裁决:得出
BETTER/WORSE/INCONCLUSIVE/FAILED之一。 - 写
result.json:按精确 schema 输出最终机器可读报告,字段不得留空,未知值用null。 - 更新
experiments.json:将状态置为completed(或failed),补齐verdict、key_metric、baseline_model、new_method。 - 更新
comparison.json:文件不存在则先创建{"experiments": []},然后追加一行汇总记录。 - 回写
log.json:追加 Phase 5 条目,收尾整个实验。
下面逐一展开。
四、第一步与第二步:收集指标、判定裁决
收集指标(Step 1)的动作很直接:log.json是贯穿 6 个阶段的唯一结构化日志,Phase 3 的 Benchmark 条目里存有每个指标的baseline值与needs_computation标记(参考 benchmark/SKILL.md 中的metrics[]结构),Phase 4 的 Implement 条目里存有metrics字典与新方法实测值(参考 implement/SKILL.md)。Evaluate 只需要把这两处数据取出来配对,无需重新运行任何 notebook。
判定裁决(Step 2)是 Evaluate 的核心智力动作,四类结论的语义如下:
| verdict | 触发条件 |
|---|---|
BETTER | 新方法在多数关键指标上优于基线 |
WORSE | 新方法在多数关键指标上劣于基线 |
INCONCLUSIVE | 结果混杂,或差异落在噪声范围内 |
FAILED | 实验无法产出可对比结果(依赖安装失败、实现崩溃、数据不兼容) |
注意两个要点:
- "多数"(majority)意味着裁决不是看单一指标,而是看指标集合的整体方向;这要求 Phase 3 定义的对比指标足够全面(分类任务建议 accuracy / precision / recall / F1 / AUC-ROC,回归任务建议 MSE / RMSE / MAE / R²,另外都建议计入训练时间
training_time_seconds)。 FAILED不是"白干一场":系统提示词明确指出A failed experiment is more valuable than a fake success(失败实验比伪造的成功更有价值)。依赖失败、实现崩溃、数据不兼容都会走 FAILED 路径,但依然要产出合法的result.json。
五、第三步:result.json完整 Schema 与字段规则
result.json是实验的最终交付物,也是"是否应该切换到新方法"这一问题的机器可读答案。原文档给出了精确 schema,这里完整保留并逐字段注释:
{ "name": "{research_name}", "verdict": "BETTER", "summary": "2-3 paragraphs explaining what the new method does, how it fundamentally differs from the baseline, and what trade-offs it makes.", "explanation": "2-3 sentences explaining WHY this verdict was reached. Reference specific metrics and their differences. Be concrete — mention numbers, not vague statements.", "comparison": { "metrics": [ { "name": "accuracy", "current": 0.853, "new": 0.872, "diff": 0.019, "diff_display": "+0.019", "unit": null, "higher_is_better": true, "better": "new" }, { "name": "training_time_seconds", "current": 2.0, "new": 45.0, "diff": 43.0, "diff_display": "+43.0", "unit": "seconds", "higher_is_better": false, "better": "current" } ] }, "file_locations": { "current_notebook": "experiments/{research_name}/current.ipynb", "current_data": "experiments/{research_name}/current_data/", "new_notebook": "experiments/{research_name}/new.ipynb", "research_source": "experiments/{research_name}/research.pdf", "experiment_log": "experiments/{research_name}/log.json" } }5.1 顶层字段规则
name:必须等于调用方给定的{research_name},与实验文件夹名一致。verdict:只能是"BETTER"、"WORSE"、"INCONCLUSIVE"、"FAILED"四选一,不允许其他取值。summary:2~3 段纯文本,说明新方法做什么、与基线在原理上有何不同、做了哪些取舍。明文规定不得使用 Markdown 标题,只能写短段落。explanation:2~3 句话,说明为什么得到该裁决,必须引用具体指标与具体差异数值("mention numbers, not vague statements"),禁止空泛表述。- 永远输出合法 JSON:任何字段都不得处于 undefined 状态,未知值一律用
null。这是全系统 CRITICAL RULES 第 8 条("Every bookkeeping file is valid JSON")的具体落实;在实现上,如果担心原子性,可以先写临时文件再重命名。
5.2comparison.metrics[]逐项规则
这是报告中最容易被写错的部分,原文档给出了严格的字段契约:
current/new:必须是数字;如果某一侧无法计算出该指标,则用null(例如基线 notebook 从未计算过训练时间)。diff:原始数值差,定义为diff = new - current,不附加任何格式化。diff_display:带符号的短字符串,如"+0.019"、"-0.03",用于人类可读的表格展示。better:"new"|"current"|"tie"|null四选一,由diff与higher_is_better联合计算得出。当higher_is_better=true时diff > 0意味着新方法更好,反之亦然;差异为 0 记"tie",无法判断记null。unit:短单位字符串("seconds"、"%"等)或null。higher_is_better:布尔值,指明该指标是"越大越好"还是"越小越好"——训练时间这类指标就是false,所以上例中diff = +43.0却判定better = "current"。
这里能看到该 schema 的精妙之处:指标方向信息被显式建模,后续任何渲染层(表格、仪表盘)都不需要猜某个指标是越大越好还是越小越好。在源码层,agent.py 的ExperimentResult.table属性原样返回这组字典(键为name、current、new、diff、diff_display、unit、higher_is_better、better),并在_repr_html_中渲染成带 Metric / Current / New / Diff / Better 五列的 HTML 表格。
5.3file_locations规则
- 所有路径相对于 experiments 目录根(即写为
experiments/{research_name}/...形式),不是绝对路径,也不是只写文件名。 research_source必须与 Phase 0 实际落地(materialize)的产物一致:可能是research.pdf、research_source.{ext}(如纯文本 idea 落盘为research_source.md),也可能是克隆仓库对应的research_source/目录。Phase 0 的落地命名规范详见 experiment_management/SKILL.md 的 "Pick a sensible local name" 部分。
六、第四步:更新experiments.json(实验登记表收尾)
experiments.json位于experiments/根目录,是所有实验的总登记表。Evaluate 阶段对其做就地更新(不是追加新条目,条目在 Phase 0 已注册为in_progress),需要完成四件事:
- 将
status置为"completed"(实验失败则置为"failed"); - 填充
verdict(四选一); - 填充
key_metric,其结构为对象:{"name": "...", "baseline": <num>, "new": <num>}; - 填充
baseline_model与new_method。
完整的登记表条目格式(来自 system_prompt.md)如下:
{ "experiments": [ { "name": "research_name", "date": "YYYY-MM-DD", "status": "completed | failed | in_progress", "paper": "paper title", "baseline_model": "e.g. XGBoost", "new_method": "e.g. TabPFN", "verdict": "BETTER | WORSE | INCONCLUSIVE | FAILED", "key_metric": {"name": "accuracy", "baseline": 0.85, "new": 0.87}, "path": "experiments/research_name/" } ] }在源码层,这个文件由ExperimentRegistry(agent.py 中的ExperimentRegistry类)读取:__getitem__、get、items等接口每次访问都会重新解析磁盘文件,保证后台运行期间状态永远新鲜。ExperimentRecord.status属性即映射这里的三态取值in_progress | completed | failed。
七、第五步:更新comparison.json(跨实验对比表追加行)
comparison.json用于跨实验汇总,方便日后在多个research_name之间横向比较。协议很简单:
- 若文件不存在,先创建
{"experiments": []}; - 追加一行:
{ "name": "{research_name}", "date": "YYYY-MM-DD", "baseline": "{baseline_model}", "new_method": "{new_method}", "key_metric": {"name": "accuracy", "baseline": 0.853, "new": 0.872}, "verdict": "BETTER" }注意该表每行是精简快照,只保留key_metric单指标与最终verdict,详细的多指标对比仍以每个实验目录下的result.json为准(result.json的file_locations指向experiments/{research_name}/log.json可回溯全量过程)。comparison.json的另一种等价格式(来自 system_prompt.md)也值得参考,其中baseline/new_method直接填模型名(如"XGBoost"/"CatBoost")。
八、第六步:回写log.json(Phase 5 条目)
最后在log.json的phases数组中追加(绝不覆盖先前条目)一个 Phase 5 条目,作为整个实验的终章记录:
{ "name": "Phase 5: Evaluate", "completed_at": "2026-04-17T11:40:00Z", "verdict": "BETTER", "key_change": "accuracy +0.019 (new > current)", "files_written": ["result.json", "experiments.json", "comparison.json"] }completed_at使用 UTC ISO-8601 时间戳;key_change用一句话概括关键变化(带具体数值);files_written如实记录本次写出的文件清单,保证"如果没写进 log,就等于没发生"的可审计性。追加而不是覆盖的原则与前几个阶段一致(参见 benchmark/SKILL.md 中 "Do not overwrite earlier entries; append to thephasesarray" 的同样要求)。
九、失败处理:FAILED也是合法且重要的结论
原文档强调:不是每次实验都会成功,失败也要诚实处理并产出合法 JSON。三类典型失败及对应处理方式如下:
| 失败类型 | 处理方式 |
|---|---|
| 依赖失败(某包装不上) | experiments.json.status = "failed",progress.json.status = "FAILED",result.json.verdict = "FAILED"并在explanation中写清错误 |
| 实现失败(新方法训练中崩溃) | 同上,explanation中包含错误细节 |
| 数据不兼容(新方法无法处理该数据格式) | 同上,explanation中说明原因 |
也就是说,Evaluate 阶段的FAILED路径与成功路径走的是同一套文件协议——只是 verdict 取值与 status 不同。这保证了无论是失败还是成功,下游消费者都能用同一套解析逻辑处理result.json。系统提示词的措辞是:失败实验依然产出result.json,因为它告诉我们"这个方法对这个用例不适用",这本身就是有价值的信息。
十、源码印证:从磁盘到渲染的完整链路
Eval 阶段的产出不只是四个 JSON 文件,仓库源码还提供了完整的读取与展示层,值得对照学习。相关实现集中在 agent.py(1213 行),核心类如下:
10.1AppliedScientist与Experiment:如何发起一次会走到 Phase 5 的实验
AppliedScientist继承自PrebuiltAutonomousAgentBase,将 agent 与template/目录下的技能模板绑定,并通过new_experiment(...)暴露高层 API。返回的Experiment对象支持四种运行方式:
run():前台运行,带终端美化输出(底层走run_console);run_async():异步运行,无 TTY 格式化;run_stream():同步流式迭代文本块(或事件);run_in_background():后台线程运行,静默所有打印,适合 Jupyter 单元格,通过is_running/is_done/wait()/stop()控制生命周期。
stop()使用框架的upsonic.run.cancel.cancel_run做协作式取消:标记运行中的 run,让 agent 在下一个流水线检查点自行中止,而非强杀线程。
10.2ExperimentResult:result.json的结构化视图
ExperimentResult把result.json解析为四个调用方真正关心的属性,与 Evaluate 的产出字段一一对应:
verdict——"BETTER"/"WORSE"/"INCONCLUSIVE"/"FAILED";summary—— 新方法的 2~3 段说明;explanation—— 为什么得到该裁决;table—— 对比指标列表(即comparison.metrics)。
它还在 Jupyter 中提供_repr_html_,渲染出"实验名 + 带颜色的 verdict 徽章(BETTER 绿 / WORSE 红 / INCONCLUSIVE 橙 / FAILED 灰)+ 五列指标表 + Why + 摘要"的卡片式 HTML 视图。也就是说,Evaluate 写出的result.json可以直接在 notebook 里以漂亮的可视化形式呈现。
10.3ExperimentRecord与ExperimentRegistry:磁盘状态的只读视图
ExperimentRecord由experiments.json中的条目 + 实验目录下的 JSON 文件共同支撑,每个属性都重新从磁盘读取,因此后台运行期间轮询永远拿到最新状态。它暴露status、verdict、key_metric、baseline_model、new_method、paper、date等登记表字段,以及progress(解析progress.json)、log(解析log.json)、result(解析result.json)三个文件读取器。ExperimentRegistry是experiments.json的 dict-like 视图,__getitem__/get/keys/items每次访问都重新加载文件;Experiment.progress_bar属性会基于磁盘上的progress.json生成 Rich HTML 进度条(含每个阶段的 ✓/●/○/✗ 图标与进度百分比),last_logs(n)则渲染log.json末尾 n 条日志卡片——这两个 Jupyter 友好工具让用户在后台实验运行期间可以实时刷新查看 Phase 5 是否已经完成。
另外,_auto_inputs()辅助函数演示了输入处理哲学:对用户传入的候选路径只做本地磁盘存在性检查(Path(value).expanduser()后path.exists()),URL、git 引用、arXiv 链接、纯文本 idea 等一律跳过,留给 agent 在 Phase 0 用自己的技能去抓取——这与 experiment_management/SKILL.md 中"不依赖固定前缀检测列表"的原则完全一致。
十一、端到端实战示例:从发起实验到拿到裁决
仓库提供了两个开箱即用的入口文件,可以组合成一次完整演示。
1. CLI 直跑(example.sh):
claude \ --system-prompt-file "./system_prompt.md" \ --dangerously-skip-permissions \ --effort "medium" \ "New experiment. **Research paper:** example_1/tabpfn.pdf **Current notebook:** example_1/Baseline XGBoost Adult.ipynb **Current data:** downloaded in notebook (ucimlrepo, id=2) Run the full experiment pipeline. Go from Phase 0 through Phase 5 without stopping. I want to see \`result.md\` at the end telling me whether this new method is better than what we have. Start now."这里system_prompt.md即 应用科学家系统提示词,它把实验协议(6 条 CRITICAL RULES、5 项输入、6 阶段)一次性注入 agent;agent 在 Phase 5 会调用evaluate技能完成裁决与收尾。
2. 模板消息(first_message.md)则展示了调用方应当提供的五个占位参数:
New experiment. **Experiment name:** {research_name} **Research source:** {research_source} **Current notebook:** {current_notebook} **Current data:** {current_data} **Experiments directory:** {experiments_directory}3. 通过 Python API 在 Jupyter 中运行:使用AppliedScientist.new_experiment(name, research_source=..., current_notebook=..., current_data=..., experiments_directory=...)创建实验对象后,调用experiment.run_in_background()返回控制权,轮询experiment.progress_bar查看 Phase 0~5 进度,实验完成后用experiment.result拿到ExperimentResult(其verdict属性即为 Evaluate 阶段写入result.json的最终裁决),或用scientist.experiments[name]读取登记表记录。
一次完整实验结束后,experiments/目录的最终形态如下(结构来自 system_prompt.md):
experiments/ ├── experiments.json # Registry: every experiment ever run ├── comparison.json # Cross-experiment summary (JSON array) └── {research_name}/ ├── current.ipynb # COPY of the original notebook (never the original) ├── current_data/ # COPY of the original data (never the original) ├── current_requirements.txt ├── new.ipynb # New implementation ├── new_requirements.txt ├── research.pdf # Materialized research source ├── log.json # Phase-by-phase structured log ├── progress.json # Live progress snapshot └── result.json # The final machine-readable comparison report十二、Evaluate 实战检查清单
最后,把原文档与系统提示词中的硬性约束汇成一份可对照执行的清单:
- 指标必须成对:Phase 3 定义的每个指标,基线侧取
log.jsonPhase 3 条目的baseline,新方法侧取 Phase 4 条目的metrics;某侧缺失用null,不要编造。 - 裁决依据"多数关键指标":整体方向决定
BETTER/WORSE;混杂或噪声内差异给INCONCLUSIVE;无法对比给FAILED。 verdict四选一:不得出现其他取值。diff = new - current,diff_display带符号:数值与展示字符串分工明确。better由diff+higher_is_better推导:训练时间这类"越小越好"的指标方向不要搞反。summary/explanation纯文本:无 Markdown 标题,短段落,explanation必须带具体数字。file_locations用相对 experiments 根的路径:research_source与 Phase 0 落地产物严格一致。- 三个配套文件都要更新:
experiments.json(改状态)、comparison.json(追加行,先建默认结构)、log.json(追加 Phase 5 条目)。 - 所有文件合法 JSON:不写半成品,需要原子性时先写临时文件再 rename;
progress.json的status保持大写枚举(RUNNING/COMPLETED/FAILED),updated_at始终刷新。 - 失败同样出报告:
FAILED的result.json比伪造的成功更有价值。
这套 Evaluate 协议的可贵之处在于:它把"新方法是否值得切换"这个模糊的研究问题,压缩成了四个可枚举的裁决、一份带符号差值与指标方向的机器可读报告、以及三份彼此可交叉引用的 JSON 簿记文件——让实验结论既诚实可审计,又能被仪表盘与 notebook 直接消费。如果你想在自有研究流程中复刻这套体系,直接以 evaluate/SKILL.md 为模板定义你的裁决契约,再参照 agent.py 中的读取/渲染层即可无缝接入。
【免费下载链接】gpt-computer-assistantBuild autonomous AI agents in Python.项目地址: https://gitcode.com/GitHub_Trending/gp/gpt-computer-assistant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考