1. 为什么 repo-level harness 比换模型更值得先做
AI coding agent 用久了,你大概率会遇到同一个场景:新开一个 session,agent 又把pnpm写成npm,又去手动改generated/目录下的文件,又忘了跑typecheck,上次踩过的坑这次原样再踩一遍。你当场把错误修掉,关掉窗口,下次换个任务,同样的错误再来一次。
问题不在模型智商,在于你的仓库没有给 agent 留下任何"记忆"和"边界"。Mitchell Hashimoto 在讲 AI adoption journey 时提到一个关键动作:当 agent 犯错时,不要只修那个错误,而要改进 harness,让下一次更不容易犯同样的错。这个思路放到 repository 层面,就是 repo-level harness engineering。
我把它拆成 6 个要素来落地:Instructions(规则)、Constraints(机器可查的约束)、Feedback(快速验证回路)、Memory(失败与决策记忆)、Evaluation(效果度量)、Governance(维护流程)。这 6 个要素不是理论,而是你打开一个仓库就能逐条对照的检查清单。
这篇文章适合三类人:正在用 Claude Code / Codex / Cursor 做真实项目、被 agent 反复犯同一个错折磨、想把项目规则从 chat log 搬进 repo 的开发者。我会给出可复制的AGENTS.md片段,并把 Base URL 改到 TaoToken 完成一次真实的 agent 任务闭环验证。整套流程在本地仓库就能跑通,不需要复杂基础设施。
核心检索词先明确:repo-level harness 是什么?它是把 AI coding agent 工作所需的规则、约束、反馈、记忆、度量和治理,全部沉淀到 repository 里的一套工程实践。能做什么?让 agent 在新 session 里自动继承项目规则,减少越界修改和重复失败。适合谁?任何把 agent 当协作者而非一次性问答工具的开发者。
2. TaoToken 前置:把 agent 的模型出口统一到一处
在讲 6 要素之前,先把模型接入这一层理清楚。因为 harness 要跑起来,agent 得先能稳定地调用模型。我用 TaoToken 作为统一的模型出口,原因是它兼容 Anthropic 和 OpenAI 两种协议风格,Claude Code、Codex、Cline 这类工具都能接,Base URL 和 Key 的管理集中在一处,切换模型时不用改一堆配置文件。
你需要先拿到两样东西:API Key 和 Base URL。API Key 在控制台的 API Keys 页面创建,地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。Base URL 统一用 https://taotoken.net/api ,注意这个地址后面不加任何 UTM 参数,配置里原样填。
模型 ID 这块,Claude 系列常用claude-sonnet-4-5、claude-opus-4-1,OpenAI 风格可以用gpt-5、gpt-5-codex这类。具体可用列表以控制台和文档为准,文档入口在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。如果你只是想先验证模型通不通,可以直接用模型对话页面试一条请求:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。
这里有个关键点:harness 的 6 要素里,Instructions 和 Constraints 是写在 repo 里的,但 agent 执行任务时调用的模型出口是环境变量或配置文件决定的。把出口统一到 TaoToken,意味着你换模型、换工具时,repo 里的AGENTS.md不用动,只需要改一处 Base URL 和 Key。这就是为什么我把它放在前置步骤——先固定出口,再谈仓库内的规则沉淀。
对于长期跑编码任务和 Agent 工作流的场景,可以考虑 Coding Plan,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。它的定位是给持续性的编码会话用,比按次调用更适合 harness 这种需要反复验证的闭环。
3. 可复制配置:AGENTS.md 六要素与 settings 片段
这一节是全文最核心的部分,给出可以直接抄进仓库的配置。我按 6 要素组织,每个要素对应AGENTS.md里的一段,再补上工具侧的 settings 片段。
3.1 Instructions:AGENTS.md 基础骨架
在仓库根目录建AGENTS.md,内容如下。这段是给 agent 的"项目说明书",每次新 session 都会读到:
# AGENTS.md ## 包管理与命令 - 包管理器:pnpm(禁止使用 npm / yarn) - 安装依赖:pnpm install - 开发:pnpm dev - 测试:pnpm test - 类型检查:pnpm typecheck - Lint:pnpm lint ## 目录边界 - 禁止修改:generated/、dist/、node_modules/ - 禁止手动编辑:*.gen.ts、prisma/migrations/ - 敏感目录:secrets/、.env*(禁止读取或输出内容) ## 提交前检查 - 必须通过:pnpm lint && pnpm typecheck && pnpm test - 禁止提交:console.log 调试残留、注释掉的死代码 ## 规则来源 - 决策记录:docs/decisions/ - 失败记忆:docs/failures/ - 约定:docs/conventions/这段的价值在于:以前你要在 chat 里反复解释的东西,现在写一次,所有 session 共享。我实测下来,光是"包管理器用 pnpm"这一条写进去,agent 用错命令的概率就明显下降。
3.2 Constraints:把规则变成机器可查的 check
Instructions 靠 agent 自觉,Constraints 靠机器拦截。在package.json里加脚本,或者用 lint 规则实现:
{ "scripts": { "check:boundary": "node scripts/check-import-boundary.mjs", "check:generated": "node scripts/check-generated-hygiene.mjs", "gate": "pnpm lint && pnpm typecheck && pnpm check:boundary && pnpm check:generated && pnpm test" } }check-import-boundary.mjs的作用是扫描routes/下是否直接 import 了 database 模块。与其在AGENTS.md里写"不要从 routes 直接 import database",不如让脚本在 CI 里直接拦下来。能检查的规则,就不要只交给 agent 的自律。
3.3 工具侧 settings:Claude Code 与 Codex 的接入片段
Claude Code 的配置放在~/.claude/settings.json,把 Base URL 指向 TaoToken:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "你的_TaoToken_API_Key", "ANTHROPIC_MODEL": "claude-sonnet-4-5" } }Codex 的配置在~/.codex/auth.json和~/.codex/config.toml。auth.json放 Key:
{ "OPENAI_API_KEY": "你的_TaoToken_API_Key" }config.toml指定 Base URL 和模型:
model = "gpt-5-codex" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" wire_api = "chat"三件套必须齐全:Base URL 是https://taotoken.net/api,Key 是你在控制台创建的,Model ID 是claude-sonnet-4-5或gpt-5-codex这类。缺任何一个,agent 都跑不起来。
3.4 Memory:docs/failures 的写法
在docs/failures/下建文件,每条失败记录至少包含四段:
# Failure: agent 手动修改 generated 文件 ## 发生了什么 agent 在修复类型错误时,直接编辑了 generated/api.gen.ts ## 为什么重要 该文件由 codegen 生成,手动修改会在下次生成时被覆盖,且掩盖真实问题 ## 下次如何检测 运行 pnpm check:generated,检测 generated/ 下文件的 git diff ## 预防措施 在 AGENTS.md 目录边界中明确禁止,并在 gate 脚本中加入 check:generatedfailure memory 必须连接到 detection 或 prevention,否则它只是阅读材料,不是 harness。
3.5 Evaluation 与 Governance 的配置
Evaluation 用 task outcome record 记录,放在docs/outcomes/,字段包括:expected file boundary、actual changed files、是否 wrong-file edit、是否重复 known mistake、first-pass verification 是否通过、human rework 分钟数。
Governance 用 prompt-level command 约定,写进AGENTS.md:
## Harness 维护命令 - /harness doctor:只诊断,不修改文件 - /harness update:更新参考,不覆盖 target repo - /harness refresh:检查 stale / duplicated guidance - /harness review:从反方视角检查当前 change set这些不是魔法命令,是交给 agent 的 workflow 约定。
4. 验证请求:跑通一次 agent 任务闭环
配置写完,必须验证。我给出两条验证路径:一条直接测模型出口,一条测 agent 在 repo 里的行为。
4.1 直接验证模型出口
先用 curl 测 TaoToken 的接口通不通:
curl https://taotoken.net/api/v1/messages \ -H "x-api-key: 你的_TaoToken_API_Key" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-5", "max_tokens": 128, "messages": [ {"role": "user", "content": "只回复两个字:通了"} ] }'如果返回 JSON 里content数组有文本内容,说明 Base URL、Key、Model ID 三件套正确。这一步排除了接入层问题,后面 agent 报错就不用怀疑是出口的问题。
4.2 验证 agent 是否读到 AGENTS.md
在仓库里启动 Claude Code,给它一个会触发边界规则的任务:
claude "在 routes/user.ts 里加一个查询用户列表的接口"观察 agent 的行为。如果AGENTS.md生效,它应该:先读AGENTS.md,确认包管理器是 pnpm,不去碰generated/,改完代码后主动跑pnpm gate。如果它直接 import database 到 routes,说明 Constraints 没拦住,需要检查check:boundary脚本是否接进了 gate。
4.3 验证失败记忆是否被继承
故意制造一个已知失败场景:让 agent 去改generated/api.gen.ts。如果docs/failures/和AGENTS.md的目录边界都生效,agent 应该拒绝修改,并引用失败记录说明原因。这一步验证的是 Memory 要素是否真正进入了 agent 的上下文。
4.4 记录 task outcome
任务结束后,在docs/outcomes/写一条记录:
# Outcome: 2025-01-15 用户列表接口 - expected boundary: routes/user.ts, services/user.ts - actual changed: routes/user.ts, services/user.ts - wrong-file edit: 否 - repeated known mistake: 否 - first-pass verification: 通过 - human rework: 3 分钟这条记录是 Evaluation 的原始数据。攒够一定数量,你才能回答"harness 到底有没有让 agent 更有效"这个问题。单次任务说明不了什么,需要 pre-harness baseline 和可比任务。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
配置过程中最容易卡在几个具体报错上,逐个拆。
5.1 401 Unauthorized
最常见的原因是 Key 没填对或没生效。检查三处:~/.claude/settings.json里的ANTHROPIC_AUTH_TOKEN是否是完整的 TaoToken Key;~/.codex/auth.json里的OPENAI_API_KEY是否对应;环境变量里有没有旧的ANTHROPIC_API_KEY覆盖了新配置。如果同时存在环境变量和配置文件,环境变量优先级更高,容易导致你以为改了配置其实没生效。
5.2 local proxy failed
这个报错通常出现在工具尝试走本地代理但代理没起来。检查你的工具配置里有没有残留的http_proxy/https_proxy环境变量,或者 settings 里有没有指向localhost:xxxx的 base_url。把 Base URL 明确写成https://taotoken.net/api,不要留任何本地转发配置。
5.3 reading choices 相关报错
这类报错一般是响应格式和工具预期不匹配。Codex 的config.toml里wire_api要设成chat,如果设成responses而模型走的是 chat 协议,就会在解析响应时出错。Claude Code 侧则要确认ANTHROPIC_MODEL填的是 Anthropic 协议支持的模型 ID。
5.4 OAuth 相关报错
如果你用的是需要 OAuth 登录的工具,报错提示 token 过期或授权失败,先确认是不是工具本身要求走官方登录流程。TaoToken 走的是 API Key 认证,不需要 OAuth。如果工具强制 OAuth,检查是否有 API Key 模式的开关,或者换用支持自定义 Base URL 的接入方式。
5.5 agent 不读 AGENTS.md
配置都对,但 agent 行为没变化。检查AGENTS.md是否在仓库根目录,文件名大小写是否正确,以及工具是否支持读取该文件。Claude Code 读CLAUDE.md和AGENTS.md,Codex 读AGENTS.md,Cline 走 MCP 配置。如果工具不认,把规则同步一份到它认的文件名里。
5.6 gate 脚本在 CI 里失败但本地通过
通常是环境差异。检查 CI 里是否装了 pnpm、Node 版本是否一致、check:generated依赖的 git diff 在 CI 的 shallow clone 下是否可用。shallow clone 会导致 diff 基准缺失,需要在 CI 配置里拉全历史或调整 diff 范围。
6. 把 harness 跑起来:从一次闭环到持续维护
到这里,6 要素都有了对应的落地位置。Instructions 在AGENTS.md,Constraints 在 gate 脚本,Feedback 在测试和 typecheck,Memory 在docs/failures/,Evaluation 在docs/outcomes/,Governance 在/harness系列约定。模型出口统一到 TaoToken,Base URL 是https://taotoken.net/api。
接下来你要做的是让这个闭环转起来。每次 agent 犯错,不要只修错误,问一句:这个错误能不能变成AGENTS.md里的一条规则,或者 gate 里的一个 check?能变成 check 的,就不要只写成文字。每次任务结束,写一条 outcome 记录。攒到十几条,你就能看出 agent 在哪些类型的任务上容易越界,哪些失败被重复触发。
如果你要长期跑编码和 Agent 工作流,Coding Plan 比按次调用更适合这种反复验证的模式,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。需要管理多个 Key 或查看用量,去控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。接入细节和协议说明看文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
最后说一个我踩过的坑:一开始我把所有规则都塞进AGENTS.md,结果文件越来越长,agent 反而抓不住重点。后来我把能机器检查的规则全部移到 gate 脚本,AGENTS.md只留目录边界和命令约定,agent 的遵守率明显提升。harness 不是文档越多越好,而是让该被检查的被检查,该被记住的被记住。