ARTICLE DETAIL

资讯详情

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

Harness架构实战:一个人九个月20万行代码构建智能体系统

Harness架构实战:一个人九个月20万行代码构建智能体系统 1. 先搞清楚这个标题到底在说什么一个人、九个月、20万行代码、每月40亿 token——这几个数字摆在一起任何一个写过代码的人都会先愣一下。20万行代码如果按一个成熟工程师每天有效产出200行来算需要1000个工作日也就是接近三年。而这里只有九个月还是一个人。更离谱的是每月40亿 token的消耗量这个量级意味着背后有一个持续运转、高频调用的智能体系统在支撑而不是那种跑一次就停的脚本。这个项目的核心是围绕Harness 架构构建一款应用。Harness 这个词在当下的开发语境里指的是一套把大模型能力、工具调用、上下文管理、任务编排串起来的运行骨架。你可以把它理解成一个智能体的操作系统——模型是CPUHarness 是主板和总线负责把记忆、工具、文件系统、外部服务全部接起来让智能体能够长时间、多步骤地完成复杂任务。关键词里出现的 Markdown、Agent、Claude Code、Obsidian其实已经把这个项目的技术栈轮廓勾出来了用 Markdown 作为知识载体和交互界面用 Agent 作为执行主体用 Claude Code 这类命令行智能体工具作为开发与运行环境用 Obsidian 作为本地知识库和可视化层。这套组合不是随便凑的它对应的是一个非常具体的需求场景——个人知识工作者或独立开发者想要一个能长期陪伴、持续积累、自动干活的智能体系统。这篇文章适合谁看如果你正在琢磨怎么把大模型从聊天玩具变成生产力工具如果你对 Agent 开发、Harness 架构、本地知识库集成有兴趣或者你只是好奇一个人怎么在九个月里堆出20万行代码还烧掉那么多 token那这篇内容应该能给你一些实在的参考。我不会只讲概念会把架构选型、token 消耗的构成、Markdown 作为核心载体的原因、以及实际开发中踩过的坑都摊开来说。2. 为什么是 Harness 架构而不是直接调 API2.1 裸调 API 的天花板在哪里很多人做 AI 应用的第一步就是写个函数调一下模型接口把用户输入塞进去拿回输出展示出来。这个模式在简单问答场景下没问题但一旦任务变复杂问题就全冒出来了。最直接的问题是上下文断裂。裸调 API 每次请求都是独立的模型不记得上一轮干了什么。你可能会说那就把历史消息都带上呗。但历史一长token 消耗就爆炸而且模型对超长上下文的注意力会稀释早期关键信息容易被忽略。更麻烦的是当任务需要调用外部工具——比如读文件、查数据库、执行命令——裸调 API 根本没有这套机制你得自己在外层写一堆胶水代码来解析模型的意图、执行动作、再把结果塞回去。还有一个隐性成本状态管理。一个持续运行的应用需要知道当前任务进行到哪一步、哪些文件被修改过、哪些工具调用失败了需要重试。这些状态如果全靠业务代码维护很快就会变成一团乱麻。Harness 架构的价值就是把这些脏活累活收拢到一个统一的运行时里。2.2 Harness 到底管了什么用生活化的类比来说裸调 API 像是你每次要用车都临时去租一辆还得自己加油、自己导航、自己处理违章。Harness 则是你自己的车库加司机加调度中心车随时待命路线自动规划出了问题有人兜底。具体到技术层面一个 Harness 架构通常包含这几个核心模块上下文管理器决定每一轮给模型看什么、不看什么。它不是简单地把所有历史都塞进去而是做摘要、做检索、做优先级排序。比如最近几轮对话保留原文更早的内容压缩成摘要相关的文件内容按需注入。工具注册与调度层把文件读写、命令执行、网络请求、数据库查询等能力封装成模型可以调用的工具并处理调用参数校验、超时、重试、结果格式化。任务编排引擎支持多步骤任务的拆解与执行能够根据上一步的结果决定下一步做什么支持循环、条件分支、并行执行。持久化与状态存储把会话状态、任务进度、工具调用记录落盘保证进程重启后能恢复。安全与权限控制限制智能体能碰哪些文件、能执行哪些命令防止它把系统搞崩。这五块里任何一块自己手写都不难难的是让它们协同工作并且在长时间运行中保持稳定。20万行代码里很大一部分就是在处理这些模块之间的边界情况和异常路径。2.3 每月40亿 token 是怎么烧掉的40亿 token 听起来吓人但拆开看就合理了。假设系统每天运行16小时一个月30天总共480小时。40亿除以480每小时约833万 token每分钟约14万 token。这个量级意味着系统在持续进行模型调用而不是偶尔跑一下。消耗大头通常来自这几个方面消耗来源占比估算说明上下文注入40%-50%每轮请求都要带上系统提示、工具定义、相关文件片段、历史摘要工具调用往返20%-30%每次工具调用后要把结果送回模型继续推理任务规划与反思15%-20%复杂任务需要模型先规划再执行执行后还要检查结果重试与纠错5%-10%工具调用失败、输出格式不对时的重试开销关键洞察是token 消耗的大头不是用户输入而是系统为了维持智能体运转而注入的上下文。这也是为什么 Harness 架构里上下文管理器的设计直接决定了成本。一个优化不好的上下文管理器可能让 token 消耗翻三倍。3. Markdown 为什么成了这套系统的核心载体3.1 Markdown 不只是标记语言在这个项目里Markdown 承担的角色远超写文档的格式。它同时是知识存储格式、交互界面、以及智能体的工作产物。这个选择背后有很实际的考量。首先Markdown 是纯文本天然适合版本控制和差异对比。智能体修改了一个文件你可以直接用 git diff 看到改了哪几行而不像二进制格式那样两眼一抹黑。其次Markdown 的结构化程度刚好——它有标题层级、列表、表格、代码块足够让智能体理解内容的组织方式又不像 XML 或 JSON 那样对格式要求苛刻模型生成时不容易出错。更重要的是Markdown 是当下大模型最熟悉的格式之一。训练数据里海量的技术文档、README、笔记都是 Markdown模型对它的语法和语义有很好的直觉。你让模型输出 Markdown 表格它基本不会搞错你让它输出某种自定义格式它可能每次都要你纠正。3.2 Obsidian 作为可视化与检索层Obsidian 在这个架构里扮演的是人机接口的角色。智能体在后台读写 Markdown 文件人在 Obsidian 里浏览、编辑、建立双链。这种分工的好处是人不需要盯着智能体的每一步操作只需要在知识库层面做审核和补充。Obsidian 的双链和图谱功能恰好补上了智能体不擅长的一块——跨文档的语义关联。智能体可以生成大量内容但让它主动发现这篇笔记和三个月前那篇有关系并不容易。而 Obsidian 的链接机制和插件生态可以让人或者辅助脚本来做这件事。实际使用中有几个细节值得注意文件命名规范要统一。智能体读写文件时依赖路径如果命名混乱它很容易找不到目标文件或者创建重复文件。建议用日期前缀加主题的方式比如2024-06-15-harness-architecture.md。frontmatter 要结构化。在 Markdown 文件头部用 YAML 写元数据比如标签、状态、关联项目这样智能体可以通过解析 frontmatter 快速筛选文件而不需要读全文。避免过深的文件夹嵌套。Obsidian 的搜索和双链在扁平结构下效率更高智能体遍历文件时也不容易迷路。3.3 Markdown 表格与数据交换的坑热词里出现了markdown表格转换excel和markdown表格复制这说明实际使用中表格处理是个高频痛点。智能体生成 Markdown 表格很顺手但要把表格数据导入 Excel 或者从 Excel 导出就没那么直接了。我试过几种方案比较靠谱的是用 Python 的pandas做中转import pandas as pd from io import StringIO markdown_table | 模块 | 职责 | 优先级 | |------|------|--------| | 上下文管理 | 控制注入内容 | 高 | | 工具调度 | 执行外部调用 | 高 | | 状态存储 | 持久化进度 | 中 | df pd.read_csv(StringIO(markdown_table), sep|, skipinitialspaceTrue) df df.dropna(axis1, howall) df.columns df.columns.str.strip() df df.iloc[1:] print(df)反过来从 DataFrame 生成 Markdown 表格用df.to_markdown()就行但要注意中文字符宽度对齐问题在等宽字体下可能错位这是 Markdown 表格的固有限制不是代码的问题。提示如果表格里有竖线字符|必须转义成\|否则会破坏表格结构。智能体生成内容时经常忽略这一点需要在后处理里做校验。4. Agent 开发中的上下文管理与 token 优化4.1 上下文窗口不是越大越好很多人有个误区觉得模型支持的上下文窗口越大就把所有东西都塞进去。实际恰恰相反上下文越长模型对中间部分的注意力越弱而且成本线性增长。在每月40亿 token 的规模下上下文策略的微小调整都会带来巨大的成本差异。我的做法是分层管理上下文固定层系统提示、工具定义、核心规则。这部分每轮都要带但内容要精简到极致。工具定义能用一行说清楚就不用三行。近期层最近几轮对话的原文。通常保留3到5轮超过的压缩成摘要。检索层根据当前任务从知识库里检索相关文件片段注入。这里的关键是检索精度宁可少注入也不要注入无关内容。摘要层更早的历史压缩成一段话放在上下文末尾作为背景。这个分层策略的核心逻辑是把 token 预算花在模型当前最需要的信息上。固定层和近期层保证行为一致性检索层提供任务相关的具体知识摘要层维持长期连贯性。4.2 工具定义的瘦身技巧工具定义是上下文里的常驻开销。如果你注册了20个工具每个工具的描述加参数说明占200 token那一轮就是4000 token乘以每天成千上万次调用成本非常可观。几个实用的瘦身方法合并同类工具。比如读文件和写文件可以合并成一个文件操作工具用参数区分动作。这样描述只需要写一份。参数说明用简写。不需要每个参数都写完整句子用关键词加类型就够了。模型理解能力足够强不需要手把手的自然语言解释。动态加载工具。不是所有任务都需要所有工具。可以根据任务类型只注入相关工具的定义。比如纯文本处理任务就不需要注入数据库查询工具。我实测下来经过瘦身的工具定义可以从平均4000 token 降到1200 token 左右降幅超过70%而且模型调用工具的准确率没有明显下降。4.3 缓存与去重在长时间运行的系统里很多请求的上下文前缀是相同的。比如系统提示加工具定义这部分每轮都一样。如果模型服务支持前缀缓存这部分可以大幅降低成本。即使不支持缓存也可以在应用层做去重。比如把相同的文件内容片段做哈希如果这一轮和上一轮注入的是同一份内容就不重复发送而是用引用代替。这需要模型服务支持某种形式的引用机制或者你在提示里说明以下内容与上轮相同不再重复。另一个容易忽略的点是工具调用结果的精简。有些工具返回大量数据比如读取一个长文件直接把全文塞回模型会消耗大量 token。更好的做法是在工具层做预处理只返回模型真正需要的部分比如文件摘要、匹配到的段落、或者结构化后的关键信息。5. 九个月20万行代码的工程实践5.1 代码量背后的真实构成20万行代码听起来很多但拆开看真正的手写业务逻辑可能只占一半剩下的包括自动生成的代码比如从接口定义生成的类型声明、从配置生成的工具注册代码。测试代码单元测试、集成测试、端到端测试。在智能体系统里测试尤其重要因为行为不确定性高。文档与注释Markdown 文档、代码注释、架构说明。配置与脚本部署脚本、数据迁移脚本、运维工具。所以20万行这个数字更多是说明项目的复杂度和迭代强度而不是说每一行都是精雕细琢的手写逻辑。理解这一点对评估自己的工作进度很重要——不要被大数字吓到也不要盲目追求代码行数。5.2 一个人怎么组织开发节奏独立开发最大的挑战不是技术而是注意力的分配。九个月里如果什么都想做最后什么都做不完。我的经验是采用主线加支线的模式主线是核心功能的可用版本必须保持每周都有可运行的进展。支线是优化和扩展在不影响主线的前提下推进。具体到每天我会把时间分成三块上午处理需要深度思考的架构和算法问题下午写实现和调试晚上做测试和文档。还有一个关键习惯是每天结束前让系统跑一遍完整流程。智能体系统的很多问题只在长时间运行后才暴露比如内存泄漏、状态不一致、工具调用超时累积。每天跑一遍问题当天发现当天修不会拖成顽疾。5.3 版本控制与回滚策略在快速迭代中代码经常改坏。如果没有好的版本控制习惯很容易陷入改一个bug引入两个新bug的困境。我的做法是小步提交。每完成一个可独立验证的小功能就提交一次提交信息写清楚改了什么、为什么改。分支策略简单化。一个人开发不需要复杂的分支模型主分支保持可用新功能开短生命周期分支合并前跑通测试。关键节点打标签。比如第一个可用的工具调用版本上下文管理重构完成方便出问题时回退到已知稳定状态。注意智能体系统里配置文件的变更和代码变更同样重要。建议把配置也纳入版本控制并且每次变更配置后记录变更原因和预期影响。6. 实际运行中踩过的坑与排查思路6.1 工具调用失败的重试陷阱智能体调用工具失败是常态网络抖动、参数格式错误、目标服务不可用都会导致失败。最初我的做法是简单重试三次但很快发现这会导致重复执行副作用操作。比如发送邮件这个工具第一次调用其实成功了但返回超时重试就会发第二封。正确的做法是区分幂等操作和非幂等操作。读文件、查询数据库是幂等的可以放心重试。写文件、发请求、执行命令是非幂等的重试前必须确认上一次是否真的失败了。实现上可以给每个工具调用分配唯一ID服务端记录已执行的ID重试时先查记录。6.2 上下文膨胀导致的性能衰减系统运行几个小时后响应越来越慢token 消耗越来越高。排查后发现是上下文管理器没有正确清理过期内容。早期版本里所有工具调用结果都保留在历史里越积越多。修复方案是给上下文加生命周期标记。每个注入的内容块标记类型和过期时间比如工具调用结果保留最近5轮文件内容保留到任务结束系统提示永久保留。清理线程定期扫描并移除过期内容。这个改动让长时间运行时的平均 token 消耗下降了约35%。6.3 状态不一致的排查链路有一次系统重启后任务进度显示混乱有些已完成的任务被重新执行。排查过程是这样的先看日志发现重启时状态文件写入了一半JSON 格式不完整。检查写入逻辑发现没有做原子写入直接覆盖原文件写入过程中断电或崩溃就会损坏。修复方案是先写临时文件再原子重命名。同时增加状态文件的校验和加载时验证完整性损坏则回退到上一个备份。进一步加固状态变更时先写日志再更新状态文件重启时可以通过日志重放恢复。这个坑的教训是任何持久化操作都要考虑中断场景。在智能体系统里状态就是一切状态丢了之前的工作全白费。6.4 模型输出格式不稳定的应对即使你明确要求模型输出 JSON它偶尔还是会加个解释性前缀或者用 Markdown 代码块包起来。在自动化流程里这会导致解析失败。我的应对策略是宽容解析加严格校验。解析时先用正则提取可能的 JSON 片段尝试多种常见格式。解析成功后再用 schema 严格校验字段类型和必填项。校验失败则把错误信息返回给模型让它重新生成。这个重试循环通常一到两次就能得到正确格式。另外在提示里给出具体的输出示例比抽象描述有效得多。与其说输出 JSON 格式不如直接给一个完整的示例模型模仿示例的准确率明显更高。7. 这套架构还能怎么扩展7.1 多智能体协作的可能性当前架构是单智能体为主但 Harness 的分层设计天然支持扩展成多智能体。比如可以有一个规划者负责拆解任务多个执行者分别处理不同子任务一个审核者检查结果质量。它们共享同一个上下文存储和工具层通过消息队列通信。这种扩展的挑战在于协调开销。智能体之间的通信本身就要消耗 token如果任务拆得太细协调成本可能超过收益。我的建议是从简单场景开始比如只把代码生成和代码审查拆成两个角色验证效果后再逐步扩展。7.2 与本地知识库的深度集成Obsidian 目前主要作为文件存储和可视化层。进一步可以做的是语义检索集成。把知识库里的文档做向量化索引智能体在需要时通过语义搜索找到相关笔记而不是靠文件名匹配。这需要在 Harness 里增加一个检索工具并维护索引的更新。索引更新的时机很关键。实时更新成本高定时批量更新又有延迟。折中方案是增量更新加定期全量重建。文件修改时标记为脏后台线程定期处理脏文件每周做一次全量重建保证一致性。7.3 成本监控与预算控制每月40亿 token 的规模必须有成本监控。我建议在 Harness 里内置一个计量模块记录每次调用的 token 消耗、模型类型、任务归属。基于这些数据可以做实时预算告警当日消耗超过阈值时通知。按任务归因知道哪些任务最烧钱优先优化。模型路由简单任务用便宜模型复杂任务用强模型在质量和成本间找平衡。这个模块本身不复杂但需要从第一天就设计进去后补会很痛苦因为要改动所有调用点。7.4 从个人工具到可分享产品的距离如果想把这套东西分享给别人用还有不少工作要做。配置要外置化不能硬编码路径和密钥。安装流程要简化最好一条命令搞定。文档要补齐特别是常见问题的排查指南。最重要的是错误处理要友好个人使用时可以看日志排查给别人用就得有清晰的错误提示和恢复建议。不过话说回来这套架构的核心价值在于它验证了一个可能性一个人借助合适的工具和架构确实可以构建出相当复杂的智能体系统。20万行代码和40亿 token 的背后是九个月里无数次的调试、重构和取舍。如果你也在走类似的路希望这些经验能帮你少踩几个坑。
返回列表