资讯动态

Harness架构AI Agent应用实录:上下文管理与Token成本控制

发布时间:2026/10/1 6:10:45 来源:尧图企业网站定制
过去九个月我几乎每天都在面对同一个问题“你到底在做什么”每次我回答“我在做一个 Harness 架构应用”对方都会先沉默三秒然后问“那是什么东西”这个问题很难三句话讲清因为 Harness 在软件工程里本来就有“测试夹具、装配装置”的意思在 AI Agent 领域又被人说得云里雾里。这篇文章算是一次完整复盘一个人九个月超过 20 万行代码每个月烧掉 40 亿以上的 token最终把一款以 Harness 为核心的 AI Agent 应用从零干到了能够稳定在真实仓库里完成端到端任务的阶段。我会把架构思路、关键实现、成本控制、踩过的坑都摊开讲。想自己做 AI 原生应用、研究 Agent 框架、或者单纯好奇“一个人到底能不能用 token 堆出一个产品”的人这篇应该能给你一点真正的参考。1. 一款 Harness 架构应用到底是什么1.1 大模型是引擎Harness 是装车工程要解释 Harness 架构我最喜欢用汽车做类比。大模型就像一台 F1 引擎单看数据很猛转速高、功率大可你没法直接把它装进家用车底盘就上路。你需要进排气系统、冷却系统、电控单元、传动轴、刹车配合甚至需要重新设计车架。这套让引擎真正发挥作用的中间工程就是 harness。所以当我说“我在做一款 Harness 架构应用”时我说的不是用 LangChain 拼几个 chain也不是在 API 后面套一层代理。我说的是我自己构建了一套完整的“装车工程”让大模型在真实代码仓库里能稳定地完成阅读代码、定位缺陷、修改文件、运行测试、生成提交信息这一整条链路。1.2 为什么叫 Harness而不是 Agent 或 Workflow当时市面上已经有 Agent 框架和 Workflow 引擎但我试了一圈后发现它们解决的是不同层面的事情。传统的 Workflow 是预先画好轨道第一步做什么、第二步做什么全部写死。好处是稳定坏处是面对没见过的任务直接失灵。Agent 则反过来把决定权几乎全交给模型让模型自己选工具、自己拆步骤。好处是灵活坏处是不可控模型可能在一个死循环里反复调同一个工具也可能把上下文撑爆。Harness 站在两者中间。它不是替模型做决定而是给模型提供一套“可控的驾驶舱”模型可以决定往哪开但油门、刹车、仪表盘、导航数据全部由 harness 管理。重点在于隔离、可观测、可回滚、可评估。说白了我要的不是一个“自由发挥的 AI”而是一个“能上生产线、能修 bug、能跑测试的 AI 员工”。Harness 就是给这个员工制定的操作手册和作业环境。1.3 这个应用能做什么用一句话描述给大模型装上一套可编程的“神经系统”让它能在真实代码仓库里完成端到端任务。更具体一点这个应用具备以下能力接手一个 GitHub issue自动在仓库里定位相关代码读取多个文件内容结合构建日志和测试失败信息判断根因生成并应用 patch自动补类、改函数签名、调整依赖在沙箱里运行构建和测试验证修改是否生效根据 diff 和测试结果生成 PR 描述。这套东西最适合的场景是“半自动化代码维护”修 bug、补测试、做机械重构、升级依赖。它不是一个聊天机器人也不是一个自动补全插件而是一个贴近真实工程环境的自主系统。整个系统的核心就是那层被称为 Harness 的运行时。2. 整体架构与核心模块设计2.1 分层结构把模型隔离在核心之外整个应用我最终做成了六个逻辑层每一层之间通过接口通信。这样做的目的是让模型成为可以被替换的组件而不是整个系统的上帝。接入层CLI、API Server、IDE 插件。用户从这里发起任务。控制层也叫 Harness Core。负责会话管理、任务规划、工具调度、上下文策略。模型层统一封装不同模型后端包括 OpenAI、Claude、DeepSeek 和本地部署模型。对外暴露同样的协议。执行层提供沙箱环境运行 shell、编译、测试、文件编辑等工具。数据层存储会话日志、任务记录、token 用量、耗时、成功率等指标。评估层维护 golden task 集合每次改动后可以自动回归。实际代码里我并没有为了分层而分层。控制层是核心模型层只是它的一个依赖。执行层里的工具全部通过“插件注册表”装进系统新增一种工具只需要实现一个接口。2.2 上下文工程为什么是独立模块很多 Agent 项目把上下文管理散落在各处调工具时拼一下生成回答时拼一下结果就是上下文越来越乱。我在做第一版原型时就发现如果把上下文当成一个独立模块来设计后面能省掉 80% 的 token 浪费。我们的上下文模块维护了一个“上下文栈”栈底是任务目标、角色设定、工具说明中间是事件流每轮模型调用和工具返回都会被记录栈顶是当前最新状态。工具返回不是一股脑塞进去而是经过清洗、截断、格式化后再入栈。而栈也不是无限增长的超过阈值就会触发压缩策略。2.3 工具调用不是简单的 function calling业内常说 function calling实际做起来会发现它远不止“让模型输出一个 JSON”。工具调用要解决权限、幂等、重试、超时、输出截断、并发冲突等一系列问题。我为本应用设计了一个统一工具模型。每个工具定义三个部分输入 schema、执行函数、输出 schema。模型只负责提供参数harness 负责判断这个调用合不合法、能不能并发、有没有超时、返回结果该怎么截断。这样做的核心原因是安全我不能让模型随随便便执行rm -rf /也不能让模型在同一个文件上同时跑两个互相冲突的编辑操作。工具注册表用 Python 实现了一个很小的装饰器模式核心接口大致长这样tool( nameedit_file, description对指定文件应用一个 patch需要在沙箱内执行, schema{ file_path: {type: string, description: 仓库内相对路径}, patch: {type: string, description: 统一 diff 格式的补丁}, }, ) def edit_file(file_path: str, patch: str) - ToolResult: # 校验路径是否在白名单内 # 应用 patch 前先计算哈希用于回滚 # 执行后返回 diff 的新状态 ...每个工具结果都附带ok、error、metadata三个字段。模型看到的不只是“命令成功”而是执行后的摘要信息。这种设计在调试时帮了很大的忙。2.4 为什么没有上微服务一个人维护 20 万行代码如果还拆成几十个微服务光处理服务间认证和网络故障就能拖垮我。所以整个项目采用“模块化单体”代码按功能分成 package但最终打包成一个进程运行。这带来几个实际好处类型定义可以跨包共享不用写一堆 DTO本地调试不需要 docker compose 起五六个容器内存中的上下文缓存可以直接命中。当然代价是没有独立的水平扩展能力但对这个阶段的应用来说单机处理几十个并发任务已经足够。我个人观点是个人项目起步阶段单体是默认选择只有当你明确知道某个模块需要独立扩缩容时再把它拆出来。3. 九个月里的关键实现细节3.1 上下文管理token 爆炸的第一号凶手所有 Agent 项目都会遇到同一个问题任务越长上下文越大。如果不管一个 30 轮的任务到后半段会把整个对话历史全部塞给模型token 消耗呈线性甚至超线性增长。我最初的版本很粗糙直接把每轮事件追加到 messages 里。结果一个稍微复杂点的 issue跑完要花掉 200 万 token而且后面几轮模型已经开始遗忘前面的关键信息。后来我做了三件事第一分层摘要。每个任务维护三份摘要短期摘要覆盖最近 5 轮事件中期摘要覆盖已完成的工具调用和结果长期摘要记录任务目标、当前进度、已知风险。模型每次只看到短期摘要加长期目标而不是全部历史。第二按需检索。仓库文件不可能全部放进上下文。工具返回的代码片段会进入一个向量索引模型后续需要更详细的内容时可以调用检索工具主动获取而不是被动接收所有文件内容。第三主动遗忘。如果一个文件在连续 5 轮中都没有被引用harness 会在摘要阶段把它从上下文中移除。这个“遗忘机制”听起来简单实则是 token 控制最有效的一招。从实际账单看上下文管理做好以后单任务平均 token 消耗降低了约 60%。3.2 工具链给模型装上手和眼睛一个只会写文本的模型无法操作真实仓库所以工具链就是它的手和眼睛。我早期只做了四个工具read_file、write_file、run_command、search_files。后来随着真实场景变多工具列表涨到了二十多个。其中最有挑战的是run_command。它是整个系统里最强大也最危险的工具。我在沙箱里执行命令限制网络访问只允许仓库目录写入并且强制所有长时间命令设置超时。为了保持可控每个命令执行完只返回前 2000 个字符作为输出摘要完整日志会写入文件模型需要时可以再次调用read_file去查看细节。在实际开发中我发现工具返回的“可读性”直接影响模型决策质量。如果返回一段杂乱无章的原始 shell 输出模型很容易迷失。所以工具结果全部经过结构化处理错误信息单独提取测试失败会解析出失败用例列表diff 会按文件和行号分组。模型看到的永远是一份“干净的报告”而不是一堆 raw text。3.3 自我评估与回归40 亿 token 烧在哪里每个月烧掉 40 亿 token很多人第一反应是“太浪费了”。但如果把这笔账拆开看大头并不是平时开发调试而是“评估”和“回归”。我给应用建立了一个评估集包含 100 个种子 issue 和 20 个跨仓库任务。每个任务都有一条标准路径先定位代码再修改最后跑测试。每次我对 harness 核心逻辑做改动都会整个跑一遍评估集。一次评估集跑完平均需要 300 亿 token不实际没有这么多。我的经验是每条任务平均 40K token100 条任务就是 4M token 一轮。如果一天跑 3 轮一个月大概烧掉 3.6 亿 token。剩下的大部分 token 消耗来自开发调试、新场景试错、以及模型在难任务上的多轮挣扎。所以 40 亿 token 这个数字准确说是“学费 测试费 生产 use case 模拟费”的总和。它不是拿来炫技的而是让我能在改动任何一行代码后立刻知道有没有把模型带偏。3.4 模型无关的抽象层2014 年做机器学习我们要兼容 TensorFlow 和 PyTorch现在做 LLM 应用要兼容 OpenAI、Claude、DeepSeek、本地模型。模型无关的抽象层不是可选项而是刚需。做法不复杂定义ModelBackend接口包含chat、complete、count_tokens、get_max_context几个方法。每种模型一个 implementation内部处理各自的协议差异。比较头疼的是 token 计费规则不同以及某些模型不支持结构化输出。针对这个问题我在 harness 层做了一个“能力探测”启动时检查模型是否支持 JSON mode、是否支持工具调用、最大上下文长度是多少然后动态决定某些功能开不开启。这个抽象层让我后来切换模型几乎没有成本。同一个评估集今天用闭源模型跑一遍明天换本地模型跑一遍对比结果一目了然。4. 实操过程一个人怎么推进 20 万行代码4.1 从 0 到 1先跑通最丑的端到端路径我启动项目的策略不是先画完整架构图而是先做一条“最丑的端到端路径”让模型在一个真实小仓库里定位一个 bug然后修改代码然后跑测试。第一步的实现只有 2000 行极其简陋但它的意义在于验证了一个假设模型可以在 harness 的帮助下完成全流程。拿到这个成功案例后我才开始重构。先加工具抽象再加上下文管理然后加评估集。每加一块功能都要确保那条最丑路径不会断。这个过程很像滚雪球先滚出一个能用的核再一圈圈加厚而不是一次性盖大楼。4.2 模型辅助开发的日常很多人问 20 万行代码是不是全是 AI 写的。答案是一半是一半不是。我确实大量使用模型生成代码但我自己承担架构设计、代码走查和最难部分的调试。一个典型的开发循环是这样的我先写一个接口定义或测试用例明确我想要的行为把当前相关文件丢给模型让它生成实现我 review diff主要看边界条件和错误处理手动修掉 AI 容易忽略的细节跑回归通过则提交不通过则继续迭代。这个过程最关键的认知是模型可以作为高效的编码员但架构决策必须自己来。比如“上下文模块应该放在哪个层”“评估集该不该做重采样”“工具执行的沙箱边界怎么划”这些问题模型给不了可靠答案。4.3 20 万行代码构成拆解20 万行听起来很吓人但拆开看就合理很多。我的仓库最终构成大致如下核心 Harness 运行时约 4 万行包括上下文管理、会话调度、工具调度、评估核心工具插件约 2 万行涉及 shell、文件编辑、AST 解析、git 操作、lint、测试执行评估集与测试配置约 8 万行主要是 golden task 描述、仓库 fixture、JSONL 配置临时工具和脚本约 3 万行很多是一次性数据分析、日志清洗、token 统计脚本接口协议、文档、示例约 3 万行。所以 20 万行的产品代码只有 6 万行左右其余是测试、配置、以及为了验证假设而写的一次性代码。这提醒大家不要迷信代码量真正的复杂度在那些 4 万行核心代码里。4.4 Token 成本控制策略清单烧钱烧到第 5 个月的时候我开始认真做成本控制。围绕 token 消耗我总结了一套很实操的方法按任务难度分流简单任务用便宜模型困难任务才调用最强模型。比如“读取文件、生成一个简单 helper”这种任务完全不需要顶级模型。启用 prompt 缓存对同一仓库的固定 prompt 前缀做缓存重复读取同一段文件上下文时不再重复计费。压缩会话日志日志只保留关键事件不保留完整输出需要再按 ID 查回。设置每日预算上限超过预算后新的任务自动进入低配模式或者暂停 collect。常态化评估而不是每次全量跑平日只跑种子集的 10%每周跑一次全量回归。这套策略让月均 token 从峰值 60 亿降回了 40 亿左右而且没有牺牲多少效果。真正的教训是token 成本不是靠砍功能降下来的而是靠减少无效上下文和无效调用。5. 常见问题与排查实录5.1 模型陷入“调用同一个工具”的死循环在开发中期我频繁遇到一种情况任务跑到 10 轮以后模型开始反复调用read_file读取同一个文件而且每次返回都一模一样模型就是不进入下一步。一开始我以为是模型抽象能力不行后来发现根因在 harness我把“已经读取过这个文件”的信息放进了上下文但因为摘要压缩模型可能只记得“这个文件很重要”却忘了“我已经读过它并且拿到了内容”。解决方式是给事件流加一个“动作状态断言”。每个工具调用完成后harness 会把它记录成结构化事件并在下一次模型请求前注入一条简短提示“你已经读过 src/models.py不要重复读取。”同时如果检测到同一个动作的参数完全相同且已经连续执行 3 次harness 会主动中止当前子任务强制模型回到计划阶段。这个机制一上线死循环类问题减少了 80%。5.2 上下文窗口溢出任务中途崩溃长任务最痛的坑是跑了 25 轮API 突然返回context length exceeded。因为你不知道模型已经遗忘哪些信息也不知道到底是哪一轮把窗口撑爆的。为了排查它我在每一次模型请求前都计算当前 token 总量并与模型最大上下文长度对比。如果超过阈值的 70%就触发主动摘要。另外我做了任务检查点。每完成一个重要节点就把当前任务状态序列化到本地。一旦失败模型可以从中断点恢复而不是从头再来。这个设计看起来很基本但真的救了很多次命。没有检查点之前一个跑了 2 小时的任务崩掉只能浪费时间重跑有检查点之后崩溃变成了一件成本很低的小事。5.3 token exchange failed认证管道里的隐性问题做模型无关抽象层的时候我接了不少第三方 API 和内部代理刚开始经常看到token exchange failed: error sending request之类的报错。很多人第一反应是“网络出问题了”但我在日志里翻了几次之后发现真正原因五花八门某个后端服务的 refresh_token 被前一个请求异常更新后后续请求拿到的是空字符串并发请求同时刷新 token导致旧 token 刚被覆盖新请求还在用旧 token某些端点对过期的 access_token 会直接返回 403而不是提示刷新。排查思路是先区分“OAuth 流程问题”还是“业务 token 问题”。如果是 OAuth我会检查刷新令牌是否被并发写坏最终用串行刷新队列加双 token buffer 解决。如果是业务 token我会在请求前检查剩余有效期提前刷新而不是等到失败重试。这类问题非常隐蔽但几乎每个多服务集成的项目都会遇到。5.4 同一份代码eval 一会儿过一会儿不过做自动评估最让人抓狂的事情是代码没改提交没变这次跑通过下次跑就失败。一开始我以为是评估集有问题后来定位到两个原因模型输出的非确定性以及外部工具比如测试 runner的偶发超时。我的对策有三条把模型温度固定为 0尽量复用相同的输入顺序关键任务重复跑 3 次取多数结果对 patch 的比较不做字节级比对而是解析成 AST 后做语义比较消除格式差异。这个策略并不能完全消除偶发失败但能把误报率从 20% 压到 5% 以内。对个人项目来说这个水平已经足够让我信任“回归通过”的信号。6. 一些数字背后的真心话6.1 每个月 40 亿 token 是什么样的体验40 亿 token 如果全部用商业 API一个月就是一笔不小的开销。还好我并不是全花在商业模型上很大一部分 token 消耗来自本地模型权重、稀疏评估和开源权重。这不是为了省钱而是为了做对比实验同一套 harness不同模型跑出来的能力差异、行为模式差异经常能帮我找到架构中偏袒某一类模型的隐性 bug。这种“用 token 买经验”的方式在个人项目里是可行的但前提是你要清楚每一笔消耗是为了验证什么。如果只是在调试中无意义地重试烧再多 token 也只是自欺欺人。6.2 一个人写 20 万行的最大障碍不是写代码真正的限制因素是心智负担。一个大型系统的上下文分散在代码、配置、测试、文档里你不可能全部记住。我个人的解决方式是“外部化记忆”每一个核心决策都写成简短的 ADR放在 docs 目录每天结束前把当天进度和遇到的问题写进一个daily-log.md所有 TODO 和怪现象都记录在案让模型也能读取这些文档。这套习惯让我的“有效上下文”远超大脑容量。很多次我已经忘记某个模块为什么那样设计但翻开 ADR 就能想起来。对于单人开发大型 AI 应用这比任何技巧都重要。6.3 给想单干做 AI 应用的人几句实在话如果你想做类似的事情我给出几个听起来不性感但非常实际的建议第一先选一个具体场景哪怕再小也要先端到端跑通。不要一上来就研究架构。第二把评估集当成资产而不是负担。没有评估集你根本无法知道一次改动是让模型变好还是变坏。第三从第一天就记录 token 消耗和失败模式。数据会让你少走很多弯路。第四不要指望 AI 帮你写所有代码你要把精力花在架构边界和系统行为上。这个项目做完我最深刻的体会是Harness 架构的价值不是让模型变得更强而是让模型的不稳定变得可控。一个人烧掉 20 万行代码和几十亿 token最终买到的不是一个万能模型而是一套能驯服模型的系统。就像真正的赛车不只是发动机厉害底盘、悬挂和车手共同构成的那个整体才是它跑得快的原因。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑