☰
一个人+AI开发20万行代码:Harness架构与Token成本实战复盘
2026/10/3 18:46:55 网站建设 项目流程

去年春天,我给自己定了一个有点疯狂的目标:一个人,不组团队,用九个月时间,写出一款基于Harness架构的应用,最终代码量累计20万行,每个月要烧掉40亿+ token的大模型用量。当时朋友听到后的第一反应是“一个人写20万行?还全靠AI?这项目能收得尾吗”。现在这个系统已经上线并稳定运行了三个多月,我想把这九个月的踩坑和沉淀完整复盘一遍。这篇文章不写宏大叙事,只讲我验证过的做法:Harness架构怎么搭、AI辅助开发下token成本怎么控、以及我被各种token失效问题折磨出来的排查手册。它可以给想走同样“一个人+AI”路线的开发者做一个参考,也适合那些在观望AI编程工具到底能干多少活的人。

1. 先回答三个问题:为什么一个人、为什么Harness、为什么排九个月

1.1 一个人做项目,不是热血而是算清楚账

我做的项目是一个数据处理自动化工作流平台,核心是把“数据清洗、特征提取、模型训练、报表生成”这类多步骤任务串联起来,由大模型在受控框架内决策和调用工具,而不是做一个聊天机器人。启动前我认真评估过组队方案,结论是没必要:核心架构还没定型,带人进来需要大量时间对齐认知,沟通成本远大于实际产出。尤其在大模型应用这个方向,很多设计问题只有自己动手跑过才知道答案,别人很难靠开会帮你解决。一个人做,决策链路最短,晚上想到一个方案,凌晨就能改代码验证。

这不代表一个人什么都能扛。我的原则是:确定性工程尽量自己设计,重复性编码尽量交给AI。数据库表结构、接口契约、模块边界这类东西必须人脑想清楚,因为它们一旦错了,AI生成的20万行代码会全部跟着错。AI的角色是“放大我的工程量”——我出一份明确的设计说明书,它能快速产出实现代码,我再审、再改、再压测。这个循环跑顺了,一个人确实能抵得上一个小团队。

1.2 Harness架构到底是什么:把大模型包进可控循环

Harness这个词在圈内没有严格定义,我更愿意把它理解为“模型工作流的外壳”——外部请求进来,先被解析成标准化任务,再进入调度器;调度器把任务拆成多个子步骤,每个子步骤可以调用函数、外部API、数据库,也可以跟大模型交互;每完成一步,结果都会被校验,不合格就带上错误信息回炉重试。结果是:大模型只负责“决策”和“生成”,不直接持有业务状态,所有对外行为都有日志、有重试、有监控。

为什么选这种架构而不是直接堆一个智能体聊天框?因为智能体方案最怕“不可控”:模型自由发挥,工具随意调用,出了问题只能看对话记录。Harness等于在模型外面套了一层交通信号灯,红灯停、绿灯行,走错了就绕路重来。我做数据分析平台时尤其需要这种可控性——数据不能被模型随便写错,每一步都要可回滚、可审计。用Harness架构,模型只是“车间里的机械臂”,生产线什么时候转、往哪儿转,都由调度器说了算。这种稳定性和可观测性,比裸调模型高一个量级。

1.3 九个月周期是怎么排出来的

九个月不是拍脑袋。我把任务分成两类:一类是确定性工程,比如数据库设计、REST接口开发、前端页面,时间可控,按周估算;另一类是LLM探索性任务,比如工具调用格式、提示词策略、成本优化,不确定性大,我采取“最小可行版本先行”的办法,先用简陋方案跑通,再一轮轮打磨。九个月被切成三段:前2个月打骨架,中间4个月堆功能,最后3个月重构和压测。实际进度偏差控制在了一个月以内,主要靠每个阶段预留的缓冲余量。

2. 20万行代码是怎么“长”出来的:九个月时间线拆解

2.1 第1-2个月:先做能跑通的主循环,哪怕它很简陋

前两个月的核心任务只有一个:把“调度→执行→校验→重试”的主循环跑通。我第一版主循环只有三个组件:任务队列、执行器注册表、结果校验器。为了快速验证设计,我甚至故意没接数据库,直接用内存存任务状态,先证明“模型能在一个受控循环里稳定干活”。这个决定当时看着草率,回头看是关键——Harness架构最怕的不是功能少,而是模型返回结果和业务执行器之间对不上。只有尽早暴露调度与LLM的交互问题,后期堆功能才敢放开手脚。

这段时间AI编程工具帮了大忙。我给助手描述清楚“任务对象长什么样、执行器返回什么格式”,它能直接生成一整套数据模型和接口客户端代码。但我踩了个很典型的坑:AI生成的数据模型太理想化,把所有字段都设成必填,导致实际接入业务数据时反复校验失败。从那时候起我固定了一个习惯——AI生成代码后的第一件事不是补功能,而是先审数据模型和类型定义。类型不对,后面写什么都是错的。

2.2 第3-6个月:功能堆量期,按“执行器”为单位批量生产

中间四个月是代码量增长最快的阶段,也是40亿+token消耗最凶的时期。我的工作方式变成了“执行器工厂”:每周设计2到3个新的执行器,比如数据清洗执行器、特征工程执行器、XGBoost模型训练执行器、报表生成执行器等。每个执行器提交给AI编程工具时,我会附上一段统一的输入输出协议,要求它按协议生成代码和单元测试。这一段产出速度非常惊人,AI生成的代码占到了总量的七成,但我的工作一点没轻松,因为审查和修正它们花了大量时间。

我总结了AI在这类任务上的强弱边界:它擅长写样板代码、单元测试、接口封装、数据校验逻辑;不擅长跨模块重构,也不擅长理解复杂并发场景。比如让它改一个执行器的状态流转,它常常只改局部,忘了调用方,结果编译都过不了。所以在堆量期我给自己定了一条规矩:跨模块改动必须手动设计好接口再交给AI填充,绝不让AI自己“顺便”改别的模块。这个规矩后来帮我少删了至少两万行废代码。

2.3 第7-9个月:重构、收尾和把LLM调用压出峰值

最后三个月是前半段爽快堆代码的“还债期”。第一轮重构主要清理的是AI生成代码里的重复逻辑,很多同类处理被散布在十几个执行器里,我抽出了公共基类和工具函数,20万行里大概有大几千行纯重复代码被干掉。第二轮重构改的是错误处理,把散落的try/except统一成错误分类体系,可重试错误和不可重试错误分开处理。收尾阶段还做了高并发压测,发现LLM调用是绝对瓶颈——任务一多,外部API的限流和超时就会拖垮流水线。

也是在这三个月,我真正把token成本当成了头等大事。压测期间单日token峰值能到1.5亿,如果不加控制,一个月的额度根本撑不住。我加了缓存、上下文压缩和并发限流,最终让每月消耗稳定在40亿+token的可控区间。20万行代码的构成也很值得说:核心库约4万行,上百个执行器约9万行,前端约3万行,测试代码约4万行。没有AI,这个体量一个人九个月不可能完成;没有人工审查,这个质量九个月后也不可能上线。

3. 每月40亿+ token烧在哪:AI辅助开发的成本账

3.1 先搞明白Token是怎么被悄悄花掉的

很多人觉得40亿token是个吓人的数字,其实高强度AI辅助开发下很容易达到。主要花费用在三个地方:第一是IDE里的代码补全,它每次请求都会携带当前文件的上下文,我打开一个5000行的模块文件时,一次补全请求可能就要消耗8000到10000 token,一天写代码加上反复触发补全,轻松烧掉上百万;第二是调试对话,把报错日志、堆栈和代码片段喂给模型分析,一段日志就是几千token,多轮追问后单次会话能上万;第三是我自己写的大批量自动化任务脚本,比如批量生成单元测试、批量重构接口、批量审查代码风格,一晚上处理几百个文件,消耗几千万token非常正常。

还有一个隐蔽的浪费点:上下文重复发送。很多AI编程工具的补全和对话请求会把整个文件、甚至整个项目索引都带进上下文,哪怕模型只需要看一个函数。这个问题在长文件上尤其致命,我见过一次简单的“给函数加注释”操作,实际消耗却相当于读了一篇短文档。如果不做管理,token账单会像漏水的水龙头一样流走。

3.2 40亿Token一个月的成本,按参考价算笔账

按40亿token来估算:假设其中80%是输入token(约32亿),20%是输出token(约8亿),以通用大模型API的常见参考价格——输入约每百万token 1美元、输出约每百万token 8美元——算下来是:

类型数量单价参考费用
输入token32亿1美元/百万约3200美元
输出token8亿8美元/百万约6400美元
合计40亿-约9600美元

9600美元一个月,折合人民币大概七万,听着确实肉疼。但这是“全按标准价、不打折”的上限。实际运行中我做了几件事把成本压到三分之一水平:命中缓存输入token会便宜很多,简单生成任务用轻量模型处理,只有复杂推理才用最强模型。所以标题里的“40亿+token”不是虚张声势,而是规模化使用AI编程后的真实消耗,只是最终开销比数字本身温和得多。

3.3 我实测有效的几个降本技巧

成本控制从第一周就该做,而不是月底看账单时才做。我用过的技巧里有几个效果特别明显:

  • 上下文压缩:让模型读“关键函数签名+注释摘要”而不是整个文件。我写了个脚本,自动提取文件里的类名、函数名、输入输出类型,砍掉函数体,补全和讨论都基于压缩版上下文,单次请求能从8000 token降到1500 token左右。
  • 会话分段:一个任务结束就开新会话,不把历史对话留在上下文里重复计费。遇到长任务,我会先让模型输出阶段性结论,再带着结论开新会话继续,避免几十轮对话的上下文不断翻倍。
  • 分级模型策略:能干的活不请“专家”。简单翻译、写单元测试、生成样板代码全部走轻量模型,只有架构设计、复杂调试、工具链协议设计才用最强的模型。整体token消耗不变,但单价降了一大截。
  • 高频代码片段缓存:我自己维护了一个“执行器模板库”,把常用执行器的骨架、错误处理、日志格式都固化下来。AI生成新执行器时直接基于模板改,而不是每次从零写,生成质量和token开销都大幅改善。

4. Harness架构应用的核心实现:调度、状态与失败处理

4.1 核心调度循环是整栋楼的地基

Harness架构里最关键的是调度循环。我第一版写得很简单,基本就是“从队列拿任务,找执行器跑,校验结果,不对就重试”。这个循环跑通之后,后面所有功能都是往这个圈里加东西。简化版的调度循环大概长这样:

# harness core loop (simplified) while task_queue: task = task_queue.dequeue() executor = registry[task.executor_name] result = executor.run(task.payload) if not validator.check(task, result): task.retries += 1 backoff = 2 ** task.retries task_queue.requeue(task, delay=backoff) else: outbox.emit(task, result)

这段代码虽然只有十来行,但决定了整个系统的性格:任务和执行器解耦,校验失败自动重试,重试有指数退避,成功结果走outbox异步分发。后来加的高并发、优先级、定时调度都长在这个骨架外面。你可以把调度循环想象成餐厅的传菜台:菜单进来排队,后厨有人接单,菜品出锅先经过检验,不合格退回重做,合格的才端给客人。传菜台的规则稳了,整个餐厅才不会乱。

4.2 工作流状态机:每个任务都有明确的身份证

复杂任务跑起来之后,单靠队列和重试就不够了。我给每个任务引入了状态机,把任务的生命周期明确定义出来。项目里的状态切换记录在案,方便定位问题是出在调度、执行、还是校验环节。

状态含义可转移到
pending等待被调度running
running执行器正在处理succeeded / failed / requeued
succeeded校验通过completed
failed执行出错且不可重试completed
requeued需要延迟重试pending

状态机带来的直接好处是:一个任务卡在哪里、为什么卡住,打开表格一眼就能看到。我还给每个任务分配了全局唯一的task_id,所有日志都带这个ID,排障时按ID聚合日志就行。没有状态机的时候,任务失败后到底有没有重试、重试了几次,全靠猜,这在生产环境是不可接受的。

4.3 失败重试与幂等设计是稳定性之本

Harness架构里最容易被低估的是幂等设计。任务重试意味着同一个执行器可能会被调用两次,如果这个执行器不是幂等的,数据就会被重复处理。我的解决办法是给每个任务生成一个request_id作为幂等键,执行器在处理前先查“这个幂等键处理过没有”,处理过就直接返回旧结果。存储型执行器尤其依赖这个机制,写数据库、发消息、调外部API,都必须带上幂等键。

我还把错误分类成两类:可重试错误和不可重试错误。网络超时、服务端5xx属于可重试;业务参数错误、数据格式非法属于不可重试,重试一万次也是白搭。调度循环里的validator.check就是错误分诊台,它判断错误类型后决定到底重新入队还是直接标记失败,这个判断让我少走了很多弯路。

4.4 让大模型“按格式干活”:协议与结构化输出

Harness要是直接让大模型自由发挥,结果一定乱套。我的做法是给模型规定严格的输入输出协议:输入是标准化的任务对象,输出必须是符合JSON Schema的固定格式。比如让模型判断一个数据清洗步骤是否完成,它必须返回{"status": "success", "summary": "...", "score": 0.95},少一个字段就算校验失败。

校验失败后不是直接放弃,而是把校验错误信息重新喂给模型,让它自己修。这个“生成→校验→反馈→再生”的循环是Harness的精髓,类似于人写代码时跑测试看报错再改:模型第一次输出可能有缺陷,但有了反馈信号后,第二次第三次的准确率会明显上升。我在执行器协议里都会写清字段含义和示例,用上这招之后,模型的结构化输出成功率从60%左右提到了95%以上。

5. Token体系实战:登录、续签与那些让人崩溃的错误

5.1 Access Token和Refresh Token到底是什么关系

做应用登录认证离不开Token,我在这块也啃了不少硬骨头。简单说,access token是短期凭证,像停车场的临时卡,有效期短,过期就得重新领;refresh token是长期凭证,像车牌号,可以用来换新的临时卡。JWT(JSON Web Token)是现在最常见的access token格式,因为它自带签名和过期时间,服务端不用存session就能校验。

但JWT有个天然痛点:一旦签发,在过期前没法主动撤销。所以主流的做法是“短access token + 长refresh token”:access token设30分钟到2小时过期,refresh token设7到30天,并且可以旋转。用户快过期时,前端用refresh token去换新的access token,用户无感知。这样的好处是即使access token泄露,攻击窗口也很短。

5.2 我给应用做的JWT续签方案

我实现续签逻辑时,写了一个简单的token管理模块,核心思路是提前几分钟检查过期时间,快过期就用refresh token换新。简化代码长这样:

import time import jwt def get_valid_token(access_token, refresh_token, access_expires_at): # 提前5分钟检测,避免请求打到一半token失效 if time.time() > access_expires_at - 300: new_pair = exchange_refresh_token(refresh_token) return new_pair["access_token"], new_pair["refresh_token"], new_pair["expires_at"] return access_token, refresh_token, access_expires_at

这里有个容易踩的坑:refresh_token只能用一次,如果客户端并发多个请求同时去续签,后到的那个会失败。我的解法是在续签路径上加一个线程锁,保证同时只有一个续签请求在跑,其他请求等待新token下发。每次续签还会检测refresh_token是否被重复使用,一旦发现重复使用就判定为泄露,让整个会话失效,强制重新登录。

5.3 高频报错排查实录:一张表帮你定位Token问题

开发期间和上线后,我被各种token报错折磨过。下面这张表是我根据实战整理的速查手册,报错信息和排查思路都验证过。

报错信息可能原因排查与解决
sign-in could not be completed token exchange failedOAuth授权码换token时出错,回调地址不一致或授权码已过期核对redirect_uri与注册配置完全一致,开启PKCE,重新走授权流程
token endpoint returned 403 forbidden客户端账号权限不足或被服务端策略拒绝检查账号是否启用、scope是否合法、请求头是否正确
failed to refresh token: 400 bad requestrefresh_token为空、格式错误或已经失效检查token存储字段没被截断,重新登录生成新refresh_token
your access token could not be refreshedrefresh_token旋转失败,服务端已把它标记失效清理本地缓存凭证,让用户重新登录
auth token is unavailable本地凭证文件不存在、格式损坏或环境变量没加载重新登录生成凭证文件,检查环境变量和配置文件路径

还有一个我经常忽略的原因:服务器时间漂移。JWT的签名和过期时间校验依赖系统时间,服务器时间不准,会直接导致“token居然提前过期”的诡异现象。排查这个只需要在服务器上对一次时间,偏差大就启用NTP时间同步,能解决一大批莫名其妙的认证问题。

5.4 和AI编程工具联调时的认证坑

项目开发期间我每天跟AI编程工具打交道,工具的登录态问题也踩了不少。最常见的是token exchange failed类错误,通常发生在登录授权流程里:授权端点返回异常、回调地址不匹配、本地缓存凭证过期。这些错误看起来吓人,排查思路很固定——先看本地配置文件里凭证是否存在和过期,再看登录流程有没有走完,最后确认账号权限。

我的建议是:使用AI编程工具的CLI登录时,尽量一次性走完整授权流程,不要在多个终端窗口同时触发登录;本地凭证文件要做好备份,因为不少工具一旦凭证失效就是“请完全登出再重新登录”,不会自动修复。另外,环境变量配置要认真检查,很多API客户端会优先读某个环境变量,配置了旧值会导致新token不生效,表现就是“明明重新登录了还是报认证失败”。

6. 一个人维护20万行代码:经验与边界

6.1 怎么审查AI生成的代码,才不会变成“高级Ctrl+C”

AI写代码速度快,但质量是参差的。我给自己总结了一套审查流程:先看类型和接口定义,再看异常处理,最后看并发和资源释放。类型不对,直接打回重写;没有异常处理的代码,跑一次真实数据就会露馅;涉及文件句柄、数据库连接、线程池的代码,必须人工确认资源释放,不能全信AI。审查不通过的部分,我会把具体问题反馈给AI让它改,而不是自己默默改,这样它能慢慢学会我的代码风格。

我还有一个私人的“熔断规则”:同一个模块如果AI连续三次生成还需要大改,我就停下手来自已写,不再跟它耗。因为这说明这个模块的复杂度超出了它从上下文里能理解的范围,继续生成的边际成本很高。宁可自己写两小时,也别跟AI耗半天的对话token。

6.2 模块边界和命名比想象中更重要

20万行代码靠记忆力维护是痴人说梦,模块边界是唯一可靠的导航。我把系统分成核心调度层、执行器层、基础设施层三层,每一层有明确的依赖方向:执行器只能调用基础设施,不能反向触碰调度核心。这个约束我在代码评审时盯得很死,一旦发现执行器里出现了调调度器的代码,一律重构。

命名规则也吃了不少亏才定下来。执行器统一定义成“动词+对象”格式,比如clean_data、train_model、generate_report,任务状态统一用枚举而不是魔法字符串。AI生成的代码里经常出现process_data_v2_final这种命名,我会统一清理掉。规范命名不是为了好看,是为了让AI在下一次生成时也能遵循同样的协议,减少沟通成本。

6.3 给也想“一个人+AI”做项目的朋友的建议

如果你也在考虑一个人加AI做大型项目,我最大的建议有这三条:

  • 先把主循环跑通,再谈漂亮功能。无论做什么系统,先用一个最简陋的版本打通端到端链路,后面所有功能都是往这个链路上挂新节点,而不是推倒重来。
  • 把token账算明白再动手。不要等账单出来了才优化。从一开始就做上下文压缩、分级模型、缓存策略,否则成本失控会让你在项目中途被迫换方案。
  • 给不确定任务留缓冲。排期时先搞定确定性工程,把LLM探索性任务前置到项目前期,因为模型协议、提示词策略这类问题不试不知道答案,留到最后会挤掉重构时间。

我的体会是:AI确实能让一个人干出十个人团队级别的代码量,但架构决策、问题定界、成本控制这些核心能力,最终还是得靠自己撑起来。项目上线三个月后,我正在做的是把这套Harness内核抽出来,做成一个可复用的调度底座。如果你这也是一个人在硬扛复杂项目,记住一句话:先让主循环跑起来,再谈剩下的。九个月听起来很长,但真的足够造出一款能稳定运行的应用,前提是每一步都踩在可控的节奏上。

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

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

立即咨询