Life-Harness 审阅报告与使用指南
> 一份"先看骨架、再看血肉、最后上桌吃饭"的工程审阅。 > 评审对象:Tianshi-Xu 等的 Life-Harness 仓库与 arXiv 2605.22166。 > 评审时刻:2026-07-03。 > 评审立场:动手拆、贴代码、不空谈。
---
序:为什么要审这个项目
盖仓库自陈"不动模型、不改环境、不改任务",却声称在 7 套环境、18 个 backbone、126 个 model-environment 组合里把成功率相对拉高 88.5%。这等好事若是真的,比砸 GPU 跑 SFT 痛快太多;若不是,又属于"工具奇迹论"那一类 AI 鸡汤。
费曼有言:"You must not fool yourself — and you are the easiest person to fool." 不拆穿这种奇迹论之前,谁也不该按下"复制粘贴"键;拆穿了还要追一句——奇迹和咒语的差别,就在「最早的可验证点」。
下面这报告,做三件事:
- 拆骨架:四层干预(H2/H3/H4/H5)到底是什么动作、什么时机、用什么代码实现的。
- 看血肉:在
agent_industrial的家族里,这些干预的真正工作量、真正陷阱是什么。 - 上桌吃饭:从环境安装到跑出第一条 baseline,给一份可执行的指南。
---
第一部分:项目本质
1.1 一句话定位
Life-Harness 是一套"接口适配层"——在 frozen model 与 deterministic benchmark 之间,插一段可修改的运行时壳,依据训练轨迹里反复出现的失败模式,对"输入契约、行动闸门、轨迹监管、技能注入"四类干涉点做加性矫正。它承诺:模型不动、数据不动、打分不动,只动壳。
论文锚点(arXiv 2605.22166 v2,2026-05-27):
> *On seven deterministic environments from τ-bench, τ2-bench, and AgentBench, Life-Harness improves 116 out of 126 model–environment settings across 18 model backbones, with an average relative improvement of 88.5%.*
代码同步发布,README 顶部的 README 头图便把这数字贴在了第一个像素。
1.2 几个数字的含义
仓库 README 与论文摘要给出四个数字:7、18、116/126、88.5%。逐一拆之。
| 数字 | 含义 | 注意事项 |
|---|---|---|
| 7 | benchmark 数量 | 跨 τ-bench(airline/retail/telecom 单轨道);τ2-bench(airline/retail/telecom + banking_knowledge 实际跑的是 uv 轨的 4 个);AgentBench(ALFWorld/DBBench/OS/WebShop 4 个)。加和=8,论文说 7,应是 banking_knowledge 算辅助对照 |
| 18 | model backbones | Qwen3-4B-Instruct(开发来源)→ 17 个其它模型。只在 Qwen3-4B 上做 harness 演化,再迁移 |
| 116 / 126 | 改进 / 总组合 | 116 个组合相对基线有提升;10 个无改进甚至回退 |
| 88.5% | 平均相对增益 | 是相对值,不是绝对成功率。基线 20%、harness 后 37.7%,相对+88.5% |
1.3 它改的"层"在哪里
老规矩,要识别一层是否真的存在,先问"它做了一种独立的事"。
仓库方法层文档(Harness.md / Harenss.md,两份内容略有出入但骨架一致)明确说:
| 任务协议层 | 综述中对应层 | 改动类型 |
|---|---|---|
| H0 任务解析 | 不计 | 一次性解析任务上下文(家庭场景的目标 / 目的地 / 任务类型) |
| H1 基线 | agent loop | 啥也不改 |
| H2 行动闸门 | Action Gate | 改 agent 输出 → 工具调用这一步 |
| H3 契约嵌入 | Tool Description Embedding | 改 tools[].function.description |
| H4 轨迹监管 | Post-Execution Monitor | 改工具返回(annotation)+ 改下一步提示 |
| H5 技能注入 | Procedural Skill | 改 system prompt 前缀 |
1.4 工程规模
两个子项目:
Life-Harness/
├── AgentBench/ # Docker 轨,Python 3.9 + conda
└── TauBench/ # uv 轨,Python 3.12 + uv
我手数了一下两条轨的核心 harness 代码:
| 子项目 | harness 文件 | 代码量 | 风格 |
|---|---|---|---|
| AgentBench | alfworld.py 单文件集成 H0–H5 | 1580 行 | 厚文件、强约束 |
| AgentBench | dbbench.py / os_interaction.py / webshop.py | 2736/2514/2364 行 | 同上 |
| TauBench | airline.py retail.py telecom.py | 小而分 | 协议 + Mixin |
| TauBench | base.py h3_tools.py policy_rag.py skills.py | 各自专注 | 协议导向 |
---
第二部分:方法论——四层干预(费曼视角)
2.1 三类失败模式(Harness.md 的判断框架)
Harness.md §3.1 给了一个干净分类:
> Type 1: Action-Interface Errors(H2 管)—— 行动对不上接口。 > Type 2: Tool-Usage Mismatch(H3 管)—— 工具语法对、语义错或低效。 > Type 3: Self-Reinforcing Trajectory Failure(H4 管)—— 同类动作/观测反复出现,自我强化到耗尽预算。
判断准则是:绝大多数失败不是模型推理能力不够,而是执行层机械错误。这一点值得逐字掂量。想想你跑 agent 时打印出的"格式错了、参数错了、循环震荡"——是不是占了失败案例的 80% 以上?是的话,作者的判断站得住。
2.2 五层接口 + 触发时机表
| 层 | 触发时机 | 失败类型 | 核心机能 |
|---|---|---|---|
| H2 | agent 输出后、工具执行前 | Type 1(interface) | 修复格式 / 校验 / 强行执行 |
| H3 | episode 初始化,一次 | Type 2(mismatch) | 把环境约束塞进 tool description |
| H4 | 工具执行后、下回合前 | Type 3(repetition) | 错误恢复 / 停滞检测 / 预算管理 |
| H5 | episode 开始 | general | 任务相关技能注入 |
2.3 H2:行动闸门——ALFWorld 的 verb-safe similar
ALFWorld 的 harness 在 pre_validate_action 里实现了一个细节验证:动作字符串走进 admissible 名单前,先做"动词级的相似度兜底"。代码骨架(alfworld.py:803-871):
def pre_validate_action(self, raw_action, admissible):
# 1) 若 H4 强行硬塞动作(forced_action),且 agent 当前是 task-critical verb,
# 让位——勿打断 agent 的核心动作
# 2) 精确匹配直接放行
# 3) 否则走 _gate_action 做"verb-safe similarity"
# 4) invalid_consecutive_count 累计;超阈值就 block
要点:
- 不让 H4 一票压死 H2:若 agent 自己也在做 task-critical 动作(take / put / clean / heat / cool / use / examine),即使 H4 已记下要强塞,也放行。这是一种"在确定的执行点留出双向通道"的设计。
- verb-safe similarity(
alfworld.py:569起的_gate_action):用difflib.SequenceMatcher做限定-动词的首段相似度,低于阈值拒收。这比"全字符串相似度"更可控。 - block threshold ≠ 1:连续无效 N 次才硬截,否则仅提示。这样 H2 是"渐进升级",不是"一刀切"。
2.4 H3:契约嵌入——为什么放在 tool description 里
TauBench 的实现(TauBench/src/tau2/harness/h3_tools.py)暴露了一处精确设计:
> Unlike the system prompt (a message at position 0 that gets pushed down by accumulating conversation turns), tool descriptions are re-sent in every LLM API request as part of the tools parameter. The model therefore sees them at full attention weight regardless of how long the dialogue has grown.
这是 H3 区别于 system-prompt injection 的关键。System prompt 随对话长度衰减(位置编码),而 tools[].function.description 每个请求都重发,等于"永久视窗"。
实现路径(h3_tools.py:54-82):
def _append_hint_to_tool(tool, hint):
orig_func = tool._func
new_doc = (orig_func.__doc__ or "").rstrip() + "\n\n" + hint
@functools.wraps(orig_func)
def _patched(*args, **kwargs):
return orig_func(*args, **kwargs)
_patched.__doc__ = new_doc
return Tool(func=_patched, use_short_desc=tool._use_short_desc, **tool._predefined)
这是教科书级的"minimal-invasive patch":原函数不被替换,只把 __doc__ 拓展,schema 仍由 OpenAI 工具调用层从 docstring 抽取,效果就是"LLM 看到的是新描述,运行时仍是原函数"。
例子(TauBench airline):
"search_direct_flight": """\
SELECTION HINT:
• Args are exactly origin, destination, date. Do NOT pass cabin.
• Results are candidates, not recommendations. ...
• A candidate is usable only if available_seats for the requested cabin is at
least the reservation's passenger count."""
写得节制:候选框而非唯一答案;明确参数精确语义;提醒"results ≠ recommendations"防住 agent 一上来挑第一项的坏习惯。
一个隐藏奖励:H3 内容来自 harness 的 _AIRLINE_H3_HINTS 字典,与环境合约绑死在字符级——这意味着 H3 的"小抄"看起来像 inline 提示,但既不动 eval、也不暴露 gold answer。它的代价是 token 开销:每请求每工具描述都要重发,但相对 system-prompt,它对长对话更可靠。
2.5 H4:轨迹监管——三个具体干预
ALFWorld 的 post_step_monitor 是真正能体现工程质感的部分。我挑三处具体模式(alfworld.py:875-1145)。
#### 2.5.1 L1:「examine 不亮灯」陷阱
if self._current_subgoal() == _SG_EXAMINE and self.world.inventory is not None:
nothing_special_count = sum(
1 for obs in self.last_observations if "nothing special" in obs
)
if nothing_special_count >= 2:
lamp_loc = self.world.lamp_location
response["recovery_prompt"] = (
f"Harness: 'examine {tt}' without the desklamp does nothing. "
+ (f"Go to {lamp_loc} — " if lamp_loc else "Find the lit desklamp — ")
+ f"you must be AT the lit desklamp to examine {tt} with it."
)
这是一个很费曼的干预。它没说"快去开灯",而是直接告诉 agent:当前观察里出现 nothing special 两次,这是 ALFWorld 中"未持灯"状态的环境反馈。检测这一信号比读懂 agent 内心要容易得多——这正是费曼说的"不看 agent 的内心,看世界的反馈"。判定条件是行为序列的统计学属性,而非内容语义。
#### 2.5.2 L2:导航震荡(A↔B↔A↔B)
recent_navs = [a for a in self.last_actions[-6:] if a.startswith("go to ")]
if len(recent_navs) >= 5:
unique_dests = set(recent_navs)
if len(unique_dests) <= 2 and self.task_ctx:
...
response["audit_reason"] = "nav_oscillation"
比"3 步相同动作"更精细。"A→B→A→B"在 6 步内出现 5 次,且目的地 ≤ 2 个,自动判震荡——比 task.py 里那个"3-identical-action terminator"覆盖更广。
#### 2.5.3 L3:死循环(dead_end_loop)
alfworld.py:1057-1100 区域里用 last_actions 6 步窗口判别"持有物品却反复 open/close 同容器"或"反复切换页面"等死循环。一旦触发,自动给出"execute a different exploration action next turn"的硬指令——在 H4 预算分支里,是唯一允许 hard-force 的子层。
关键设计原则:H4 默认是 soft(只给提示),budget 子分支里才允许 hard-force。这就是"在运行时玩哪种交涉"的判断。
2.6 H5:技能注入——不只是 prompt 调优
ALFWorld 用 BM25 在 ALF_SKILLS 库里检索 top-k(alfworld.py:96-117 的 retrieve_skills_for_task),TauBench 用同一模式(TauBench/src/tau2/harness/skills.py 起)。共同点是:
- 两层过滤:先按 task_type 硬筛,再 BM25 排序。
- 任务级 ≠ 工具级:H5 是 task-LEVEL 的策略,不应"重复" H2/H3 已经约束的工具级规则。skills.py 顶部即指明:
ALF_SKILLS 我粗看了一下,task_types=[] 的"策略型技能"被标记为"covered by H3/H4"——这种自我删除注释,在工程里极少见。说明研发过程是把 H5 的能力逐步"降级"到 H3/H4,因为前两层更精准。这就是 Lessons 文档反复讲的"Prefer H3 over H5 when both could fit; reserve H5 for genuine planning failures that need multi-turn glue"。
H5 的 top-k 是个敏感旋钮。--h5-top-k 默认 3,airline 经验调成 1(多了会出现"重复高显著 → 任务漂移"——见 airline lessons 第一条)。
2.7 触发时机一览
我重画一个时间线,方便对照:
[episode 开始]
└─ H0:parse_task_context(ALFWorld 一次性解析目标/任务类型)
└─ H5:cold_start_skill_hints(按 task_type 注入 1 条工具合约技能)
└─ H3:_append_hint_to_tool(重写关键工具的 description)
└─ environment 给出 init_prompt + tools list
[每回合]
1. agent 产出 raw_output
2. H2:pre_validate_action
├─ 若有 forced_action 来自上回合 H4,且 agent 不在 task-critical verb → 用 forced
└─ 否则:精确 / 相似 / 否决
3. environment 执行 action
4. 拿到 observation
5. H4:post_step_monitor
├─ 更新 world model
├─ 推进 subgoal state machine
├─ 检查 examine-without-lamp / nav_oscillation / container_oscillation / dead_end_loop
├─ 可能设 forced_action 到下一回合
└─ 产出 recovery_prompt(拼接在下一步 message)
6. 返回 agent,进入下一回合
这是一个有序的事件链。任何两层互相打架都意味着 H4 在不该塞的时候塞了,或 H5 在不该注的时候注了。Lessons 文档里反复出现"move this to a more precise layer"——翻译成人话:把"H5 的策略提示"挪去 H3 的工具描述里更稳。
2.8 何时干预——五句心诀
把上面这些细节收成五句话,给正在写新 harness 的人:
1. 判定越早越好:能 H0 判就不 H5 推。 2. 动作前先 H2 拦:能在工具执行前发现错,就别等错完了再讲。 3. 契约塞 H3:永久视窗优于 system prompt 衰减。 4. 事后 H4 兜:行为序列统计优于内容语义理解。 5. 技能 H5 兜底:但只用于"看 agent 看不到的元模式"。
费曼那条"the first principle is that you must not fool yourself"在这里变成了具体的工程原则:每一层只解决自己那一类失败,别跨层。跨层就会出现 Lessons 文档里说的"duplicated high-salience H5 detail"——把 H3 那种约束反复塞进 H5,反而把 agent 推入过拟合。
---
第三部分:双轨架构——为什么拆成两个仓库
3.1 两条轨道的差别
仓库根 README 解释为何分两个子项目:"the two benchmark families in separate folders because their environments and dependencies are intentionally different."
| 维度 | AgentBench 轨 | TauBench 轨 |
|---|---|---|
| 包管理 | conda + pip + Docker | uv(极简 Python 3.12) |
| Python | 3.9 | 3.12 |
| 任务容器 | Docker Compose(Redis + controller + worker) | in-process + uv 环境 + LiteLLM |
| 任务难度 | 交互步多(ALFWorld 50/DBBench 15/OS 8/WebShop 20) | 长对话(200 max steps) |
| 用户模拟 | 自带 user simulator(在 worker 里) | 单独 user LLM(同 API key,但不同 model identity) |
| Harness 入口 | assigner 脚本读 yaml 配置 | scripts/eval_harness.py |
3.2 ALFWorld harness 的具体骨架(AgentBench)
ALFWorld 是 AgentBench 轨里改造最深的环境。alfworld.py 的节奏(按行号):
| 行段 | 内容 |
|---|---|
| 1–266 | 数据模型、配置 dataclass、H2/H3/H4/H5 默认开关、H4 stall 阈值 |
| 270–370 | H0:TaskContext、_detect_task_type、parse_task_context |
| 376–564 | H4 world model:物品状态机、容器位置 |
| 569–615 | H2 verb-safe similar、_pick_forced_action |
| 639–800 | ALFWorldHarnessRuntime 类(包含 H4 state、force_next_action、H5 step-guidance dedup state) |
| 803–871 | H2:pre_validate_action |
| 875–1144 | H4:post_step_monitor(含 L1/L2/L3 stall detection、dead_end_loop) |
| 1148–1173 | H5 cold start |
| 1175–1420 | H4-E step guidance(_current_subgoal、skill-injection self-dedup) |
| 1424–1444 | patch_take_action_tool_description:H3 description augment |
| 1446–1568 | ALF_SKILLS 列表(含 task_types=[] "去除注释"——研发过程遗留) |
| 1570+ | first_sentence_query(用于 BM25 query 提取) |
3.3 TauBench harness 的具体骨架
TauBench 拆得更细:
| 模块 | 行数 | 职责 |
|---|---|---|
base.py | 小 | HarnessRule / HarnessAnnotator 协议 |
h3_tools.py | 中 | 工具描述 patch、H3 字典 |
policy_rag.py | 中 | airline 的 policy chunks retrieval(RAG 而非 BM25 skill 注入) |
skills.py | 大 | 跨域技能库(含 RETAIL_SKILLS / TELECOM_SKILLS)+ BM25 检索 + format_skills_block |
airline.py / retail.py / telecom.py | 小 | 域特定的 H2 rule 类 + H4 annotator |
banking_knowledge.py | 小 | banking 域(论文对照用) |
eval_harness.py:621-625:config_kwargs = dict(
...
harness_enabled=harness_h2,
harness_h3=harness_h3,
harness_h4=harness_h4,
harness_h5=harness_h5,
harness_h5_rag=False,
harness_h5_rag_top_k=args.h5_top_k,
harness_h5_rag_tool=retrieve_policy,
)
四个独立 bool + harness_enabled master。一个 200 行的 run script 涵盖了 base + h2 + h3 + h4 + h5 + malicious_user + nl_assertion 七种开关,是个简洁的 ablation 表生成器。
3.4 双轨的统一抽象
AgentBench 轨里没有 base.py 这种协议基类,是用一个 ALFWorldHarnessConfig dataclass 把所有开关拍扁。TauBench 走的是另一条路:HarnessRule + HarnessAnnotator Protocol,让域规则可以插件化。
哪种更好?看你脑里的目标——AgentBench 的"4 个环境、1 套统一 shell"够用;TauBench 的"4 个域 × 5 个干预"需要 decoratable。同一份代码原型,两套接口风格,说明作者对每轨的可扩展性期望不同。读者也得明白:这种"分叉式统一"在工程里很容易过度繁殖。
---
第四部分:性能声明的诚实评估
4.1 数字含义
论文摘要级声明:
> 7 个 deterministic 环境,18 个 backbone,116/126 组合有提升,平均相对增益 88.5%。
88.5% 是"相对",不是绝对。README 的"Table 1"那一栏直接写"Avg. relative gain"——仓库没藏。
相对增益是诚实计算:
$$\text{relative gain} = \frac{R_{\text{harness}} - R_{\text{base}}}{\max(1.0, R_{\text{base}})}$$
其中 $R_{\text{base}}$ 是基线 1 if pass^k = 1/3 里的小数。这个分母的处理详见 eval_harness.py:416-475。一旦基线是 1(完美)就分母为 1,不爆炸。
4.2 可信度——多源一致
我有几个独立的来源都支持论文的方法论与声明:
1. arXiv 论文 + GitHub README:数字、声明一致。
2. lessons/ 文档:所有迭代实验日志时间戳明确(2026-05-03 至 2026-05-07),子集 + qwen3.5-9B 比较都有具体 task id 列表(task 17 fixed 之类),是工程化记录的写法。
3. 代码骨架:ALF_SKILLS 与 RETAIL_SKILLS 等都基于 train split 训练轨迹提炼,且 README 强调"Harnesses evolved only from Qwen3-4B-Instruct trajectories transfer to 17 other models"——这是一个非常刻意的实验设计,反向支撑了"harness 不是模型特异性的作弊"。
但请注意:
- 未独立复现:本评审没真正本地拉起 docker compose 跑 116 个模型组合。所有数字以论文/仓库签字为准。
- sub30 噪声:Lessons 文档反复提"single-trial is noisy"——子集实验有 1-trial 比例,单点 pass/fail 翻转不应被过度解读。
- 工作进度(Work in progress):arXiv 摘要页明确写"Comments: Work in progress"——论文还在修订中。
4.3 限制与边界
诚实一些:
- banking_knowledge 不计入"7":仓库 README 提到 4 个 TauBench 任务,7 个环境中有一个应是跨域子集。具体细节看 README 的 benchmark 列表与论文 Table 1 对照。
- H4 hard-force 是有代价的:
pre_validate_action里让位 task-critical verb 是补丁,证明 H4 forced action 与 H2 之间会有冲突——这种 plumbing 修复暗示两条规则可能在某些 case 上互相打架。 - H5 top-k 是经验值:airline 用 1,其它用 3。没有"自动 k 选择器"。
---
第五部分:风险与陷阱——货物崇拜检测
下列每条都来自代码、Lessons 文档或 README 的明确警告。我按"在哪一行、违反后会出什么事、怎么避免"组织。
5.1 Replay safety——H4 annotator 必须无副作用
base.py 顶部(TauBench/src/tau2/harness/base.py:51-56)写了 sentinel-level 警告:
> *"Annotators run AFTER the tool executes, so they see the updated DB state. Like HarnessRule, implementations MUST only read from DB/kwargs; they must NOT have side-effects or maintain cross-call state."*
为什么强调? 因为环境是 deterministic 的,同一组输入必产生同一组输出。如果 annotator 带副作用,两次 replay 会得到不同的轨迹——loss 在第二次跑完后突然变大。Re-run 实验时若发现两次轨迹不一样,第一时间检查 H4 annotator 是否带状态。
ALFWorld harness 把 retry 防撞写得很到位——__pycache__ 里 world.* 字段是新实例化的,但 h4_forced_action、last_actions、last_observations 都是 runtime 实例的字段,重启 episode 时会被新 TaskContext 重置。
5.2 训练-测试泄露(train-test leakage)
retail_harness_lessons.md 第一条就讲:
> *"Do not use training-task-specific item IDs, order IDs, names, or expected answers in harness logic. Harnesses should describe reusable failure modes."*
我在 RETAIL_SKILLS 抽样看了一下——确实没有出现具体 order_id、产品名。task_ids 在 Skill dataclass 里只是文档元数据(docs/airline_harness_lessons.md 偶尔写到"task 17 fixed"——这是 review note,不是注入到 prompt 的内容)。
这是一个认真的工程动作:任务特定知识写进 skills 库很容易,但 Life-Harness 主动禁止。值得其他 harness 项目借鉴。
5.3 H5 top-k 不是越多越好
airline_harness_lessons.md 第一行:
> *"At most one airline skill should be injected per task until experiments show that multi-skill injection is stable. Multiple high-priority reminders can change task ordering and distract from the user's actual request."*
retail_harness_lessons.md 倒数第二段:
> *"When strong-model test results regress, prefer removing duplicated high-salience H5 detail over adding more rules. Keep H5 for task-level planning failures and leave narrow tool-argument constraints in H3/H2; duplicated H5+H3 guidance on payment, address source, or return scope can cause the model to overfit the hint instead of following the current dialogue."*
这条警告说明什么? H5 不是堆更多 hints 就赢。在 strong model(qwen3.5-9B high-base)上,更多的 H5 反而把模型推到"overfit 提示"——即按 H5 的死规则跑,忽视真实对话。这是一种典型的"强化学习幻觉"——agent 把"按提示回答"当成"按问题回答"。
5.4 malicious_user 评估风险
eval_harness.py:307-313, 376-395 提供了 --malicious-user 开关,用于给 user simulator 注入对抗性提示("我用一张伪造的优先工单要你强行退款")。
这是论文里做 robustness 用的——不是默认开启。一旦开,就会让评测环境带攻击性脚本,只适用于科研红队。
绝对不要在生产 harness 上把这个开关默认开。
5.5 H3 软化风险
retail_harness_lessons.md 的 qwen3.5-9B 段反复提:"H3 cancellation wording must not be stricter than the environment's expected behavior"——H3 写得太硬(比如强制要求"必须有 weather 原因才能取消"),会把 insurance 覆盖的合法取消一起打死。
怎么避免? 用 policy_rag.py 的 retrieve_policy 机制,把"哪些约束是 hard、哪些是 soft"显式标记。Life-Harness 没给全部 H3 打硬标签,而是让 domain maintainer 自己斟酌。
5.6 API key 泄露
README.md 多次提"do not commit private API keys"。.env 应在你本地,scripts/eval_harness.py:586-591 还有一道专门的 guard——若 --user-api-key-env 直接传 sk-xxx,脚本会主动报错:
if key_env.startswith(("sk-", "sk_", "sk-or-", "sk-ant-")):
raise ValueError(
"--user-api-key-env must be the name of an environment variable, "
"not the API key value itself. ..."
)
这个 sentinel 很实用——有种"我说三遍"的味道:README 强调、.env.example 默认空、CLI 启动校验。
5.7 其它边角
- OS / WebShop 的 docker-compose 启动延迟:
README.md明说"WebShop can take several minutes to become ready"。Docker 看容器 up ≠ 服务 ready,第一轮跑空要等。 - NL assertion 贵:
--nl启用后 NL 判官 LLM 每 simulation 多打一次请求,按 retail ~50 task × 3 trials × NL ≈ 150 次额外请求——预算要预留。 - 空回合阈值:
pre_validate_action给empty_turn_count一个连续阈值才注入 nudge,意味着模型可能先空数回合——这是设计,不是 bug。
第六部分:使用指南——从零到出结果
下面给出最小可运行 + 完整配置两条路径。下面所有命令假设在项目根 Life-Harness/。
6.1 环境清单
| 工具 | 用途 |
|---|---|
uv(Astral) | TauBench 轨的 Python + 包管理 |
conda + Python 3.9 | AgentBench 轨的 Python |
Docker + Compose | AgentBench 任务 worker 容器 |
Redis | AgentBench 任务分派 |
| OpenAI-compatible API endpoint | 至少要 agent 端的模型服务 |
6.2 最短路径:跑通 airline 的 harness 评估
# 1. 进入 TauBench 轨
cd TauBench
# 2. 同步依赖(uv 自动拉 Python 3.12 + 装包)
uv sync
# 3. 配置 API key(不要提交)
cp .env.example .env
# 编辑 .env:
# AGENT_API_BASE="http://localhost:30001/v1" # 任一 OpenAI 兼容
# AGENT_API_KEY="EMPTY" # 本地服务器
# USER_API_KEY_ENV="OPENAI_API_KEY" # 用户模拟器用另一把
# OPENAI_API_KEY="sk-..." # 真实 key
# 4. 验证数据(可选,但推荐)
uv run tau2 check-data
# 5. 单条 trial smoke test
uv run python scripts/eval_harness.py \
--domain airline --split test --trials 1 \
--enabled --h2 --h3 --h4 --h5 --h5-top-k 1 \
--output airline/smoke
结果存在 TauBench/data/simulations/airline/,含 results.json 和 harness_summary.json。
6.3 全量评估(3 trials)
# Airline, 3 trials, 完整 harness
uv run python scripts/eval_harness.py \
--domain airline --split test --trials 3 \
--enabled --h2 --h3 --h4 --h5 --h5-top-k 1 \
--output airline/harness-final
# Retail(默认无 NL assertion,省钱)
uv run python scripts/eval_harness.py \
--domain retail --split test --trials 3 \
--enabled --h2 --h3 --h4 --h5 --h5-top-k 3 \
--output retail/harness-final
# Telecom:限 max_steps 防 runaway
uv run python scripts/eval_harness.py \
--domain telecom --split test --trials 3 \
--enabled --h2 --h3 --h4 --h5 --h5-top-k 1 \
--concurrency 10 --max-steps 50 \
--output telecom/harness-final
6.4 AgentBench 轨:拉起 docker + 跑 ALFWorld
cd AgentBench
# 1. conda 环境
conda create -n agent-bench python=3.9
conda activate agent-bench
pip install -r requirements.txt
# 2. 配置 agent 端点:编辑 configs/agents/api_agents.yaml
# 把 qwen3-4b-instruct 段的 url/headers 改成你的 API
python -m src.client.agent_test \
--config configs/agents/api_agents.yaml --agent qwen3-4b-instruct
# 3. 拉 Docker 镜像(首次较慢)
docker pull mysql:8 ubuntu
docker build -f data/os_interaction/res/dockerfiles/default data/os_interaction/res/dockerfiles --tag local-os/default
docker build -f data/os_interaction/res/dockerfiles/packages data/os_interaction/res/dockerfiles --tag local-os/packages
docker build -f data/os_interaction/res/dockerfiles/ubuntu data/os_interaction/res/dockerfiles --tag local-os/ubuntu
# 4. 起 worker
docker compose -f extra/docker-compose.yml up -d --force-recreate redis controller alfworld-std
# 5. 等 WebShop:可能要几分钟首启;其它一般 < 1min
# 6. 跑评估
python -m src.assigner --config configs/assignments/alfworld.yaml
configs/assignments/alfworld.yaml 里 trials: 1、output: outputs/.../{TIMESTAMP}-only-h3-h5 已经配好。
6.5 Harness 演化循环——codex CLI 一节
TauBench/README.md 与 AgentBench/README.md 几乎逐字复述同一段:"Harness evolution is an iterative code-editing loop"。这就是 Life-Harness 的第二个真正有价值的工程动作——
对比一下传统复现方法:先跑 baseline → 人工看 trajectories → 改 prompt → 再跑 → 反复改。Life-Harness 把"看 trajectories"一步直接外包给 codex CLI,并在 prompt 里嵌一句"directly modify the harness code; do not stop at producing an analysis report"。
执行范例(TauBench):
cd TauBench
HARNESS_DIR=src/tau2/harness
TRAJECTORY_DIR=data/simulations/airline/<prev-timestamp>_harness
DESIGN_GUIDE=Harness.md
codex "
You are a coding agent responsible for improving a runtime harness for a
deterministic LLM-agent environment. ...
Inputs:
- current harness implementation: ${HARNESS_DIR}
- trajectory directory from the previous iteration, including summary metrics: ${TRAJECTORY_DIR}
- harness design guide: ${DESIGN_GUIDE}
Inspect the previous iteration's trajectories ... Directly implement targeted,
minimal updates in the appropriate harness layer. Do not only return an
analysis report. ...
"
跑完再去 eval 一次,把这次 output 当下一次 input。这是个很朴素但极难做好的循环——多数项目卡在"分析完不知道改啥"。Life-Harness 通过把"用 H2/H3/H4/H5 四个钩子改"明示在 prompt 里,把"改什么"约束到有限选项,避免了 codex 的开放空间。
6.6 怎么读结果
每次 eval 后:
results.json—— 完整 conversations、reward、tokensharness_summary.json—— 各开关是否真正生效(harness_h2_selectedvsharness_h2)、pass@k、pass^k、agent_tokens
harness_summary.json 有这一段:{
"harness_master_enabled": true,
"harness_h2_selected": true,
"harness_h3_selected": true,
"harness_h4_selected": true,
"harness_h5_selected": true,
"harness_enabled": true,
"harness_h2": true,
...
}
注意 selected 是你传的 CLI 值,harness_enabled 是实际生效值(master AND 子项)。如果两者对不上,意味着某个子项的"实际生效"受 master 限制。拿这个对账找出"为什么我开了 h5 它没生效"——常见原因就是少了 --enabled。
6.7 调参与坑
| 现象 | 检查项 |
|---|---|
| Harness 看起来没生效 | --enabled 是否传;harness_summary.json 里 harness_h* 字段值 |
| pass@1 = 0, pass^1 = 1 | 单 trial 翻车;3 trial 多跑 |
| WebShop 跑空 | docker compose 起服务慢,等 1–2 分钟 |
| NL assertion 用了但想省 | eval_harness.py 不带 --nl |
OPENAI_API_KEY is not set | .env 配过吗?--user-api-key-env 是变量名还是 key 字面值 |
| token 用得太狂 | --h5-top-k 1、H3 hint 长度、--max-steps 50 |
| 模型太聪明反被 H5 干扰 | Lessons 文档说"prefer removing duplicated H5 detail"——先减 H5 |
第七部分:故障排查速查表
按"症状 → 诊断 → 处理"排序。
| 症状 | 诊断 | 处理 |
|---|---|---|
| 跑出 0%,AgentBench | docker compose worker 没起 | docker compose ps、强制 recreate |
pre_validate_action 把合法动作 block | 阈值 invalid_block_after 过严 | 提高到 4–5,或加 never-block 前缀 |
| H5 提示一个跟任务无关的 skill | BM25 ranking 偏到泛词 | Lessons:"strict task_types guard"\ |
| Token 用量飙升 | H3 太长 + H5 top_k=3 + 长对话 | H3 压缩;H5 top_k=1;max_steps 上限 50 |
| RLHF 模型反而跑输 baseline | H5 overfit 提示——模型把 hint 当 gold | 删 H5 的高显著项,只留 H3/H4 |
--h2 没生效 | 缺 --enabled | 加 --enabled |
_with_malicious_user_prompt 改了 task 但忘了 reset | 评估态污染 | 重启 worker、清 data/simulations/ |
tau2 check-data 报错 | TAU2_DATA_DIR 未指 | 加上 export TAU2_DATA_DIR=... |
agent_tokens 累计 0 | usage 字段缺失(某些 mock 提供) | 换回真的 OpenAI 兼容 provider |
pass@k 与 pass^k 差异巨大 | 单 task 内 trial 间一致性差 | 增 trials 到 3+、识别 stochastic 任务 |
附录:仓库文档地图
我没隐藏什么——若 PDF 版打印丢失了这条信息,可以从这份表重新找路:
Life-Harness/
├── README.md # 入口:四个数字 + 论文锚点 + Benchmark 概要
├── Harenss.md (AgentBench/Harness.md 同) # AgentBench 轨方法论 + 性质清单
├── TauBench/Harness.md # TauBench 轨方法论(两套等价,措辞略不同)
├── TauBench/README.md # 安装 + API 配置 + 三条评估命令 + 演化循环
├── TauBench/docs/
│ ├── getting-started.md # uv 安装 + tau2 CLI
│ ├── airline_harness_lessons.md # 迭代经验 1:top_k / 范围精度 / RL 已训模型
│ ├── retail_harness_lessons.md # 迭代经验 2:overfit guard / strong model / 命名 vs 实物
│ ├── cli-reference.md # tau2 全 CLI
│ ├── running_simulations.md
│ └── leaderboard-submission.md
├── TauBench/src/tau2/harness/ # 模块化 harness
│ ├── base.py # HarnessRule / HarnessAnnotator 协议
│ ├── h3_tools.py # _append_hint_to_tool + 域 hint 字典
│ ├── policy_rag.py # airline policy retrieval
│ ├── skills.py # RETAIL/TELECOM skills + BM25
│ ├── airline.py retail.py telecom.py
│ └── banking_knowledge.py
├── TauBench/scripts/eval_harness.py # 主 CLI(200+ 行 argparse)
├── AgentBench/README.md # AgentBench 轨安装 + docker
├── AgentBench/src/server/harness/ # 各环境 harness(厚文件风格)
│ └── alfworld.py # 1580 行,最大一份
├── AgentBench/configs/
│ ├── agents/ # agent profile yaml
│ ├── tasks/ # env task yaml(h2/h3/h4/h5 开关在这里)
│ └── assignments/ # 任务分派(concurrency、trials、output)
└── assets/ # README 用图表
Cite:
@article{xu2026adapting,
title={Adapting the Interface, Not the Model: Runtime Harness Adaptation for Deterministic LLM Agents},
author={Xu, Tianshi and Wen, Huifeng and Li, Meng},
journal={arXiv preprint arXiv:2605.22166},
year={2026}
}
---