1. 先搞清楚这个项目到底在做什么
一个人,九个月,20万行代码,每个月消耗40亿以上的token,最终交付一个Harness架构的应用。这组数字放在任何一个技术社区里都足够炸裂。我第一次看到这个项目描述的时候,脑子里冒出来的第一个念头不是"厉害",而是"这人到底怎么扛下来的"。因为但凡自己动手写过超过一万行代码的人都知道,代码量到了一定规模之后,真正消耗你的不是写代码本身,而是架构决策、上下文管理、调试排查和持续迭代的心力。
这个项目的核心关键词是Harness架构。所谓Harness,直译过来是"挽具"或者"约束框架",在AI应用开发语境下,它指的是一种围绕大语言模型构建的工程化外壳——把模型能力、工具调用、上下文管理、状态持久化、错误恢复等环节全部编排在一个可控的框架里。你可以把它理解成:模型是发动机,Harness就是底盘、变速箱和方向盘的总成。没有Harness,你只是在一堆零件里打转;有了Harness,你才有一辆能上路的车。
这个项目之所以值得拆解,是因为它触及了当前AI应用开发最核心的几个痛点:如何让Agent在长周期任务中保持稳定、如何管理海量上下文而不丢失关键信息、如何用Markdown这类轻量格式承载复杂的知识结构、如何把Claude Code这类工具真正嵌入到日常开发流里。适合阅读这篇文章的人包括:正在做AI Agent开发的工程师、想用Claude Code提升效率的独立开发者、用Obsidian管理知识体系的技术写作者,以及所有对Harness架构好奇但还没动手的人。
我接下来会从架构设计思路、核心细节拆解、实操流程、常见问题排查四个维度,把这个项目的骨架和血肉尽量还原出来。不是泛泛而谈,而是落到具体的技术选型、参数配置和踩坑经验上。
2. Harness架构的整体设计与选型逻辑
2.1 为什么是Harness而不是裸调API
很多人做AI应用的第一步是直接调API:写个函数,传prompt,拿返回,结束。这在demo阶段没问题,但一旦任务周期超过几分钟、涉及多轮工具调用、需要读写文件或维护状态,裸调API就会迅速崩溃。原因很简单:模型本身是无状态的,它不记得上一轮做了什么,也不知道当前环境里有哪些文件、哪些任务已完成、哪些步骤失败了。
Harness架构要解决的就是这个问题。它本质上是一层编排层,负责在模型和真实世界之间做翻译和调度。具体来说,它要管四件事:
- 上下文组装:每一轮调用模型之前,把历史对话、当前任务状态、相关文件内容、工具定义等按优先级拼装成prompt。
- 工具路由:模型输出工具调用请求后,Harness负责解析、执行、把结果回填给模型。
- 状态持久化:把任务进度、中间产物、错误日志写到磁盘或数据库,防止进程崩溃后一切归零。
- 错误恢复:当模型输出格式错误、工具执行失败、上下文超限时,Harness要有重试、降级、截断等策略。
这个项目选择Harness架构,本质上是因为20万行代码的规模不可能靠一次性对话完成。它必然是一个长期运行、多轮迭代、需要断点续传的系统。没有Harness,这个项目在第三个月就会变成一团无法维护的乱麻。
2.2 技术栈选型的背后考量
从热词来看,这个项目涉及的技术栈包括Claude Code、Obsidian、Markdown、Agent框架等。我基于常见实践推测一下选型逻辑:
Claude Code作为核心执行引擎。Claude Code的优势在于它原生支持文件读写、终端命令执行、代码搜索等操作,而且对长上下文的理解能力在同类工具中属于第一梯队。用它作为Harness的底层执行器,可以省去大量工具封装的重复劳动。你不需要自己写文件读写工具,Claude Code已经内置了。
Obsidian作为知识底座。Obsidian的核心价值是本地Markdown文件管理加双向链接。对于这个项目来说,20万行代码产生的文档、笔记、决策记录如果散落在各处,根本没法检索。Obsidian的vault结构可以让所有知识以Markdown形式存在本地,同时通过链接关系形成图谱。热词里出现的"如何将zotero的笔记导入obsidian"和"obsidian创建项目管理台账"说明这个项目很可能用Obsidian做了项目管理和文献管理。
Markdown作为通用交换格式。Markdown的好处是纯文本、可版本控制、人机皆可读。在Harness架构里,Markdown可以作为模型输出和人类阅读之间的中间层。模型生成Markdown格式的任务清单、代码注释、决策日志,人类可以直接在Obsidian里查看和编辑。热词里的"markdown表格转换excel"和"markdown数学公式插件"说明项目里涉及了结构化数据的展示和转换。
Agent框架作为调度层。热词里出现了"agent框架"和"吴恩达 agent 教程",说明项目参考了主流的Agent设计模式。常见的Agent框架包括ReAct、Plan-and-Execute、Multi-Agent协作等。对于一个20万行代码的项目,我推测它采用的是分层Agent架构:顶层是规划Agent,负责拆解任务;中层是执行Agent,负责具体编码;底层是审查Agent,负责质量检查。
2.3 每月40亿token的消耗结构拆解
40亿token一个月,按30天算,每天约1.33亿token。这个量级听起来吓人,但拆开看就合理了。假设每天有效工作时间10小时,每小时消耗1330万token。如果每轮对话平均消耗5万token(包括系统提示、历史上下文、文件内容、工具定义),那就是每小时266轮调用,每分钟约4.4轮。对于一个需要持续读写文件、执行命令、检查输出的开发任务来说,这个频率完全正常。
token消耗的大头通常在三个地方:一是长上下文的历史对话累积,二是大文件的反复读取,三是工具调用结果的回填。优化token消耗的核心思路是:能摘要就不传全文,能引用就不重复,能缓存就不重算。这个项目能烧掉40亿token,说明它在上下文管理上可能比较激进,倾向于给模型更多信息以换取更高的输出质量。
3. 核心细节解析与实操要点
3.1 上下文窗口的精细化管理
Harness架构最核心的技术难点就是上下文管理。模型的上下文窗口是有限的,但项目的知识总量是无限的。你怎么决定每一轮给模型看什么、不看什么?
我自己的做法是三层过滤:
第一层是任务相关性过滤。当前任务涉及哪些文件、哪些模块、哪些历史决策,只把这些内容放进上下文。比如你在改一个登录模块,就不需要把支付模块的代码也塞进去。
第二层是时间衰减过滤。越早的对话越可能被摘要或丢弃。通常保留最近5到10轮的完整对话,更早的内容压缩成摘要。摘要的粒度可以是每10轮生成一个200字左右的概要。
第三层是优先级排序。系统提示和工具定义永远在最前面,然后是当前任务描述,然后是相关文件内容,最后是历史对话。这样即使上下文被截断,被截掉的也是优先级最低的部分。
注意:上下文截断一定要从中间截,不要从两头截。系统提示和最近一轮对话是最重要的,中间的历史可以压缩。
3.2 Markdown作为状态载体的具体用法
这个项目用Markdown做状态管理,我觉得是非常聪明的选择。具体来说,可以设计几类Markdown文件:
任务台账。一个tasks.md文件,用表格记录所有任务的编号、描述、状态、负责人(哪个Agent)、依赖关系。格式大概是这样:
| 任务ID | 描述 | 状态 | 依赖 | 最后更新 |
|---|---|---|---|---|
| T001 | 搭建项目骨架 | 完成 | 无 | 2024-01-15 |
| T002 | 实现用户模块 | 进行中 | T001 | 2024-01-18 |
| T003 | 实现支付模块 | 待开始 | T002 | - |
决策日志。一个decisions.md文件,记录每个关键技术决策的背景、选项、最终选择和理由。比如"为什么选PostgreSQL而不是MySQL"、"为什么用REST而不是GraphQL"。这个文件在后续Agent做类似决策时可以直接参考,避免重复推理。
代码地图。一个codemap.md文件,用层级列表记录项目的目录结构和每个模块的职责。Agent在需要找某个功能时,先读这个文件定位,再读具体代码,比全局搜索高效得多。
错误日志。一个errors.md文件,记录每次失败的原因和解决方案。这个文件的价值在于,当同类错误再次出现时,Agent可以直接查表,不需要重新推理。
3.3 Claude Code的集成方式与配置要点
Claude Code在这个项目里扮演的是"手"的角色——负责实际执行文件操作和命令。集成方式通常有两种:
一种是子进程调用。Harness通过命令行调用Claude Code,传入任务描述,捕获输出。这种方式简单直接,但每次调用都要启动新进程,开销较大。
另一种是会话保持。Harness维护一个Claude Code的持久会话,通过标准输入输出持续交互。这种方式效率高,但需要处理会话状态同步的问题。
从热词"vscode配置claude code"和"claude code安装"来看,这个项目很可能是在VS Code环境里使用Claude Code的。配置要点包括:
- 设置合理的超时时间,避免长任务被中断
- 配置文件读写权限,确保Agent只能访问项目目录
- 开启详细日志,方便排查问题
- 配置模型参数,比如temperature设为较低值以保证输出稳定性
实操心得:Claude Code在处理大文件时容易超时,建议先把大文件拆成小块,或者用摘要代替全文传入。
3.4 Obsidian与Harness的联动机制
Obsidian在这个项目里的角色是"知识仓库"和"人类接口"。Agent产生的所有Markdown文件都放在Obsidian的vault里,人类可以随时打开查看、编辑、补充。同时,Obsidian的插件生态可以提供额外的能力,比如:
- Dataview插件:用查询语言动态生成任务列表、统计报表
- Templater插件:自动化生成标准格式的文档
- Git插件:自动提交变更,形成版本历史
Agent和Obsidian的联动通常通过文件系统实现。Agent写文件,Obsidian监听文件变化并刷新界面。人类在Obsidian里编辑文件,Agent下一轮读取时就能看到最新内容。这种松耦合的设计比直接调API更灵活,也更符合"本地优先"的理念。
4. 实操过程与核心环节实现
4.1 从零搭建Harness骨架的步骤
假设你现在要从零开始搭一个类似的Harness,我会建议按以下顺序推进:
第一步:定义状态模型。先想清楚你的系统需要维护哪些状态。通常包括:当前任务ID、任务队列、已完成任务列表、当前上下文摘要、错误计数、重试次数。把这些状态用一个JSON文件或SQLite数据库存起来。
第二步:实现上下文组装器。写一个函数,输入是当前状态,输出是给模型的prompt。这个函数要处理优先级排序、长度截断、摘要生成等逻辑。建议先用简单规则实现,后续再优化。
第三步:封装工具调用层。把文件读写、命令执行、代码搜索等操作封装成统一的工具接口。每个工具要有明确的输入输出格式、错误处理逻辑和超时设置。
第四步:实现主循环。主循环的逻辑是:组装上下文 -> 调用模型 -> 解析输出 -> 执行工具 -> 更新状态 -> 判断是否继续。这个循环要能处理各种异常情况,比如模型输出格式错误、工具执行失败、上下文超限等。
第五步:接入持久化。把状态定期写入磁盘,支持断点续传。同时把关键事件写入日志,方便回溯。
第六步:接入Obsidian。把项目目录设置为Obsidian的vault,配置必要的插件,让人类可以随时介入。
4.2 参数计算与配置示例
以上下文窗口管理为例,假设你用的是128K上下文窗口的模型,你需要留出足够的空间给输出。通常建议输入不超过窗口的70%,即约90K token。这90K怎么分配?
- 系统提示和工具定义:5K
- 当前任务描述:2K
- 相关文件内容:50K
- 历史对话摘要:20K
- 最近完整对话:13K
如果相关文件内容超过50K,就需要做筛选或摘要。筛选的策略可以是:按修改时间排序取最近的、按与当前任务的关键词匹配度排序、按文件大小从小到大取(确保覆盖更多文件)。
注意:不同模型对token的计算方式略有差异,建议用官方提供的tokenizer做精确计算,不要凭感觉估算。
4.3 一次完整任务执行的现场记录
我模拟一次典型的任务执行流程,让你感受一下Harness是怎么工作的:
任务:在用户模块中添加密码重置功能。
第1轮:Harness组装上下文,包含任务描述、用户模块的文件列表、相关的数据库schema。模型输出一个计划:先读用户模型文件,再读路由文件,然后写重置逻辑,最后写测试。
第2轮:Harness执行文件读取工具,把用户模型文件内容回填给模型。模型输出具体的代码修改方案。
第3轮:Harness执行文件写入工具,把修改写入文件。然后执行测试命令,捕获测试结果。
第4轮:测试失败,Harness把错误信息回填给模型。模型分析错误原因,输出修复方案。
第5轮:Harness再次执行文件写入和测试,测试通过。Harness更新任务状态为完成,写入决策日志。
整个过程可能涉及几十轮调用,消耗几十万token。但因为有Harness管理状态,即使中间进程崩溃,重启后也能从上次的状态继续。
4.4 性能优化与成本控制
40亿token一个月的成本不是小数目。优化方向主要有三个:
减少无效调用。有些调用是重复的,比如反复读取同一个文件。可以在Harness层加缓存,同一个文件在短时间内只读一次。
压缩上下文。用更紧凑的格式表示信息。比如用YAML代替JSON,用缩写代替全称,用引用代替重复内容。
选择合适的模型。不是所有任务都需要最强的模型。简单的格式转换、文件读写可以用小模型,复杂的推理和代码生成再用大模型。这种分层策略可以显著降低成本。
5. 常见问题与排查技巧实录
5.1 Harness failed to load plugins的排查思路
热词里出现了"harness failed to load plugins",这是Harness架构中常见的问题。排查思路如下:
| 可能原因 | 排查方法 | 解决方案 |
|---|---|---|
| 插件路径配置错误 | 检查配置文件中的插件目录路径 | 修正为绝对路径或正确的相对路径 |
| 插件依赖缺失 | 查看插件目录下的requirements或package文件 | 安装缺失的依赖 |
| 插件版本不兼容 | 查看Harness和插件的版本号 | 升级或降级到兼容版本 |
| 权限问题 | 检查插件目录的读写权限 | 修改权限或更换目录 |
| 插件代码语法错误 | 查看启动日志中的具体报错行 | 修复插件代码 |
实操心得:插件加载失败时,先把日志级别调到DEBUG,通常能看到具体的失败原因。不要凭猜测改配置。
5.2 上下文超限的应急处理
上下文超限是Harness架构最常见的运行时错误。应急处理步骤:
- 立即停止当前调用,避免浪费token
- 检查当前上下文的组成,找出占用最大的部分
- 对大文件做摘要,只保留关键函数签名和注释
- 对历史对话做压缩,只保留决策点和结论
- 如果还是超限,考虑拆分任务,把一个大任务分成多个小任务
长期解决方案是建立上下文预算机制,在组装上下文之前就预估token数量,超过预算就自动触发压缩。
5.3 Agent输出格式错误的修复技巧
模型有时候不按预期格式输出,比如该输出JSON却输出了Markdown,该调用工具却直接回答了问题。修复技巧包括:
- 强化格式约束:在系统提示里用明确的示例说明期望格式
- 增加校验层:Harness在解析输出前先做格式校验,不符合就重新请求
- 使用结构化输出:如果模型支持,开启JSON mode或function calling
- 降低temperature:减少输出的随机性
我自己的经验是,格式错误80%是因为提示不够明确。与其在解析层做各种兼容,不如把提示写清楚。
5.4 长周期任务的断点续传实现
20万行代码的项目不可能一次跑完,断点续传是刚需。实现要点:
- 每个任务完成后立即写入状态文件,不要攒批
- 状态文件要包含足够的恢复信息:当前任务ID、已完成步骤、中间产物路径
- 启动时先读状态文件,如果有未完成任务,从断点继续
- 定期做状态快照,防止状态文件本身损坏
注意:断点续传的关键是幂等性。同一个步骤重复执行不能产生副作用,否则恢复后会出现数据不一致。
5.5 token消耗异常的监控与告警
每月40亿token,如果某天突然翻倍,你需要能快速定位原因。建议做以下监控:
- 按小时统计token消耗,画出趋势图
- 按任务类型统计token消耗,找出最耗token的任务
- 设置阈值告警,比如单日消耗超过2亿就发通知
- 记录每次调用的token明细,方便回溯
排查token异常时,重点看三个地方:是不是有死循环导致重复调用、是不是有大文件被反复读取、是不是上下文压缩失效导致每次都传全文。
6. 一些个人体会和后续扩展方向
这个项目最让我佩服的地方不是20万行代码本身,而是这个人能在九个月里保持节奏。做AI应用开发最容易陷入的陷阱是"无限优化"——总觉得提示还能再改改、架构还能再调调、模型还能再换换。但真正能交付的项目,靠的不是完美主义,而是持续迭代和快速试错。
Harness架构的价值在于它把AI应用开发从"手工作坊"变成了"流水线"。你可以把上下文管理、工具调用、错误恢复这些通用能力沉淀到Harness里,然后专注于业务逻辑本身。这个思路不仅适用于代码生成,也适用于数据分析、内容创作、自动化运维等场景。
后续如果要扩展,我会考虑几个方向:一是多Agent协作,让规划、执行、审查三个角色互相制衡;二是引入向量数据库做长期记忆,解决上下文窗口的硬限制;三是把Harness做成可配置的框架,让不同项目能复用同一套基础设施。
最后分享一个小技巧:在Harness里加一个"人类确认"环节。对于高风险操作,比如删除文件、修改数据库、执行部署命令,先暂停并请求人类确认。这个环节看起来降低了自动化程度,但实际上大大提高了系统的可靠性。我踩过的坑里,有一半是因为Agent自作主张执行了不该执行的操作。加了这个确认环节之后,类似问题再没出现过。