1. 先聊聊这个项目到底在做什么
一个人,九个月,20 万行代码,每个月消耗 40 亿以上的 token——这几个数字摆在一起的时候,我第一反应不是"牛",而是"这人到底在解决什么问题,值得这么烧"。
先把结论放前面:这个项目本质上是在做一款Harness 架构的应用。所谓 Harness 架构,你可以理解成"给 AI Agent 套上一副马具"——模型本身是匹野马,力气大但方向不定,Harness 就是那套缰绳、鞍具和路标,让它在一条可控的轨道上跑。它不是一个单纯的聊天界面,也不是一个简单的提示词模板集合,而是一整套围绕 Agent 执行、上下文管理、工具调用、状态持久化构建的工程体系。
为什么这件事值得单独拿出来讲?因为绝大多数人做 Agent 项目,卡的不是模型能力,而是工程化落地。模型能写代码、能查资料、能调工具,这些早就不是新闻了。真正难的是:怎么让它在几十轮、上百轮交互之后还记得自己是谁、在干什么、下一步该干嘛;怎么让它的输出稳定可复现;怎么把它的中间产物沉淀成可检索、可复用的知识资产。这个项目九个月烧掉 20 万行代码,绝大部分精力其实都花在这些"不性感但致命"的地方。
这篇文章适合谁看?三类人。第一类是想自己动手做 Agent 应用但不知道从哪下手的开发者,我会把架构选型、模块拆分的逻辑讲透。第二类是已经在做 Agent 但被上下文爆炸、状态丢失、工具调用混乱折磨过的工程师,我会分享具体的排查思路和避坑经验。第三类是对 Harness、Claude Code、Obsidian 这套组合感兴趣、想搞清楚它们怎么串起来的人,我会把每个环节的实操细节补全。
需要提前说明的是,下面涉及的具体参数、目录结构、配置方式,有一部分是基于这类项目的常见工程实践做的合理补全,因为原始信息里没有给出全部实现细节。我会明确标注哪些是通用做法、哪些是我个人经验推断,你照着抄的时候记得结合自己的场景调整。
2. 为什么是 Harness 架构,而不是别的方案
2.1 从"提示词工程"到"马具工程"的认知转变
早期做 Agent,大家的思路基本停留在提示词层面:写一个足够详细的 system prompt,把角色、任务、输出格式全塞进去,然后祈祷模型别跑偏。这套做法在单轮任务里还行,一旦任务变成多步骤、长周期,立刻就崩。原因很简单——提示词是无状态的,而真实任务是连续的。
Harness 架构的核心洞察就在这里:与其把希望寄托在模型"记住"上,不如在模型外面搭一套脚手架,主动管理它的输入输出、状态和工具。这就像训马,你不能指望马自己记得路线,你得给它套上缰绳、装上马鞍、沿途设好路标。模型负责"跑",Harness 负责"往哪跑、跑多远、什么时候停"。
这个认知转变带来的直接后果是:项目的重心从"写更好的提示词"变成了"设计更好的执行框架"。20 万行代码里,真正跟提示词相关的可能不到 5%,剩下 95% 全是状态管理、工具编排、上下文压缩、错误恢复这些工程活。
2.2 为什么选 Claude Code 作为执行内核
在众多可选方案里,这个项目选择了 Claude Code 作为核心执行引擎,这个选择背后有几层考量。
第一是工具调用的成熟度。Claude Code 本身就是一个为"读写文件、执行命令、搜索代码"设计的 Agent 运行时,它的工具集天然贴合"在真实项目里干活"这个场景。你不需要从零实现文件读写、命令执行这些基础能力,直接复用就行。
第二是上下文管理的可控性。Claude Code 对上下文的处理相对透明,你能比较清楚地知道哪些内容进了上下文、哪些被截断、压缩策略是什么。这对一个要跑九个月、烧几十亿 token 的项目来说至关重要——上下文管理不当,token 消耗会指数级失控。
第三是可扩展性。Claude Code 支持通过配置扩展工具、自定义命令、挂载外部能力。这意味着 Harness 层可以在它之上叠加自己的逻辑,而不是被它的能力边界锁死。
提示:选执行内核的时候,别只看"哪个模型最强"。要看的是"哪个运行时的工具生态、上下文策略、扩展接口最贴合你的场景"。模型能力会迭代,但架构选错了,后面每一步都是逆风。
2.3 Markdown 作为中间格式的战略价值
这个项目里,Markdown 不只是一个"输出格式",而是整个系统的中间表示层。Agent 的思考过程、任务拆解、执行结果、知识沉淀,全部以 Markdown 形式落盘。为什么这么设计?
因为 Markdown 同时满足三个条件:人类可读、机器可解析、工具生态丰富。你让 Agent 输出 JSON,人看着累;你让它输出纯文本,机器解析难;Markdown 卡在中间,两边都照顾到了。而且 Markdown 的表格、列表、代码块这些结构,天然适合表达"任务清单""参数对照""代码片段"这类 Agent 高频产出的内容。
更关键的是,Markdown 是 Obsidian 的原生格式。这就引出了下一个设计——用 Obsidian 做知识底座。
2.4 Obsidian 承担的角色:不只是笔记软件
很多人把 Obsidian 当笔记软件用,但在这个项目里,它是Agent 的长期记忆和知识检索层。Agent 每完成一个任务,产出的 Markdown 文件直接进入 Obsidian 库,通过双链、标签、文件夹结构组织起来。下次遇到相关任务,Agent 可以通过检索这些历史文件,快速找回上下文,而不是从零开始。
这套设计的精妙之处在于:它把"记忆"从模型内部(不可控、会丢失、成本高)转移到了外部文件系统(可控、持久、检索便宜)。模型不需要记住所有东西,它只需要知道"去哪找"。这跟人类专家的做法其实一样——真正的高手不是什么都记在脑子里,而是知道遇到问题该翻哪本书、查哪个文档。
3. 核心模块拆解与实操要点
3.1 上下文管理:40 亿 token 是怎么烧掉的
先算一笔账。每个月 40 亿 token,按 30 天算,每天约 1.33 亿 token。如果按单次交互平均消耗 5 万 token(包含系统提示、历史上下文、工具返回结果),那一天就是约 2660 次交互。九个月下来,累计交互次数在几十万量级。
这个量级下,上下文管理不是"优化项",而是"生死线"。项目里主要用了三层策略:
第一层是滑动窗口加摘要压缩。保留最近 N 轮完整对话,更早的内容压缩成摘要。N 的取值很讲究——太小,Agent 会"失忆";太大,token 爆炸。实践中 N 通常设在 10 到 20 轮之间,具体看单轮平均长度。
第二层是结构化外置。把任务状态、待办清单、关键决策这些"必须记住"的信息,从对话历史里抽出来,单独存成 Markdown 文件。每次新对话开始时,只加载这个精简版状态,而不是把全部历史塞进去。
第三层是检索增强。需要历史细节时,通过关键词检索 Obsidian 库,按需加载相关片段。这比"全量加载"省 token 得多。
| 策略 | 作用 | 典型 token 节省 | 适用场景 |
|---|---|---|---|
| 滑动窗口+摘要 | 压缩近期历史 | 40%-60% | 连续多轮对话 |
| 结构化外置 | 精简状态加载 | 60%-80% | 长周期任务 |
| 检索增强 | 按需加载历史 | 70%-90% | 知识密集型任务 |
注意:摘要压缩是有损的。我踩过的坑是,早期摘要策略太激进,把一些看似无关但后续关键的细节压没了,导致 Agent 反复问同样的问题。后来改成"摘要+关键实体保留",把任务涉及的文件名、函数名、参数值这些硬信息原样保留,只压缩叙述性内容,效果好很多。
3.2 工具编排:让 Agent 知道"什么时候用什么"
Agent 最容易出问题的地方,不是不会用工具,而是不知道该用哪个工具、什么时候用。工具一多,选择困难就来了。这个项目里,工具编排做了几件事:
工具分组与场景绑定。不是把所有工具一股脑丢给 Agent,而是按场景分组。比如"代码修改"场景只暴露读写文件、执行测试相关的工具;"资料检索"场景只暴露搜索、读取文档的工具。这样 Agent 的选择空间被收窄,出错概率大幅下降。
工具调用的前置校验。每次工具调用前,Harness 层会做一次参数校验和权限检查。比如写文件操作,会先确认路径在允许范围内、文件不是只读的、内容不是空的。这些校验看起来琐碎,但能挡掉大量"Agent 自信满满地执行了一个错误操作"的情况。
失败重试与降级。工具调用失败是常态,不是异常。Harness 层需要定义清楚:什么错误可以重试、重试几次、重试间隔多久、重试还失败怎么办。项目里对不同类型的工具调用设了不同的重试策略,比如网络类操作重试 3 次,文件类操作重试 1 次(因为文件错误通常是逻辑错误,重试没用)。
3.3 状态持久化:Agent 的"记忆"怎么存
状态持久化是这个项目最花功夫的部分之一。核心思路是:把 Agent 的"工作记忆"和"长期记忆"分开存。
工作记忆是当前任务的临时状态,存在内存或临时文件里,任务结束就清理。长期记忆是跨任务的知识沉淀,存进 Obsidian 库,永久保留。
工作记忆的结构大概是这样:
# 当前任务状态 ## 任务目标 重构用户认证模块,支持多因素认证 ## 已完成 - [x] 梳理现有认证流程 - [x] 设计新流程的接口 ## 进行中 - [ ] 实现 TOTP 验证逻辑 ## 待办 - [ ] 编写单元测试 - [ ] 更新文档 ## 关键决策 - 选择 TOTP 而非短信验证码,因为不依赖外部服务 - 验证逻辑放在独立模块,便于测试 ## 相关文件 - src/auth/authenticator.py - tests/test_authenticator.py这个结构的好处是:Agent 每次恢复任务,只需要读这一个文件,就能快速回到状态。不需要翻几十轮对话历史。
长期记忆的组织则依赖 Obsidian 的双链和标签体系。每个完成的任务生成一个 Markdown 文件,文件里用[[双链]]关联相关概念,用#标签标记领域。这样检索的时候,既可以通过关键词搜,也可以通过双链跳转,还可以通过标签聚合。
3.4 Markdown 处理:那些不起眼但坑很多的地方
Markdown 看着简单,实际处理起来坑不少。项目里踩过的几个典型问题:
换行问题。Markdown 里单个换行不产生新段落,需要空行或行尾两个空格。Agent 生成的内容经常在这上面出错,导致渲染出来的格式跟预期不符。解决办法是在 Harness 层做一次规范化处理,把 Agent 输出的换行统一成标准格式。
表格转换。Agent 经常需要把 Markdown 表格转成 Excel 或其他格式。这个转换看着简单,但涉及对齐、转义、合并单元格等细节。项目里专门写了一个转换模块,处理各种边界情况。
数学符号。涉及公式的时候,Markdown 的数学符号渲染依赖特定语法。如果 Agent 输出的公式格式不对,渲染出来就是一堆乱码。Harness 层需要做格式校验和修正。
实操心得:Markdown 处理这块,别想着"一次写对"。最好的做法是写一套校验规则,Agent 输出后自动检查,不符合规范的自动修正或打回重写。我一开始想靠提示词让 Agent 自己注意格式,效果很差,后来改成程序化校验,问题少了一大半。
4. 完整实操流程与关键环节
4.1 环境搭建:从零到能跑起来
假设你现在要从零搭一套类似的 Harness 应用,第一步是环境准备。核心组件包括:执行内核(Claude Code 或同类)、知识底座(Obsidian)、开发环境(VS Code)、版本控制(Git)。
安装顺序建议这样走:
先装 Obsidian,建好库结构。库的目录结构提前规划好,比如
tasks/放任务文件、knowledge/放知识沉淀、templates/放模板、archive/放归档。这个结构一旦定下来,后面所有 Agent 产出都往这里放,检索才有序。再配 VS Code 和 Claude Code。VS Code 里装好 Claude Code 扩展,配置好 API 密钥、模型选择、工作目录。工作目录建议直接指向 Obsidian 库,这样 Agent 读写文件跟知识库是打通的。
最后搭 Harness 层。这是你自己写的部分,负责上下文管理、工具编排、状态持久化。初期可以很简单,一个主循环加几个工具函数就行,后面逐步加功能。
注意:环境搭建阶段最容易犯的错是"目录结构没想清楚就开干"。我见过太多项目,文件到处乱放,跑了两周发现检索根本没法做,只能推倒重来。花半天时间把目录结构设计好,后面省的是几十个小时。
4.2 核心循环:Agent 是怎么跑起来的
Harness 应用的核心是一个循环:接收任务 → 加载状态 → 规划步骤 → 执行工具 → 更新状态 → 判断是否完成 → 循环或结束。
这个循环看着简单,但每个环节都有讲究。
接收任务阶段,要做任务分类。不同类型的任务,加载的上下文、暴露的工具、使用的提示词都不一样。分类可以基于关键词,也可以让模型自己判断。
加载状态阶段,从工作记忆文件里读取当前任务状态。如果是新任务,初始化一个空状态;如果是恢复任务,加载已有状态。
规划步骤阶段,让模型基于当前状态和任务目标,输出下一步要做什么。这里的关键是限制规划粒度——不要让模型一次规划十步,那样很容易跑偏。一次规划一到三步,执行完再规划,灵活性和可控性都好很多。
执行工具阶段,根据规划结果调用相应工具。每次调用前后都要记录日志,方便排查问题。
更新状态阶段,把执行结果写回工作记忆文件。这一步不能省,否则任务中断后没法恢复。
判断完成阶段,检查任务目标是否达成。达成则归档到长期记忆,未达成则继续循环。
4.3 参数调优:那些需要反复试的数字
这类项目里有一堆需要调优的参数,没有标准答案,只能根据实际情况试。列几个关键的:
| 参数 | 作用 | 典型范围 | 调优方向 |
|---|---|---|---|
| 上下文窗口大小 | 保留多少轮历史 | 10-20 轮 | 任务越复杂,窗口越大 |
| 摘要触发阈值 | 何时开始压缩 | 窗口 80% 满 | 太早压缩丢信息,太晚爆 token |
| 工具重试次数 | 失败后重试几次 | 1-3 次 | 网络类多试,逻辑类少试 |
| 单次规划步数 | 一次规划几步 | 1-3 步 | 步骤越多越容易跑偏 |
| 状态保存频率 | 多久存一次状态 | 每步都存 | 存太勤影响性能,存太疏丢状态 |
调这些参数的通用方法是:先设一个保守值,跑一批任务,看哪里出问题,针对性调整。别想着一次调到位,那是幻想。
4.4 知识沉淀:让每次任务都变成资产
这个项目最有价值的设计之一,是每个任务完成后自动生成知识文件。文件内容包括:任务描述、解决思路、关键代码、踩过的坑、可复用的模式。
这些文件进入 Obsidian 库后,通过双链和标签组织起来。下次遇到类似任务,Agent 可以先检索这些历史文件,站在过去的肩膀上,而不是从零开始。
知识文件的模板大概长这样:
# [任务名称] ## 背景 [什么场景下遇到的这个问题] ## 解决思路 [核心思路是什么,为什么这么选] ## 关键实现 [核心代码或配置] ## 踩坑记录 [遇到什么问题,怎么解决的] ## 可复用模式 [这个方案还能用在哪些场景] ## 相关 [[相关任务1]] [[相关任务2]] #标签实操心得:知识沉淀这件事,最大的敌人是"懒得写"。我的做法是把它做成自动化的——任务一完成,Harness 层自动生成知识文件草稿,Agent 只需要补充关键细节。这样人的负担降到最低,坚持下来的概率高很多。
5. 常见问题与排查技巧实录
5.1 Agent 跑着跑着就"失忆"了
这是最高频的问题。表现是:Agent 在任务中途突然问一些之前已经确认过的问题,或者做出跟之前决策矛盾的举动。
排查思路分三步。第一步,检查上下文窗口。是不是窗口太小,关键信息被挤出去了。第二步,检查摘要策略。是不是摘要把关键实体压没了。第三步,检查状态文件。是不是状态没及时保存,恢复时读到了旧版本。
解决办法通常是:加大窗口、优化摘要保留策略、提高状态保存频率。三个一起调,效果最明显。
5.2 工具调用失败但 Agent 不知道
有时候工具调用返回了错误,但 Agent 把错误当成了正常结果,继续往下走,导致后面全错。
这个问题的根源是错误处理没做好。Harness 层需要在工具调用返回后,判断结果是不是错误,如果是错误,要么重试,要么把错误信息明确告诉 Agent,让它决定怎么办。
注意:别让 Agent 自己判断"这个结果是不是错误"。模型对错误的识别能力有限,经常把错误当正常。错误判断应该在 Harness 层用程序做,确定是错误了再告诉 Agent。
5.3 Token 消耗失控
40 亿 token 一个月,如果管理不当,很容易翻倍。失控的常见原因:上下文没压缩、工具返回结果全量塞进上下文、重复加载相同内容。
排查方法:给每次交互记录 token 消耗,找出消耗大户。通常是某几类操作在偷偷烧 token,比如读取大文件、搜索结果全量返回、历史上下文重复加载。
优化手段:大文件分块读取、搜索结果先摘要再返回、历史上下文用检索代替全量加载。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决手段 |
|---|---|---|---|
| Agent 失忆 | 上下文窗口小/摘要过度 | 检查窗口和摘要策略 | 加大窗口、保留关键实体 |
| 工具错误被忽略 | 错误处理缺失 | 检查工具返回处理逻辑 | Harness 层做错误判断 |
| Token 消耗失控 | 上下文未压缩/重复加载 | 记录 token 消耗找大户 | 分块读取、检索代替加载 |
| 任务跑偏 | 规划粒度过大 | 检查单次规划步数 | 减小规划粒度 |
| 状态丢失 | 保存频率低 | 检查状态保存时机 | 提高保存频率 |
| 格式渲染错误 | Markdown 不规范 | 检查输出格式 | 程序化校验修正 |
5.5 几个不那么常见但很坑的问题
问题一:Agent 在长任务里"性格漂移"。跑了几十轮之后,Agent 的语气、风格、决策倾向跟开始时不一样了。这通常是上下文里积累了太多"噪音",把初始设定冲淡了。解决办法是定期"重置"——把核心设定重新注入上下文。
问题二:工具描述歧义导致误用。两个工具功能相近,Agent 经常用错。解决办法是把工具描述写得更明确,突出差异点,或者在 Harness 层做路由,根据场景自动选工具。
问题三:Obsidian 库大了之后检索变慢。文件多了,全文检索性能下降。解决办法是建索引、分库、用标签缩小检索范围。
6. 这套架构还能怎么扩展
跑通基础版本之后,这套 Harness 架构有几个明显的扩展方向。
多 Agent 协作。单个 Agent 能力有限,可以让多个 Agent 分工——一个负责规划、一个负责执行、一个负责审查。Harness 层负责协调它们之间的通信和状态同步。
领域特化。针对特定领域(比如前端开发、数据分析、文档写作)定制工具集和提示词,让 Agent 在垂直场景里表现更好。
人机协作增强。在关键决策点引入人工确认,Agent 提出方案,人做选择。这样既保留了 Agent 的效率,又保证了关键决策的可靠性。
知识库自动化维护。让 Agent 定期整理 Obsidian 库,合并重复内容、更新过时信息、建立新的双链关系。库越大,这个能力越有价值。
我个人在实际操作中的体会是:这套架构最值钱的地方,不是某个具体功能,而是它把 Agent 从"一次性工具"变成了"持续积累的系统"。每跑一个任务,系统就聪明一点;每沉淀一份知识,下次就快一点。这种复利效应,才是九个月 20 万行代码真正换来的东西。
最后再分享一个小技巧:如果你也想做类似的项目,别一上来就追求大而全。先用最小可行的 Harness 跑通一个简单任务,然后逐步加功能。我见过太多人,架构设计得天花乱坠,结果连第一个任务都跑不通。能跑起来的最小系统,永远比设计完美的空架子有价值。