LLM 负责把任务写自然,compiler 负责把任务变成能跑的 benchmark 文件。
有了 seed 之后,下一步就是生成任务草稿。
这一层我觉得设计得很清楚:LLM 参与自然语言任务生成,最终可执行文件由规则 compiler 生成。
这就避免了一个很常见的问题:让 LLM 直接写 benchmark 代码,结果格式、字段、评测逻辑都容易抖。
1. 输入是 seed 子图
LLM 的输入来自前面采样出来的 seed。
seed 里有真实实体和关系:
{
"seed_id": "SEED_L3_PERMISSION_001",
"difficulty": 3,
"motif_id": "L3_PERMISSION_APPROVAL",
"domains": ["document", "permission"],
"actors": [
{
"user_id": "user017",
"name": "Alice",
"role": "document_owner"
},
{
"user_id": "user031",
"name": "Bob",
"role": "requester"
}
],
"entities": {
"permission_request": {
"request_id": "req000123",
"document_id": "doc000456",
"requester_id": "user031",
"owner_id": "user017",
"permission_requested": "edit",
"status": "pending"
}
},
"allowed_tools": [
"list_permission_requests",
"get_document",
"approve_permission_request",
"list_document_permissions"
]
}
这相当于把题目材料都准备好了。
LLM 要做的是把这个结构化材料改写成自然、多阶段、可评测的用户任务。
2. task_draft.json 的结构
LLM 输出的是 task_draft.json。
这个 draft 是中间表示,后面还会继续编译成最终任务文件。
它里面通常包含:
task_meta
stages
evaluation_outline
每个 stage 里会写:
stage_id
role
user_id
user_says
stage_goal
depends_on_stages
required_tools
optional_tools
forbidden_tools
fill_blanks
state_checks
state_change
这里有两个很关键的评测结构。
第一个是 fill_blanks。
它用于读任务,比如让 Agent 查出 request_id、document_id、requester_id。
第二个是 state_checks。
它用于写任务,比如审批后检查 PermissionRequest 是否变成 approved,DocumentPermission 是否出现 edit 权限。
这两个结构让任务可以自动评测。
3. 多阶段任务怎么表达
以权限审批为例,task draft 可能有两个阶段。
第一阶段:
用户说:找到 Partner Ops launch checklist 对应的 pending edit request。
目标:定位 request_id、document_id、requester_id。
工具:list_permission_requests、get_document。
评测:fill_blanks 检查三个 ID。
第二阶段:
用户说:审批刚才那个请求,并确认对方获得 edit 权限。
目标:沿用第一阶段找到的 request_id。
工具:approve_permission_request、list_document_permissions。
评测:state_checks 检查数据库状态。
这就能测试 Agent 的跨阶段记忆。
如果第一阶段找到的是 req000123,第二阶段就要审批同一个 request。
任务里会有 cross-stage rules:
Stage 2 approve 的 request_id 要来自 Stage 1。
最终权限要对应同一个 document_id 和 requester_id。
这种任务比单轮查询更像真实企业工作。
4. compiler 做什么
LLM 生成 draft 之后,compiler 会把它编译成两类文件:
task.md
task.py
task.md 给人看,方便人工检查任务语义。
task.py 给 benchmark runner 和 evaluator 用,里面有可执行任务定义、数据库补丁、阶段评测要求和跨阶段检查。
编译逻辑大概是:
读取 task_draft.json
读取 seed.json
补全 expected 字段
根据 seed 构造 db_setup patches
校验 stage / fill_blanks / state_checks
生成 cross-stage checks
render task.md
render task.py
写入 benchmark/tasks
这里我觉得最好的设计是:LLM 输出 JSON,compiler 确定性生成最终任务。
自然语言表达交给 LLM。
可执行结构交给规则代码。
5. 为什么要多一个 draft 层
很多项目会直接让 LLM 写最终代码。
但 benchmark 任务文件对格式要求很高,字段错一个就跑崩。
EntBench-Agent 多加一层 task_draft.json,就有了缓冲区。
LLM 的输出先落到结构化 JSON。
规则门控可以检查 JSON。
compiler 可以修补一些可推断字段。
最终 .py 文件保持稳定。
这样做的好处很明显:
任务自然性来自 LLM
实体真实性来自 seed
可执行性来自 compiler
评测稳定性来自 state_checks
这就是这一层最重要的工程思路。