1. 为什么我开始折腾 harness 而不是继续调 prompt先说清楚 harness 是什么。你可以把它理解成模型外面那层胶水代码决定模型看到什么输入、上下文怎么拼、检索走哪条路、输出怎么后处理。同一个模型换一套 harness效果能差出好几个点——这不是玄学是每天都在发生的事。Meta-Harness 这篇论文的核心思路很直接不改模型权重让 coding agent 去迭代优化这层 Python 代码。它把 harness 当成一个可搜索的代码空间agent 读历史候选的源码、分数、执行轨迹提出新版本评估写回文件系统循环大约 20 轮。消融实验里有个结论我印象很深——只给 agent 看分数和文字摘要中位数准确率 34.9给它看完整的执行轨迹和源码直接到 50.0。压缩过的信息几乎没用。这对我这种做 LLM 应用的人启发很大。以前我调 harness 全靠手改改完跑一遍看分数失败了也不知道是检索阶段的问题还是 prompt 格式的问题。现在我想在本地复现一套可复现的 harness 优化流程用 TaoToken 统一 Key 和 API 通道接入模型把 coding agent 的调用成本和行为集中管理起来。这篇要交付的东西很具体一套能跑起来的 harness 目录结构、agent 配置、运行命令以及优化前后任务通过率的对比验证动作。适合已经在写 LLM 应用、想系统化优化外围代码的开发者。如果你还在纠结要不要换个更大的模型不妨先看看模型外面那层代码还有多少空间。我试过把 harness 的每次修改都记下来包括失败 case 的完整轨迹这个习惯比换模型带来的收益更稳定。下面从环境准备开始一步步搭起来。2. 用 TaoToken 统一 Key 接入 coding agent 的前置准备在跑 harness 优化之前得先解决一个工程问题coding agent 要反复调用模型如果每个环节都单独配 Key、单独管额度很快就会乱。TaoToken 在这里的作用是提供一个统一的 API 通道把模型对话、coding agent 的调用都收敛到一个 Base URL 和一把 Key 上。你需要准备的东西不多一个 TaoToken 账号、一把 API Key、一个能跑 Python 的本地环境。Key 在控制台创建地址是 https://taotoken.net/api-keys 创建后复制出来后面配置里会用到。模型对话的入口在 https://taotoken.net/models 可以先在那里确认你要用的模型 ID 是否可用。这里要强调一个概念TaoToken 不是替代你的编辑器或 agent 框架它是模型调用的通道。你的 coding agent 还是原来那个只是把请求发到统一的 Base URL 上。这样做的好处是harness 优化过程中 agent 会发起大量请求统一通道方便你观察用量、排查问题。关于 Base URLAPI 调用统一用 https://taotoken.net/api 注意这个地址不带任何查询参数。如果你用的是 Claude Code 这类工具它的配置里需要填 Anthropic 兼容的 Base URL具体写法在接入文档里有说明https://taotoken.net/doc 。我建议在动手写 harness 之前先用最简方式验证一次通道是否通。可以用 curl 发一个最小的请求确认返回正常再进入后面的目录搭建。这一步能省掉很多以为是代码问题其实是 Key 没配对的排查时间。另外提醒一点harness 优化是个迭代过程agent 每轮会读大量文件、发多次请求。建议在 TaoToken 控制台里先看清楚当前的用量和限额避免跑到一半中断。控制台地址是 https://taotoken.net/console 登录后能看到调用记录。准备好 Key 和通道之后下一步就是搭 harness 的目录结构。这个结构的设计直接决定了 agent 能不能高效地翻文件——论文里 agent 每轮平均读 82 个文件靠的就是文件系统里信息组织得清楚。3. 可复制的 harness 目录结构与 agent 配置这一节是核心给你一套可以直接抄的目录结构和配置。整个思路是把 harness 代码、历史候选、评估结果、执行轨迹分开放让 agent 能用 grep、cat 自己去翻。先看目录结构meta-harness/ ├── harnesses/ │ ├── current/ │ │ └── harness.py # 当前最优 harness │ ├── candidates/ │ │ ├── cand_001.py # 历史候选源码 │ │ ├── cand_002.py │ │ └── ... │ └── archive/ │ └── scores.jsonl # 每个候选的分数记录 ├── traces/ │ ├── cand_001/ │ │ ├── case_0.json # 单条 case 的完整执行轨迹 │ │ ├── case_1.json │ │ └── errors.log │ └── ... ├── tasks/ │ ├── train.jsonl # 训练任务 │ └── val.jsonl # 验证任务 ├── eval/ │ └── run_eval.py # 评估脚本 ├── agent/ │ └── settings.json # coding agent 配置 └── run_search.py # 搜索主循环关键设计点traces/目录必须保存原始执行轨迹不能只存分数。论文的消融实验已经证明压缩信息对 agent 几乎没用。每条 case 的输入、模型输出、中间推理、是否通过全部落盘。接下来是 agent 的配置文件。以 Claude Code 风格的 settings 为例路径放在agent/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的_TaoToken_Key, ANTHROPIC_MODEL: claude-opus-4-6 }, permissions: { allow: [Read, Grep, Glob, Bash(cat:*), Bash(grep:*)] }, workingDirectory: ./meta-harness }这里三件套要写全Base URL 是https://taotoken.net/apiKey 是你创建的那把Model ID 填你要用的模型。如果你用的是 Codex 风格的配置对应的是auth.json结构类似把 base_url 和 api_key 填进去即可。harness 本身长什么样一个最小的文本分类 harness 示例放在harnesses/current/harness.pyimport json from openai import OpenAI client OpenAI( base_urlhttps://taotoken.net/api, api_key你的_TaoToken_Key ) def build_prompt(sample, few_shot_examples): shots \n.join( f输入{e[text]}\n标签{e[label]} for e in few_shot_examples ) return f参考示例\n{shots}\n\n输入{sample[text]}\n标签 def classify(sample, few_shot_examples, modelclaude-opus-4-6): prompt build_prompt(sample, few_shot_examples) resp client.chat.completions.create( modelmodel, messages[{role: user, content: prompt}], temperature0 ) return resp.choices[0].message.content.strip()这个 harness 很简单就是拼 few-shot 然后调模型。agent 要优化的就是build_prompt里的样本选择、排序、格式以及classify里的调用逻辑。你可以把它换成检索、多轮调用、后处理等更复杂的结构。评估脚本eval/run_eval.py负责跑一遍 harness 并落盘轨迹import json from harnesses.current.harness import classify def run_eval(tasks_path, candidate_id): tasks [json.loads(l) for l in open(tasks_path)] results [] for i, task in enumerate(tasks): try: pred classify(task, few_shot_examples[]) passed pred task[label] except Exception as e: pred, passed str(e), False trace { case_id: i, input: task[text], prediction: pred, label: task[label], passed: passed } results.append(trace) with open(ftraces/{candidate_id}/case_{i}.json, w) as f: json.dump(trace, f, ensure_asciiFalse) acc sum(r[passed] for r in results) / len(results) with open(harnesses/archive/scores.jsonl, a) as f: f.write(json.dumps({candidate: candidate_id, acc: acc}) \n) return acc搜索主循环run_search.py的逻辑是让 agent 读历史、提新 harness、评估、写回。这里不展开 agent 的完整 prompt核心是给它文件系统访问权限让它自己决定看哪些文件。配置里有个容易踩的坑ANTHROPIC_BASE_URL后面不要加/v1之类的后缀TaoToken 的 API 地址就是https://taotoken.net/api加了反而会 404。这个我在接入文档里确认过。4. 运行搜索循环并验证优化前后的通过率配置搭好之后跑起来其实就几条命令。先初始化目录和第一版 harnessmkdir -p meta-harness/{harnesses/{current,candidates,archive},traces,tasks,eval,agent} cp your_baseline_harness.py meta-harness/harnesses/current/harness.py准备任务数据tasks/train.jsonl每行一条{text: 这是一段待分类的文本, label: 类别A}先跑一次基线评估拿到优化前的通过率cd meta-harness python eval/run_eval.py --tasks tasks/val.jsonl --candidate baseline输出会写到harnesses/archive/scores.jsonl同时traces/baseline/下会有每条 case 的轨迹。记下这个基线准确率后面要对比。然后启动搜索循环。这里用 coding agent 驱动命令形式取决于你用的 agent。以 Claude Code 为例cd meta-harness claude --settings agent/settings.json \ --prompt 读取 harnesses/ 和 traces/ 下的所有历史记录提出一版新的 harness.py写入 harnesses/candidates/cand_001.py然后运行 python eval/run_eval.py --tasks tasks/train.jsonl --candidate cand_001agent 会自己去 grep 历史分数、cat 失败 case 的轨迹然后改代码。跑完一轮后你手动或脚本触发下一轮把当前最优复制到harnesses/current/。大约跑 15 到 20 轮然后做对比验证。把优化后的 harness 放到 current再跑一次验证集python eval/run_eval.py --tasks tasks/val.jsonl --candidate optimized对比scores.jsonl里的两条记录cat harnesses/archive/scores.jsonl你会看到类似这样的输出{candidate: baseline, acc: 0.342} {candidate: cand_001, acc: 0.361} {candidate: cand_007, acc: 0.408} {candidate: optimized, acc: 0.437}从 0.342 到 0.437涨了 9 个多点。这个幅度和论文里 LawBench 上的表现量级接近。注意验证集和训练集要分开避免过拟合——论文里也提到 USPTO 上泛化不太稳定所以验证集的结果才是你真正该信的。验证动作里有个细节不要只看总准确率去traces/optimized/里翻翻哪些 case 从失败变通过了哪些反而退化了。agent 优化出来的 harness 往往有明确的策略倾向比如先确认再质疑的两阶段分类理解它的适用边界比记住分数更重要。如果你想让 agent 长期跑、反复迭代可以考虑用 Coding Plan 这类按周期计费的方式比单次调用更适合这种高频迭代场景https://taotoken.net/coding-plan 。5. 跑 harness 优化时常见的报错与排查这一节列几个我在跑这套流程时真实遇到的报错以及怎么定位。401 Unauthorized。最常见的原因是 Key 没配对或者 Base URL 写错了。检查agent/settings.json里的ANTHROPIC_API_KEY是不是从 https://taotoken.net/api-keys 复制的那把注意不要有多余空格。Base URL 必须是https://taotoken.net/api不要加/v1。如果用的是环境变量确认 shell 里没有旧的同名变量覆盖。local proxy failed。这个报错通常出现在 agent 尝试走本地代理但配置不一致的时候。检查你的 settings 里有没有残留的 proxy 配置项把它删掉让请求直接走 Base URL。TaoToken 的通道本身就是统一入口不需要额外代理层。Error reading choices / choices 字段为空。这是响应解析的问题多半是模型返回了非预期格式或者你用的模型 ID 和实际可用的不一致。先去 https://taotoken.net/models 确认模型 ID 拼写然后在 harness 里加一层防御resp client.chat.completions.create(...) if not resp.choices: raise ValueError(f空响应: {resp})OAuth 相关报错。如果你用的是 Claude Code 且看到 OAuth 字样说明它在尝试走账号登录而不是 API Key。在 settings 里显式指定ANTHROPIC_API_KEY和ANTHROPIC_BASE_URL把 OAuth 路径绕开。接入文档里有针对 Claude Code 的完整配置示例https://taotoken.net/doc 。评估脚本报 FileNotFoundError。检查traces/{candidate_id}/目录是否存在。run_eval.py在写轨迹前不会自动建目录加一行os.makedirs(ftraces/{candidate_id}, exist_okTrue)就好。agent 读不到历史文件。确认permissions.allow里给了 Read、Grep、Glob 权限并且workingDirectory指向meta-harness根目录。如果 agent 的工作目录不对它 grep 不到harnesses/archive/scores.jsonl就会瞎猜。排查这类问题的通用思路是先确认通道通不通curl 一个最小请求再确认配置对不对三件套 Base URL、Key、Model ID最后才怀疑代码逻辑。大部分报错都出在前两步。6. 把 harness 当成一等公民来维护跑完这一轮我最大的感受是harness 优化这件事自动化框架只是加速器真正有价值的是它逼你建立的习惯。第一每次修改都留痕。scores.jsonl里每一行都是一个候选的分数配合traces/里的原始轨迹你能回溯任何一次改动的因果。这比我记得上次改了个 prompt 好像好了一点靠谱得多。第二失败 case 的完整轨迹比分数重要。论文的消融已经证明了只给分数 agent 学不到东西。你自己 debug 也一样看 10 条具体失败 case 的推理过程比看一个失败率 30%的数字有用。第三harness 是可搜索的代码空间不是一次性写好的固定代码。哪怕你不用 agent 自动搜索手动迭代时也应该把它当成一个可以系统探索的空间而不是改到能跑就停。如果你想把 coding agent 长期挂在这套流程上反复迭代Coding Plan 的按周期方式会比单次调用更省心https://taotoken.net/coding-plan 。模型对话入口在 https://taotoken.net/models 接入文档在 https://taotoken.net/doc API Key 在 https://taotoken.net/api-keys 。通道统一之后剩下的就是让 agent 去翻文件、改代码、跑评估你负责看轨迹和判断方向。