1. 先聊聊这个项目到底在做什么
一个人,九个月,二十万行代码,每个月消耗四十亿以上的 token,最终跑出来一款基于 Harness 架构的应用。这几个数字摆在一起,任何一个写过代码的人都会先愣一下——不是因为二十万行代码有多夸张,大厂里一个中型项目随随便便就这个量级,而是因为这二十万行几乎是一个人写出来的,而且是在九个月里完成的。如果按传统的手写节奏,一个人一天稳定产出三百到五百行有效代码已经算高效,九个月满打满算也就十万行出头,还得是在没有任何返工、没有任何调试、没有任何架构调整的理想状态下。所以这二十万行里,必然有相当大一部分不是"手打"出来的,而是借助 AI Agent 协作生成的。
这就是这个项目最核心的信息:它不是一个人硬扛出来的代码量,而是一个人驾驭了一套 Agent 工作流,把 AI 当成一支可以随时调度的工程团队来用。Harness 在这里不是一个抽象概念,它指的是一整套"约束、编排、驱动 Agent 干活"的工程框架。你可以把它理解成一个"AI 施工队的管理系统"——Agent 是工人,Harness 是工地上的脚手架、施工规范、质检流程和调度中心。没有 Harness,Agent 就是一个会聊天的代码补全工具;有了 Harness,它才变成一个能持续产出、能自我校验、能接续上下文的工程实体。
那四十亿 token 一个月是什么概念?按主流大模型的计费口径,四十亿 token 如果全部走中高端模型,成本是相当可观的。这个数字说明两件事:第一,Agent 的调用频率极高,不是偶尔问一句,而是几乎每一次代码生成、每一次重构、每一次测试都在走模型;第二,上下文注入的量非常大,每次调用都带着大量的项目背景、规范约束、历史记录。换句话说,这个项目的作者不是在"用 AI 写代码",而是在"运营一个由 AI 组成的开发流水线"。
这篇文章适合谁看?如果你是一个独立开发者,想搞清楚一个人怎么把项目做到这个体量;如果你正在研究 Agent 开发、Agent 框架、Harness 架构,想知道真实落地时到底要解决哪些问题;如果你在用 Claude Code、Obsidian 这类工具搭建自己的知识和工作流,想看看别人是怎么把它们串起来的——那这篇内容值得你花时间读完。我会尽量把"为什么这么设计""具体怎么落地""踩过哪些坑"讲透,而不是停留在概念层面。
2. Harness 架构到底解决了什么问题
2.1 为什么裸用 Agent 一定会失控
很多人第一次接触 Agent 开发,都是从一个简单的循环开始的:给模型一个任务,模型输出一段代码,执行,看结果,再喂回去。这个循环在玩具项目上跑得很好,一旦项目规模上去,立刻崩盘。原因不复杂,我把它归纳成三个层面的失控。
第一层是上下文失控。一个二十万行的项目,你不可能每次都把全部代码塞进上下文窗口。模型看不到的部分,它就当不存在。于是它会在 A 文件里定义一个函数,在 B 文件里又定义一个同名但行为不同的函数,因为它根本不知道 A 的存在。这种问题在项目超过几千行之后就会频繁出现,而且极难排查,因为代码本身语法没错,错的是"全局一致性"。
第二层是任务失控。你给 Agent 一个"实现用户登录功能"的任务,它可能一口气写了认证、写了数据库、写了前端表单、还顺手改了配置文件。听起来很能干,但实际上它越界了——它改的那些文件可能正被另一个任务占用,或者它的改动破坏了你原本的设计约定。没有边界的 Agent 就像一个不打招呼就到处改代码的实习生,能力越强破坏力越大。
第三层是质量失控。Agent 生成的代码"看起来对"的比例很高,但"真的对"的比例要低不少。它会写出能通过编译但逻辑有漏洞的代码,会写出没有处理边界情况的函数,会写出性能上完全不可接受的实现。如果你没有一个自动化的校验环节,这些代码会一路混进主干,等到集成测试时才爆出来,那时候排查成本已经翻了好几倍。
Harness 架构的本质,就是针对这三层失控分别建立约束机制。它不是一个库,也不是一个框架,而是一套围绕 Agent 构建的工程约束体系。你可以自己实现,也可以用现成的工具拼装,但核心思想是一致的:把 Agent 的自由度限制在一个可控的范围内,同时给它足够的上下文和反馈来保证产出质量。
2.2 Harness 的四个核心组成
我把一个完整的 Harness 拆成四个部分,这四个部分缺一不可,任何一个缺失都会导致整个系统在某类场景下失效。
任务编排层负责把一个大目标拆解成一系列可执行的小任务,并管理它们的依赖关系和执行顺序。这一层要解决的是"先做什么、后做什么、什么可以并行"。在单人项目里,这一层往往被简化成一个任务队列加依赖图,但即使是简化版,也必须存在,否则 Agent 会陷入"想到哪做到哪"的混乱。
上下文管理层负责在每次调用 Agent 时,精准地注入它需要的背景信息。这一层是 Harness 里技术含量最高的部分,因为它要在"给太少导致 Agent 瞎猜"和"给太多导致 token 爆炸"之间找平衡。常见的做法是分层注入:全局规范永远带上,项目结构按需带上,具体文件内容按任务相关性动态检索。
执行与校验层负责真正跑 Agent 的输出,并对结果做自动化检查。这一层包括代码生成、格式化、静态检查、单元测试、类型检查等一系列环节。关键点是校验必须是自动的、快速的、可重复的,否则人工介入会成为瓶颈,整个流水线的效率就废了。
记忆与状态层负责持久化整个项目的状态,包括已完成的任务、已知的问题、设计决策、代码变更历史等。这一层让 Agent 在跨会话、跨任务时保持连续性。没有这一层,每次新会话 Agent 都从零开始,前面所有的上下文积累全部作废。
2.3 为什么选 Harness 而不是别的方案
市面上做 Agent 开发的方案很多,有偏重对话的,有偏重自动化的,有偏重多 Agent 协作的。这个项目选择 Harness 架构,我认为核心原因是它要解决的是"长期、大规模、单人维护"的问题,而不是"一次性完成某个任务"的问题。
一次性任务的方案,重点是让 Agent 在单次会话里尽可能聪明;而长期项目的方案,重点是让 Agent 在成百上千次会话里保持稳定和一致。这两者的设计目标完全不同。前者可以容忍偶尔的失误,因为大不了重来;后者不能容忍系统性偏差,因为偏差会随着时间累积,最后变成无法收拾的技术债。
Harness 架构的另一个优势是可观测。因为所有的任务、上下文、校验都是显式定义的,你可以清楚地知道每一步发生了什么、为什么失败、怎么修。相比之下,一些"端到端自动化"的方案虽然用起来爽,但一旦出问题就是黑盒,排查起来非常痛苦。在九个月的长周期里,可观测性的价值会被无限放大。
3. 核心细节拆解:二十万行代码是怎么攒出来的
3.1 任务拆解的粒度控制
任务拆解的粒度是 Harness 落地时第一个要拍板的参数,也是最容易拍错的参数。拆得太粗,Agent 一次要处理的信息量太大,容易顾此失彼;拆得太细,任务之间的协调成本会急剧上升,而且大量时间浪费在上下文切换上。
我在实践中总结的经验是:一个任务的理想粒度,是 Agent 能在一次会话内完成,且完成后能通过一个明确的校验。这个"明确的校验"很关键,它可以是"这个函数的所有单元测试通过",也可以是"这个接口的返回结构符合 schema",但必须是一个非黑即白的判断,不能是"看起来差不多"。
举个具体的例子。假设你要实现一个 Markdown 解析器。粗粒度的拆法是"实现 Markdown 解析功能",这个任务对 Agent 来说太模糊了,它不知道要支持哪些语法、输出什么格式、错误怎么处理。细粒度的拆法是"实现标题解析""实现列表解析""实现表格解析"……每个任务对应一个明确的语法特性,完成后可以用一组测试用例验证。这种粒度就是合适的。
但要注意,粒度不是越细越好。如果把"实现标题解析"再拆成"识别 # 符号""提取标题文本""生成 HTML 标签",那就过度了,因为这三个步骤耦合太紧,分开做反而增加协调成本。判断标准是:如果两个子任务之间需要频繁来回修改对方的产出,那它们就不该被拆开。
3.2 上下文注入的分层策略
上下文注入是 token 消耗的大头,也是决定 Agent 输出质量的关键。四十亿 token 一个月,绝大部分都花在这里。所以怎么组织上下文,直接决定了项目的成本和效果。
我采用的是一个三层结构。第一层是常驻层,包含项目的核心规范、编码约定、架构原则。这部分内容每次调用都带上,因为它定义了"这个项目的代码应该长什么样"。常驻层要尽量精简,控制在几千 token 以内,否则每次调用都背着这个包袱,成本会失控。
第二层是任务层,包含当前任务相关的文件内容、接口定义、数据结构。这部分按任务动态组装,只带真正需要的。组装的方式可以是基于文件路径的规则匹配,也可以是基于语义检索的相关性排序。在项目早期,规则匹配就够了;项目大了之后,语义检索的收益会越来越明显。
第三层是按需层,包含 Agent 主动请求的额外信息。当 Agent 在执行任务时发现缺少某个信息,它可以主动发起一次检索请求,Harness 负责把结果喂回去。这一层是"懒加载"的思路,避免了一次性注入过多无关内容。
三层结构的关键在于边界清晰。常驻层的内容不应该随任务变化,任务层的内容不应该跨任务复用,按需层的内容不应该被缓存太久。一旦边界模糊,就会出现"这个信息到底该放哪层"的纠结,最后导致上下文组织混乱。
3.3 校验环节的自动化设计
校验环节是 Harness 里最容易被低估的部分。很多人把精力都花在"怎么让 Agent 生成更好的代码"上,却忽略了"怎么快速发现 Agent 生成的代码有问题"。结果是 Agent 产出速度很快,但人工 review 的速度跟不上,最后瓶颈卡在人这里。
自动校验的核心是分层拦截。第一层是语法和格式校验,包括编译、lint、格式化,这一层最快,几秒钟就能跑完,能拦掉大部分低级错误。第二层是单元测试,针对当前任务涉及的模块跑测试,这一层几十秒到几分钟,能拦掉逻辑错误。第三层是集成测试和类型检查,这一层最慢,但能发现跨模块的问题。
关键设计是每一层都要能独立运行,且失败时能给出明确的错误定位。如果校验失败只告诉你"测试没通过",Agent 还得自己去猜哪里错了,那校验的价值就大打折扣。理想情况下,校验失败应该直接告诉 Agent"第 42 行的函数在输入为空时抛异常",这样 Agent 下一轮就能精准修复。
还有一个经验是:校验要跑在 Agent 的产出被合并之前。也就是说,Agent 生成的代码先进入一个隔离区,校验通过才合并到主干。这样即使 Agent 生成了破坏性代码,也不会污染主项目。这个隔离机制在长周期项目里是必须的,否则一次严重的错误生成可能让你花好几天去回滚。
3.4 记忆持久化的落地方式
记忆持久化决定了 Agent 能不能"记住"之前做过什么。在九个月的项目里,这个能力至关重要,因为没有人能记住所有设计决策和踩过的坑。
我用的方案是结构化记忆 + 非结构化记忆双轨制。结构化记忆是机器可读的,包括任务状态、依赖关系、已知问题列表、接口定义等,用 JSON 或 YAML 存储,方便程序读取和更新。非结构化记忆是给人看的,包括设计文档、决策记录、经验总结,用 Markdown 存储,方便检索和阅读。
这两者的分工是:结构化记忆驱动自动化流程,非结构化记忆辅助人工决策和 Agent 的上下文注入。比如当 Agent 要修改某个模块时,Harness 会从结构化记忆里查出这个模块的依赖关系,从非结构化记忆里查出相关的设计决策,一起注入上下文。
这里有个坑要提醒:记忆不是越多越好,过期的记忆比没有记忆更危险。如果一个设计决策三个月前已经改了,但记忆里还留着旧版本,Agent 会基于错误的前提工作。所以记忆必须有版本和时效标记,定期清理和更新。
4. 实操过程:从零搭一套能跑的 Harness
4.1 环境准备与工具选型
搭 Harness 不需要什么特殊的硬件,一台能跑开发环境的机器就够。真正的门槛在工具选型和配置上。我按功能模块说一下我的选择,以及为什么这么选。
Agent 执行引擎这块,我用的是 Claude Code 作为主力。选它的原因不是它最聪明,而是它的工具调用能力和文件操作能力比较成熟,能直接读写文件、执行命令、跑测试,这些是 Harness 落地的基础。其他方案要么工具能力弱,要么集成成本高,在长周期项目里都不太划算。
知识管理这块,Obsidian 是我的主力。它的价值在于纯本地、纯 Markdown、插件生态丰富。项目里的设计文档、决策记录、经验总结全部放在 Obsidian 库里,通过双链和标签组织。Agent 需要读取这些内容时,直接读 Markdown 文件就行,不需要任何转换。
版本控制用 Git,这个没什么好说的。但要注意的是,Agent 的产出要单独开分支,校验通过才合并。我一般给每个任务开一个分支,命名规则是task/{任务ID}-{简短描述},这样出问题能快速定位。
校验工具链包括格式化工具、lint 工具、测试框架、类型检查器。具体选哪个取决于项目语言,但原则是每个环节都要能命令行调用,且输出格式要结构化(比如 JSON),方便 Harness 解析。
工具选型的一个通用原则是:优先选可脚本化的,其次选可配置的,最后才考虑图形界面的。因为 Harness 的核心是自动化,任何需要人工点击的环节都会成为瓶颈。
4.2 任务队列与依赖管理
任务队列的实现可以很简单,一个 YAML 文件加一个调度脚本就够了。我用的是这样的结构:
tasks: - id: task-001 title: 实现 Markdown 标题解析 depends_on: [] status: done files: - src/parser/heading.ts - tests/parser/heading.test.ts - id: task-002 title: 实现 Markdown 列表解析 depends_on: [task-001] status: in_progress files: - src/parser/list.ts - tests/parser/list.test.ts调度脚本的逻辑是:扫描所有status: pending的任务,检查它们的depends_on是否都已完成,如果完成就挑一个出来执行。执行过程是:组装上下文、调用 Agent、跑校验、更新状态。
这个结构看起来简单,但有几个细节要注意。第一,任务 ID 要全局唯一且稳定,不要用序号,因为序号会随着任务增删而变化,导致依赖关系错乱。第二,files 字段要准确,它决定了上下文注入的范围,写错了会导致 Agent 看不到该看的文件。第三,status 的流转要严格,pending → in_progress → done 或 failed,不允许跳转,否则状态会混乱。
依赖管理的一个常见问题是循环依赖。A 依赖 B,B 又依赖 A,调度器会卡死。解决办法是在添加依赖时做一次环检测,发现环就报错。这个检测不难实现,一次深度优先搜索就够了。
4.3 Agent 调用的上下文组装
上下文组装是每次调用 Agent 前要做的事,也是 token 消耗的主要来源。我的组装逻辑分四步。
第一步,加载常驻层。从固定的文件里读取项目规范、编码约定、架构原则。这部分内容我放在harness/context/base.md里,每次调用都带上。
第二步,加载任务层。根据当前任务的files字段,读取相关文件的内容。同时从结构化记忆里查出这个任务涉及的接口定义、数据结构,一起带上。
第三步,加载相关记忆。从非结构化记忆里检索和当前任务相关的设计决策、经验总结。检索方式早期用关键词匹配,后期换成向量检索。
第四步,组装 prompt。把上面三层内容按固定模板拼起来,加上任务描述和输出要求,形成最终的 prompt。
这里有个优化点:常驻层和任务层的内容可以做缓存。如果两次调用的常驻层内容一样,就不需要重新读取和拼接。这个优化在调用频率高的时候能省不少时间。
还有一个坑:上下文长度要留余量。模型的上下文窗口是有限的,如果你把窗口塞满了,Agent 就没有空间生成输出了。我的经验是输入控制在窗口的 60% 以内,留 40% 给输出和后续对话。
4.4 校验流水线的搭建
校验流水线是一串命令,按顺序执行,任何一步失败就中断并返回错误。我用的是一个 shell 脚本,大致长这样:
#!/bin/bash set -e echo "Step 1: Formatting..." npm run format echo "Step 2: Linting..." npm run lint echo "Step 3: Type checking..." npm run typecheck echo "Step 4: Unit tests..." npm run test:unit echo "Step 5: Integration tests..." npm run test:integration echo "All checks passed."set -e是关键,它让脚本在任何一步失败时立即退出,不会继续往下跑。每一步的输出会被捕获,失败时把错误信息返回给 Agent。
这个脚本看起来简单,但有几个设计要点。第一,顺序要按"快慢"排,快的放前面,这样大部分错误能在几秒内被发现,不用等几分钟的集成测试。第二,每一步都要有明确的成功/失败信号,不能有"警告但继续"的模糊状态。第三,错误信息要结构化,最好是 JSON 格式,方便 Agent 解析。
校验失败后的处理逻辑是:把错误信息注入下一轮 Agent 调用的上下文,让 Agent 基于错误信息修复。这个循环最多跑三轮,三轮还修不好就标记任务失败,转人工处理。三轮这个数字是经验值,太少容易误判,太多会浪费 token。
4.5 记忆系统的日常维护
记忆系统不是搭好就完事的,它需要日常维护,否则会越来越乱。我的维护习惯是每天收工前花十分钟做三件事。
第一件,更新任务状态。把当天完成的任务标记为 done,把遇到问题的任务标记为 blocked 并记录原因。这个动作看起来琐碎,但它是第二天继续工作的基础。
第二件,记录设计决策。当天如果做了任何影响后续的设计决策,写进harness/memory/decisions/目录,文件名用日期加简短描述。决策记录要写清楚"决定了什么""为什么这么决定""什么情况下需要重新考虑"。
第三件,清理过期记忆。检查有没有已经失效的记忆,标记为 deprecated 或直接删除。这一步最容易被忽略,但最重要,因为过期记忆会误导 Agent。
维护记忆的一个技巧是用模板。决策记录、经验总结、问题排查都有固定模板,这样写起来快,读起来也快。模板不用复杂,几个字段就够,比如决策记录模板可以是"背景、选项、决定、理由、影响"。
5. 常见问题与排查技巧实录
5.1 Agent 输出质量不稳定的排查
Agent 输出质量不稳定是最常见的问题,表现是同样的任务,有时候一次就过,有时候改好几轮还是错。排查这个问题,我一般按下面的顺序看。
先看上下文。大部分质量问题根源在上下文。检查一下这次调用注入的内容是不是完整、是不是准确、是不是有冲突。常见的情况是:任务层漏了某个关键文件,导致 Agent 看不到相关代码;或者常驻层里有互相矛盾的规范,导致 Agent 无所适从。
再看任务描述。任务描述太模糊是第二大原因。如果任务描述里没有明确的验收标准,Agent 就只能猜,猜对了是运气,猜错了是常态。解决办法是把验收标准写进任务描述,比如"实现一个函数,输入是字符串,输出是解析后的 AST,所有测试用例必须通过"。
最后看模型状态。如果上下文和任务描述都没问题,那可能是模型本身的状态波动。这种情况不多,但确实存在。应对办法是重试,或者换个时间再跑。如果某个任务反复失败,可以考虑换个模型试试。
下面这张表是我整理的常见质量问题和对策:
| 问题表现 | 可能原因 | 排查方向 | 解决对策 |
|---|---|---|---|
| 生成的代码引用不存在的函数 | 上下文缺失 | 检查任务层文件列表 | 补全相关文件 |
| 代码风格和项目不一致 | 常驻层规范缺失 | 检查 base.md | 补充编码规范 |
| 逻辑正确但测试不过 | 验收标准模糊 | 检查任务描述 | 明确验收标准 |
| 反复修改同一处 | 任务粒度过粗 | 检查任务拆分 | 拆成更小的任务 |
| 输出被截断 | 上下文过长 | 检查 token 用量 | 精简上下文 |
5.2 Token 消耗失控的优化
四十亿 token 一个月听起来很多,但如果优化不到位,实际消耗可能是这个数字的好几倍。我踩过的坑和对应的优化手段有这么几个。
坑一:常驻层太臃肿。一开始我把所有规范都塞进常驻层,结果每次调用都背着几万 token 的包袱。优化办法是精简常驻层,只留最核心的规范,其他内容移到任务层按需加载。这一项优化能省 30% 以上的 token。
坑二:任务层重复加载。相邻任务往往涉及相同的文件,如果每次都重新加载,就是浪费。优化办法是加缓存,相同文件在短时间内只加载一次。这一项能省 10% 到 20%。
坑三:校验失败后全量重试。校验失败时,如果重新注入全部上下文再跑一遍,成本很高。优化办法是只注入错误信息和相关文件,让 Agent 做局部修复。这一项能省 20% 以上。
坑四:记忆检索不精准。如果检索出来的记忆大部分不相关,就是白花 token。优化办法是改进检索算法,从关键词匹配升级到向量检索,提高相关性。这一项的效果取决于项目规模,项目越大收益越明显。
5.3 任务卡死的处理流程
任务卡死是指某个任务反复失败,怎么改都过不了。这种情况在长周期项目里一定会遇到,处理不好会浪费大量时间和 token。
我的处理流程是三步走。第一步,暂停这个任务,不要继续无脑重试。第二步,人工介入,看看是任务本身有问题,还是 Agent 能力不够,还是上下文有隐藏的坑。第三步,根据诊断结果决定:如果是任务描述问题,重写任务;如果是能力问题,拆成更小的任务;如果是上下文问题,修复上下文。
这里有个经验:卡死的任务往往不是孤立的,它可能暴露了上游的某个设计问题。比如一个任务反复失败,最后发现是因为依赖的接口定义有歧义。这种情况下,修复上游比死磕当前任务更有效。
还有一个技巧是换个角度描述任务。有时候 Agent 卡住是因为任务描述的方式让它陷入了某种思维定式,换个说法可能就通了。比如把"实现一个解析器"改成"给定输入字符串,输出对应的数据结构",Agent 的思路可能就打开了。
5.4 记忆污染的识别与清理
记忆污染是指记忆里存了错误或过期的信息,导致 Agent 基于错误前提工作。这个问题很隐蔽,因为 Agent 不会告诉你"我基于一条错误记忆做了决定",它只会默默地输出错误结果。
识别记忆污染的信号有几个。信号一,Agent 的输出和当前代码明显不符,比如它引用了一个已经删除的函数。信号二,Agent 反复建议某个已经被否决的方案,说明记忆里还留着那个方案的记录。信号三,Agent 对某个模块的理解和实际代码不一致,说明记忆里的描述过期了。
清理记忆污染的办法是定期审计。我一般每周做一次,把记忆里的关键条目和实际代码对照一遍,发现不一致就更新或删除。审计的重点是设计决策和接口定义,这两类记忆最容易过期,也最容易造成误导。
预防记忆污染的根本办法是记忆和代码同步更新。每次改代码时,顺手更新相关记忆。这个习惯一开始会觉得麻烦,但养成之后能省掉大量排查时间。
6. 一些不那么显然的经验
6.1 关于"一个人"这件事的真实含义
标题里说"一个人",但实际做下来,这个"一个人"的含义和传统意义上的独立开发完全不同。传统独立开发,一个人要扮演产品、设计、开发、测试、运维所有角色,每个角色都要花时间。而这个项目里的"一个人",更像是一个导演——他不需要亲自演每一个角色,但他要负责选角、说戏、剪辑、把控整体节奏。
具体来说,这个人花时间最多的地方不是写代码,而是设计任务、组织上下文、搭建校验、维护记忆。这四件事加起来占了大部分时间,真正"写代码"的时间反而很少。这个转变是很多人没意识到的:用 Agent 开发,你的核心技能从"写代码"变成了"设计让 Agent 写好代码的环境"。
这个转变带来的一个后果是:你的瓶颈不再是打字速度,而是思考速度。以前写代码,手速是瓶颈;现在,你能不能把一个模糊的需求拆成清晰的任务,能不能设计出有效的校验,能不能组织好上下文,这些才是瓶颈。所以提升效率的方向也变了,不是练打字,而是练拆解和设计。
6.2 关于"九个月"的节奏感
九个月听起来很长,但拆到每一天,节奏其实很紧。我的实际节奏是:每天上午设计任务和组织上下文,下午跑 Agent 和做校验,晚上维护记忆和规划第二天。这个节奏不是一开始就有的,是摸索了两三个月才稳定下来的。
节奏感的关键是不要追求每天都有大量产出。Agent 的工作是脉冲式的,有时候一天能完成十几个任务,有时候一个任务卡一整天。如果按"每天必须完成 N 个任务"来要求自己,遇到卡壳的日子会非常焦虑。更好的心态是按周看进度,一周下来整体有推进就行。
还有一个节奏上的经验:留出缓冲时间。九个月里,我大概有一个月的时间是纯粹在处理意外情况的——重构、修 bug、清理技术债。如果一开始就把九个月排满,遇到意外就会崩盘。留 10% 到 15% 的缓冲,是长周期项目的必备。
6.3 关于"二十万行"的构成
二十万行代码听起来很多,但拆开看,构成其实很清晰。大概的比例是:业务逻辑代码占一半,测试代码占三成,配置和工具代码占两成。测试代码占比这么高,是因为 Harness 的校验环节需要大量测试支撑。没有足够的测试,自动校验就是空谈。
这个构成说明一件事:在 Agent 驱动的项目里,测试的重要性被放大了。传统开发里,测试是"有空就写"的东西;在 Agent 开发里,测试是"必须写"的东西,因为它是校验的基础。你写的测试越多,Agent 的产出质量越有保障,整个流水线越稳定。
另一个观察是:代码的重复度比传统项目低。因为 Agent 生成代码的成本低,遇到相似但不完全相同的需求,直接生成新代码比抽象复用更划算。这导致代码里有很多"看起来相似但实际不同"的模块。这个特点有利有弊,利是每个模块都能针对具体场景优化,弊是维护时需要理解更多细节。
6.4 关于"四十亿 token"的成本控制
四十亿 token 一个月,成本是实打实的。控制成本的核心不是"少用",而是"用得更精准"。我的成本控制手段有这么几个。
手段一,分级使用模型。不是所有任务都需要最强的模型。简单的格式化、重命名、小修复,用便宜的小模型就够;复杂的架构设计、算法实现,才用强模型。分级使用能省不少钱。
手段二,缓存高频内容。常驻层的内容、常用的接口定义、反复引用的文档,这些都可以缓存,避免重复计费。有些平台支持 prompt caching,用好了能省一大笔。
手段三,批量处理。把多个小任务合并成一次调用,比分多次调用更省。当然,合并的前提是这些任务确实相关,否则会互相干扰。
手段四,及时止损。一个任务如果跑了两三轮还没过,就停下来人工看看,不要无脑重试。无脑重试是最烧钱的,而且往往解决不了问题。
7. 这套东西能复用到哪些场景
Harness 架构不是只能用来写代码。它的核心思想——用约束和反馈驱动 Agent 持续产出——可以迁移到很多场景。
内容创作是一个明显的场景。你可以把文章拆成大纲、段落、润色几个任务,每个任务有明确的验收标准,用 Harness 驱动 Agent 完成。Obsidian 在这里可以当素材库和成品库,Markdown 是天然的中间格式。
数据分析也是一个场景。把分析任务拆成数据清洗、特征提取、建模、报告几个环节,每个环节有校验,用 Harness 串起来。Agent 负责生成分析代码,Harness 负责跑代码和验证结果。
知识管理本身也可以用 Harness 的思路来做。把知识整理拆成采集、分类、关联、更新几个任务,用 Agent 辅助处理,用 Obsidian 存储,用校验保证质量。
迁移的时候要注意,不同场景的"校验"定义不同。代码场景的校验是测试通过,内容场景的校验可能是事实准确、逻辑通顺,数据场景的校验可能是结果可复现。校验的定义决定了整个 Harness 的有效性,所以迁移时第一件事是想清楚"这个场景里什么叫对"。
我在实际使用中发现,Harness 这套东西最大的价值不是省了多少时间,而是让长周期、大规模的工作变得可持续。传统方式下,一个人做九个月的大项目,很容易在中途因为疲惫、混乱、失去方向而放弃。有了 Harness,每天的工作有明确的输入和输出,进度可见,问题可查,这种"可持续性"才是它真正的杀手锏。最后再分享一个小技巧:如果你刚开始搭 Harness,不要一上来就追求完整,先把任务队列和校验这两个环节跑通,其他部分可以后面慢慢补。这两个环节是 Harness 的骨架,骨架立起来了,血肉自然能长上去。