ARTICLE DETAIL

资讯详情

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

Harness架构实战:一个人九个月20万行代码的工业级Agent工程之道

Harness架构实战:一个人九个月20万行代码的工业级Agent工程之道 1. 先搞清楚这个项目到底在造什么一个人、九个月、20万行代码、每月40亿 token的消耗量——这几个数字摆在一起任何一个写过代码的人都会先愣一下。20万行代码如果按常规业务系统来算大概是一个十人团队干一年半的产出而每月40亿token的调用量意味着这套系统几乎每时每刻都在和模型对话不是那种用户点一下才调一次的轻量应用而是一个持续运转、自主决策的Agent系统。这个项目的核心关键词是Harness架构。如果你最近在Agent开发圈子里泡过应该对这个词不陌生——它指的是一种把模型当作被约束的执行单元用外部框架去驱动、校验、纠偏的工程范式。和早期那种给模型一个prompt让它自己跑的裸奔式Agent不同Harness强调的是框架层对模型行为的强约束模型负责生成框架负责判断、重试、回滚、记录、编排。模型是发动机Harness是底盘、变速箱和刹车。那这套东西到底解决什么问题简单说就是让AI Agent从玩具变成能干活的工具。裸奔式Agent最大的毛病是跑三步就开始飘上下文一长就忘事遇到工具调用失败就卡死产出质量全凭运气。而Harness架构要做的是把这些不确定性用工程手段兜住——用状态机管理流程、用校验器卡住输出、用重试机制处理失败、用持久化存储对抗上下文丢失。这个项目适合谁来参考三类人一是正在做Agent产品但被稳定性折磨的开发者二是想理解工业级Agent和Demo级Agent差距在哪的技术负责人三是对Harness工程之道感兴趣、想看看真实项目里这套理念怎么落地的人。下面我会从架构设计、token消耗的真相、Markdown与Obsidian的集成、以及踩过的坑这几个角度把这个项目拆开讲。2. Harness架构的核心把模型关进笼子里干活2.1 为什么裸奔式Agent一定会崩先说一个我自己的观察几乎所有Agent项目在Demo阶段都很惊艳一到真实场景就拉胯。原因不复杂——Demo阶段的输入是精心设计的模型只要走对一次就行真实场景的输入是脏的、长的、带歧义的模型走错一步后面全盘皆输。裸奔式Agent的典型结构是一个system prompt 一堆工具定义 一个while循环。模型输出工具调用框架执行把结果塞回上下文再让模型继续。这个结构在3-5步的短任务里能跑一旦任务超过10步问题就来了上下文爆炸每一步的工具返回都塞进历史几千token的任务很快变成几万token模型开始忘记最早的目标。错误累积第3步调用失败模型可能在第4步基于错误结果继续推理越走越偏。状态丢失进程一重启整个任务状态归零用户得从头再来。无法审计出了错你根本不知道是哪一步、哪个决策导致的只能看一堆日志猜。Harness架构的本质就是针对这四个问题各下一刀。2.2 Harness的四层结构拆解这个项目里Harness被拆成了四层我按自己的理解重新梳理一下第一层是编排层Orchestration。这一层管的是任务怎么走。它不关心模型说什么只关心当前处于哪个状态、下一步该触发什么动作。实现上通常是一个显式的状态机每个状态对应一个明确的输入输出契约。比如解析需求状态只接受用户原始输入输出必须是结构化的任务列表执行子任务状态接受单个任务输出必须是执行结果或失败原因。状态之间的转移由代码控制不由模型自由发挥。第二层是约束层Constraint。这一层管的是模型能说什么。每个状态对模型的输出格式都有硬性要求比如必须是合法JSON、必须包含特定字段、字段值必须在枚举范围内。模型输出后框架先校验不合格就打回重试重试N次还不行就降级或报错。这一层是Harness和裸奔式Agent最大的区别——模型没有随便说的自由。第三层是记忆层Memory。这一层管的是什么该记住。不是所有历史都值得塞回上下文Harness会做主动的记忆管理把已完成子任务的详细过程压缩成摘要只保留结论把关键决策点单独存成结构化记录把工具调用的原始返回存到外部存储需要时再按需检索。这样上下文长度就能控制在可控范围内而不是线性膨胀。第四层是执行层Execution。这一层管的是工具怎么调。包括工具注册、参数校验、超时控制、失败重试、结果归一化。工具调用失败不是简单地把错误信息塞回给模型而是先判断失败类型网络超时可以自动重试参数错误要返回明确的修正提示权限问题直接终止任务。这些判断逻辑都在框架层不依赖模型自己想明白。2.3 状态机设计里最容易踩的坑状态机听起来简单实际设计时坑很多。我踩过最深的一个是状态粒度过细。一开始我把每个小动作都设成一个状态结果状态图变成了蜘蛛网转移条件复杂到没法维护而且模型在每个状态之间切换时都要重新理解上下文token消耗反而更高。后来调整成粗粒度状态 状态内子步骤的结构状态数量控制在10个以内每个状态内部允许模型做多步推理但状态的进入和退出有严格契约。这样既保证了流程可控又给了模型足够的发挥空间。另一个坑是状态转移的触发条件。如果完全由模型决定我完成了可以进入下一状态那模型很容易过早宣布完成。正确做法是让框架来判断比如执行子任务状态的退出条件是所有子任务都有明确的成功或失败标记这个判断由代码做模型只负责产出结果。3. 每月40亿token到底烧在哪了3.1 token消耗的构成拆解40亿token一个月平均每天1.3亿按24小时算每秒1500token左右。这个量级听起来吓人但拆开看就合理了。我根据这类系统的常见消耗结构估算一下消耗项占比估算说明系统提示词15%-20%每次调用都要带长系统提示词是隐形杀手历史上下文30%-40%多轮对话累积是最大的变量工具返回结果20%-25%文件内容、搜索结果、API返回模型生成15%-20%实际输出的token重试与校验5%-10%格式不合格打回重试的额外消耗看到没真正有用的模型生成只占15%-20%剩下80%都是上下文和框架开销。这就是Harness架构必须解决的核心成本问题——不是让模型少说话而是让框架少塞东西。3.2 上下文压缩的三种实战策略这个项目里用了三种压缩策略我按效果从高到低排第一种是摘要替换。当一个子任务完成后把整个过程可能几千token压缩成一段200字以内的结论原始过程存到外部。这样历史上下文里只留结论需要细节时再检索。实测这一招能砍掉40%左右的历史token。第二种是结构化裁剪。工具返回的原始结果往往包含大量冗余比如一个JSON里只有两个字段有用框架在塞回上下文前先做字段提取只保留相关部分。这一招对文件读取类工具效果特别明显一个1000行的文件读进来可能只需要其中50行。第三种是滑动窗口 关键锚点。不是简单丢弃最早的历史而是保留关键决策点比如用户的核心需求、已经确认的方案丢弃中间的探索过程。实现上需要给每条消息打标记标记为锚点的永不丢弃其他的按窗口滑动。提示压缩策略一定要可配置、可回滚。我一开始把压缩做得太激进结果模型丢失了关键上下文产出质量断崖式下跌。后来改成压缩后如果校验不通过自动回退到未压缩版本重试稳定性才上来。3.3 系统提示词的瘦身经验系统提示词是每次调用都要带的固定开销一个5000token的系统提示词调用100万次就是50亿token。这个项目里系统提示词被压到了1500token以内做法是把通用行为规范和当前任务指令分离通用部分尽量短任务部分动态注入。用结构化格式JSON schema代替自然语言描述工具模型理解成本更低token也更少。把示例从系统提示词里挪到few-shot消息里只在需要时注入。这里有个反直觉的点系统提示词不是越长越好。我做过对比测试同一个任务3000token的系统提示词和1500token的成功率只差2个百分点但token成本差一倍。那2个百分点完全可以通过重试机制补回来。4. Markdown、Obsidian与Agent的集成实战4.1 为什么选Markdown作为中间格式这个项目的产出大量以Markdown形式落地原因很实际Markdown是人和模型都能高效读写的格式。模型生成Markdown的准确率远高于生成HTML或富文本人阅读Markdown也不需要额外工具。而且Markdown天然适合做版本控制每次产出都能diff。但Markdown有个坑换行和空格的语义在不同解析器里不一致。比如两个空格加换行在有些解析器里是软换行有些是硬换行表格的对齐语法各家实现也有差异。项目里的做法是定义一套内部Markdown规范生成时严格遵守解析时用统一的解析器避免生成的和解析的对不上。4.2 Obsidian作为知识沉淀层的价值Obsidian在这个项目里扮演的是长期记忆外部存储的角色。Agent产出的所有结构化笔记、决策记录、任务总结都以Markdown文件形式存进Obsidian库。这样做有几个好处双向链接Obsidian的[[链接]]语法让笔记之间能建立关联Agent可以通过链接关系做知识检索比纯向量检索更精准。本地优先所有数据在本地不依赖外部服务隐私和可控性都好。插件生态Dataview插件能把笔记当数据库查询Agent可以用它做结构化检索。实际集成时Agent通过文件系统直接读写Obsidian库的Markdown文件不需要走Obsidian的API。写入时注意保持frontmatter格式规范这样Dataview才能正确索引。4.3 从Zotero到Obsidian的笔记流转热词里出现了如何将zotero的笔记导入obsidian这其实是这类知识管理Agent的常见需求。Zotero的笔记导出成Markdown后格式往往很乱——引用标记、多余空行、不规范的标题层级。项目里的处理流程是Zotero导出为Markdown用Better BibTeX插件。Agent读取原始Markdown做格式清洗统一标题层级、移除冗余引用标记、规范化列表缩进。按主题分类写入Obsidian对应文件夹并自动生成双向链接。用Dataview生成索引页方便后续检索。这个流程的关键是清洗规则要可配置因为不同人的Zotero笔记格式差异很大硬编码规则很快就会失效。5. 九个月20万行代码的工程真相5.1 代码量为什么这么大20万行代码如果全是业务逻辑那确实夸张。但Agent项目的代码构成很特殊框架层状态机、校验器、重试机制、记忆管理这部分大概占30%。工具层每个工具的定义、参数校验、结果处理工具越多代码越多占25%。提示词与配置如果把提示词模板、配置schema都算进去占15%。测试Agent系统的测试极其繁琐要模拟各种模型输出、各种失败场景占20%。胶水代码各种格式转换、适配器、工具函数占10%。真正核心逻辑可能就几万行剩下都是让系统稳定运转的必要开销。这也是为什么一个人九个月能写出20万行——大量代码是模式化的、可复制的不是每行都需要深度思考。5.2 一个人做Agent项目的节奏控制一个人干九个月最大的挑战不是技术是节奏。我的经验是分三个阶段前三个月做骨架。不要急着接模型先把状态机、校验器、记忆管理这些框架层的东西搭起来用mock模型跑通流程。这个阶段产出慢但决定了后面能不能规模化。中间三个月做工具和集成。把需要的工具一个个接进来每接一个就写测试、跑通端到端。这个阶段最容易失控——工具是无限的必须有明确的优先级只做当前任务真正需要的。后三个月做优化和打磨。压缩上下文、优化提示词、处理边界情况、补测试。这个阶段的产出最不明显但对最终质量影响最大。注意千万不要在框架没搭好之前就大量接工具。我早期犯过这个错接了十几个工具后发现状态机设计有问题全部推倒重来浪费了一个多月。5.3 并发与稳定性Agent怎么扛住真实流量热词里有ai agent 怎么扛并发这是Agent从Demo走向生产必须过的坎。这个项目里的做法是无状态化Agent的每个任务状态都存在外部存储数据库或文件进程本身无状态可以水平扩展。任务队列所有任务进队列worker从队列取任务执行避免请求直接打到模型。限流与降级模型调用有速率限制超限时任务排队而不是失败模型服务不可用时降级到备用模型或返回明确错误。幂等设计每个任务有唯一ID重复提交不会重复执行这对重试场景很重要。实测下来这套结构在单机4个worker的情况下能稳定处理每秒几十个任务瓶颈主要在模型API的速率限制上不在框架本身。6. 那些文档里不会写的踩坑记录6.1 模型输出格式的薛定谔稳定性你以为定义了JSON schema模型就会乖乖输出JSON太天真了。实测下来即使schema写得很清楚模型仍有5%-10%的概率输出格式不对——多一个逗号、少一个引号、字段名拼错、该是数组的给了对象。这些错误在Demo阶段可能一次都遇不到一到生产就集中爆发。项目里的应对是三层校验第一层用正则做快速格式检查第二层用JSON parser做结构校验第三层用schema validator做语义校验。任何一层不过就打回重试重试时把具体的错误信息告诉模型你的输出第3行缺少闭合引号比笼统说格式错误有效得多。6.2 工具调用的超时与重试陷阱工具调用失败是常态但盲目重试是灾难。我踩过的坑一个文件写入工具因为路径问题失败框架自动重试了5次每次都失败5次重试消耗的token比正常执行还多而且最后任务还是失败了。正确的做法是按失败类型决定策略失败类型处理策略网络超时自动重试2-3次指数退避参数错误不重试返回明确修正提示给模型权限问题不重试直接终止任务并报告资源不存在重试1次仍失败则让模型换方案模型格式错误重试附带具体错误信息这个策略表是项目里最值钱的东西之一它把重试从一个盲目动作变成了有判断的决策。6.3 上下文丢失导致的失忆问题Agent跑长任务时最诡异的现象是前面明明确认过的需求跑到后面模型突然忘了开始按自己的理解瞎干。这不是模型的问题是上下文管理的问题——压缩策略把关键信息压没了或者滑动窗口把锚点滑出去了。解决办法是显式锚点机制用户的核心需求、已确认的关键决策、不可违背的约束这些信息打上永久锚点标记任何压缩策略都不能动它们。实现上就是在消息结构里加一个is_anchor字段压缩时先过滤出锚点消息永远保留。6.4 提示词版本管理的血泪史提示词改了效果变差了想回滚——结果发现没存旧版本。这种事我干过不止一次。后来强制要求所有提示词必须进版本控制每次修改都要记录改了什么、为什么改、改前改后的效果对比。提示词文件用Markdown写带frontmatter记录版本信息这样diff起来也方便。7. 从这套架构里能复用的经验7.1 什么场景适合上Harness不是所有Agent项目都需要Harness。如果你的任务步骤少于5步、失败率可以接受、不需要审计那裸奔式Agent就够了上Harness是过度工程。但如果你满足以下任意两条就该考虑Harness任务步骤超过10步且步骤之间有依赖。失败成本高不能接受跑一半崩了。需要审计和回溯要能说清楚每一步发生了什么。需要长期运行进程重启后任务要能恢复。有多个模型或多个工具需要编排。7.2 最小可行Harness的搭建顺序如果你想自己搭一套我建议的顺序是先做状态机用最简单的内存状态机把任务流程跑通。加校验层给每个状态的输出定义schema加校验和重试。加持久化把状态存到外部支持恢复。加记忆管理实现摘要替换和锚点机制。加工具层按需接工具每个工具配测试。加监控记录每步的token消耗、耗时、成功率。这个顺序的好处是每一步都有可运行的产出不会出现搭了三个月还跑不起来的情况。7.3 成本控制的几个硬指标最后分享几个我在项目里盯着的成本指标你可以直接拿去用单任务平均token消耗这是最核心的指标任何优化都应该让它下降。token消耗/有效产出比有效产出指最终被采纳的结果这个比值越低越好。重试率重试次数/总调用次数高于15%说明校验或提示词有问题。上下文压缩率压缩后token/压缩前token太低说明压缩不够太高说明可能丢信息。缓存命中率相同或相似请求的缓存命中比例这个直接省钱。我个人在实际操作中的体会是Harness架构的价值不在于让Agent更聪明而在于让Agent更可控。模型的能力在涨但工程上的确定性永远得靠框架来保证。一个人九个月能造出这套东西靠的不是什么黑科技就是把每个环节的不确定性一个个用工程手段摁住。这个过程很枯燥但跑通之后你会发现自己对Agent的理解和那些只会调API的人完全不在一个层次上。
返回列表