1. 这个项目到底在做什么:先把概念掰开揉碎
一个人,九个月,20 万行代码,每个月消耗 40 亿以上的 token,最终交付一款基于 Harness 架构的应用。这组数字第一次看到的时候,我的反应和大多数人一样——先怀疑,再好奇,最后是那种"这活儿到底怎么干下来的"的困惑。因为单看任何一个数字都不算离谱,但把它们叠在一起,就变成了一个非常反常识的工程样本。
先把 Harness 这个词说清楚。在 AI Agent 开发语境里,Harness 不是某个具体产品,而是一类架构思路:它指的是包裹在模型外面的一整套"挽具"——负责把模型的原始输出,转化成可执行、可校验、可回滚的动作。你可以把它理解成马和马车之间的关系,模型是马,力气大但方向感差,Harness 就是那套缰绳、鞍具和车架,决定这匹马拉什么、往哪拉、什么时候停。没有 Harness 的 Agent,本质上就是一个会聊天的接口;有了 Harness,它才变成一个能真正干活的系统。
这个项目的核心价值,恰恰在于它把 Harness 从"论文里的概念"变成了"跑在生产环境里的东西"。它解决的不是"模型能不能回答问题",而是"模型能不能稳定地、可预期地、在长周期任务里持续做对的事"。适合谁来参考?我认为有三类人:一是正在做 Agent 项目、被"模型一抽风整个流程就崩"折磨的开发者;二是想理解 Agent 工程化落地路径的技术负责人;三是像我这样,对"一个人能不能扛起一个完整 AI 应用"这件事既怀疑又向往的独立开发者。
九个月、20 万行、每月 40 亿 token,这三个数字背后其实对应着三件事:时间投入的密度、代码组织的复杂度、以及模型调用的真实规模。40 亿 token 是什么概念?按主流模型的计费方式粗算,如果全部走中高价位模型,一个月的成本足以让大多数个人项目直接放弃。所以这个项目能跑下来,本身就说明它在 token 使用策略上做了大量优化——这也是我后面要重点拆的部分。
2. 架构选型的底层逻辑:为什么是 Harness,而不是别的
2.1 从"提示词工程"到"挽具工程"的思维转变
大多数人做 Agent 的第一反应是堆提示词。写一个超长的 system prompt,把各种规则、示例、边界条件全塞进去,然后祈祷模型每次都听话。我早期也这么干过,结果就是:简单任务表现不错,一旦任务链条拉长到十几步,模型就开始"忘记"前面的约束,输出格式飘忽,工具调用参数错位,整个流程像多米诺骨牌一样倒掉。
Harness 架构的核心洞察是:不要指望模型记住规则,而要把规则变成模型无法绕过的结构。这就像管理一个新员工,你不会指望他背下所有规章制度就永远不犯错,而是设计一套流程——每一步都有检查点,每个输出都有 schema 校验,每次失败都有明确的回退路径。模型负责"生成候选答案",Harness 负责"判断这个答案能不能进入下一步"。
这个思维转变带来的直接好处是可测试性。纯提示词方案里,你很难写单元测试,因为输出是自然语言,边界模糊。而 Harness 把每个环节的输入输出都定义成结构化数据,你就可以像测试普通函数一样测试 Agent 的每个节点。这个项目能堆到 20 万行代码,很大程度上就是因为大量代码是在做校验、重试、状态管理和边界处理,而不是在写提示词。
2.2 为什么选择 Markdown 作为中间表示层
热词里反复出现 Markdown、Obsidian、Claude Code,这不是巧合。这个项目的一个关键设计决策,是把Markdown 作为 Agent 内部和外部沟通的通用中间格式。为什么是 Markdown 而不是 JSON 或 YAML?
我的理解是这样:JSON 适合机器读,但对人极不友好,一个复杂的 Agent 状态用 JSON 表示,人根本没法快速审查。而 Markdown 是"人和机器都能读"的甜点区——它有结构(标题、列表、表格、代码块),但又不强制严格语法,模型生成起来自然,人审查起来轻松。更重要的是,Markdown 天然适配 Obsidian 这类知识管理工具,意味着 Agent 的产出可以直接沉淀成人类可用的笔记,而不是一堆需要二次解析的数据。
具体到实现上,这个项目大概率是这样做的:Agent 的每一步产出都写成 Markdown 片段,用特定的标题层级和标记来承载元信息。比如用##表示任务阶段,用列表表示待办项,用代码块包裹需要执行的命令或代码。Harness 层再写解析器,把这些 Markdown 结构还原成可执行的动作。这样做的好处是调试极其方便——出问题的时候,你直接打开那个 Markdown 文件,就能看到 Agent 当时"脑子里在想什么"。
提示:用 Markdown 做中间层有个坑,就是换行和空格的语义在不同解析器里不一致。项目里一定要统一一套解析规则,最好自己写解析器而不是依赖第三方库,否则 markdown 换行这类小问题会在长流程里被无限放大。
2.3 Claude Code 与本地模型的混合调用策略
热词里既有 Claude Code,又有"claude code 调用 lmstudio 的本地模型",这说明项目在模型选型上是混合策略,而不是死磕一个模型。这是被 40 亿 token 逼出来的必然选择。
我的经验是,Agent 任务可以粗略分成两类:需要强推理的决策节点和大量重复的格式化/转换节点。前者必须用强模型,后者完全可以用本地小模型甚至规则引擎搞定。一个成熟的 Harness 架构,应该能根据任务类型动态路由到不同的模型。比如"分析这段代码的潜在 bug"走强模型,"把这段输出转成指定格式"走本地模型。这样能把 token 成本压下来一大截,同时保持关键环节的质量。
Claude Code 在这个项目里的角色,我判断是作为开发辅助和部分 Agent 能力的载体。它的优势在于对代码和文件系统的操作能力强,适合做需要读写文件、执行命令的任务。而本地模型(通过 LM Studio 之类的方式调用)则承担那些对延迟不敏感、但对成本敏感的批量任务。这种混合架构的难点在于统一的抽象层——你得设计一套接口,让上层 Harness 不关心底层是哪个模型,只关心输入输出。这个抽象层本身就是大量代码的来源。
3. 20 万行代码都花在哪了:拆解真实工作量
3.1 状态管理与持久化:被低估的吞代码大户
很多人以为 Agent 项目的代码量主要在"调用模型"上,实际上调用模型可能只占 5%。真正吃代码的是状态管理。一个长周期 Agent 任务,可能持续几小时甚至几天,中间涉及几十个步骤,每步都有输入、输出、中间产物、错误记录。这些状态怎么存、怎么恢复、怎么在崩溃后接着跑,是一整套工程问题。
这个项目九个月堆出 20 万行,我推测状态管理至少占了三分之一。具体包括:任务状态的序列化和反序列化、检查点机制(每隔几步存一次快照)、断点续跑逻辑、并发任务的状态隔离、以及状态变更的审计日志。这些代码写起来枯燥,但缺了任何一块,Agent 就没法在真实场景里稳定运行。
举个具体例子:假设 Agent 正在处理一个需要调用外部工具的任务,调用到一半进程崩了。如果没有持久化,重启后一切从头开始,前面烧的 token 全白费。有了检查点,重启后能从最后一个成功步骤继续。这个"从哪继续"的判断逻辑,涉及状态版本管理、幂等性保证、以及外部副作用的回滚,每一块都是硬骨头。
3.2 工具调用层:把不确定性关进笼子
Agent 和普通程序最大的区别,是它的"动作"来自模型生成,而不是硬编码。这意味着每个工具调用都可能是错的——参数类型不对、必填项缺失、调用了不存在的工具、或者参数值超出合理范围。Harness 架构的核心工作之一,就是在模型和真实工具之间加一层严格的校验和转换。
这层代码通常包括:工具定义的 schema 描述、参数校验器、类型转换器、调用前的预检查、调用后的结果验证、以及失败时的重试和降级策略。我做过统计,一个功能完整的工具调用层,每个工具平均需要 200 到 500 行代码来包裹。如果项目里有几十个工具,光这一块就是上万行。
更麻烦的是工具之间的依赖和编排。有些任务需要按特定顺序调用多个工具,前一个的输出是后一个的输入。这种编排逻辑如果用代码硬写,会非常脆弱;如果用声明式的方式描述,又需要一套解析和执行引擎。这个项目大概率采用了后者,因为声明式的编排更容易让模型参与生成和修改,而这正是 Agent 的价值所在。
3.3 输出解析与格式修复:和模型的"不听话"长期斗争
模型输出格式不对,是 Agent 开发里最日常的痛点。你要求它输出 JSON,它给你输出带解释文字的 JSON;你要求它用特定分隔符,它偶尔用中文标点;你要求它输出 Markdown 表格,它给你来个 markdown 表格转换 excel 都费劲的畸形结构。这些看似小问题,在自动化流程里就是致命的。
所以这个项目里必然有大量代码在做输出解析和格式修复。策略通常是分层的:第一层用严格解析器,能过就过;第二层用宽松解析器,容忍常见偏差;第三层用模型自己修复(把错误输出和格式要求一起丢回去让它重写);第四层才报错并记录。这四层每一层都需要代码,而且需要大量测试用例来覆盖各种畸形输出。
实操心得:格式修复的提示词里,一定要给"反例"。只告诉模型"要输出 JSON"效果一般,告诉它"不要输出解释文字,不要用 markdown 代码块包裹,第一个字符必须是左花括号"效果会好很多。这个技巧我在多个项目里验证过,能显著降低修复层的触发率。
3.4 测试与可观测性:让黑盒变成灰盒
Agent 系统最难的地方在于它是"黑盒"——你给它输入,它给你输出,中间发生了什么很难看清。20 万行代码里,必然有相当一部分是在做可观测性:日志、追踪、指标、以及回放能力。
具体来说,每一步模型调用都要记录完整的输入输出、耗时、token 消耗、使用的模型版本。每个工具调用要记录参数、结果、耗时、是否重试。整个任务链路要能串起来,形成一个可回放的 trace。这样出问题的时候,你能精确知道是哪一步、哪个模型、哪个参数导致的。没有这套东西,调试 Agent 就是纯靠猜,效率极低。
测试方面,除了常规的单元测试,还需要回归测试集——收集历史上出过问题的案例,每次改动后跑一遍,确保没有退化。这个测试集本身就是宝贵资产,随着项目推进不断积累。我估计这个项目的测试代码占比不会低于 20%,因为 Agent 的行为太容易受各种因素影响,没有测试兜底根本不敢改代码。
4. 每月 40 亿 token 是怎么烧的,又怎么省下来的
4.1 token 消耗的真实构成
40 亿 token 一个月,平均每天 1.3 亿多。这个量级如果全部走强模型,成本会非常吓人。所以要理解这个数字,必须拆开看构成。我的经验是,Agent 项目的 token 消耗大致分四块:系统提示词、上下文历史、工具定义、以及实际任务内容。
系统提示词和工具定义是"固定开销",每次调用都要带上。如果系统提示词写得很长(Agent 项目常见几千 token),工具定义又很多,那每次调用的基础成本就很高。上下文历史是"累积开销",随着任务推进不断增长,如果不做压缩,很快就会撑爆上下文窗口。实际任务内容反而是最不可控的部分。
这个项目能把成本控制在可承受范围,我推测做了几件事:系统提示词精简和缓存(很多模型支持提示词缓存,重复部分不计费或打折)、上下文压缩(定期把历史总结成摘要,丢弃原始细节)、工具定义按需加载(不是所有任务都需要所有工具,用到才加载)、以及前面提到的模型分级路由。
4.2 上下文压缩:Agent 长任务的生死线
上下文窗口是有限的,但 Agent 任务可能很长。怎么在有限窗口里塞进足够的信息,是 Harness 架构必须解决的问题。常见策略有三种:滑动窗口(只保留最近 N 轮)、摘要压缩(把旧内容总结成短摘要)、以及检索增强(把历史存到外部,需要时检索相关片段)。
这个项目大概率是三者结合。滑动窗口保证最近的上下文完整,摘要压缩处理中期历史,检索增强应对需要回溯很久之前信息的情况。难点在于摘要的质量——摘要太粗会丢关键信息,太细又省不了多少 token。我的做法是让模型在摘要时明确保留"决策、结论、未完成事项、关键参数"这几类信息,其他细节可以丢。这个策略在实测中能把上下文压缩到原来的 20% 到 30%,同时保持任务连续性。
注意:上下文压缩有个隐蔽的坑,就是压缩后的摘要如果被反复压缩,会像复印件的复印件一样越来越模糊。所以一定要保留原始记录,摘要只是给模型看的,原始数据要存好,必要时能重新生成摘要。
4.3 缓存与批处理:把重复劳动的成本降到零
Agent 任务里有很多重复模式。比如同一个系统提示词,在成百上千次调用里反复出现;同一批文档的处理,逻辑完全一样只是数据不同。这些地方都是优化的空间。
提示词缓存是最直接的省钱手段。主流模型厂商都支持对固定前缀做缓存,命中缓存的部分按折扣计费。把系统提示词、工具定义这些固定内容放在前面,任务相关内容放在后面,就能最大化缓存命中率。这个优化做不做,成本可能差好几倍。
批处理则是另一个维度。如果任务不要求实时响应,可以把多个请求打包一起发,通常有折扣。这个项目每月 40 亿 token,如果有相当比例能走批处理,省下来的钱相当可观。当然批处理的代价是延迟,所以要区分哪些任务能等、哪些不能等。
4.4 失败重试的成本陷阱
Agent 项目里有个容易被忽视的成本黑洞:失败重试。模型输出格式不对要重试,工具调用失败要重试,校验不通过要重试。每次重试都是一次完整的模型调用,token 照烧。如果重试率是 20%,那实际成本比理论值高 25%。
控制重试成本的关键是让重试更聪明。不要简单地把同样的输入再发一遍,而是把失败原因、期望格式、以及上次的错误输出一起发回去,让模型有针对性地修正。同时设置重试上限,超过就降级或报错,避免无限重试烧钱。我见过最夸张的案例是一个格式问题导致模型重试了十几次,一次任务烧掉几十万 token,就是因为没有重试上限。
5. 一个人怎么扛下这个项目:协作与工具链
5.1 与 AI 结对:把 Claude Code 用成"第二双手"
一个人写 20 万行代码,纯靠手敲九个月是绝对不可能的。这个项目的真实工作模式,必然是人机结对——人负责架构决策、关键逻辑、以及质量把关,AI 负责大量样板代码、测试用例、以及重复性实现。Claude Code 这类工具的价值就在这里:它能理解项目上下文,按你的意图生成符合风格的代码,还能帮你重构和修 bug。
但用 AI 写代码有个前提:你得能判断它写得对不对。如果自己不懂,AI 生成的代码就是定时炸弹。所以这个项目的作者,本身必然是有深厚工程功底的,AI 只是放大了他的产出效率,而不是替代了他的判断。这一点很多人误解,以为有了 AI 就能零基础做项目,实际上 AI 放大的是能力,不是填补空白。
我的实操经验是,把任务拆成"AI 能独立完成的"和"必须自己把关的"两类。前者比如写测试、写文档、写数据转换逻辑,直接交给 AI,自己抽查。后者比如核心状态机、并发控制、错误处理策略,自己设计框架,让 AI 填充细节。这样既保证了效率,又守住了质量底线。
5.2 Obsidian 作为项目大脑:知识沉淀决定长期效率
热词里 Obsidian 出现频率很高,这不是偶然。九个月的项目,涉及大量决策、踩坑、方案对比,如果这些不沉淀下来,很快就会忘记当初为什么这么设计。Obsidian 这类工具的价值,就是把这些碎片化的知识组织成可检索、可关联的网络。
我推测这个项目的 Obsidian 库里,至少有这几类笔记:架构决策记录(每个重要选择的原因和备选方案)、踩坑日志(遇到的问题和解决方法)、API 和工具的使用笔记、以及任务规划和进度追踪。这些笔记本身就是项目的"外脑",让作者在九个月里保持思路连贯,不至于重复踩同一个坑。
实操心得:Obsidian 笔记一定要有统一的模板和标签体系,否则记多了就变成垃圾堆。我的做法是每篇笔记开头用固定格式写"背景、问题、方案、结论"四段,标签用"领域/类型/状态"三级结构。这样几个月后回看,依然能快速定位。
5.3 版本控制与实验管理:让每次改动可追溯
Agent 项目的特点是"改动影响面大"——改一个提示词可能影响所有任务的表现。所以版本控制和实验管理极其重要。每次改动都要能追溯,每个版本的表现都要能对比。
具体做法包括:代码用 Git 管理,提示词也纳入版本控制(不要硬编码在代码里);每次重要改动记录变更原因和预期影响;建立评估集,改动后跑一遍看指标变化。这套流程听起来繁琐,但在长周期项目里是保命的。我见过太多项目因为改了一个提示词导致整体退化,又找不到是哪个改动引起的,最后只能回滚到很久之前。
6. 常见问题与排查技巧实录
6.1 Agent 跑着跑着就"失忆"了怎么办
这是长任务最常见的症状:前面明明确认过的信息,后面模型又忘了,导致重复询问或做出矛盾决策。根因通常是上下文被压缩或截断,关键信息丢失。
排查思路:先看上下文压缩策略,是不是把重要信息压没了;再看摘要生成的质量,是不是丢了关键结论;最后看是否有信息被放在了会被截断的位置。解决方法是把"必须记住的信息"显式提取出来,放在每轮调用的固定位置,而不是依赖模型从历史里自己找。这个技巧叫"状态外置",效果立竿见影。
6.2 工具调用参数总是差一点
模型生成的工具参数,经常出现类型不对、格式不对、或者值不合理的情况。比如要求整数给了字符串,要求路径给了描述。这类问题的排查要看是提示词没说清,还是校验太宽松。
解决分三步:第一,工具定义里把参数类型、格式、示例写清楚,越具体越好;第二,加严格的校验层,不合格直接打回让模型重写;第三,对高频错误做针对性提示,比如"路径必须是绝对路径,以斜杠开头"。实测下来,这三步能把参数错误率降到很低。
6.3 成本突然飙升怎么定位
token 成本突然涨了,通常有几个原因:重试率上升、上下文变长、或者某个任务进入了死循环。排查要先看监控数据,定位是哪个任务、哪个环节消耗异常。
常见速查方向我整理成表:
| 症状 | 可能原因 | 排查方法 | 解决方向 |
|---|---|---|---|
| 单任务 token 暴涨 | 上下文未压缩 | 看历史长度曲线 | 加压缩策略 |
| 整体成本上升 | 重试率变高 | 统计重试次数 | 优化提示词和校验 |
| 某类任务异常 | 进入循环 | 看调用序列 | 加循环检测和上限 |
| 缓存命中率下降 | 提示词前缀变动 | 对比提示词版本 | 固定前缀内容 |
6.4 模型升级后行为变了
模型版本更新是常事,但新版本可能在某些任务上表现不同,甚至退化。这时候不能盲目升级,要先跑评估集对比。
我的做法是维护一个"黄金测试集",包含各类典型任务和边界案例。新模型上线前先跑一遍,对比通过率和质量。如果关键指标退化,就暂缓升级或者针对新模型调整提示词。这个流程能避免"升级即翻车"的尴尬。
7. 这套架构能复用到哪些场景
Harness 架构的价值不限于这一个项目。任何需要"模型稳定执行长周期任务"的场景,都能借鉴这套思路。比如自动化数据处理流水线、智能客服的多轮任务处理、代码生成与审查、以及知识库的自动整理。核心都是把模型的生成能力和工程的确定性结合起来。
我个人在实际操作中的体会是,Harness 架构的投入产出比,在任务越复杂、周期越长的时候越明显。简单任务用纯提示词就够了,硬上 Harness 是过度设计。但一旦任务链条超过十步,或者需要跨会话保持状态,Harness 就是必需品。判断标准很简单:如果你发现自己在反复处理"模型又抽风了"的问题,那就是该上 Harness 的时候了。
最后再分享一个小技巧:Harness 的每个环节,都要设计成"可单独测试"的。不要写一个巨大的函数从头跑到尾,而是拆成小步骤,每步有明确输入输出。这样出问题能精确定位,改动能局部验证,整个系统的可维护性会高一个数量级。这个原则我在多个项目里坚持,长期看省下的调试时间远超拆分时多花的那点功夫。