ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

t3code:让AI代码生成从玩具走向工程实战

t3code:让AI代码生成从玩具走向工程实战 在AI写代码这件事上我发现一个很残酷的现实很多人不是不会用AI而是被AI写出来的“幻觉代码”坑得死去活来。尤其是当你接手一个需要严格遵循团队规范的工程化项目AI补全的代码常常看起来头头是道一编译全是错一Review全是雷。我之前也深陷这个泥潭直到自己动手折腾了一套内部代号为t3code的AI编码辅助工作流才算是把“AI结对编程”从玩具状态拉到了能真刀真枪干活的状态。这个 t3code 并不是什么商业产品而是我在实际项目里反复打磨出的一套工程化代码生成与审查实践。它的核心理念很简单让AI写代码“快、准、专”。快是指从需求到代码初稿的时间压缩到分钟级准是指生成的代码严格贴合项目上下文而不是泛泛的Demo模板专是指所有输出都收敛在团队既定的架构约束里。这篇文章我打算把整套方案从思路到落地、再到实战拆解和踩坑记录全部摊开来讲希望能给正在折腾AI辅助开发的读者省下几个晚上的折腾时间。1. 内容整体设计与思路拆解1.1 为什么不是单纯“让AI写一段代码”单纯让AI“帮我写个订单模块”你会得到一份看着完整、实际哪哪都不能用的垃圾代码。这不是模型不行而是提问的方式错得离谱。真实工程环境里有大量上下文是模型不知道的数据库里t_order表有哪几个索引、支付回调是异步还是同步、鉴权是在网关层还是业务层、日志规范要求用slf4j还是log4j2。你如果这些一概不提AI只能按训练语料里最常见的那套Spring Boot MyBatis Plus的习惯去写跟你们组的代码风格大概率八字不合。t3code 的出发点就是解决这个信息断层。它不是一次性的问答而是把“项目骨架”“模块约定”“数据模型”这些静态信息提前喂给模型让每次生成代码都像是有一个很了解你们项目的结对程序员坐在旁边你说一他补全二。1.2 三个核心维度的取舍逻辑我把 t3code 的整套思路拆成了三个维度去构建这也是名字里3的由来第一维度是工程上下文。代码生成工具必须能看到你项目的目录结构、核心依赖、命名规范和关键业务表结构。工具拿到这些信息后才能生成与现有代码风格一致的代码而不是随手给你一套另一个项目的代码风格。第二维度是Prompt工程。这个维度很多人会忽略其实恰恰是决定质量的关键。你需要给模型定义清晰的指令边界包括输入参数、输出格式、禁止事项。我会在后面的章节把具体的Prompt模板展开来讲。第三维度是质量闭环。生成代码不是终点而是起点。你要在AI生成之后马上接上编译检查、单测跑通、静态扫描这三板斧把AI当成一个产出速度极快的初级工程师但验收标准不能降低。这三个维度同时满足AI才从“聪明但不可靠的实习生”变成“效率极高的结对伙伴”。如果你只做其中一两个短期看起来省了时间长期会为填坑付出更多精力。1.3 这套方案的适用边界说句实在话t3code 不是所有场景都适用。经过我这段时间的实测它在结构化程度高的业务代码面前表现极佳比如CRUD接口、批量导入导出、定时任务脚本、配置转换类代码。这类代码有规律可循数据库表结构已经固定AI非常擅长。但如果是那种探索性很强的逻辑比如一个全新的推荐算法实现、复杂的跨模块状态机流转AI更多的还是给你一个思路参考指望它直接出成品不现实。我对这类需求的处理方式是只让AI出方案和伪代码核心实现还是自己动手或者拆得更细之后一块块生成。所以如果你指望一个工具解决所有编码问题可以先调整一下预期。t3code 本质解决的是“重复编码的自动化”而不是“创造性编程的自动化”。2. 核心细节解析与实操要点2.1 工程上下文的采集与组织方式想要让AI生成贴合项目风格的代码最关键的第一步是给AI提供足够的项目信息。我见过很多人把整个仓库几十个文件的代码一股脑塞给AI这是错误示范。上下文窗口有限塞太多无关代码进去真正有用的信息反而被稀释生成的代码质量断崖式下跌。我的做法是把项目上下文分三层采集按需取用。第一层是项目级约定通常是一个自定义的 PROJECT.md 文件放在仓库根目录内容包含技术栈版本、分层架构规范、命名约定、日志规范、统一返回结构说明、异常处理约定。这个文件是手工维护的大约一页纸的量但它承担着让AI“入乡随俗”的作用价值远高于一堆散乱的旧代码。第二层是模块级信息针对每个业务模块维护一段 brief比如模块职责、涉及的核心表、对外接口清单、与其他模块的依赖关系。这些信息不需要每个类都写挑关键的和容易踩坑的解释清楚就行。第三层是当前任务涉及的具体代码片段这个直接从编辑器里选中复制。比如你要在某个Service里加一个方法就把这个Service现有代码贴进去再附上对应的Mapper接口和实体定义AI就能准确沿用现有的语义和风格。这三层信息配合使用我实测生成代码和项目的匹配度能从50%提升到接近90%。说到底AI写代码很像新同事接手项目你把项目文档写得越清楚他上手越快。2.2 Prompt模板的设计思路Prompt设计是整个 t3code 里最吃功夫、回报也最明显的环节。我用的完整模板结构大概长这样你现在是[项目名]项目的资深开发请严格按照以下项目规范完成任务。 项目背景 [粘贴PROJECT.md中的核心约定] 模块说明 [粘贴模块brief中的内容] 任务目标 [一句话清晰描述要做什么比如在OrderService中新增一个分页查询订单方法] 输入条件 [方法入参、查询条件、涉及的表结构尽量贴原始字段名] 输出要求 1. 只输出代码不要输出解释性文字 2. 代码风格与现有代码保持一致方法名遵循项目命名规范 3. 必须包含必要的参数校验返回统一包装类型ResultT 4. 生成的代码不得引入现有项目之外的依赖 5. 核心逻辑项加注释说明取舍原因 已有参考代码 [粘贴当前文件或同模块相似代码]这个模板看着不复杂但每条都有它的作用。特别是“输出要求”里的四条约束直接决定了AI是老老实实按项目规则来还是放飞自我写一套漂亮的“通用代码”。你们也完全可以按自己团队的情况增删规则。2.3 代码块的落位与重构策略AI生成的代码我不会直接用而是先落位再重构。所谓落位就是把它放到真实的文件路径里让它和周围的真实代码相处。直接在新文件里看AI的输出很容易觉得“还不错”一旦放到项目里就会发现各种问题没引入就必须的import、忽略了已有的常量类、返回结构跟网关层约定不一致。我通常会先把AI输出放进一个临时目录然后对照项目编译一下让编译器替我挑毛病的头一遍。这个过程基本能暴露所有低级错误。接着我会逐行读一遍重点看三个地方有没有绕过已有基础类自己造轮子、有没有硬编码的魔法值应该抽取成常量、有没有该用事务的地方只在方法上简单贴了个注解。重构之后这段代码才能真正记到你的账上。你自己Review过的代码后面出问题才能快速定位这是AI替代不了的。3. 实操过程与核心环节实现3.1 从需求到代码初稿的完整流程我这边的需求来源绝大多数是团队内部的业务流转在迭代排期下来之后开发和测试的同事会同步一份需求说明。我拿到需求后的第一件事实是整理上下文而不是直接开写。先把涉及的页面、接口、表结构梳理成一页文字说明这份说明不仅是给AI看的也是留给自己做任务拆解的。第二步把这页说明里的关键信息提取出来按照上面的Prompt模板组装成一个完整请求。我习惯把请求拆成多次对话一次只让AI做一个原子级别的任务定义一个DTO、写一个Mapper方法、实现一个Service逻辑、生成一组单元测试。第三步AI返回初稿后我会用10分钟左右做一次快速审查把明显不符合要求的点标出来然后带着修正指令让AI重新生成。多数情况下两三轮就能收敛到可落地的质量水平。这套流程下来一个中等复杂度的接口从需求确认到代码提交差不多在一个小时内搞定。没有t3code这套流程之前同样的工作量我得花大半天而且很多还是在重复之前的旧代码套路。3.2 实战案例订单查询接口的生成与校准拿一个真实场景来说需求很常见分页查询订单列表支持按订单号、买家手机尾号和订单状态筛选。我先把相关表结构整理成文本表 t_order - id bigint PK - order_no varchar(64) unique - buyer_id bigint - buyer_mobile varchar(16) - status tinyint (0待支付 1已支付 2已发货 3已完成 4已取消) - pay_type tinyint - total_amount decimal(10,2) - create_time datetime - 索引 idx_status(status), idx_buyer_id(buyer_id), idx_create_time(create_time)然后带上模板中提到的项目约定和OrderMapper、Order实体类的现有代码一起发给AI。一次生成的结果核心代码长这样Override public PageResultOrderVO pageQuery(OrderQuery query) { LambdaQueryWrapperOrderDO wrapper Wrappers.lambdaQuery(); if (StrUtil.isNotBlank(query.getOrderNo())) { wrapper.eq(OrderDO::getOrderNo, query.getOrderNo().trim()); } if (query.getBuyerMobileTail() ! null) { wrapper.apply(RIGHT(buyer_mobile, {0}) {1}, query.getBuyerMobileTail().length(), query.getBuyerMobileTail()); } if (query.getStatus() ! null) { wrapper.eq(OrderDO::getStatus, query.getStatus()); } wrapper.orderByDesc(OrderDO::getCreateTime); PageOrderDO page orderMapper.selectPage( new Page(query.getPageNum(), query.getPageSize()), wrapper); return PageResult.of(page.getTotal(), page.getRecords().stream() .map(orderDO - orderConverter.toVO(orderDO)).toList()); }讲道理这个初稿质量已经可以打80分。但站在我自己的项目规范角度有两个问题必须修正一是手机尾号的过滤用了数据库函数RIGHT不走索引数据量上来一定扛不住二是查询排序固定写死为createTime倒序灵活性不够。我给出修正指令手机尾号过滤改成在应用层做用buyer_mobile直接条件查出候选集合再二次筛选并补充排序字段的白名单映射。第二轮生成的结果就把这两块都处理好了。这一来一回就是t3code的价值既有AI的高效产出又有人工的工程判断。3.3 基于上下文的代码补全与建议除了整段生成t3code 在代码补全上的表现也很有实用价值。我把项目上下文喂给模型之后在IDE里写代码它能根据你的光标继续预测而且预测结果非常贴近项目风格。这个我建议大家都尝试一下做法未必复杂只要你把项目约定文档作为长期对话的System Prompt前置在会话里后续所有代码补全请求都会自动带上这些上下文。比如你在写一个方法敲到一半停止让模型补全剩余部分它会更愿意沿用你刚写的局部变量命名习惯也更了解你这个项目里状态码定义在哪、统一返回结构是谁。不过补全和整段生成有个本质区别补全的准确率还不如整段生成稳定。因为你在IDE里写的代码往往是碎片化的模型看了上文去猜下文猜错的情况不少。我的经验是补全建议只接受那些语义明确、模式重复度高的场景比如写单元测试、配置类字段映射。涉及核心业务逻辑宁可是整段生成再手工调整效率反而更高。4. 常见问题与排查技巧实录4.1 生成代码与现有项目风格冲突这个是我被问得最多的问题也是我初期最头疼的问题。AI的默认审美偏向通用范式喂给它一个新项目它很自然会用那种教科书式的写法。最典型的就是返回结构AI写出来的方法往往是直接返回实体或Map而我们的项目约定是统一返回Result 非要从头到尾改一遍不可。问题根源在项目约定没有被有效传递。很多人以为把代码仓库前缀给AI就行了实际上模型面对的是一大堆无关冗余文件真正规范性的东西很容易淹没在噪声里。我猜你们也有过类似体验把整个README扔进去AI生成代码的水平并没有提升多少。我自己验证过的有效办法是建立一份专门的项目约定文件站在代码生成的角度去写而不是站在项目文档的角度。这份文件里要写清楚分层职责、命名规范、依赖注入风格、异常处理策略、业务状态机的规定值、统一结果封装类全限定名。这份文件要保证AI每次请求都能读取到效果立竿见影。4.2 生成代码引入未知依赖AI在生成代码时有一种无法克制的冲动就是总想用全局最优解。你让它写一个日期转换它就顺手给你引入Apache Commons Lang或Hutool工具类你让它写个JSON解析它就不知不觉给你加进去一个阿里巴巴Fastjson。问题在于很多团队的依赖管理是有严格审批流程的新增依赖要走架构评审。AI随手引入一个依赖等于把评审负担又丢回给你。我的处理方式是在Prompt模板里强制加上一条“生成代码仅允许使用已有项目依赖不得新增任何maven或npm依赖”并在代码落位检查时用IDE的依赖分析面板扫一遍确认没有隐式引入未声明的包。如果你发现AI生成代码里总按它自己的想法添加依赖最好的办法不是后续手动删而是先补上这条约束重跑。4.3 结果不稳定AI输出偏离预期同一个需求同一个Prompt跑两次出来的代码不一样这种情况我见得太多了。最气人的是第一次结果还凑合第二次反而变得很离谱。这背后的原因有模型采样策略、上下文顺序变化、甚至请求本身的细微表述差异。我的做法是让任务描述和约束条件保持极度稳定把项目约定、模块说明、输入条件都统一保存在文档里复制粘贴而不是每次临时输入文字。同时在Prompt的“输出要求”里用强语气动词比如“必须”“严禁”提升约束的匹配权重。还有一个技巧是给AI设置“角色悬崖”告诉它如果生成结果不能满足约束条件就不要给出代码而是说明哪里不满足以及需要补充什么信息。这个做法能逼AI在条件不充分时主动反馈项目上下文缺失项而不是闷头硬憋一份不合格答案。过了这个坎之后输出的稳定性提升非常明显。4.4 AI生成的单测代码不可直接信任很多人觉得让AI写单元测试是捡到宝了实际上AI写单测的正确率比写业务代码低得多。它常常会凭空捏造Mock方法参数、忽略依赖注入的初始化、把多个依赖关系完全臆想出来导致单测跑起来百分之百报错。我的做法是把单测任务单独拆出来带上源码和测试基类的现有写法并明确告诉AI测试基类已经提供了哪些Mock工具。比如项目里已经有BaseTest提供了MockMvc和MockUser的构造方式单测生成就必须基于这个去写不得自己再new一套。然后在单测生成之后先干跑一遍把全部编译错误和断言失败修完再审查Mock粒度和断言条件。实际上经过两三轮修正后的AI单测仍然有大约20%左右的断言逻辑不对多发生在边界条件和异常分支。所以我现在把AI单测定位成“主要路径覆盖的生产者”而边界分支的单测我一定会亲自补写。这样既保证了效率也把正确性的风险牢牢控制在手里。5. 适用边界与方法论沉淀5.1 什么样的团队适合引入这套工作流t3code这套方法说到底是把工程规范前置到Prompt里然后靠反复校验来收敛质量。所以它对工程化成熟度有一定的要求。如果你的团队还在起步阶段代码风格都还没定架构天天在变我建议你先别急着上这套工作流这会变成不断改Prompt和不断废掉重写的恶性循环。反过来如果团队已经有了清晰的规范文档、稳定的技术栈、Maven或Gradle管理着依赖项目结构也分层合理那这套工作流的价值会非常大。我自己带的小团队就是这样的状态引入之后新人上手写代码的速度明显加快因为他们可以通过AI快速产出符合规范的初稿再靠代码审查把不完善的地方打磨掉。坦白说t3code的门槛不在技术实现而在于你团队愿不愿意花一点时间把项目约定文档化。这个前期投入对于几百行的短文来说是值得的很多团队缺的不是工具而是把规则说清楚的耐心。5.2 任务拆分的粒度把控我和AI协作这么久的最大体会是任务粒度直接决定结果质量。把“开发订单模块”这种大任务丢给AI你得到的就是一堆需要推倒重来的废纸工程但把“将订单列表页的筛选条件与后端接口参数对接”这种原子任务丢过去基本一次成型。我习惯把一个模块拆成五个层级的任务领域对象定义、存储层方法、业务逻辑方法、接口适配、测试用例。每一层级都是一个独立的对话上一层的输出作为下一层对话的参考代码。这样做的额外好处是每一层可用清晰验收哪一层不行就单独重来哪一层不会殃及整体。拆分粒度还取决于代码结构的复杂度。复用度高的CRUD接口可以适当放宽模块之间存在复杂依赖关系的任务必须拆细。拿不准的时候宁可拆细一点后期的返工成本比你多发起一次请求的成本高多了。5.3 从工具到工作方法的沉淀工具的意义永远不只是替代重复劳动更重要的是逼你去重新思考自己的工作方式。我在整理t3code的过程中最大的收获其实不是代码生成效率的提升而是重新梳理了团队的工程规范把以前口口相传的各种约定变成了白纸黑字。这些东西过去全凭老师傅脑子里记新人踩坑踩到怀疑人生。现在上了这套流程之后项目约定文档是活的每次Review或者踩到新坑我都会顺手更新它。AI生成的代码因为能看到最新的约定文本等于每一次迭代都在自动对齐团队的认知这个滚雪球效应是我完全没有预料到的。我现在回头看t3code真正做的事情是把“经验”从人脑里搬到了结构化的Prompt和工程文档里让它变成可复制、可持续利用的资产。如果你也在折腾AI辅助编码我的建议很直接不要一上来就追求全自动生成先试着把你们项目的规范文档化找一个简单模块跑通整条链路再逐步扩大范围。全自动是终点不是起点。最后一个实用技巧分享给大家把你们组代码Review中常被指出的问题整理成一份“NegativeExamples”清单模板放在Prompt里当反面教材。这比任何正向约束都管用模型看到具体的反面例子才知道你的项目里什么事情坚决不能干。我用了三个月时间踩过无数坑之后才攒出这份清单现在AI生成代码的Review通过率已经从最初的不到三成提升到了八成以上效率提升带来的好处肉眼可见。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进