ARTICLE DETAIL

资讯详情

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

Harness架构实战:一人九个月20万行代码的AI Agent工程化之路

Harness架构实战:一人九个月20万行代码的AI Agent工程化之路 1. 先搞清楚这个项目到底在造什么一个人九个月20万行代码每个月烧掉40亿以上的token最终交付的是一款Harness架构的应用。这几个数字摆在一起任何一个写过代码的人都会先愣一下——不是因为20万行有多夸张而是因为“一个人”和“九个月”这两个限定词。正常来说20万行代码的工程量放在一个五人左右的团队里按一年周期算都算紧凑的更别提还要持续消耗如此规模的token去驱动Agent完成大量工作。所以这个项目的核心看点从来不是“代码行数”本身而是它验证了一条路径用Harness架构把AI Agent组织成一支可管理的工程队伍让一个人具备接近一个小团队的产出能力。这里的关键词是Harness。很多人第一次看到这个词会以为是某个具体框架的名字其实不是。Harness在AI Agent语境下指的是一套“约束与编排层”——它不负责模型推理本身而是负责把模型、工具、记忆、任务流、权限边界这些东西串起来让Agent的行为可控、可复现、可调试。这个项目要解决的问题很具体当你让一个Agent去改代码、查资料、写文档、跑测试的时候它会失控。它会忘记上下文会重复劳动会在错误的文件上动手会在没有权限的地方乱试。Harness就是那根缰绳。它决定了Agent能看到什么、能做什么、做完之后结果怎么回流、失败了怎么重试、多个Agent之间怎么分工。适合谁来读这篇内容三类人。第一类是自己动手写过Agent脚本、但发现越写越乱、维护成本飙升的开发者。第二类是正在用Claude Code、Obsidian这类工具做个人知识管理和自动化工作流、想进一步系统化的重度用户。第三类是对Agent架构感兴趣、想知道“一个人怎么撑起一个项目”的独立开发者。如果你只是想让AI帮你写个周报这篇内容可能偏重了但如果你想搞清楚Agent工程化的真实门槛在哪里下面的内容应该对你有用。2. 为什么是Harness架构而不是直接堆Agent2.1 裸Agent的三个致命问题我最早接触Agent开发的时候思路很朴素写一个循环让模型读任务、调工具、拿结果、再决策直到任务完成。这个模式跑demo没问题但一旦任务变复杂三个问题会同时爆发。第一个是上下文漂移。Agent跑了十几轮之后早期的关键约束会被淹没在对话历史里。你明明在第三轮告诉它“不要动config目录”到第十五轮它已经忘了直接进去改了一个配置文件。这不是模型笨是上下文窗口的注意力分配问题。第二个是工具滥用。Agent手里有文件读写、命令执行、网络请求这些工具它会倾向于“多试几次”。一个简单的格式化任务它可能先读文件、再写文件、再读一遍验证、再写一遍修正token消耗翻好几倍而且中间任何一步出错都会污染后续状态。第三个是失败不可复现。同一个任务跑两次Agent走的路径可能完全不同。今天成功了明天换个时间跑就失败你根本不知道是哪一步的随机性导致的。这在个人玩具项目里可以忍但在一个要持续迭代九个月的项目里不可复现等于不可维护。2.2 Harness层到底加了什么Harness架构的本质是在Agent和任务之间插入一层确定性的控制逻辑。它做的事情可以拆成四块。任务分解与路由。不是让一个Agent从头干到尾而是把大任务拆成有明确输入输出的子任务每个子任务交给专门的Agent或专门的提示词模板处理。比如“重构某个模块”这个任务会被拆成“分析依赖关系”“生成修改方案”“执行修改”“运行测试”“生成变更说明”五个步骤每一步的Agent只关心自己那一段。状态外置。Agent的中间状态不放在对话历史里而是写到外部存储。这个项目里用的是Markdown文件加Obsidian库的组合。每一步的产出、决策、待办都落盘成Markdown下一步的Agent读文件而不是读历史。这样做的好处是上下文永远干净而且人可以直接打开Obsidian看到Agent在干什么、干到哪了。工具权限分级。不是所有Agent都能调所有工具。分析类Agent只能读不能写执行类Agent只能在指定目录写测试类Agent只能跑测试命令。这个权限矩阵是硬编码在Harness里的Agent绕不过去。失败重试与回滚。每一步执行前先做快照失败之后按预设策略重试重试次数用完就回滚到上一个稳定状态并生成一份失败报告。这份报告本身也是Markdown人可以直接读。2.3 为什么不用现成的Agent框架市面上Agent框架不少但这个项目最终选择自建Harness层原因很实际。现成框架的抽象层次往往和实际需求错位——它们要么太通用什么都能做但什么都不精要么太封闭想改一个重试逻辑要翻半天源码。而且这个项目重度依赖Claude Code作为执行引擎Claude Code本身有一套自己的工作方式硬套一个外部框架反而增加摩擦。自建Harness的代价是要写很多“胶水代码”但收益是每一层逻辑都透明。九个月里这套Harness被改了上百次每次改都是因为实际跑的时候发现了新的边界情况。如果用现成框架很多改动根本做不了只能等上游更新。提示自建Harness不等于从零造轮子。这个项目里大量复用了Claude Code的原生能力、Obsidian的插件生态、以及Markdown作为中间格式的通用性。Harness层只负责编排不重复实现底层功能。3. 20万行代码是怎么堆出来的3.1 代码构成拆解20万行这个数字听起来吓人但拆开看就合理了。这个项目里真正手写的核心逻辑大概只占15%左右剩下的是几类内容。代码类型占比说明Harness核心逻辑约15%任务路由、状态管理、权限控制、重试机制Agent提示词模板约25%每个子任务对应的提示词含大量边界情况说明工具适配层约20%把Claude Code、Obsidian、文件系统、测试框架接进来测试与验证代码约25%每个Harness模块的单元测试、集成测试、回归测试文档与配置约15%Markdown格式的架构说明、配置模板、变更日志提示词模板占了四分之一这个比例很多人想不到。但在Harness架构里提示词就是代码。一个子任务的提示词要写清楚输入格式、输出格式、可用工具、禁止行为、失败处理方式写全了就是几百行。几十个子任务加起来体量自然就上去了。3.2 token消耗的真实去向每个月40亿以上的token平均到每天大概1.3亿。这个量级听起来夸张但拆到每个Agent调用上就正常了。一次典型的子任务执行输入上下文可能就有几万token——包括任务说明、相关文件内容、历史决策记录、工具定义。输出可能几千token。如果这个子任务要重试三次消耗直接翻三倍。token消耗的大头不在模型推理本身而在上下文组装。Harness每一步都要把当前任务需要的所有信息拼成一个完整的提示词这个拼接过程会重复读取大量文件。优化token消耗的核心就是优化上下文组装策略——哪些信息必须带、哪些可以摘要、哪些可以按需加载。这个项目里用了一个很实用的技巧分层上下文。把上下文分成三层。第一层是永远携带的核心约束几百token。第二层是当前任务相关的文件摘要按需加载。第三层是完整文件内容只在Agent明确请求时才注入。这样大部分调用的上下文能控制在合理范围内只有少数复杂任务才会吃满窗口。3.3 九个月的时间分配九个月听起来很长但实际分配下来很紧。前两个月基本在搭Harness骨架和调试基础流程中间四个月是主力开发期最后三个月在做稳定性打磨和边界情况处理。真正“写新功能”的时间大概只占一半另一半都在修bug、优化提示词、处理Agent跑飞的情况。这个时间分配比例很真实。任何做Agent工程的人都会告诉你Agent的不可预测性决定了调试时间远超预期。一个在demo里跑得好好的流程换一个输入就崩你得反复复现、定位、加约束、再测试。九个月里光是处理“Agent在某个特定文件上反复失败”这类问题就花掉了大量时间。4. 核心实操Harness架构的落地步骤4.1 第一步定义任务边界与状态格式动手写代码之前先把两件事定下来。第一是任务边界——这个Harness要处理哪些类型的任务不处理哪些。第二是状态格式——Agent的中间状态用什么结构存。这个项目里任务边界定得很窄只处理“代码修改、文档生成、资料整理”三类任务。其他的一律不走Harness人工处理。这个窄边界是刻意的因为边界越窄Harness能做的约束就越具体Agent跑飞的概率就越低。状态格式用的是Markdown加YAML frontmatter。每个任务对应一个Markdown文件frontmatter里放结构化字段任务ID、状态、依赖、重试次数正文放自然语言描述和产出内容。选Markdown的理由很直接人可读、Obsidian可直接渲染、Git可diff、Agent读写都方便。--- task_id: refactor-module-a status: in_progress depends_on: [analyze-deps] retry_count: 1 --- ## 任务说明 重构module-a的依赖注入方式改为构造函数注入。 ## 当前产出 Agent写入的内容4.2 第二步搭建任务路由与调度Harness的核心是一个调度循环。它读任务队列按依赖关系取出可执行的任务组装上下文调用Agent处理结果更新状态然后取下一个。调度逻辑本身不复杂复杂的是异常处理。Agent可能返回格式错误的结果、可能超时、可能调用了不该调用的工具。每一种异常都要有对应的处理分支。这个项目里异常处理代码占了Harness核心逻辑的近一半。一个实用的设计是状态机。每个任务在任意时刻只处于一个明确的状态pending、running、blocked、failed、done。状态之间的转换有严格规则比如running只能转到done或failed不能直接跳回pending。这样任何异常都能被定位到具体状态转换上。4.3 第三步接入Claude Code作为执行引擎Claude Code在这个架构里扮演“执行手”的角色。Harness负责决策和编排Claude Code负责实际动手。接入方式是通过命令行调用Harness把组装好的提示词和工具权限传给Claude Code拿回执行结果。这里有几个实操要点。第一每次调用都是无状态的。不要指望Claude Code记住上一次调用的上下文所有需要的信息都要在本次调用的提示词里带全。第二工具权限要显式声明。Claude Code默认能做的事情很多Harness要在调用时明确限制它能碰哪些目录、能跑哪些命令。第三输出格式要强约束。要求Claude Code按指定格式返回结果Harness才能可靠解析。注意Claude Code的调用有速率限制和并发限制。这个项目里Harness做了请求队列和退避重试避免因为触发限制导致整个流程卡死。4.4 第四步用Obsidian做状态可视化Obsidian在这个项目里的角色是“驾驶舱”。所有任务状态、产出、失败报告都以Markdown形式存在Obsidian库里人打开Obsidian就能看到全局。这个设计的好处是调试成本极低。Agent跑飞了不用去翻日志文件直接在Obsidian里打开对应的任务文件看它写到哪一步、产出了什么、报了什么错。Obsidian的图谱视图还能直观看到任务之间的依赖关系哪个任务卡住了、卡在谁身上一目了然。实操上Harness每更新一个任务状态就同步写一次Markdown文件。Obsidian会自动检测文件变化并刷新。如果需要更实时的反馈可以配一个简单的文件监听脚本状态一变就触发Obsidian刷新。4.5 第五步建立重试与回滚机制重试不是简单地再跑一遍。这个项目里的重试策略分三级。第一级是原样重试适用于网络抖动、临时超时这类问题最多重试两次。第二级是降级重试把任务拆得更细或者换一个更保守的提示词模板再试。第三级是回滚加人工介入回滚到上一个稳定状态生成失败报告暂停后续依赖任务等人处理。回滚的实现依赖快照。每个任务执行前Harness会对涉及的文件做一次快照用Git stash或文件复制。失败回滚时恢复快照保证状态干净。这个机制在九个月里救了无数次场尤其是Agent改坏了多个文件的时候。5. 踩过的坑与排查实录5.1 Agent反复修改同一个文件这是最常见的问题。Agent改完一个文件读回来验证觉得不对又改再读再改陷入循环。根因通常是提示词里没有明确“完成标准”。Agent不知道什么算改好了就只能反复试。解决办法是在提示词里写死完成条件。比如“当文件通过lint检查且测试全部通过时任务完成不要再修改”。同时Harness层面加一个修改次数上限超过就强制终止并报错。5.2 上下文组装导致token爆炸早期版本里Harness把整个Obsidian库的相关文件都塞进上下文一次调用轻松超过十万token。后来改成按需加载加摘要token消耗降了一个数量级。具体做法是先让一个轻量Agent读任务描述判断需要哪些文件只加载这些文件的摘要。执行Agent拿到摘要后如果需要完整内容再显式请求。这个两段式加载策略是token优化的关键。5.3 Markdown格式被Agent写坏Agent有时候会生成不合法的Markdown比如表格列数不对、frontmatter缺字段、代码块没闭合。这会导致Obsidian渲染异常也会让Harness解析失败。应对方式是格式校验前置。Agent产出后Harness先用一个校验器检查Markdown结构不合法就打回重写。校验器本身很简单检查frontmatter字段完整性、代码块配对、表格列数一致性这几项就够覆盖大部分问题。5.4 常见问题速查表问题现象可能原因排查方向解决方式Agent卡住不动任务依赖未满足检查depends_on字段手动完成依赖或调整依赖关系输出格式错误提示词约束不足查看提示词模板补充格式示例和校验规则token消耗异常高上下文组装过载检查加载了哪些文件启用分层上下文和摘要任务反复失败完成标准不明确查看失败报告明确完成条件加修改次数上限Obsidian不刷新文件写入未触发监听检查文件路径和权限确认写入路径在库内重启监听5.5 几个反直觉的经验第一个经验是提示词越具体越好但不要写死所有情况。写太死Agent遇到边界情况不会变通写太松Agent乱来。平衡点在于核心约束写死执行细节给示例但不强制。第二个经验是不要追求100%自动化。这个项目里大概有20%的任务需要人工介入。强行追求全自动只会让Harness越来越复杂最后维护不动。留出人工接口反而让整体更稳。第三个经验是日志要写给人看不是写给机器看。失败报告用自然语言写清楚“发生了什么、为什么失败、建议怎么处理”比一堆结构化字段有用得多。人在排查的时候读一段清晰的描述比解析JSON快得多。6. 这套架构还能怎么扩展Harness架构本身是领域无关的。这个项目用它做代码和文档换一个领域只要把工具适配层和提示词模板换掉核心调度逻辑可以复用。一个自然的扩展方向是多Agent协作。当前架构里主要是单Agent串行执行如果任务之间没有依赖可以并行跑多个Agent。Harness需要增加并发控制和资源锁避免多个Agent同时改同一个文件。另一个方向是引入更细粒度的权限模型。当前是目录级权限未来可以做到文件级甚至函数级。这样Agent能做的事情更精确跑飞的影响面更小。还有一个方向是把Harness本身也Agent化。让一个元Agent监控整个流程发现异常时自动调整策略而不是等人来改配置。这个方向风险较高因为元Agent的决策可能引入新的不可预测性需要非常谨慎地设计约束。我个人在实际操作中的体会是Harness架构的价值不在于它让Agent变聪明了而在于它让Agent的愚蠢变得可管理。Agent一定会犯错关键是犯错之后系统能不能兜住、能不能恢复、能不能让人快速定位。把这三点做好一个人撑起一个项目就不是神话而是工程上的必然结果。
返回列表