ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Harness架构实战:如何用Agent和Markdown九个月打造20万行代码应用

Harness架构实战:如何用Agent和Markdown九个月打造20万行代码应用 1. 先搞清楚一个人九个月20万行到底意味着什么先把数字摊开看。九个月按每月22个工作日算大概198个工作日。20万行代码摊下来平均每个工作日要产出1000行左右的有效代码。这个量级如果靠手敲基本不可能——就算全天不摸鱼、不调试、不写文档纯打字也顶不住。所以这个数字背后一定有一套高度自动化的生产链路而不是一个人硬肝。再看每月烧掉40亿 token。这个数字更值得琢磨。40亿token是什么概念按主流大模型API的计费口径输入输出混算一个月40亿token对应的成本是相当可观的。能烧到这个量级说明这个应用的核心逻辑不是调一次模型返回一段文本而是持续性的、多轮次的、带工具调用的Agent循环。每一次用户操作背后可能是几十甚至上百次模型往返。关键词里出现了Harness、Agent、Claude Code、Obsidian、Markdown这一串基本可以勾勒出这个项目的轮廓一个以Harness架构为核心、深度集成Agent能力、用Markdown作为主要数据载体、并且和Obsidian这类知识管理工具打通的桌面级应用。所谓Harness架构简单说就是给模型套一个可控的执行外壳。模型本身只会输出文本它不能读文件、不能跑命令、不能记住上一次干了什么。Harness就是那层壳它负责把用户的意图翻译成模型能理解的prompt把模型的输出解析成具体的动作再去执行这些动作把结果喂回给模型循环往复直到任务完成。你可以把它理解成模型的驾驶舱——模型是发动机Harness是方向盘、油门、仪表盘和刹车。这个项目之所以值得拆是因为它踩中了当前AI应用开发最核心的几个命题怎么让Agent稳定跑长任务、怎么管理海量上下文、怎么把非结构化的模型输出变成可靠的结构化操作、怎么让一个单人开发者扛住工程复杂度。下面我按实际开发中会遇到的顺序一层层拆开讲。2. Harness架构的核心把不可控的模型关进可控的笼子2.1 为什么不能直接调API就完事很多人做AI应用的第一反应是用户输入一句话我拼个prompt发给模型拿到回复展示出来完事。这个模式在问答场景下没问题但一旦涉及帮我改这个文件帮我查一下这个项目的依赖帮我把这段Markdown转成表格就立刻崩了。原因很简单模型没有手。它只能说不能做。你说帮我改文件它只能回你一段你应该把第3行改成XXX然后呢还得用户自己去改。这不是Agent这是高级一点的搜索引擎。Harness要解决的就是这个手的问题。它的基本循环是这样的接收用户输入组装上下文历史对话、当前文件状态、可用工具列表调用模型模型返回一个动作意图比如读取文件AHarness解析这个意图执行对应工具把执行结果追加到上下文再次调用模型直到模型返回任务完成这个循环听起来简单但每一步都有坑。比如第3步模型返回的动作意图是自然语言你怎么可靠地解析第5步执行结果可能非常长全塞回上下文会爆token。第6步怎么判断任务完成而不是模型在偷懒2.2 工具调用的协议设计让模型说人话但干人事Harness架构里最关键的设计决策之一是工具调用的协议格式。目前主流有三种做法方案做法优点缺点纯JSON要求模型输出严格JSON解析简单模型容易漏括号、加注释解析失败率高函数调用API用模型厂商提供的tool use能力格式由厂商保证绑定特定厂商跨模型迁移麻烦标记语言用自定义XML/Markdown标记包裹动作容错性好模型熟悉需要自己写解析器这个项目里大量出现Markdown关键词我推测它大概率采用了标记语言方案而且很可能是基于Markdown的扩展语法。为什么因为Markdown是模型训练语料里最常见的格式之一模型对Markdown的语感最好。你让它输出action typeread patha.md它可能偶尔写错但你让它输出### ACTION: read_file - path: ./notes/todo.md它几乎不会错。因为这种标题列表的结构在训练数据里海量存在模型闭着眼都能写对。解析器的写法也就顺理成章按行扫描遇到### ACTION:就开启一个新动作后续的- key: value作为参数直到遇到下一个###或空行结束。这种解析器代码量不大但容错性极好——模型多写一个空行、少写一个冒号你都能通过正则兜住。实操心得解析器一定要写宽松模式。我见过太多项目因为模型输出多了一个句号就整个流程崩掉。正确做法是能解析多少算多少解析不了的当作普通文本忽略而不是抛异常中断整个Agent循环。2.3 上下文管理40亿token是怎么烧掉的现在来回答那个核心问题一个月40亿token到底花在哪了。假设一个典型任务用户说帮我把这个项目里所有TODO注释整理成一张表。Harness的执行链路可能是这样的第一次调用模型决定先列出项目文件 → 消耗约2000 token执行列文件返回300个文件路径 → 追加约5000 token第二次调用模型决定读取其中20个可能含TODO的文件 → 消耗约8000 token执行读取返回20个文件内容 → 追加约50000 token第三次调用模型分析内容提取TODO → 消耗约60000 token模型决定写入结果文件 → 消耗约2000 token执行写入返回成功 → 追加约100 token第四次调用模型确认完成 → 消耗约70000 token一个任务下来累计消耗接近20万token。如果这个应用有1000个活跃用户每人每天跑5个这样的任务一天就是10亿token一个月30亿。再加上重试、纠错、多轮对话40亿完全说得通。所以token消耗的大头不在模型思考而在上下文反复传递。每一轮循环之前所有的历史都要重新发给模型。这是Harness架构的固有成本也是为什么上下文压缩技术如此关键。这个项目能做到20万行代码说明它在上下文管理上一定下了功夫。常见的优化手段包括滑动窗口只保留最近N轮对话老的截断摘要压缩把老对话用模型总结成一段短文本外部记忆把重要信息写到文件里需要时再读回来工具结果裁剪读取大文件时只返回前M行或者只返回匹配的行其中外部记忆这一条正好解释了为什么项目深度绑定了Obsidian和Markdown。Obsidian的库本身就是一堆Markdown文件Agent读写这些文件相当于把Obsidian当成了它的长期记忆体。这比在内存里维护一个向量数据库要简单得多也透明得多——用户可以直接打开文件看Agent记了什么。3. Markdown不只是格式是这个项目的数据总线3.1 为什么选Markdown而不是数据库一个应用要存数据常规做法是上数据库。但这个项目偏偏把Markdown作为核心载体这背后有很实际的考量。第一Markdown对人类和模型都友好。用户可以直接用任何编辑器打开、修改、版本控制。模型对Markdown的理解能力也远超对SQL或JSON的理解。你让模型把这段内容整理成表格它输出Markdown表格的准确率极高。第二Markdown天然适合做Agent的输入输出。Agent读一个Markdown文件就像人读一篇笔记结构清晰、语义明确。Agent写一个Markdown文件用户立刻就能看懂不需要任何转换。第三Obsidian生态的加持。Obsidian本身就是基于本地Markdown文件的知识管理工具有双链、标签、图谱等能力。Agent把结果写成Markdown用户在Obsidian里就能直接看到关系网络。这比让用户去查数据库直观太多。3.2 Markdown解析中的那些坑但Markdown也不是没有代价。它的语法看似简单实际解析起来坑非常多。这个项目20万行代码里估计有相当一部分是在处理Markdown的各种边界情况。换行问题是最经典的。Markdown里单个换行在渲染时通常被忽略要强制换行得在行尾加两个空格或者用br。但模型输出的时候它不知道你要的是软换行还是硬换行经常该换的地方不换不该换的地方乱换。处理办法是在解析层做归一化把所有连续空行压成一个把行尾空格统一处理。表格转换是另一个高频需求。关键词里出现了markdown表格转换excel说明用户有这个诉求。Markdown表格的语法是| 列1 | 列2 | |-----|-----| | a | b |但实际解析时会遇到列数不匹配、对齐符号缺失、单元格内有竖线、单元格内有换行。一个健壮的解析器需要处理所有这些情况。我一般的做法是先按行分割找到分隔行就是那行全是---的以它为基准确定列数然后逐行解析列数不足的补空超出的合并到最后一列。数学符号也是坑。Markdown里的$...$和$$...$$在不同渲染器里行为不一致。有些渲染器要求$前后有空格有些不要。如果Agent要处理含数学公式的笔记解析时得特别小心别把公式里的下划线当成斜体标记。实操心得不要自己从零写Markdown解析器。用成熟的库比如JavaScript生态的markdown-it、Python的markdown或mistune。但要注意这些库主要面向渲染而Agent需要的是结构化解析。你可能需要在库的基础上再包一层把AST转成自己需要的格式。3.3 Markdown作为Agent间通信协议这个项目里Markdown的另一个重要用途是作为多个Agent之间的通信格式。当一个复杂任务需要拆给多个Agent时它们之间怎么传递信息用JSON用自然语言Markdown的好处是它既是结构化的有标题、列表、表格又是自然语言的段落就是普通文字。Agent A可以把任务描述写成## 任务整理项目文档 ### 输入 - 源目录./docs - 输出文件./summary.md ### 要求 1. 提取所有二级标题 2. 生成目录表格 3. 标注每个文件的最后修改时间Agent B读到这个既能理解结构知道有输入、输出、要求三部分又能理解语义知道要提取标题、生成表格。这种半结构化的特性是JSON和纯文本都不具备的。4. 和Claude Code、Obsidian的集成不是简单调个API4.1 Claude Code在这里扮演什么角色关键词里claude code出现频率很高还有vscode配置claude codeclaude code安装等。这说明这个项目和Claude Code有深度关联。Claude Code本质上是Anthropic官方出的一个命令行Agent工具它能在终端里读写文件、执行命令、跑测试。它的核心能力是代码理解和代码操作。这个Harness项目集成Claude Code大概率是为了借用它的代码操作能力——比如让Agent能读懂项目结构、能修改代码、能跑构建命令。但集成不是简单调个API就完事。Claude Code有自己的会话管理、自己的工具集、自己的权限模型。你要把它嵌到自己的Harness里得解决几个问题会话隔离每个用户的任务应该是独立的Claude Code会话不能互相污染权限控制不能让Agent随便执行危险命令得有个白名单结果捕获Claude Code的输出是流式的你得实时解析并转成自己的事件格式错误处理Claude Code挂了、超时了、返回异常了你的Harness得能兜住我推测这个项目在Claude Code外面包了一层适配器把它的输入输出转成自己Harness的标准协议。这样上层逻辑不用关心底层用的是Claude Code还是别的什么Agent引擎换引擎只需要换适配器。4.2 Obsidian集成的真正价值Obsidian集成不只是能读写Obsidian库这么简单。它的深层价值在于把Obsidian变成了Agent的可视化界面。想象一下这个场景你让Agent帮你整理一周的工作笔记。Agent在后台跑了一堆操作读文件、分析内容、生成摘要、写入新文件。如果这些操作只发生在终端里你只能看到一堆日志。但如果Agent把结果写进Obsidian库你打开Obsidian就能看到新建了一篇周报.md里面是整理好的内容还自动加了标签和双链。更妙的是Obsidian的插件生态可以进一步扩展这个能力。比如用Dataview插件可以把Agent写入的结构化数据Markdown表格、frontmatter动态查询出来生成看板、日历、统计图。Agent负责生产数据Obsidian负责展示数据分工明确。关键词里还有obsidian创建项目管理台账这正好是一个典型用例Agent读取项目里的各种文件提取任务、进度、负责人写成Markdown表格Obsidian用Dataview把它渲染成台账视图。整个过程用户只需要说一句帮我更新项目台账。4.3 集成的技术细节文件监听与增量同步Obsidian库是本地文件夹Agent要读写它最直接的方式就是文件IO。但这里有个坑Obsidian自己也在监听文件变化。如果Agent写入文件的同时Obsidian正在读取可能读到半截内容。解决办法是原子写入先写临时文件写完再重命名覆盖。重命名在大多数文件系统上是原子操作Obsidian要么读到旧文件要么读到新文件不会读到中间状态。另一个问题是增量同步。如果Agent每次都要扫描整个库库大了会很慢。更好的做法是维护一个文件索引记录每个文件的修改时间和哈希只处理变化的文件。Obsidian自己有个.obsidian配置目录里面有些元数据可以利用但不要直接改它容易把用户的配置搞坏。实操心得和Obsidian集成时一定要尊重用户的库结构。不要擅自移动、重命名用户的文件除非用户明确要求。Agent写入新文件时最好放在一个专门的目录比如./agent-output/让用户自己决定要不要整理进主库。5. 单人扛20万行工程化上的取舍5.1 为什么代码量会这么大20万行听起来夸张但拆开看就合理了。一个完整的Harness应用至少包含这些模块核心循环引擎任务调度、状态机、错误恢复约2万行工具系统文件读写、命令执行、网络请求、Markdown解析约3万行上下文管理压缩、摘要、检索、缓存约2万行模型适配层对接不同模型API处理流式输出、重试、限流约2万行Claude Code适配器会话管理、权限、结果解析约1.5万行Obsidian集成文件监听、原子写入、索引约1.5万行UI层如果是桌面应用界面代码量很大约4万行配置与插件系统约2万行测试与工具脚本约2万行加起来正好20万左右。所以这个数字不是吹的是实打实的工程复杂度。5.2 单人开发的效率秘诀让AI写AI一个人九个月写20万行平均每天1000行这靠手敲不可能。合理的推测是这个项目大量使用了AI辅助编码。用Claude Code写Harness用Harness跑AgentAgent再反过来帮自己写代码。这是一个自我强化的循环。具体怎么操作我自己的经验是样板代码全交给AI工具函数的注册、API的封装、类型定义这些有固定模式的代码直接让AI生成自己只做review测试用例让AI写描述清楚输入输出AI生成的测试用例覆盖度往往比人写的还高重构让AI做把要重构的文件丢给AI说清楚目标结构它能把拆分、改名、更新引用一次做完文档让AI补代码写完后让AI生成注释和README自己改改就行但有个前提你得能看懂AI写的代码。如果AI写错了你发现不了那效率反而更低。所以核心架构必须自己设计关键路径必须自己写AI只负责填充细节。5.3 那些没人告诉你的坑坑一Agent循环会失控。模型有时候会陷入死循环反复执行同一个动作。比如读文件失败它不换策略而是一直重试读同一个文件。解决办法是加循环检测记录最近N次动作如果发现重复模式强制中断并让模型换策略。坑二token成本会爆炸。前面算过一个任务可能烧20万token。如果不加控制用户随便跑几个任务成本就上去了。必须加预算控制每个任务设token上限超了就停告诉用户任务太复杂请拆分。坑三模型输出不稳定。同一个prompt今天输出这个格式明天输出那个格式。解决办法是输出校验重试解析失败时把错误信息喂回给模型让它重新输出。通常重试一两次就能对。坑四文件并发写冲突。多个Agent同时写同一个文件后写的覆盖先写的。解决办法是文件锁写之前先加锁写完释放。简单实现可以用一个.lock文件复杂点可以用操作系统的文件锁。坑五Obsidian库太大导致扫描慢。几万个文件的库全量扫描要几十秒。解决办法是增量索引第一次全量扫描后存下索引之后只扫描修改时间变化的文件。Obsidian的文件系统事件API可以帮你做到实时监听。6. 从40亿token里省出真金白银成本控制的实战手段6.1 上下文压缩的几种策略对比前面提到上下文是token消耗的大头这里展开讲几种压缩策略的实际效果。策略做法压缩率信息损失适用场景截断只保留最近N轮高高短任务摘要用模型总结老对话中中长对话检索只取相关片段高低大知识库结构化把对话转成结构化状态高低任务型Agent这个项目大概率是组合使用。比如最近5轮保留原文5轮之前的做摘要涉及文件内容的用检索只取相关段落任务状态用结构化JSON维护。摘要策略有个细节摘要本身也要花token。你不能每轮都重新摘要一遍那样成本更高。合理做法是当上下文超过阈值时把最老的一半对话摘要一次然后继续。这样摘要的频率是O(log n)级别的。6.2 模型选择的性价比账不是所有任务都需要最贵的模型。一个典型的Harness任务里不同环节对模型能力的要求不一样意图理解需要强模型因为要准确解析用户想干什么工具选择中等模型就够因为工具列表是固定的选择空间有限结果分析看任务复杂度简单任务用便宜模型复杂分析用强模型格式整理便宜模型完全够用就是个体力活所以一个优化的Harness应该支持多模型路由根据任务类型自动选择模型。这样能把成本降下来一大截。我自己的经验是合理路由能省30%到50%的token成本。6.3 缓存被低估的省钱利器很多模型API支持prompt缓存如果两次请求的前缀相同第二次的前缀部分不计费或打折。Harness场景下系统prompt、工具定义、历史对话这些前缀经常是重复的缓存命中率可以很高。要利用缓存关键是保持前缀稳定。不要把变化的内容比如当前时间、随机ID放在prompt开头那样每次缓存都失效。正确的做法是固定的系统prompt和工具定义放最前面然后是历史对话最后才是当前用户输入。这样前缀尽可能长地保持一致。实操心得缓存这东西用好了能省一半钱用不好一点效果没有。建议在开发阶段就加日志记录每次请求的缓存命中情况根据数据调整prompt结构。7. 这套架构能复用到哪些场景7.1 知识管理自动化这是最直接的应用。Agent读取你的笔记库自动整理、分类、生成摘要、建立双链。比如每天结束时Agent扫描当天修改的笔记生成一篇日报标注关键决策和待办事项。Obsidian负责展示Agent负责生产。7.2 代码仓库维护Agent定期扫描代码库发现TODO、FIXME、过时的注释生成清理任务。或者监控依赖更新自动跑测试生成升级报告。Claude Code在这里能发挥很大作用因为它本身就擅长代码操作。7.3 文档工作流关键词里有markdown转word工作流这是一个很实际的需求。Agent可以把Markdown文档转成Word、PDF自动加目录、页眉页脚、样式。反过来也可以把Word转成Markdown方便版本控制。这个流程用Harness来做比手动操作或者写死脚本要灵活得多。7.4 个人项目管理用Obsidian建项目台账Agent负责更新状态。你只需要在聊天里说把任务A标记为完成Agent就去改对应的Markdown文件更新表格可能还顺便调整一下看板视图。这比打开Obsidian手动改要快得多。8. 如果让我重新做一遍我会怎么调整第一先把工具协议定死。我一开始可能会想边做边改但工具协议是Agent和Harness之间的契约改一次就要改一堆解析代码。不如一开始就设计得宽松一点、扩展性强一点。第二上下文管理要早做。不要等到token账单爆炸了才想起来压缩。第一天就要把滑动窗口、摘要、检索这套机制搭起来哪怕一开始策略简单也比没有强。第三日志要详细。Agent的每一步决策、每一次工具调用、每一个token消耗都要记下来。出问题的时候这些日志是唯一的线索。我建议用结构化的日志格式比如JSON Lines方便后续分析。第四测试要覆盖边界情况。模型输出格式错误、工具执行失败、网络超时、文件不存在这些情况在开发阶段可能遇不到但上线后一定会遇到。提前写好测试比事后救火强。第五不要追求一次做完美。20万行代码不是一天写成的。先跑通最小闭环一个工具、一个模型、一个任务。然后逐步加工具、加模型、加场景。每加一个东西确保它不影响已有的功能。最后分享一个我踩过的坑不要用模型来判断任务是否完成。模型有时候会幻觉完成明明没做完却说做完了。更可靠的做法是用代码来校验任务结果。比如任务要求生成一个文件那就检查文件是否存在、内容是否符合预期。代码说完成了才算完成模型说的不算。这套Harness架构的价值不在于它有多复杂而在于它把模型能力和工程可靠性结合起来了。模型负责理解和生成Harness负责执行和校验。两者配合才能做出真正能用的Agent应用。一个人九个月能做出这样的东西靠的不是蛮力是对这套架构的深刻理解和大量自动化工具的辅助。
返回列表