尧图精选

Harness架构实战:一人九个月写出20万行代码,AI辅助编码的极限挑战

🕒 发布时间:2026/10/1 21:57:42 📁 来源:尧图网络
先说结论这个项目做完之后我再也不迷信“人多力量大”了。一个人、九个月、20万行代码、每个月烧掉40亿以上的token最后交付的是一套基于Harness架构的复杂应用。这里的Harness不是某一个开源框架的名字而是我在架构层面自己构建出来的一套约束机制把核心业务逻辑、模型调用、工具调度、外部系统访问全部收敛进一层可编排、可审计、可回退的“缰绳”里。我自己也没想到AI辅助编码会把一个人做大型项目的边界推到这种程度。如果你正在做AI应用、Agent类系统或者明明单兵作战却要维护一个庞大的代码库这篇文章应该能帮上忙。我会把为什么选择Harness架构、40亿token烧在哪、20万行代码怎么管理、以及九个月里踩过的坑全部摊开讲清楚。1. 为什么一个人敢碰这种项目先想清楚Harness架构要解决什么问题1.1 单打独斗最大的敌人不是工作量是“复杂度乱窜”一个人写20万行代码听起来像天方夜谭。按九个月算平均每天写700多行如果全靠手打确实不现实。但有了代码大模型辅助之后难度已经从“写不完”变成了“改不动”。这完全是两个维度的问题。真正的风险在于复杂度失控。当系统只有两三个模块时你脑子里装得下所有调用关系但当你面对几十个模块、上千个文件改一个基础类型会导致十几个调用点报错换一次外部API会让业务逻辑跟着遭殃。这种“复杂度乱窜”是最消耗精力的你会发现每天不是在写新功能而是在处理由改动能带来的旧问题最后整个人被细节淹没。Harness架构的第一性原理就是通过强制控制依赖方向避免复杂度乱窜。所有不稳定的东西包括外部模型、第三方SDK、其他服务接口都不允许直接接触核心业务。核心业务只面对稳定接口外部世界的变化被一层专门负责“翻译、路由、限流、回退”的中间层挡住。这个中间层就是我一直说的Harness层。生活化地理解骑手不需要控制马的每一块肌肉只需要握住缰绳。模型、工具、外部系统就是马骑手是业务核心Harness层是缰绳。缰绳本身不会替马跑但它能把上百个不可控的动作收敛成几个稳定指令比如加速、减速、转向、停下。这正是大型单体项目最需要的东西。1.2 我从单体应用到Harness架构的转变过程最早我也用过传统的分层架构Controller、Service、DAO一层套一层看起来很清晰。但AI生成代码之后边界很快就崩了。模型特别喜欢把外部SDK直接import到最核心的domain service里于是外部返回对象、第三方异常类型开始顺着调用链往核心蔓延。那次事故我记得很清楚某模型供应商升级了接口一个返回字段改了名结果核心业务流程挂了大半天。排查的时候发现代码里有十几个地方直接调用了外部SDKdomain层里还保存着外部返回对象的引用根本不可能快速替换。我花了两天才把所有调用点清理干净然后决定改变思路。我做了个实验在domain目录里禁止import任何第三方包只允许使用Python标准库和项目自定义的抽象接口。所有外部调用无论调用大模型还是调用某个内部服务都必须经过Harness层做一次转换。实验到第二周核心模块的改动量肉眼可见地下降。遇到外部接口变更原来要改20个文件现在只需要改Harness层里的一个适配文件。许多人在做单体项目时会把“快速上线”和“架构设计”对立起来觉得二选一。但我在这个项目里的体会是对于有大量模型调用、工具调用的应用来说Harness层不是额外负担它恰恰是让你快速迭代的前提。因为有了这层约束你才敢让AI大量生成代码你知道就算生成得乱它们也只会在外层乱进不了核心。1.3 项目选型九个月的时间窗口是怎么定下来的这个项目实际上是一个面向运营团队的智能流程编排平台用户可以拖拽节点组成流程每个节点可以执行代码片段也能调用多个大模型能力实现文本处理、摘要、抽取、对比。按传统方式拆至少需要产品经理、前端、后端、算法、测试四五个人干半年以上。我选择一个人上核心原因是把范围压得很死优先做引擎和管理后台前端只做够用版本所有“非核心体验”都往后放。九个月有两个约束业务方给了一个硬性交付时间我自己给自己立了一条纪律第三个月必须跑通第一条端到端链路。如果第三个月还在搭架构这个项目大概率会烂尾。因此技术栈选型全部围绕“快速出活长期可控”Python 3.11、FastAPI、PostgreSQL、Redis队列前端用Vue3加TypeScript。选Python不是因为性能最好而是它在AI数据处理生态里最成熟代码大模型的训练语料也最充足生成质量会明显更高。瓶颈根本不在语言本身而在IO和模型调用延迟。2. 核心设计拆解Harness架构的分层逻辑与关键机制2.1 Harness层到底是什么控制面与执行面分离Harness这个词源自test harness原意是测试驱动夹具。我把它引申为控制面。一个AI应用实际上同时存在两个不断变化的面一是业务规则和流程二是外部模型和工具。如果不加隔离这两个面会互相拉扯每次升级都牵一发动全身也就是代码腐败的开始。Harness层要做的就是把控制面与执行面分开。控制面负责决策这个请求走哪个模型、成本预算是多少、权限够不够、失败了怎么降级。执行面负责干活真正去调大模型的API、执行某个代码节点、访问外部系统。控制面有三个核心能力路由请求进入Harness层后根据任务类型、成本预算、延迟要求动态选择模型或工具。不是所有请求都要用最强的模型也不是所有请求都只能走同一条路径。策略每个请求在执行前会做权限校验、成本预估、敏感信息检查。一旦超预算或发现隐私风险直接阻断而不是傻乎乎地把token花掉。回退与审计调用失败时按优先级选择备用执行器成功后记录完整审计日志包括模型名、token数、耗时、入参摘要、结果状态。这一步保证了后期我有数据可查而不是靠猜。这就像一个自动驾驶系统里的安全控制器。真正开车的是模型和工具但方向盘前还有一层判断机制盯着路线、油量和异常情况。没有这层控制器AI系统的每一次输出都是“盲盒”开箱前谁也不知道是好是坏。2.2 三层代码结构领域层、Harness层、外部连接层项目的代码结构大致如下app/ domain/ # 纯业务逻辑不import外部SDK entities/ services/ contracts/ # 接口与协议定义 harness/ # 控制面 orchestrator.py router.py policy.py fallback.py audit.py connectors/ # 执行面 llm_provider.py tool_executor.py api_client.py web/ # 入口与HTTP层依赖方向是强制单向的domain不依赖harnessharness依赖domain并调度connectorsconnectors只负责翻译和协议对接。核心业务领域永远只面向自己定义的数据结构和接口。用一个“生成销售分析摘要”的流程为例。整个调用链是这样的web层接收请求做基础参数校验后交给Harness层。Harness层检查该节点是否有模型调用权限、当前预算是否充足。Harness层调用connectors里的LLM客户端传入标准化prompt。模型返回原始文本后Harness层先做schema校验再转换成domain层定义的TaskResult对象。domain层拿到TaskResult更新业务流程状态。全程没有任何外部SDK类型泄漏到domain层。后续就算我把底层模型供应商从A换成Bdomain层一行代码都不用动只需要改connectors和Harness层的路由策略。这种隔离带来的安全感是我一个人敢写下去的重要支撑。2.3 每次调用LLM都要过Harness路由、限流、回退、审计Harness层的核心方法写出来并不复杂但贵在强制统一class Harness: def __init__(self, router, policy, fallback, audit): self.router router self.policy policy self.fallback fallback self.audit audit async def execute(self, request: HarnessRequest): await self.policy.check(request) # 权限、预算、敏感信息 plan self.router.plan(request) # 选择模型与参数 try: raw await self._invoke(plan) return await self._normalize(raw, request.output_schema) except Exception as exc: return await self.fallback.handle(request, plan, exc) finally: self.audit.log(request, plan, raw, tokens_used, error)这段代码看起来平淡无奇但它强制了一个事实所有AI调用必须经过同一条路径。没有这条路径开发时图省事直接用外部SDK短期内确实赢了速度长期来看就是埋雷。而且因为所有调用都经过Harness层我每个月统计token消耗、按业务场景拆分成本就变得极其简单直接在audit日志上做聚合就能出报表。3. 每月40亿 token的真实去向量化与省token实战3.1 40亿token都烧在哪里了一张消耗清单这里的token指大模型API的计量单位包括输入token和输出token。一个月40亿 token换算下来每天大概是1.3亿多。这绝不是普通聊天软件能产生的用量而是把大模型当作代码生成器和流程执行引擎高强度使用的结果。我在月底通过Harness层的审计日志做了场景聚合大致是这个比例使用场景月占比具体情况代码生成与补全35%辅助写新模块、重构代码、补函数多轮代码修改20%让模型基于当前代码库上下文修改多处调用点测试用例生成15%生成单测、契约测试、mock数据运行时流程调用20%应用实际执行任务时调用大模型做文本处理与抽取调试与解释10%分析报错日志、解读不熟悉的代码、生成排查思路这个比例不是猜的是从审计日志里按prompt类型聚合出来的。如果没有Harness层你根本不知道自己把token花在哪了月底只能看着账单一脸茫然。3.2 token消耗的量化模型输入输出比、上下文膨胀40亿token听起来夸张但拆开算就不奇怪了。假设平均一次请求输入30k token输出5k token一次调用就是35k。每天1.3亿token除以3.5万大约等于每天3700多次调用。加上代码生成场景经常要传多个相关文件作为上下文单次输入很容易冲到100k token。一个月几万次调用40亿是真实发生的。很多人盯着输出token其实这项目里70%的输入token都花在了“让模型看一些其实不需要看的老代码”上。举个例子我让模型改一个工具函数传统做法会把整个文件甚至整个目录都贴进去这些重复输入的token占了大头。Harness层的router可以做出更聪明的选择通过代码依赖分析只提取与目标函数直接相关的上下文片段再拼装prompt。这个问题的本质是上下文膨胀而不是模型本身贵。如果能把输入的重复率降下来token成本立刻会大幅变化。我在中后期做了成本估算按主流大模型API的混合价格折算每个月烧掉的token费用相当于一个初级开发人员的月薪甚至更高。所以标题里把token单独提出来是因为它在这个项目里已经是和人力成本同样重要的隐形支出。3.3 我把token省下来的七种办法这些方法全部经历过多轮实测有些看起来很小累计起来非常惊人。一是只传相关代码片段。用AST静态分析提取函数依赖图把被修改函数的直接调用关系找出来只生成最小上下文。这比传整个文件至少省一半输入token。二是建立库级摘要。给模型传一个项目内不同模块的README和接口说明而不是整库源码。模型不需要看所有实现只需要知道模块提供了什么能力。三是缓存相似请求。把prompt做归一化处理去掉时间戳、绝对路径、随机的临时数据然后哈希命中缓存。同一段错误日志解析短期内可以复用结果。四是强制输出格式。在prompt里要求严格JSON或代码块不让模型用自然语言滔滔不绝。这样既省输出token也降低了解析成本。五是分层使用模型。复杂推理任务才用最强模型简单任务交给便宜的小模型。由Harness层的router根据任务类型自动选择月底对账时发现成本能压下来不少。六是尽量做增量修改。AI一次重写整个类往往比修改几十行贵很多。我现在会让模型基于已有代码做局部修改上下文和输出成本都会显著下降。七是定期清理上下文历史。Agent类应用尤其明显多轮对话历史会在每一轮重新输入呈指数膨胀。每轮只保留最近几条消息更早的内容先用模型生成一段摘要再作为compact后的上下文传入。按我的实际统计缓存加上下文压缩合起来至少省掉了三分之一的token消耗而且生成质量没有下降。4. 9个月写20万行代码单人长跑的项目管理实录4.1 20万行的构成AI生成与手写代码的边界20万行代码对大型企业项目来说不算多但对一个单独维护的项目已经足够压人。从最终统计看大约六成是AI辅助生成的四成是我手工调整和手写的。这里最关键的不是比例而是边界哪些代码必须自己写哪些可以放心让AI生成。我的原则是domain层和harness层必须自己设计AI只能做代码渲染。connectors层和web层可以让AI放开手去写。AI很擅长写重复的CRUD、测试模板、DTO、配置样例但它不擅长做一致性决策比如命名规范统一、事务边界、缓存一致性这类问题。如果让AI自由发挥它会很自然地生成风格各异、依赖不清晰的代码后期维护会很痛苦。所谓“自己写”也不是逐字手打。我会先把接口签名、关键注释和业务规则写清楚然后让模型补全函数体再逐行review。相当于把AI当成一个打字速度极快的实习生而我是那个画图纸并验收的人。4.2 模块化决策小步提交、每周重构、可编译为王一个人的提交纪律必须比团队更严。没有同事互相审查唯一的防线就是自己每天的动作。我给项目定下三条硬规则每天至少提交一次且每次提交都要能编译通过。重构永远限制在一个模块内并且给其他调用方留兼容层或特征开关。每周固定半天做技术债清仓跑全量测试、用依赖分析工具查环形引用、清理废弃代码。坚持执行之后项目里几乎没有出现过“完全改不下去”的至暗时刻。AI生成代码很容易让人陷入一种错觉反正重生成一次就行。但代码库是在持续累积的不是拍照片每一次生成都会叠加在前面的状态上。小步提交的本质是让你随时能回到一个稳定状态。我甚至用过几天git worktree把当天所有改动放进临时分支如果当日结束前编译不过第二天直接从最后那个稳定提交重来。看着有点折腾但一个人写大项目时稳定的心理锚点比“多写几行代码”重要得多。4.3 测试策略Harness层用契约测试领域层用单测一个人维护20万行代码不写测试等于等死但盲目追求覆盖率也是浪费生命。我最终采用了两层差异化策略。领域层写纯单测覆盖率目标90%。因为业务规则是最不能出问题的地方这里的bug一旦漏到线上会直接影响用户数据。Harness层写契约测试和策略测试覆盖率目标70%。重点测路由选择对不对、策略能不能拦住危险请求、回退逻辑是否生效。connectors层不做高覆盖率只挑关键路径做集成测试。外部依赖变化太快如果也为它们写大量单测每次SDK升级都是灾难。契约测试是这一层最重要的东西在调用模型或外部系统时定义最小响应schema用样本数据做断言。这样当第三方接口返回字段变化时测试首先在Harness层拦截而不是等到线上业务流程挂掉才被动发现。我花了两周多补这一层测试看起来延迟了功能进度但到项目后期这套测试帮我省出了远超两周的排查时间。5. 实操过程中踩过的坑与排查技巧实录5.1 token失效与token续签JWT方案里最容易翻车的地方做平台接入时多个内部工具系统都要通过token调用。遇到最多的就是“token exchange failed”有时表现为登录时sign-in could not be completed有时是请求阶段直接报error sending request。这类问题的本质基本一致你在用旧凭证去换新凭证时服务端已经认为那个凭证无效了。我记得最典型的一次某个外部服务的access token有效期只有两个小时我的定时刷新任务设置在一个小时五十分钟时触发看起来没问题。但问题出在refresh_token上。由于同一客户端会话被多个并发请求复用服务端吊销了其中一次refresh凭证导致后续所有刷新请求全部失败线上服务大面积报错。解决思路可以总结成三点独立认证模块统一管理所有token不要在每个业务代码里自己刷新。每次交换后把access_token、refresh_token、expires_in存入状态表刷新前先校验refresh_token是否仍然有效。请求层加指数退避重试第一次失败等1秒第二次等2秒第三次等5秒避免瞬时网络抖动把token异常放大。简化后的核心逻辑长这样def get_valid_token(client_id, client_secret): token token_store.get(client_id) if token and token.expires_at now() 300: return token.access_token new_token exchange_token(client_id, client_secret, token.refresh_token) if new_token: token_store.save(client_id, new_token) return new_token.access_token重点是要保留至少5分钟的有效期余量不要等过期了再去刷新。高并发场景下第一个请求刷成功第二个请求可能已经拿着旧token发起调用结果就会撞上过期窗口然后连锁失败。5.2 代码污染问题AI生成代码侵入核心逻辑后的处理AI生成的代码会非常自然地使用外部类型这是我在项目里反复遇到过的事。某个connectors的SDK返回了ExternalResponse对象AI在domain service里直接用了它单测还通过了。直到外部SDK升级删除了这个类型定位问题时才发现core模块已经被外部类型污染了。解决办法是加一条硬性约束domain目录禁止import任何第三方包。不是靠自觉而是靠CI自动化卡门禁。我用一个简单的静态扫描脚本检查domain下所有import语句是否属于标准库或app.domain不符合直接构建失败。有了这条门禁之后AI生成代码再想在核心层“偷偷塞私货”就做不到了。如果在domain里需要用到某个外部返回的数据唯一的路径是在Harness层的connectors里把它转换成domain定义的DTO。外部对象不再跨过边界转换逻辑被强制收拢在一个很薄的地方。这条经验对任何重度使用AI编码的项目都成立。不要指望模型自己知道架构边界你必须把边界写进工具链里让机器强制执行。5.3 时间焦虑与进度保护单人项目的里程碑管理长达九个月的项目最大的敌人不是技术而是心理。第三个月时我一度觉得自己什么都没做出来因为很多模块都停留在“半成品”无法向任何人演示。后来我改变策略把计划从按技术层拆解改成按垂直切片拆解。垂直切片的意思是从数据库到接口再到页面打通一条完整链路。哪怕这条链路只能处理一个最简单场景也必须能够演示给别人看。好处非常明显每个阶段结束时都有看得见摸得着的东西正反馈会推着你继续走而不是攒了好几个月憋一个大招。我最后实际走的时间线大概是这样的第1到2个月搭建项目骨架Harness层核心逻辑最先落地跑通Hello World级调用。第3到5个月实现主链路包括用户创建流程、节点执行、模型调用、结果回写。第6到8个月扩展外部工具接入能力补测试优化token消耗。第9个月全量回归、性能基线、补文档、交付。特征开关也帮了大忙。所有未完成的功能默认关闭不会阻塞主流程上线。这样即使某些模块没做完系统依然可以交付剩余功能后续再开。6. 最后分享几个沉淀下来的经验如果让我重来一次我会把Harness层的审计日志做得更早一点。最开始我觉得审计很浪费token和存储后来才明白它是节省token的依据更是快速定位问题的地图。没有这些日志你根本不知道自己的钱、时间和复杂度分别藏在哪些角落。另外别怕烧token怕的是烧了token却把复杂度藏起来。代码量和token数都只是过程指标真正的目标是让一个复杂系统在只有一个人维护时依然保持可控。Harness架构最大的价值也许不是那套分层代码而是它在每次技术决策时都会逼着你想清楚这个变化该落在哪一层这个外部依赖要如何隔离。这个项目之后我对一个人能做多大的事有了完全不一样的理解。合理的架构不是限制开发的枷锁而是让你在AI辅助时代敢于做更大项目、同时不必为未来失控而担忧的前提。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →