with one click
add-benchmark
集成外部 benchmark 数据集到 HolyEval 框架 — 从论文/仓库到可执行评测的端到端流程。
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
集成外部 benchmark 数据集到 HolyEval 框架 — 从论文/仓库到可执行评测的端到端流程。
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Use when analyzing ESL-Bench evaluation reports - extracting scores by difficulty dimension, comparing methods, checking fail rates, token costs, duration per query. Triggers on keywords like eslbench report, benchmark results, group by difficulty.
Scaffold a new EvalAgent plugin (config + implementation + registration).
Scaffold a new TargetAgent plugin (config + implementation + registration).
引导项目初始化 — 环境安装、配置、验证、启动 Web UI(含 hma-web 公开评测平台)。
审查项目架构健康度 — 检查 GitOps 合规、Plugin 可插拔性、CLI/Web 复用。当用户要求架构审查、代码重构后验证、或新增模块后检查依赖关系时使用。
Run all benchmark test cases or a filtered subset.
| name | add-benchmark |
| description | 集成外部 benchmark 数据集到 HolyEval 框架 — 从论文/仓库到可执行评测的端到端流程。 |
| argument-hint | [benchmark paper URL or GitHub repo URL] |
将外部 benchmark 数据集(如论文、开源仓库)集成到 HolyEval 框架中,使其可以通过 python -m benchmark.basic_runner <benchmark> <dataset> 执行评测。
交互原则: 本 skill 采用「分析 → 确认 → 执行」模式。在每个关键决策点,必须通过 AskQuestion 工具向用户展示分析结论并获得确认,确认通过后才继续执行。绝不在未经用户确认的情况下开始写代码。
本 skill 仅通过插件扩展点和数据目录集成新 benchmark,严禁修改框架核心逻辑。文件按修改权限分为三级:
| 文件 | 操作 |
|---|---|
generator/<name>/__init__.py | 新建(空文件) |
generator/<name>/converter.py | 新建(数据转换器) |
benchmark/data/<name>/metadata.json | 新建(数据集元信息) |
benchmark/data/<name>/<dataset>.jsonl | 新建(由 converter 生成) |
evaluator/plugin/eval_agent/<name>_eval_agent.py | 仅 Case B: 新建(EvalAgent plugin) |
evaluator/plugin/eval_agent/__init__.py | 仅 Case B: 追加 import + __all__ + docstring。不得删除/修改已有内容 |
evaluator/core/schema.py 是 Pydantic Discriminated Union 的类型注册文件。由于 Pydantic v2 要求 Union 成员在定义时静态列举,新增配置类型必须在此文件中追加。
允许的操作(纯追加,不改已有代码):
EvalInfo Union 定义之前添加新的 *EvalInfo 配置类(及其依赖的辅助模型)EvalInfo = Annotated[..., Discriminator("evaluator")] 中追加新类型EvalInfo 列表禁止的操作:
Case A(复用现有评估器)完全不需要修改 schema.py。
以下文件为框架核心,任何修改都可能破坏全局功能:
evaluator/core/orchestrator.py — 编排引擎(do_single_test / BatchSession)evaluator/core/bench_schema.py — Benchmark 数据模型(BenchItem / merge_target / bench_item_to_test_case)evaluator/core/interfaces/abstract_*.py — 三类 Agent 抽象基类evaluator/utils/*.py — 通用工具层(llm, benchmark_reader, report_reader, agent_inspector, config)evaluator/plugin/test_agent/ — 已有 TestAgent 插件(manual / auto)evaluator/plugin/target_agent/ — 已有 TargetAgent 插件(llm_api / theta_api)evaluator/plugin/eval_agent/ 中的已有文件 — 不得修改 semantic / healthbench / keyword / preset_answer / indicatorbenchmark/basic_runner.py — 跑分执行器web/ — Web UI如果你发现需要修改 🔴 文件才能完成需求,请停下来通知用户 — 这通常意味着需求理解有误,或框架需要由维护者升级扩展点。绝不"顺手"改一下核心代码来适配新 benchmark。
外部数据集 → Converter → BenchItem JSONL → basic_runner → Orchestrator → TestAgent ↔ TargetAgent → EvalAgent → BenchReport
↑ ↑ ↑ ↑
[需实现] [复用即可] [复用即可] [可能需实现]
| 角色 | 职责 | Benchmark 集成时是否需要新增? |
|---|---|---|
| TestAgent(虚拟用户) | 模拟真实用户发送消息 | 几乎不需要 — 现有 manual/auto 覆盖所有场景 |
| TargetAgent(被测系统) | 封装被测系统的调用 | 原则上不需要 — 现有 llm_api/theta_api 已足够 |
| EvalAgent(评估器) | 评判对话质量 | 可能需要 — 取决于评估方法论是否已有对应实现 |
| 名称 | 类 | 工作方式 | 典型场景 |
|---|---|---|---|
manual | ManualTestAgent | 按序发送 strict_inputs 预设输入,用完自动结束。零 LLM 调用、完全确定性、零成本。轮次 = len(strict_inputs) + 1,忽略 max_turns | 绝大多数 benchmark 都用此类型:标准问答、rubric 评测、答案匹配等 |
auto | AutoTestAgent | 前 N 轮消费 strict_inputs,之后 LLM 自主生成对话。根据 goal/context/finish_condition 判断何时结束 | 需要 AI 模拟多轮追问的开放式对话场景(极少数 benchmark 需要) |
选择原则: 如果 benchmark 数据中包含完整的用户输入文本,一律使用
manual。仅当 benchmark 要求虚拟用户自主生成多轮对话时才考虑auto。
| 名称 | 类 | 工作方式 | 配置字段 |
|---|---|---|---|
llm_api | LlmApiTargetAgent | 通过 do_execute() 统一调用大模型(OpenAI/Gemini/Anthropic/GLM),自动维护多轮对话历史 | model(必填), system_prompt(可选) |
theta_api | ThetaApiTargetAgent | 通过 HTTP API 调用 Theta Health 后端,使用 create_message + list_message 轮询模式 | email(必填), code, agent, language, timezone |
⚠️ 关于自定义 TargetAgent: 对于外部 benchmark 集成,原则上不需要自定义 TargetAgent。
- 如果 benchmark 是评测大模型能力 → 使用
llm_api(运行时通过--target-model指定模型)- 如果 benchmark 是评测 Theta Health 产品 → 使用
theta_api- 如果你判断需要自定义 TargetAgent,这几乎一定意味着理解有误。请务必在 Checkpoint 1 中向用户确认,说明为什么现有 target 不够用,并获得明确同意后才继续。
| 名称 | 适用场景 | LLM | 评估方式 |
|---|---|---|---|
semantic | 通用多维度语义评估 | 是 | LLM 按 criteria 独立打分 → 加权总分 → 对比 threshold → pass/fail |
healthbench | HealthBench rubric 评测 | 是 | LLM 逐条判定 criterion → 按 points 加权计算 → scored(不做 pass/fail) |
keyword | 关键词/规则匹配 | 否 | 按 rules 配置检查对话文本 → 加权计分 → 对比 threshold → pass/fail |
preset_answer | 标准答案比对 | 否 | 数字容差/关键词/精确匹配 → pass/fail |
indicator | 健康指标数据比对 | 是 | 调用 Theta API 获取真实数据 → LLM 比对 → pass/fail |
外部 benchmark 的评估方法论是否已有对应的 EvalAgent?
│
├─ YES → 仅需 Converter + 数据目录 (Case A: 轻量集成)
│ 例:答案匹配类 → 复用 preset_answer
│ 例:关键词检查类 → 复用 keyword
│ 例:多维度语义评估 → 复用 semantic
│
└─ NO → Converter + EvalInfo Config + EvalAgent Plugin + 数据目录 (Case B: 完整集成)
例:HealthBench rubric 评估 → 自定义 healthbench eval
例:MMLU 多选题评估 → 自定义 mcq eval
以下模块完全通用,新增 benchmark 时绝不修改:
benchmark/basic_runner.py — 执行器(基于 BenchItem 架构,自动适配)evaluator/core/orchestrator.py — 编排器(do_single_test / BatchSession)evaluator/core/bench_schema.py — 通用数据模型(BenchItem / BenchMark / BenchReport)evaluator/utils/benchmark_reader.py — 自动发现 benchmark/data/ 目录evaluator/utils/report_reader.py — 自动发现 benchmark/report/ 目录agent_inspector 自动适配新 plugin这是最关键的一步。在写任何代码前,必须彻底理解外部 benchmark。
用户会提供论文 URL 或 GitHub 仓库 URL:
WebFetch 阅读论文内容,重点关注评估方法论章节(Evaluation / Metrics / Scoring)WebFetch 阅读 README、数据格式说明、评估脚本源码(重点:scoring / grading 函数)系统性分析以下三个维度:
数据格式:
user.strict_inputs(用户输入)?history(前置轮次)+ strict_inputs(最后一条用户输入)tags?评估方法论:
对话模式:
history(评测前对话上下文),最后一条 user message 作为 strict_inputsmanual,需生成 → auto)在进入 Phase 1 之前,必须通过 AskQuestion 向用户确认以下所有决策。
先向用户展示一段分析总结文本(Markdown),包含:
然后使用 AskQuestion 工具发起确认:
Question 1: Benchmark 名称
- prompt: "确认 benchmark 目录名(snake_case,用于 benchmark/data/<name>/ 和 generator/<name>/)"
- options: [推荐名称, 备选名称, "自定义(请在下方说明)"]
Question 2: 虚拟用户类型 (TestAgent)
- prompt: "虚拟用户类型 — 基于数据分析的推荐如下"
- options:
- "manual(脚本驱动)— 使用原始数据中的确定性输入,零 LLM 成本【推荐】"
- "auto(LLM 驱动)— 需要 AI 自主生成多轮对话"
Question 3: 被测系统类型 (TargetAgent)
- prompt: "被测系统类型 — 以下选项使用已有 TargetAgent,运行时通过 CLI 参数指定模型"
- options:
- "llm_api — 通用大模型 API(OpenAI/Gemini/Anthropic 等)【推荐】"
- "theta_api — Theta Health 产品 API"
- "⚠️ 需要自定义 TargetAgent(请说明原因)"
Question 4: 评估器类型 (EvalAgent)
- prompt: "评估器类型 — 基于评估方法论分析的推荐如下"
- options:
- 列出可能匹配的已有 eval + "[推荐原因]"
- "需要自定义评估器(Case B)"
Question 5: 数据子集方案
- prompt: "数据子集划分"
- options: [列出原始数据中的子集方案]
- allow_multiple: true
关键规则:
- 如果用户在 Question 3 选择了「需要自定义 TargetAgent」,必须追问具体原因,并尝试用现有方案替代。只有用户二次确认确实无法复用时才执行。
- 如果用户选择了意料之外的选项,主动解释可能的影响。
创建: generator/<benchmark_name>/converter.py + generator/<benchmark_name>/__init__.py
参考实现: generator/healthbench/converter.py
generator/
├── <benchmark_name>/
│ ├── __init__.py # 空文件
│ └── converter.py # 转换器
└── ...
"""
<BenchmarkName> → HolyEval 数据转换器
将 <原始格式> 转换为 HolyEval BenchItem JSONL。
转换映射:
<原始字段A> → strict_inputs(用户输入)
<原始字段B> → history(可选,多轮对话上下文)
<原始字段C> → eval.<评估配置>
<原始字段D> → tags
用法:
python -m generator.<benchmark_name>.converter input_file output.jsonl
"""
import argparse
import json
import logging
from pathlib import Path
from typing import Any, Dict, List, Optional
logger = logging.getLogger(__name__)
def _convert_single(entry: Dict[str, Any], index: int) -> Optional[Dict[str, Any]]:
"""将单条原始数据转换为 HolyEval BenchItem dict
Returns:
BenchItem dict,转换失败返回 None
"""
bench_item: Dict[str, Any] = {
"id": "<prefix>_<unique_id>",
"title": "<生成标题>",
"description": "<描述>",
"user": {
"type": "manual", # Checkpoint 1 确认的类型
"goal": "<评测目标>",
"strict_inputs": [...], # 用户输入列表
},
"eval": {
"evaluator": "<eval_type>", # Checkpoint 1 确认的评估器
# ... 评估器特定配置
},
"tags": [...],
}
# 如果原始数据含多轮对话上下文,添加 history
# history: [{role: "user", content: "..."}, {role: "assistant", content: "..."}]
if history:
bench_item["history"] = history
return bench_item
def convert(
input_path: str | Path,
output_path: str | Path,
limit: int | None = None,
) -> int:
"""批量转换,返回成功条数"""
input_path = Path(input_path)
output_path = Path(output_path)
if not input_path.exists():
raise FileNotFoundError(f"输入文件不存在: {input_path}")
output_path.parent.mkdir(parents=True, exist_ok=True)
converted = 0
skipped = 0
with open(input_path, "r", encoding="utf-8") as fin, \
open(output_path, "w", encoding="utf-8") as fout:
for i, line in enumerate(fin):
line = line.strip()
if not line:
continue
if limit is not None and converted >= limit:
break
try:
entry = json.loads(line)
except json.JSONDecodeError as e:
logger.warning("第 %d 行 JSON 解析失败: %s", i + 1, e)
skipped += 1
continue
bench_item = _convert_single(entry, i)
if bench_item is None:
skipped += 1
continue
fout.write(json.dumps(bench_item, ensure_ascii=False) + "\n")
converted += 1
logger.info("转换完成: %d 条成功, %d 条跳过, 输出: %s", converted, skipped, output_path)
return converted
def main() -> None:
"""CLI 入口"""
parser = argparse.ArgumentParser(
description="将 <BenchmarkName> 转换为 HolyEval BenchItem JSONL",
)
parser.add_argument("input", help="源文件路径")
parser.add_argument("output", help="输出 BenchItem JSONL 路径")
parser.add_argument("--limit", type=int, default=None, help="最大转换条数")
parser.add_argument("-v", "--verbose", action="store_true", help="详细日志")
args = parser.parse_args()
logging.basicConfig(
level=logging.DEBUG if args.verbose else logging.INFO,
format="%(levelname)s %(message)s",
)
count = convert(args.input, args.output, limit=args.limit)
print(f"转换完成: {count} 条 BenchItem → {args.output}")
if __name__ == "__main__":
main()
user 配置:
| 场景 | user.type | strict_inputs | max_turns |
|---|---|---|---|
| 单轮问答(最常见) | manual | ["用户提问"] | 不填(自动计算) |
| 多轮预注入 + 提问 | manual | ["背景数据...", "补充信息...", "正式提问"] | 不填 |
| 需要 LLM 自主生成 | auto | [] 或前几轮 | 必填 |
strict_inputs是一个列表(List[str]),manual模式下逐条按序发送,每条都会触发被测系统回复,对话轮次 =len(strict_inputs) + 1。当列表包含多条输入时,前面的条目用于向被测系统预注入上下文信息(如病历、检查指标、用药记录等),中间的回复不影响评估,评估器只关注完整对话的最终质量。这种方式适用于:
- 需要先提供背景数据、再提问的场景(如先发患者病历,再问诊断建议)
- 需要模拟多步交互的流程(如先报告症状,再补充检查结果,最后问治疗方案)
- 不关心中间回复内容、只评估最终对话效果的评测设计
historyvsstrict_inputs:两者都支持多轮对话,但机制不同:
historystrict_inputs(多条)注入方式 作为预加载上下文,双方 Agent 直接"看到",不经过对话循环 逐条发送,被测系统逐条回复,走完整对话循环 被测系统行为 被测系统感知历史对话存在,像"接续"之前的对话 被测系统逐条处理每条输入并生成回复 适用场景 原始数据本身包含多轮已有对话(如 HealthBench 的多轮 prompt) 需要主动向被测系统"灌入"信息再提问 格式 [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]["第一条输入", "第二条输入", ...]两者可以组合使用:
history提供已有对话背景 +strict_inputs在此基础上继续多步交互。Web UI 中
history消息会以半透明样式展示并标注"以上为历史对话",与评测对话视觉区分。
eval 配置(根据 Checkpoint 1 的选择):
| 评估器 | eval 配置示例 |
|---|---|
preset_answer | {"evaluator": "preset_answer", "standard_answer": "42", "match_mode": "number"} |
keyword | {"evaluator": "keyword", "rules": [...], "pass_threshold": 0.7} |
semantic | {"evaluator": "semantic", "criteria": [...], "threshold": 0.7} |
healthbench | {"evaluator": "healthbench", "rubrics": [{"criterion": "...", "points": 10, "tags": [...]}]} |
| 自定义 | {"evaluator": "<new_name>", ...}(需完成 Phase 2) |
id 格式: <2-3字母前缀>_<原始ID或序号>(如 hb_<prompt_id>,mmlu_<subject>_<index>),必须全局唯一。
如果 Checkpoint 1 确认需要自定义评估器,按以下步骤执行。
推荐: 直接调用
add-eval-agentskill 完成此步骤,它会自动处理 schema 修改、plugin 实现和注册。 以下为手动步骤说明,供理解整体流程。
修改: evaluator/core/schema.py
在 EvalInfo Discriminated Union 定义之前添加新的 Pydantic 配置类:
class <Name>EvalInfo(BaseModel):
"""<中文描述> — <评估方法简述>"""
model_config = ConfigDict(extra="forbid", json_schema_extra={"examples": [{...}]})
evaluator: Literal["<name>"] = Field(description="评估器类型")
model: Optional[str] = Field(None, description="LLM 模型")
# ... benchmark 特有的评估配置字段
然后将新类型加入 EvalInfo Union,同时更新 schema.py 文件顶部的 docstring。
创建: evaluator/plugin/eval_agent/<name>_eval_agent.py
核心要点:
AbstractEvalAgent,使用 name="<name>" 注册async def run(self, memory_list, session_info=None) -> EvalResultmemory_list(含 .test_reaction + .target_response),历史上下文通过 self.history 访问evaluator/utils/llm.py 的 do_execute()_cost_meta 和 _display_meta参考实现:
evaluator/plugin/eval_agent/healthbench_eval_agent.py(_build_conversation + 并发 grade + _calculate_score)evaluator/plugin/eval_agent/semantic_eval_agent.pyevaluator/plugin/eval_agent/preset_answer_eval_agent.py修改: evaluator/plugin/eval_agent/__init__.py — 添加 import + __all__ + docstring。
创建: benchmark/data/<benchmark_name>/metadata.json
{
"description": "# <Benchmark 名称>\n\n<Markdown 描述>\n\n## 子集\n\n| 子集 | 数量 | 说明 |\n|------|------|------|\n| ... | ... | ... |\n\n**评估器**: `<evaluator_name>`",
"target": [
{
"type": "llm_api",
"fields": {
"model": {"default": "gpt-4.1", "editable": true, "required": true}
}
}
]
}
字段说明:
description: Markdown 格式,Web UI 渲染展示target: TargetSpec 数组,每个元素定义一种被测系统类型(参考 benchmark/data/healthbench/metadata.json)
type: agent 类型(llm_api / theta_api)fields: 各字段的默认值、是否可编辑(editable)、是否必填(required)--target-type 指定params(可选): 共享参数字典,供 JSONL 条目通过 $ref 引用(见下方说明)当多条用例共享相同的大块数据(如 history 对话上下文)时,在 metadata.json 中定义 params,JSONL 条目通过 {"$ref": "key"} 引用,避免重复:
// metadata.json
{
"target": [...],
"params": {
"diabetic_user_history": [
{"role": "user", "content": "I have type 2 diabetes..."},
{"role": "assistant", "content": "Thank you for sharing..."}
]
}
}
// sample.jsonl — 多条用例引用同一 history
{"id": "case_1", "history": {"$ref": "diabetic_user_history"}, "user": {...}, "eval": {...}}
{"id": "case_2", "history": {"$ref": "diabetic_user_history"}, "user": {...}, "eval": {...}}
规则:
$ref 不处理){"$ref": "key"} 且仅含此一个键才触发替换history参考实现: benchmark/data/history_demo/
uv run python -m generator.<benchmark_name>.converter <input_file> benchmark/data/<benchmark_name>/<dataset>.jsonl
转换完成后,向用户展示:
然后使用 AskQuestion 确认:
Question 1: 转换结果
- prompt: "已转换 N 条数据,上方展示了首条和末条样例。请确认数据映射是否正确"
- options:
- "确认正确,继续验证"
- "有问题,需要调整(请说明)"
uv run python -c "
from evaluator.utils.benchmark_reader import load_bench_items
items = load_bench_items('benchmark/data/<benchmark_name>/<dataset>.jsonl')
print(f'成功加载 {len(items)} 条 BenchItem')
print(f'首条 ID: {items[0].id}')
print(f'评估器: {items[0].eval.evaluator}')
"
uv run python -c "
import evaluator.plugin.eval_agent
from evaluator.core.interfaces.abstract_eval_agent import AbstractEvalAgent
print('已注册 EvalAgent:', list(AbstractEvalAgent.get_all().keys()))
"
uv run python -c "
from evaluator.utils.benchmark_reader import list_benchmarks
for b in list_benchmarks():
print(f'{b.name}: {[d.name for d in b.datasets]}')
"
ruff check generator/<benchmark_name>/ evaluator/core/schema.py evaluator/plugin/eval_agent/
ruff format generator/<benchmark_name>/ evaluator/core/schema.py evaluator/plugin/eval_agent/
所有验证通过后,使用 AskQuestion 确认是否跑端到端测试:
Question 1: 端到端测试
- prompt: "数据加载和 plugin 注册均通过。是否执行小规模端到端测试?(会调用 LLM API,产生少量费用)"
- options:
- "是,跑 3 条端到端测试"
- "是,跑 1 条端到端测试"
- "跳过,我稍后手动测试"
用户确认后,启动 Web 服务并通过浏览器执行(见下方「Web 驱动执行」说明),或用 CLI 快速跑:
uv run python -m benchmark.basic_runner <benchmark_name> <dataset> \
--target-type llm_api --target-model gpt-4.1 \
--limit <N> -v
目的: 用 sample 子集实际跑分,与原始论文/实验公开的基准数据对比,验证迁移后的评测管线是否产出合理且可比的结果。如果分数偏差过大,说明 converter 映射或 EvalAgent 实现有问题。
在 Phase 0 阅读论文/仓库时,就应记录以下信息(如有):
avg_score / pass_rate 如何对应如果论文没有公开基准数据,在 Checkpoint 1 中向用户确认是否有内部参考数据,或标注"无可比基准,仅做冒烟验证"。
重要: CLI 和 Web 是独立的执行通道 — CLI 跑的任务在 Web 上看不到进度。 要获得实时进度可视化,必须通过 Web 执行跑分。
Step 1: 启动 Web 服务
检查 Web 服务是否已在运行。如果未运行,后台启动:
# 后台启动 Web 服务(block_until_ms: 0)
uv run python -m web
等待服务就绪(检查 http://localhost:8000 可访问)。
Step 2: 浏览器打开任务页面
使用 open 命令打开浏览器:
open http://localhost:8000/tasks
Step 3: 通过 Web API 创建跑分任务
通过 POST /api/tasks 创建任务(等效于在 Web UI 上点击「开始评测」):
curl -X POST http://localhost:8000/api/tasks \
-H "Content-Type: application/json" \
-d '{
"benchmark": "<benchmark_name>",
"dataset": "sample",
"target_type": "llm_api",
"target_model": "<与论文对齐的模型>",
"max_concurrency": 5
}'
API 返回 task_id,用户可在浏览器 http://localhost:8000/tasks/{task_id} 实时查看进度。
Step 4: 等待任务完成
轮询任务状态直到完成:
curl http://localhost:8000/api/tasks/<task_id>
响应中的 snapshot.completed == snapshot.total 时表示完成。
--target-model应尽量与论文中的被测模型一致,以便直接对比分数。
跑分完成后,整理以下对比表格向用户展示:
## 迁移验证报告: <Benchmark 名称>
### 测试条件
| 项目 | 原版 (论文) | HolyEval 迁移版 |
|------|-----------|----------------|
| 数据子集 | <子集名> (<N>条) | sample (<M>条) |
| 被测模型 | <model> | <model> |
| Grader 模型 | <model> | <model> |
| 评估器 | <原版实现> | <evaluator name> |
### 分数对比
| 指标 | 原版基准 | HolyEval 结果 | 偏差 | 判定 |
|------|---------|-------------|------|------|
| avg_score | <X> | <Y> | <±Z%> | ✅/⚠️/❌ |
| pass_rate (如有) | <X> | <Y> | <±Z%> | ✅/⚠️/❌ |
偏差判定标准:
- ✅ 偏差 ≤5%: 正常范围(LLM 非确定性 + 采样差异)
- ⚠️ 偏差 5-15%: 需关注,可能因采样数不足或 prompt 微调
- ❌ 偏差 >15%: 需排查,converter 映射或 eval 逻辑可能有误
### 按标签维度对比 (如有)
| 标签 | 原版 | HolyEval | 偏差 |
|------|------|---------|------|
| <tag1> | ... | ... | ... |
### 分析 & 结论
<对偏差的解释,已知差异因素,是否通过验证>
展示上述对比报告后,使用 AskQuestion 确认:
Question 1: 对比结果
- prompt: "上方为 sample 跑分与论文基准的对比报告。请确认迁移结果是否可接受"
- options:
- "结果可接受,迁移完成"
- "偏差较大,需要排查(请说明关注点)"
- "无原版基准数据,冒烟通过即可"
如果用户选择"偏差较大,需要排查",按以下方向排查:
排查修复后重新执行 5.2-5.4,直到用户确认通过。
集成完成后,更新以下文档,确保新 benchmark 在所有入口可见:
| 文件 | 更新内容 |
|---|---|
README.md | 「Benchmark 数据集」表格追加新行 + CLI 示例追加新命令 |
CLAUDE.md | Commands 区 CLI 示例追加 + Benchmark Data 目录树追加 + Data Conversion 追加转换器说明 |
web/guides/run-benchmark.md | 「可用数据集」表格追加新行 + CLI 示例追加 |
web/guides/generate-benchmark.md | 「已内置的转换器」表格追加新行 |
web/guides/overview.md | generator 目录树追加 + 评估能力表追加(仅 Case B 新增 EvalAgent 时) |
Case B 额外更新(新增了 EvalAgent):
文件 更新内容 web/guides/develop-eval-agent.md「现有评估器」表格追加 + 「关键文件」表格追加参考实现 CLAUDE.mdEvalAgent 实现表 + Key Modules 追加说明 README.md「已注册插件」表格 EvalAgent 区域追加新行
| 组件 | 文件 | 作用 |
|---|---|---|
| Converter | generator/healthbench/converter.py | HealthBench JSONL → BenchItem JSONL |
| EvalInfo | evaluator/core/schema.py → HealthBenchEvalInfo | rubrics 配置结构 |
| EvalAgent | evaluator/plugin/eval_agent/healthbench_eval_agent.py | 原版 GRADER_TEMPLATE + scoring |
| 注册 | evaluator/plugin/eval_agent/__init__.py | import 触发注册 |
| 数据 | benchmark/data/healthbench/metadata.json | 元信息(target_configurable: true) |
| 数据 | benchmark/data/healthbench/sample.jsonl 等 | 转换后的 BenchItem 数据 |
数据映射:
HealthBench 原始 → HolyEval BenchItem
─────────────────────────────────────────────────
prompt (多轮对话) → 拆分为 history + strict_inputs
prompt[:-1] (历史轮次) → history [{role, content}](41.7% 用例有多轮)
prompt[-1].content (user msg) → user.strict_inputs[0]
rubrics[].criterion → eval.rubrics[].criterion
rubrics[].points → eval.rubrics[].points
example_tags → tags
prompt_id → id (加 "hb_" 前缀)
user.type = "manual"(预设输入,零 LLM)
target = 不在 BenchItem 中(运行时决定)
评估逻辑:
achieved = Σ(points for rubric where criteria_met=True)
total_possible = Σ(points for rubric where points > 0)
score = clip(achieved / total_possible, 0, 1)
result = "scored"(不做 pass/fail 判定)
BenchItem(数据集用例 — 没有 target):
class BenchItem(BaseModel):
id: str # 唯一标识
title: str # 一句话标题
description: Optional[str] # 补充说明
user: BenchUserInfo # 虚拟用户配置(含 target_overrides)
eval: EvalInfo # 评估配置(Discriminated Union)
history: List[Dict[str, str]] # 可选,评测前历史对话 [{role, content}]
tags: List[str] # 分类标签
运行时转换链:
BenchItem + CLI runtime_target
↓ bench_item_to_test_case()
TestCase(包含 user, target, eval, history, tags)
↓ do_single_test()
TestResult(包含 score, result, feedback, trace, cost)
↓ build_bench_report()
BenchReport
完成集成后,确认以下所有项目:
generator/<name>/__init__.py 存在generator/<name>/converter.py 可正确转换数据benchmark/data/<name>/metadata.json 格式正确benchmark/data/<name>/<dataset>.jsonl 可被 load_bench_items() 加载evaluator/core/schema.py 中新增 EvalInfo 配置类并加入 Unionevaluator/plugin/eval_agent/<name>_eval_agent.py 实现完整evaluator/plugin/eval_agent/__init__.py 注册新 EvalAgentbenchmark_reader.list_benchmarks() 可发现新数据集ruff check 和 ruff format 通过