☰
Harness架构实战:一个人如何管住20万行代码与40亿token成本
2026/10/3 5:04:48 网站建设 项目流程

凌晨两点,我盯着云服务商的账单页面,看着那个数字又往上跳了一格:这个月token用量突破40亿。四十亿,后面跟着八个零,就这么被我一个人,在一个房间里,用九个月时间,一点一点喂给了大模型API。同时,项目代码仓库里的计数器停在20万行出头,不算注释和生成测试数据,也差了没多少。这是一款Harness架构应用——不是那种PPT里的架构概念,而是真的要把每一笔token的流向、每一次模型输出的质量、每一层的降级兜底都管起来的实操框架。

这篇文章写给两类人。一类是正在用大模型做产品的独立开发者,你迟早会撞上“裸调API一时爽,上线之后火葬场”的墙;另一类是对“AI应用架构”这四个字还停留在概念层的朋友,我把账单、代码和事故记录摊开给你看,省得你再用真金白银去填同一个坑。

先交代背景:这款应用本质上是一个重度依赖大模型来完成信息处理和数据洞察的工具,用户每一次操作,背后都可能触发多轮模型调用、长文档解析、跨会话记忆重建。九个月时间线里,我从第一行代码写到最后一个压测脚本,期间没有团队,没有QA,没有运维,唯一的外援是一个偶尔帮我review关键模块的老朋友。

1. Harness架构说了什么:不是给模型“踩油门”,而是给模型“套缰绳”

我见过太多团队把大模型接进业务的方式:在业务代码里直接写一个client.chat(),prompt里塞满示例,然后祈祷模型别抽风。前两个月我做Demo阶段也是这么干的,结果就是上线第三天,同一个问题换了个问法,模型输出的JSON结构变了,前端直接白屏。

Harness架构解决的就是这件事。我说的Harness,不是指某个开源框架的特定名称,而是软件工程里test harness那个概念的延伸:测试夹具负责“把被测对象固定住,按预定方式输入,检查输出是否符合预期”。大模型应用里的Harness层,做的就是同样的事——把模型固定在你设定的行为边界内,检查它每次的输出,不合格就重试、降级或走兜底逻辑。

1.1 Harness层要管好的四件事

第一,任务规划。模型不是上来就回答问题的,大部分复杂任务要被拆成“检索-推理-合成-校验”多个步骤,Harness层需要决定走哪条链路。

第二,上下文组装。这决定了你花多少钱。同样一个用户问题,是把30页历史文档全部塞进去,还是先摘要成800字再喂给模型?Harness层的上下文管理器,必须对每一段进到模型里的内容做预算审核,超了就压、该丢就丢。

第三,输出校验。模型输出天然不稳定,Harness层要用结构化解析器、正则约束、字段存在性检查、语义比对,把输出转成可靠的数据结构再交给业务层。

第四,降级路由。主模型超时、限流、返回格式跑偏的时候,是重试一次还是换模型?换更便宜的模型能不能扛住?Harness层要有明确的策略。

1.2 为什么我不用现成的编排框架

很多人听说我自己写Harness层,第一反应是“LangChain不是现成的吗”。我用过的感受是:通用编排框架的核心价值是让你快速跑通原型,但到了成本控制和细粒度约束这个层面,抽象层太高反而碍事。

对比维度通用编排框架自研Harness层
原型搭建速度快,半天就能跑通慢,第一周都在写基础设施
token预算控制粗粒度,按链路由每个请求级别的精确核算
输出可靠性靠提示词工程解析+校验+重生成闭环
故障降级简单的fallback多级熔断+成本感知路由
可观测性依赖外部平台每笔token有trace_id

我的结论是:如果你做的是工具类产品,Harness层必须自研,哪怕规模小一点。框架适合做Demo,不适合做要长期烧token的业务。

1.3 Harness层第一个版本长什么样

第一个版本其实很朴素,核心就四个文件:router.py(路由策略)、context_manager.py(上下文组装与压缩)、validator.py(输出校验)、budget_tracker.py(token预算与用量统计)。加起来不到3000行,但就是这3000行,撑起了后面10万行业务代码的稳定运行。后来20万行代码里,Harness层膨胀到了将近3万行,但最初的四文件骨架一直没有变。这给我的教训是:架构的核心稳定面要早定,业务面可以后长。

2. 20万行代码的体积感:一个人写代码,最怕的不是写不出来,是写出来没人兜底

九个月,20万行,折算下来平均每天730行。但数字没有意义,有意义的是这些代码是怎么分布的、为什么会膨胀到这个量级、哪些代码是“必须手写”的、哪些其实是“为了对抗复杂度不得不写”的。

2.1 代码量的真实分布

我的项目仓库五大模块的代码行数分布如下:

模块代码行数说明
Harness核心层约3万行上下文管理、向量检索封装、路由策略、输出校验
领域服务层约6.5万行业务实体、链式处理管线、任务编排、状态机、重试恢复
集成适配层约2.5万行各类数据源连接器、认证、导入导出、第三方工具链
前端与交互层约4万行工作台界面、结果渲染、流式输出组件
测试与运维脚本约4万行900多个单测、压测脚本、数据回放夹具、部署流水线

看到没,真正贴着“业务”的代码只有三分之一,剩下的全是在为“稳定”“可观测”“可测试”服务。很多人以为一个人写20万行是效率问题,其实是工程底线问题:没有团队里的其他人帮你盯质量,你只能靠测试脚本和日志管线自己盯自己。

2.2 手写和生成代码的边界

写这20万行的时候,LLM辅助编码已经非常成熟了。我实际写码过程中大概有三成代码是人工一句一句敲的,剩下七成是在对话式编程工具里生成、再逐段review后合入。但这里头有个极其重要的原则:基础设施和Harness核心层,一行生成代码都不要用。原因很简单——这两层一旦出问题,排查成本是几十倍的放大,而生成代码最大的弱点是“看起来对,边界情况全错”。

业务层、适配器层可以放心用生成代码,但必须有单测兜底。我给自己的规矩是:合入生成代码前,至少写一个针对它的失败用例。倒逼着生成逻辑把异常路径考虑清楚。

2.3 一个人怎么保证代码质量

没有团队,质量全靠机制。我做了三件极其偏执的事:第一,核心数据结构的类型标注覆盖率100%,不是90%,是100%,包括临时脚本里的数据结构,也先定义再使用;第二,CI流水线里强制门禁,测试覆盖率低于75%直接拒绝合入;第三,日志管线内置到Harness层,每一条LLM调用的输入输出、token花费、耗时,都带同一个trace_id落到本地存储里。

这套习惯前期很痛苦,尤其是一个人写代码还要写测试,感觉像在自我折磨。但后面发生的事证明,这三道防线救了我至少五次。

3. 40亿token的燃烧路径:每一笔token都要有去向,否则账单会教做人

每个月烧掉40亿token是什么概念?按折算,如果全用同一档位的旗舰模型,这笔钱会非常夸张。但实际账单没那么吓人,原因是我做了模型路由:90%的简单任务走廉价轻快模型,10%的重活才上旗舰模型。即便如此,40亿这个体积依然说明一个事实——LLM应用的钱,不是花在一次调用上的,是花在“一次次忘记缓存、一次次白问、一次次无限重试”上的。

3.1 先建立token台账,再谈优化

我把系统里所有LLM调用按模块打了标签。每个月末拉一次账,永远按消耗量排序看前十项。这个习惯是从第三个月才开始有的,前三个月我完全没做,结果第一个月的账单比我预估的高了一倍,而且根本说不清钱花哪了。建立台账之后,问题清晰了。

某个月的消耗分布大致如下:

模块token消耗占比说明
长文档解析与递归切分33%文档切块后多次调用,且每块自带系统提示词
多轮Agent式任务编排28%任务步骤之间反复调用,重试率偏高
数据清洗与分类18%大批量小任务,单笔便宜但量极大
高频聚合查询(无缓存)13%相同问题反复询问模型
校验失败后的重新生成8%输出格式不规范导致的重试浪费

3.2 五个消耗黑洞的成因

长文档解析之所以排第一,是因为我早期用了“把整篇文档一次性塞给模型”的粗暴方案。比如一份4万字的技术文档,光输入就吃掉几十万token,再来几份文档,一天下来就是千万级消耗。后来改成“先按语义切分成块,每块做局部摘要,再做全局聚合”,token直接砍掉一半以上。

Agent式任务的浪费在于重试风暴。我最初给每一步任务设了3次重试上限,但没考虑“重试前先检查上一步输出是否可靠”,结果经常是第一步模型产生幻觉,后面所有步骤在错误前提上反复重试,烧掉的钱全是白烧。Harness层后来加了一个前置校验门:每步输出先做语义一致性初筛,不合格的直接低成本重试,而不是带着脏数据往下跑。

校验失败重生成这个坑,在结构输出上尤其明显。让我印象最深的一次:我让模型输出带上下文的JSON,但没告诉它输出里必须做HTML转义,结果模型在某个字段里返回了一个未转义的双引号,JSON解析挂掉,系统按“失败”走了重试,同样的token又烧了一遍。后来validator里先做结构修复再决定要不要重试,把这类浪费压到了1%以下。

3.3 40亿token的真实账本

按混合路由的均价来算,假设70%的token走约$0.3/M的轻量模型、30%走约$2.5/M的旗舰模型,综合成本大概是$900+/千token。月消耗40亿token(即4000M token),每月纯token成本大约在人民币数千到一万多的量级。具体取决于模型价格浮动,但这个数量级对一个没有外部融资的独立开发者来说,是每天都在滴血的数字。

3.4 降耗三板斧:缓存、压缩、路由

我一共上了三层优化,效果按降耗占比排名如下:

  • 语义缓存:对同义改写的高频提问做向量相似度匹配,命中直接返回上次结果,这一层砍掉了约15%的token。
  • 中间对话压缩:多轮任务中超过3轮的上下文,不再拼接原始对话,而是先让廉价模型生成中间摘要,再用摘要继续。这一层降低了约20%的消耗。
  • 模型路由:用一个小型分类器判断请求难度,简单查询直接走轻量模型,只有复杂推理才让旗舰模型接手。这一层省下的成本和前面两层叠加,总消耗从最初的每月70亿降到了40亿出头。

我算过这笔账:优化前如果完全不设防地跑,第三个月就会因为成本压力终止项目。Harness架构里的budget_tracker.py,从某种意义上是这个项目的救命恩人。

4. 九个月的节奏感:一个人一支军,怎么不崩盘

九个月不仅是技术战,更是耐力战。我见过太多独立开发者做到第三个月激情褪去就搁浅了。这个项目能活下来,靠的是把九个月拆成三个明确的小周期,每个周期有完全不同的节奏和目标。

4.1 三个阶段:验证期、建设期、打磨期

第一个周期是第1到第3个月,验证期。这一个阶段的产出不是“功能”,而是“风险是否可控”。我只做了三件事:写好Harness核心层、用1000条真实工况数据跑模型输出、搭好token台账。当时定的退出条件是:如果模型输出准确率低于85%,或者单次用户请求平均token超过预算线,项目立刻砍掉,绝不恋战。

第二个周期是第4到第6个月,建设期。这一个阶段不追求完美,追求覆盖。把Harness层的路由策略、上下文管理、降级逻辑全部扩容,业务模块一个接一个填上去。到第6个月结束时,代码量从3万行涨到14万行。这个阶段最大的坑是我急于铺功能,导致测试覆盖率一度掉到60%以下,CI门禁直接把几次合入拦下来,我不得不硬着头皮回头补测试,耽误了将近两周。

第三个周期是第7到第9个月,打磨期。此时功能已经齐全,重点转到压测、调优、降耗和修边角。9个月快结束的时候,我记得自己同时开着三个终端:一个跑压测,一个看token账单实时数据,一个对着日志排查某条长尾请求为何每次都要卡6秒。

4.2 个人开发者必备的工程化习惯

一个人写九个月,如果没有工程化习惯,大概率会死在“改A坏B,C又牵连D”的连锁崩坏里。我极度依赖三样东西:

  • 每日单测:不管当天多晚回家,跑一遍全量测试再睡,保证任何改动都不会留下隔夜问题。
  • 周发布:每周五固定发一个版本,哪怕这周没有任何新功能也要发,保持交付节奏,逼自己清理坏味道。
  • 日志索引:所有LLM调用、路由决策、重试事件、token消耗全部结构化落盘,事后排查不求人。

4.3 如何对抗疲劳与信心波动

九个月里我有两次瓶颈期,都是熬到凌晨三点还对着一个诡异bug没头绪的时候。我的解药是老朋友的一句话:“不要追着bug跑,回去看数据。”LLM应用的bug尤其如此,模型不会给你线性的因果链,它是概率机器。当你觉得“怎么会输成这样”的时候,把对应的prompt、上下文、输出样本全部拉出来,对着真实数据看,往往十五分钟内就能找到是哪条上下文污染了结果。

另一个对抗疲惫的方法是做“胜利清单”。每周记下三个本周解决了的问题,不用大,小到“优化掉了一次多余的重试”也可以。九个月后回看这条清单,你会发现自己已经跨过了一道远远超过预期的坎。

5. 踩坑实录:三场靠日志救回来的事故,完整排查链路公开

我前面怎么强调写日志都显得抽象。这一章我把三个真实事故的完整排查过程写出来,你拿这个当模板去查自己的系统,大概率能省下一周时间。

5.1 事故一:一次上下文超限引发的雪崩

现象:某天高峰时段,系统连续飘出ERROR级别的HTTP 400状态码,大量请求失败,连带触发熔断。过程中我注意到失败请求的耗时单调递增,从2秒一路涨到9秒。

排查链路:先看日志里的错误码,一致指向context_length_exceeded;再拉失败请求的trace_id,发现都集中在一个入口:长文档处理模块;然后对比成功与失败请求的上下文碎片,发现失败请求都带了同一种“历史上下文”——某个用户的操作路径触发了跨会话记忆重建,而这个重建没有走上下文裁剪,直接把此前所有会话的原始片段(累计超过20万字符)拼进了prompt。

根因:Harness层的上下文管理器在构建“跨会话记忆”时,只做了按时间截断,没有做按预算压缩。修复方法很直接:跨会话记忆一律先经轻量模型压缩成结构化摘要,原始文本只保留最近两轮。修复后这个入口的失败率从9%降到0.1%以下。

5.2 事故二:模型升级带来的“悄悄回归”

现象:某次上游模型列表变更后,业务方反馈“答案变快了,但质量变差了”。且没有报错、没有超时、没有token暴涨,一切指标都正常。可怕的地方正在于此——不是显性故障,是隐性劣化。

排查链路:先查模型路由日志,发现部分本该走旗舰模型的复杂推理请求,被切到了轻量模型,因为路由分类器的输入特征里包含了“上游模型ID”这个字段,而模型ID的降级映射在上游变更后把两个高能力档位错误映射到了低能力档位。也就是说,分类器以为自己在用旗舰模型,实际发往的是轻量模型。

修复:路由表里增加了一个“版本映射校验job”,每小时检查一次模型映射关系与上游可调用清单是否一致,不一致自动暂停相关路由分支并告警。这个事故之后我加了一条铁律:模型供应商的任何配置变更,都必须先在测试环境跑50组回归样本。

5.3 事故三:token计费里的隐蔽陷阱

现象:连续两周,月度token台账显示下游平台统计的消耗是应用层记账的1.4倍。一开始我以为是统计时间窗口不一致,后来发现不是。

排查链路:逐项对账,发现差异集中在“工具调用模式”的请求上。应用层记账只算了用户prompt的token和模型回复的token,漏掉了function calling机制里包含的工具定义JSON和tool call之间的多轮内部消息。这部分token账面看不出来,但账单上一分不少。

修复:把所有带工具定义的请求在Harness层单独开启“完整计数”模式,把工具schema和隐含轮次统一纳入预算统计。从此之后,账目误差控制在1%以内。这条希望做Agent类应用的朋友特别注意,工具调用的隐性token比想象中大得多。

6. 九个月后的复盘清单:十条写给同样在单打独斗的人的建议

我没有做那种“下一阶段目标”的展望,只把给后来者的建议钉在这里。九个月里最值钱的经验,都不是从成功里得到的,而是从“烧掉的token”“白写的代码”“凌晨的崩溃”里磨出来的。

  • 第一,开头就建好Harness层。不要从裸调接口开始,否则三个月后重构成本会高到你后悔。
  • 第二,token台账第一周就要有。一开始就按模块、按接口、按trace粒度记录,省得后面补账。
  • 第三,上下文压缩必须前置。任何长文本多轮场景,先压缩再拼装,这是成本与质量的双重保障。
  • 第四,路由分类器别把上游模型ID当唯一信号。版本映射常变,要加交叉校验。
  • 第五,每次模型输出校验失败,先尝试低成本修复(如结构补全),再考虑重试,否则重试只是重复烧钱。
  • 第六,测试覆盖率不到70%不要开发新功能,先补历史债。
  • 第七,计费对账要区分“应用视角token”和“平台视角token”,工具调用和思维链的隐含消耗必须单独核算。
  • 第八,harness_id贯穿所有日志,这是排查一切问题的基础设施。
  • 第九,每周发布版本。节奏感能对冲孤独感。
  • 第十,最累的时候不要写新代码,去写测试或补日志。这些看似次要的工作,会让你在下一周感谢自己。

最后再分享一个细节:我给Harness层的每个路由决策都配了一个形如harness://{session}/{step}/{model}的调用链标识符,后来排查上游故障时,只要在日志里过滤这个标识符,就能精确看到每一次模型请求是被哪条策略路由过去的、因为什么原因降级、花了多少token。就是这些看起来笨拙的工程习惯,让“一个人”“九个月”“20万行代码”“40亿token”这些数字真正变成了一款能跑、能扛、能省钱的产品。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询