1. 先搞清楚"一个人九个月20万行"这件事到底难在哪
先把数字摊开看。九个月,按每周有效编码五天、每天真正能进入深度工作状态的时间四到五小时算,总有效工时大概在900到1000小时之间。20万行代码摊到每个有效小时,大约是200行。这个数字本身不算离谱——熟练工程师在状态好的时候,一小时写两三百行有效代码是能做到的。真正离谱的是另外两个数字:一个人,以及每个月40亿+ token。
一个人写20万行,意味着没有代码评审、没有结对、没有架构组兜底。所有的技术债、所有的设计决策、所有的"这里先这样后面再改",最后都会回到同一个人头上。而每个月40亿token的消耗量,说明这个项目里有大量工作是交给模型去完成的——不是偶尔问一句,而是把模型当成一个持续在线的协作者,让它读代码、写代码、改代码、跑测试、修bug。
这两个数字放在一起,指向的其实是一种新的工程范式:Harness架构。这个词最近在圈子里出现频率很高,但很多人对它的理解还停留在"给Agent套个壳"的层面。实际上Harness要解决的问题比这深得多——它要解决的是"当模型成为主要生产力时,人怎么还能控制住整个系统"。
我自己在这个方向上折腾了大半年,踩过的坑不比任何人少。这篇就把我理解的Harness架构、一个人怎么撑起20万行代码、以及40亿token到底烧在了哪里,从头到尾讲清楚。不管你是刚接触Agent开发,还是已经在用Claude Code这类工具做实际项目,应该都能从里面找到能直接用的东西。
2. Harness架构到底在解决什么问题
2.1 从"模型写代码"到"模型在系统里写代码"的跨越
大多数人用AI写代码的方式是这样的:打开对话框,描述需求,复制粘贴代码,手动跑一下,报错了再贴回去。这种方式在写几十行的小脚本时没问题,但一旦项目上到几万行,立刻崩溃。原因很简单——模型看不到你的整个项目,它只能看到你贴给它的那几百行上下文。
Harness架构的核心思路,是把模型放进一个它能自己感知、自己操作、自己验证的环境里。这个环境就是Harness。它不是一个简单的prompt模板,而是一整套围绕模型构建的运行时系统,通常包含几个关键部分:
- 上下文管理层:决定模型每一步能看到什么。不是把所有代码都塞进去,而是根据当前任务动态检索相关文件、相关函数、相关历史。
- 工具调用层:让模型能执行命令、读写文件、跑测试、查文档。这是它和普通对话式AI最本质的区别。
- 状态持久层:记录模型做过什么、改过什么、哪些尝试失败了。没有这一层,模型每次都是从零开始,重复踩同一个坑。
- 验证反馈层:模型改完代码后,自动跑测试、跑lint、跑类型检查,把结果反馈回去。这一步是保证20万行代码不崩的关键。
你可以把Harness理解成一个"给模型用的IDE"。人在IDE里写代码,有语法高亮、有跳转、有调试器、有版本控制。模型在Harness里写代码,也需要对应的能力。区别在于,人的IDE是给人看的,Harness是给模型看的——它的输出格式、信息密度、检索方式,全都是为模型的理解能力优化的。
2.2 为什么不是"更强的模型"而是"更好的Harness"
这里有个反直觉的结论:在20万行这个量级上,换一个更强的模型带来的提升,远不如把Harness做好带来的提升大。
原因在于,模型的能力瓶颈在长上下文场景下不是"智力不够",而是"信息过载"。你把20万行代码全塞给再强的模型,它也会在中间某处开始丢失细节。而一个好的Harness,能在模型需要改某个函数时,只给它这个函数、这个函数的调用方、这个函数的测试、以及相关的类型定义。信息量可能只有几百行,但全是相关的。
我实测过一个对比:同一个重构任务,直接把相关文件全贴给模型,成功率大概六成;用Harness做检索后只给相关片段,成功率能到九成以上。差距不在模型,在喂给模型的信息质量。
所以Harness工程的核心,其实是信息工程。你要设计一套机制,让模型在每一步都能拿到"刚好够用"的上下文。多了浪费token还干扰判断,少了模型做不出正确决策。
2.3 40亿token到底花在哪了
很多人看到"每月40亿token"第一反应是"这也太浪费了"。但如果你真的在做Harness架构的项目,会发现这个数字其实很合理,甚至偏保守。
拆解一下token的主要消耗场景:
| 消耗场景 | 占比估算 | 说明 |
|---|---|---|
| 代码检索与上下文构建 | 约35% | 每次模型操作前,Harness要检索相关代码、构建上下文 |
| 代码生成与修改 | 约30% | 模型实际写代码的部分 |
| 测试运行与错误分析 | 约20% | 跑测试、读报错、定位问题 |
| 历史状态回顾 | 约10% | 读取之前做过什么、避免重复 |
| 文档与注释生成 | 约5% | 生成和维护项目文档 |
关键在检索这一块。一个20万行的项目,每次模型要改一个函数,Harness可能要先扫描几千个文件的相关性,这个扫描过程本身就要消耗token——要么用embedding做向量检索(消耗相对小),要么用模型做相关性判断(消耗大但准)。如果追求准确性用后者,token消耗自然就上去了。
提示:如果你的Harness项目token消耗远低于这个量级,不一定是好事。很可能说明你的上下文构建太粗糙,模型在"信息不足"的状态下工作,长期看会积累大量隐性错误。
3. 一个人怎么撑起20万行:分工与节奏设计
3.1 人负责什么,模型负责什么
一个人做20万行,前提是这个人不能什么都自己写。我的分工原则是这样的:
人负责的部分:
- 架构决策。哪些模块怎么划分、接口怎么定、数据怎么流。这部分模型可以给建议,但最终拍板必须是人。
- 关键路径的核心逻辑。整个系统里最不能出错的那几百行,自己写。
- Harness本身的维护。用来管理模型的系统,不能交给模型自己管。
- 验收标准制定。什么算"做完了",这个判断权在人手里。
模型负责的部分:
- 按既定接口实现具体功能。接口定好了,实现交给模型。
- 写测试。模型写测试往往比人更全面,因为它不会偷懒。
- 重构和清理。重复代码合并、命名规范化、死代码清理。
- 文档和注释。这部分模型做得又快又好。
- Bug定位的初步排查。让模型先跑一遍,把范围缩小,人再看。
这个分工的核心逻辑是:人管"做什么"和"对不对",模型管"怎么做"和"做完没有"。一旦人开始纠结具体实现细节,效率立刻掉下来。
3.2 九个月的时间是怎么分配的
回头看这九个月,时间分配大概是这样:
- 第1到2个月:搭Harness。这部分几乎没产出业务代码,全在写上下文管理、工具调用、状态持久化这些基础设施。很多人在这里就放弃了,因为看不到"产品"。
- 第3到5个月:核心功能开发。Harness跑通后,进入高速产出期。这三个月大概写了10万行,平均每月3万多行。
- 第6到7个月:重构和补测试。前期快速开发积累的技术债在这里还。代码量增长放缓,但质量大幅提升。
- 第8到9个月:打磨和文档。最后的收尾,包括性能优化、边界处理、完整文档。
这个节奏里最关键的是前两个月。很多人想跳过Harness直接写业务,结果写到第三个月发现上下文管理一团糟,回头重来。Harness不是可选项,是这类项目的地基。
3.3 每天的工作循环长什么样
一个典型的工作日是这样的:
早上先看Harness的日志,看昨晚模型自动跑的任务哪些成功了、哪些失败了。失败的看一遍原因,大部分是上下文给得不对,调整一下检索策略,重新跑。
然后处理需要人决策的部分:新功能的接口设计、架构调整、关键bug的根因分析。这部分通常占上午两到三小时。
下午主要是"喂任务"。把设计好的任务拆成模型能执行的粒度,写清楚验收标准,交给Harness去跑。然后人做别的事——写文档、看论文、想架构。模型跑完一批,人验收一批。
晚上可以让Harness跑一些低优先级的任务,比如重构、补测试、生成文档。第二天早上看结果。
这个循环里,人真正"写代码"的时间其实不多,大部分时间在设计任务、检查结果、调整Harness。这也是为什么一个人能撑起20万行——因为大部分代码不是人一行行敲的。
4. Harness架构的关键组件拆解
4.1 上下文检索:决定模型能看到什么
这是Harness里最核心也最难做好的部分。检索策略直接决定了模型的工作质量。
我的做法是三层检索:
第一层,基于文件路径和符号名的精确匹配。模型说"我要改UserService的login方法",Harness直接定位到对应文件和函数。这层最快最准,但要求模型能准确说出它要什么。
第二层,基于向量相似度的语义检索。模型描述一个需求,Harness用embedding找出语义相关的代码片段。这层能发现模型自己没想到但实际相关的代码。
第三层,基于调用关系的图检索。找到目标函数后,顺着调用链往上往下各找一层,把调用方和被调用方也带上。这层保证模型改一个函数时,知道谁会受影响。
三层结合起来,每次给模型的上下文大概在2000到8000行之间。听起来很多,但相比20万行的总量,已经是极度精简了。
注意:检索层不要追求"给全",要追求"给对"。给多了模型会迷失,给少了模型会瞎猜。这个平衡点需要根据项目特点反复调。
4.2 工具调用:让模型能动手
模型光能看不够,还得能操作。Harness给模型暴露的工具通常包括:
- 文件读写:读文件、写文件、在文件里做精确替换
- 命令执行:跑测试、跑构建、跑lint
- 搜索:按内容搜、按文件名搜、按符号搜
- 版本控制:查看diff、回滚、提交
这里有个设计要点:工具的输出格式要专门为模型优化。比如跑测试,不要返回原始的测试输出(可能几千行),而是返回结构化的结果——哪些通过了、哪些失败了、失败的具体断言是什么。这样模型能快速抓住重点。
我一开始偷懒,直接把原始输出丢给模型,结果模型经常被无关的日志淹没,定位不到真正的错误。后来专门写了个测试结果解析器,把输出压缩成模型友好的格式,效率立刻上来了。
4.3 状态持久化:让模型有记忆
模型本身是无状态的,每次调用都是全新的。Harness要做的,是把模型的工作历史持久化下来,在需要时喂回去。
我用的方案是一个简单的任务日志系统。每个任务记录:任务描述、执行步骤、每步的结果、最终状态。模型开始新任务时,Harness会检索相关的历史任务,把摘要喂给模型。
这样做的价值在于,模型不会重复踩同一个坑。比如它上次尝试用某个方案失败了,这次Harness会告诉它"这个方案之前试过,失败原因是X",它就会换一条路。
状态持久化还有一个作用:断点续传。一个复杂任务跑到一半模型挂了或者token用完了,下次可以从上次的状态继续,不用从头再来。这在长任务里特别重要。
4.4 验证反馈:保证代码不崩
20万行代码,如果没有自动验证,早就崩了。Harness的验证层通常包含:
- 单元测试:每个函数、每个模块的测试
- 集成测试:模块之间的交互
- 类型检查:静态类型语言必做
- Lint:代码风格和潜在问题
- 构建:确保能编译通过
模型每次改完代码,Harness自动跑这一套。任何一项失败,结果反馈给模型,让它自己修。修不好再升级给人。
这套机制的关键是快。验证要能在几十秒内跑完,否则模型等不起。所以测试要分层,快速测试每次改都跑,慢速测试定期跑。
5. 40亿token的优化实战
5.1 哪些token是浪费的
烧了几个月token后,我总结出几类典型的浪费:
重复检索。同一个文件在短时间内被反复检索,每次都重新算embedding。解决办法是加缓存,检索结果缓存几分钟。
过度上下文。给模型的信息远超它需要的。比如改一个小函数,却把整个模块都塞进去。解决办法是更精细的检索粒度。
无效重试。模型失败了,Harness原样重试,模型用同样的方式再失败一次。解决办法是失败后要改变策略,比如换检索方式、换提示词、换模型。
冗余历史。把整个任务历史都喂回去,其中大部分是无关的。解决办法是历史做摘要,只保留关键决策和结果。
把这四类浪费砍掉后,我的token消耗大概降了四成,而代码质量没有下降。
5.2 缓存策略怎么设计
缓存是省token最直接的手段。我的缓存分三层:
- 检索缓存:相同查询在短时间内返回缓存结果。TTL设5分钟。
- embedding缓存:文件的embedding算一次存起来,文件没变就不重算。
- 模型响应缓存:相同输入相同上下文,直接返回缓存响应。这个要小心,只在确定性任务上用。
第三层要特别谨慎。模型响应有时候需要多样性(比如让它想多个方案),缓存会限制这种多样性。我只在"读文件""查文档"这类确定性操作上用响应缓存。
5.3 什么时候该用便宜模型,什么时候必须用贵的
不是所有任务都需要最强的模型。我的策略是分级:
| 任务类型 | 模型选择 | 理由 |
|---|---|---|
| 代码检索、文件读取 | 便宜模型 | 任务简单,不需要强推理 |
| 简单代码生成 | 中等模型 | 有明确模式可循 |
| 复杂重构、架构调整 | 强模型 | 需要深度推理和全局理解 |
| Bug根因分析 | 强模型 | 需要跨文件推理 |
| 文档生成 | 中等模型 | 有固定格式 |
这个分级能省下大量成本。很多任务用便宜模型跑,效果和贵模型差不多,因为任务本身不难。
5.4 上下文压缩的几种手法
当上下文实在太大时,需要压缩。我用过的手法:
- 摘要压缩:让模型把长文件摘要成关键点。适合文档类内容。
- 结构压缩:只保留函数签名和关键注释,去掉实现细节。适合模型只需要知道"有什么"不需要知道"怎么做"的场景。
- diff压缩:只给模型看变化的部分,不给全量。适合增量修改。
- 引用压缩:把重复出现的代码块用引用代替,只在第一次出现时展开。
这些手法组合使用,能把上下文压到原来的三分之一甚至更少,而模型的理解基本不受影响。
6. 踩过的坑和对应的解法
6.1 模型改代码改出新bug
这是最常见的问题。模型改A函数,结果B函数挂了,因为B依赖A的某个行为,而模型不知道。
解法是影响面分析。模型改代码前,Harness先算出这个改动会影响哪些地方,把这些地方的测试先跑一遍,确认当前是通过的。模型改完后,再跑一遍,看有没有破坏。破坏了就让模型看diff,自己修。
这个机制加上后,"改出新bug"的情况少了八成。
6.2 模型陷入死循环
模型有时候会卡在一个问题上,反复尝试同样的方案,每次都失败,但就是换不了思路。
解法是失败计数与策略切换。同一个问题失败三次,Harness强制换策略:换检索方式、换提示词模板、换模型。还不行就升级给人。
关键是这个计数要自动,不能靠人盯着。人盯不过来。
6.3 上下文里混入了过时信息
项目在快速迭代,昨天的代码今天可能已经改了。如果Harness缓存了过时的上下文,模型会基于错误信息做决策。
解法是缓存失效机制。文件一改,相关缓存立刻失效。这个要做细,不能一改就全清,那样缓存就没用了。我的做法是文件级失效——改哪个文件,只失效和这个文件相关的缓存。
6.4 模型生成的代码风格不统一
20万行代码如果风格乱七八糟,维护起来是灾难。模型各有各的写法,不统一。
解法是风格约束前置。在给模型的上下文里,永远带上项目的代码风格指南和几个范例。模型照着范例写,风格自然统一。另外lint要严格,风格不对直接打回。
6.5 token突然暴涨
有段时间token消耗突然翻倍,查了半天发现是检索层出了问题——某个文件的embedding算错了,导致它被判定为和所有查询都相关,每次检索都把它带上,上下文暴涨。
解法是监控与告警。token消耗要实时监控,异常时告警。另外检索结果要有上限,单次检索最多返回N个文件,防止某个异常文件污染整个上下文。
7. 给想走这条路的人几句实在话
Harness架构不是银弹,它有明确的适用边界。项目小于一万行,用不着这么复杂,直接对话式让模型写就行。项目大于十万行,Harness几乎是唯一可行的路径。中间地带看情况。
搭Harness的前期投入很大,前两个月可能一行业务代码都写不出来。这个阶段最考验人,因为看不到产出,容易怀疑自己。但这两个月省不得,省了后面要花更多时间还债。
40亿token听起来吓人,但算成钱其实还好。关键是这些token换来了20万行代码,如果雇人写,成本高得多,时间也长得多。这笔账要算清楚。
最后一点,也是最重要的:Harness再强,决策权要在人手里。模型可以写代码、可以改代码、可以跑测试,但"这个功能该不该做""这个架构合不合理""这个bug修得对不对",最终要人来判断。一个人能撑起20万行,不是因为模型替代了人,而是因为人把精力集中在了只有人能做的那部分事情上。
我在实际项目里最大的体会是,Harness做得越好,人反而越轻松,但同时对人的架构能力要求越高。因为模型把实现层面的活都干了,剩下的全是硬骨头——架构、设计、权衡、决策。这些活模型帮不上太多忙,只能靠人自己。所以想走这条路,先把架构功底练扎实,比什么都重要。