AI写代码的真实边界与工程化工作流:从代码审查到Agent开发
前几天跟一个做后端的朋友聊天他说他们组现在提测的代码大概有七成是AI先写出来的草稿人只负责改。我问他改起来累不累他想了想说写是快了但审的时候心累因为AI写的代码看起来永远是对的直到你真正压测它。刚好这几天刷到一条挺有意思的消息——一家以AI为主业的公司公开讲他们内部大约80%的代码是AI生成的同时又呼吁对更强大的AI研发踩一脚刹车。一边把AI用到底一边喊大家慢一点这个画面本身就值得琢磨。我这篇想聊的不是那条新闻本身而是把AI写代码这件事从口号落回到键盘上它现在到底能干到什么程度我们这些天天用AI编程的人真实的工作流长什么样哪些环节必须人来兜底以及如果你正在准备往AI应用开发或者AI agent开发方向转该怎么规划。不管你是刚接触代码的新手还是已经用AI辅助写过半年项目的开发者下面这些内容应该都能对上号。1. 从80%代码是AI写的说起这个数字到底在说什么1.1 代码占比这个指标本身就有很大水分先把这个80%拆开看。代码行数占比是所有研发指标里最容易被注水的一个。一段由AI生成的样板代码比如实体类、DTO、接口定义、配置文件往往几十行起步而真正有技术含量的核心逻辑可能就十几行。AI最擅长的恰恰是那种结构固定、模式重复、写完就很少再动的代码它在总行数里的占比天然就高。所以当你听到80%代码是AI写的首先要问的是统计口径是行数、是提交次数、还是按功能模块算如果按行数那这个数字其实说明不了太多问题它更多反映的是重复劳动被替代了多少而不是AI的编程能力到了什么水平。我自己统计过一个小项目一共一万两千行左右纯手写的核心业务逻辑大概两千行剩下的一万行里模型层、数据库迁移、路由注册、参数校验、日志埋点、单元测试骨架这些确实大部分是AI生成的。但从如果这段代码出错系统会怎样的角度看那一万行里出问题顶多是某个字段映射错了、某个校验规则漏了而两千行核心逻辑里出问题可能是资金算错、权限越界。这两类代码的风险等级完全不是一回事把它们混在一起谈占比参考价值很有限。真正有意义的指标我个人更推荐这几个AI生成代码经过人工修改的比例、AI生成代码在测试阶段的缺陷密度、以及核心模块中人工重写的比例。前两个衡量的是AI产出质量第三个衡量的是人对AI的信任边界。这三个指标比单纯的占比更能告诉你一个团队的AI编程到底成熟到了哪一步。1.2 为什么偏偏是AI公司自己出来喊停这件事最有意思的地方在于发声的主体。按理说最有动力把AI往前推的就是做AI的公司可偏偏是他们出来说慢一点。我理解的逻辑有几层。第一层是技术现实当模型生成代码的比例高到一定程度团队对代码库的整体理解会下降。代码是人写的人脑里会有我当时为什么这么写的记忆代码是AI写的这个记忆是缺失的后面接手的人只能靠读代码去反推意图维护成本其实是在上升的。第二层是质量与责任的错配。AI可以快速产出大量看起来能跑的代码但一旦上线出事故承担后果的是人和团队不是模型。当一个组织的代码库里塞满了没人真正读懂的AI代码这个组织的技术债就在以看不见的方式累积。第三层更现实当AI能写代码、能自我改进、能参与训练下一代模型整个迭代闭环的节奏就脱离了人工审核的节拍。这时候喊停本质上不是反对技术进步而是承认我们的治理能力还没跟上生成能力。对我们普通开发者来说这条消息真正的价值不是要不要暂停而是它把一个平时被我们忽略的问题摆到了台面上我们用AI写代码的爽感和我们审核AI代码的能力是不匹配的。爽感是即时的审核是滞后的这个时间差就是风险窗口。1.3 对天天用AI编程的人来说真正的信号是什么把这条消息翻译成日常动作我总结了三个提醒。第一个提醒是别让能跑变成你的验收标准。AI生成的代码最大的迷惑性就是它经常真的能跑通但能跑通和写对了是两回事。边界条件、并发场景、异常输入、资源回收这些才是真正考验代码的地方而恰恰是AI最容易糊弄过去的地方。第二个提醒是把理解代码当成硬性产出而不是可选项。你可以让AI写但你必须读。读不懂的代码不要合进主干。这条听起来像废话但实测下来很多团队的AI代码评审就是走个形式扫一眼觉得逻辑没问题就过了。我的做法是凡是AI生成的核心逻辑我都要能用自己的话把它复述一遍讲不清的就重写。第三个提醒是给AI的使用划出明确的边界清单。哪些类型的文件可以让AI一把梭哪些必须逐行看哪些干脆禁止AI碰这个清单每个团队都该有而且要写进协作规范里。没有边界的AI编程短期是效率长期是负债。2. AI写代码的真实能力边界哪些能交、哪些不能交2.1 可以比较放心交给AI的几类代码用了这么久我大致能摸出AI编程的能力地形图。有几类代码交给AI基本不会出大问题甚至比人写得还规范。第一类是模式高度固定的样板代码比如数据模型定义、接口的参数与返回值结构、常量表、枚举、路由声明。这类代码的特点是有明确的模板逻辑分支少AI见过的样本极多产出稳定。第二类是标准化的工具函数比如字符串处理、时间格式化、常见的数组与集合操作、简单的加解密封装。这类代码边界清晰输入输出明确只要你把需求描述清楚AI给的实现通常没问题你重点检查一下空值、越界、编码这几个点就够了。第三类是测试代码的骨架。让AI根据函数签名生成单元测试用例是投入产出比非常高的一件事它会帮你想一些你懒得想的边界输入比如空字符串、超大数值、负数、Unicode 字符。我有一次让AI给我一个金额计算函数写测试它补的用例里有两个是我自己没想到的一个涉及浮点精度一个涉及货币单位混用这两个后来都成了真实 bug。第四类是文档与注释包括接口文档、模块说明、注释补全。AI在这块非常擅长尤其是把一段没有注释的代码补上清晰的说明比自己写省事得多。但要注意AI写的注释有时会和实际逻辑不符尤其是当代码本身写得有点绕的时候它会按看起来应该是什么去描述而不是实际做了什么。所以注释也要跟着代码一起审。2.2 必须人工死磕的关键部分和上面相对的另一端是几类我绝对不敢完全交给AI的代码。排在最前面的是涉及权限与身份的判断逻辑。这类代码的复杂性不在于代码本身而在于它背后的业务规则、层级关系、边界场景。AI不知道你系统里管理员到底有几个层级也不知道某个角色在特定状态下应该被降权它只能按最常见的模式给你写一个看起来合理的判断而这个看起来合理很可能就是个越权漏洞。第二类是资金、库存、积分这类涉及数值核算的逻辑。浮点精度、事务边界、并发扣减、幂等处理这些坑AI经常会踩而且是那种测试不容易发现的踩法。我见过AI写的扣减逻辑单线程测完全正确一上并发就超卖。这类代码我会坚持手写核心计算部分AI只能用来帮我检查有没有遗漏的边界情况。第三类是并发与异步相关的代码。锁的粒度、死锁的可能、线程池的配置、异步任务异常的处理这些问题的正确答案高度依赖具体场景AI给的通用方案经常是教科书正确但生产环境翻车。比如它很爱用一把大锁把整段逻辑包起来逻辑上确实不会出并发问题但性能上可能是灾难。第四类是错误处理与降级逻辑。AI写错误处理有一个典型倾向就是吞异常——捕获了异常之后打条日志就完事不区分可恢复和不可恢复不做重试也不做降级。这种代码在正常情况下看不出问题一旦依赖服务抖动整条链路就会以奇怪的方式失败。第五类是安全相关的代码包括输入校验、SQL 拼装、文件上传、序列化与反序列化。这块的坑太多太细AI生成的代码往往是能防住明显攻击但防不住老练的攻击。这类代码我的原则是可以借助AI查资料、看思路但具体实现必须由熟悉安全的人来写和审。2.3 一份可以贴在团队墙上的AI代码验收清单把上面的经验整理成一张可以直接用的对照表判断一段AI生成的代码该给它多大的信任额度。代码类型AI参与度人工必做动作风险等级模型/DTO/常量定义可全权生成检查字段类型与默认值低通用工具函数可全权生成补边界输入测试低接口路由与参数校验生成后复核核对校验规则完整性中业务查询与聚合逻辑生成草稿逐行理解补集成测试中高权限与身份判断仅作参考人工重写安全评审高资金/库存数值核算仅作参考人工实现并发压测极高并发与异步任务生成草稿压测、故障注入高异常处理与降级生成草稿梳理失败路径逐个确认高安全相关实现仅查思路专业实现与审计极高这张表不是死规定逻辑是让团队在交给AI的爽和出事之后的痛之间有个明确的刻度。我的经验是把这张表在评审时拿出来对一遍能拦住相当一部分看起来没问题的AI代码。3. 我自己的AI编程工作流从需求到上线的完整链路3.1 需求拆解与提示词组织决定了产出质量的天花板很多人抱怨AI写的代码不靠谱其实很多时候问题出在需求表达上。你把一个模糊的大需求丢给AI它只能按最常见的理解给你一个大而全的答案里面必然塞满你不想要的东西。我的习惯是把需求拆到一个函数、一件事的粒度再给AI比如不要写帮我做一个订单模块而是写实现一个函数输入订单号和用户ID返回该订单的明细要求校验订单归属当前用户订单不存在时抛出业务异常返回结构包含商品、数量、单价。提示词里我固定带上四样东西输入输出的明确结构、必须遵守的约束比如不要引入新依赖使用现有的异常类型时间统一用 UTC、上下文把相关的已有代码一起贴给它、以及验收标准给出对应的单元测试。你给的信息越具体AI的自由发挥空间越小产出越接近你想要的。这一点有点反直觉——很多人以为给AI越多自由它越聪明实际上在工程场景里正好相反收敛才是稳定产出的前提。还有一个我踩过的坑不要在一次对话里让AI什么都做。让它一口气写五个模块它会在后几个模块里偷懒用省略号跳过实现或者把前面的接口约定记混。我现在的做法是一个大功能拆成几轮对话每轮专注一个模块写完立刻验证通过了再进下一个模块。3.2 生成、审查、测试的三步循环比一次性让AI写完靠谱得多我现在的工作节奏基本固定成一个三步循环。第一步是生成让AI产出草稿这一步不求完美只求有东西可改。第二步是审查我自己逐行读重点看三件事逻辑分支是否完整、异常路径是否处理、命名和风格是否与项目一致。这一步我会把不符合规范的地方直接改掉而不是让AI改因为让AI改容易改出新问题反复拉扯不如自己动手快。第三步是测试而且我要求AI在写实现的同时把测试一起给出然后我跑一遍再补几个它没想到的场景。这三步循环听起来慢实际比你让AI写完一大坨再统一调试要快得多因为问题被隔离在每个小循环里不会累积到最后一起爆。这里有个具体的小技巧审查的时候我会专门找一种多余的正确。AI有时候会加上一些看起来无害的防御性代码比如对不可能为空的值做空值判断对已经校验过的参数再校验一遍。这类代码本身不报错但会稀释代码的可读性也会掩盖真正的校验点在哪里。我的处理是凡是我判断不可能出现的分支要么删掉要么在注释里写清楚为什么它不可能出现。让代码里每一条分支都有明确的存在理由这是我审AI代码时最看重的标准之一。3.3 把质量关卡自动化让机器去拦机器的错人工审查很重要但人是有精力的一个人一天能认真看的代码是有限的。所以我会把一部分质量检查自动化交给工具去跑。这不是替代人审而是把人从机械检查里解放出来专注看逻辑。以 Python 项目为例我常配的一套组合是代码格式化、静态类型检查、以及测试覆盖率。格式化用 ruff 或 black保证风格统一省掉风格争论静态检查用 mypy 或 pyright这个对AI代码特别有用因为AI经常写错类型比如把 Optional 当成必填用静态检查能直接标出来测试覆盖用 pytest 加覆盖率报告重点看核心模块有没有被测到。下面是一段我常用的 GitHub Actions 配置骨架用来在每次提交时自动跑检查。这里说明一下具体版本号按你项目实际情况调整命令也只是示意不要直接复制照搬重点是理解这个关卡的设计思路。name: code-quality on: pull_request: branches: [main] jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Set up Python uses: actions/setup-pythonv5 with: python-version: 3.12 - name: Install tools run: pip install ruff mypy pytest pytest-cov - name: Lint run: ruff check . - name: Type check run: mypy src - name: Test run: pytest --covsrc --cov-fail-under70这套配置的核心价值在于当AI生成的代码提交上来风格问题、类型问题、测试覆盖不足的问题会在 CI 阶段被自动拦下评审者打开 PR 时看到的就已经是过了基础检查的代码。我实测下来这一层自动化能拦掉大概六成以上的低级问题让人的注意力集中在真正需要判断的地方。还有一点值得单独说AI生成的代码注释和文档经常和实现不同步。所以我在 CI 里还会加一个检查确保公开函数的 docstring 存在且格式正确虽然不能保证内容准确但至少能提醒作者这里需要人工确认注释是否还成立。4. 常见问题与排查技巧实录4.1 AI代码最常翻车的几类场景结合我自己的经历和跟同行交流的情况AI生成的代码有几类问题出现频率特别高提前知道能省很多事。第一类是看似处理了边界其实没处理。比如它写了if not items: return []看起来好像考虑了空列表但真正的边界是items 里有元素但元素本身是 None这种情况它往往没管。排查方法是专门为边界写测试不要看它的判断语句要看它的行为。第二类是时间与时区。AI特别喜欢用本地时间或者混合使用时间戳和日期字符串跨时区、跨夏令时的时候就会出错。排查的方法是全局搜索时间相关的函数调用确认项目里是否统一了时区策略。我一般要求所有持久化时间都用 UTC展示层再转换这条规则写进规范后AI生成的代码也更容易符合预期。第三类是资源没释放。文件句柄、数据库连接、网络连接、锁AI经常会写打开但忘了关闭的代码。排查方法是检查所有open、连接获取、锁获取的地方是否都有对应的释放逻辑最好用上下文管理器。第四类是错误处理的层级混乱。AI可能在这一层吞掉异常在另一层又把异常包装得面目全非导致排查问题时根本不知道原始错误是什么。排查方法是沿着调用链看异常是怎么传递的确认每个环节要么处理要么透传不要既处理又透传。第五类是沉默的默认值。AI很爱给参数设置默认值这在工具函数里没问题但在业务逻辑里一个不该有默认值的参数被设了默认值会导致调用方漏传参数时程序不报错而是悄悄用了错的值。这类 bug 最难查因为程序不崩溃。排查方法是审查所有业务函数的默认值问一句这个参数在业务上真的可以有默认值吗。4.2 常见问题速查表与独家避坑技巧现象可能原因排查方向单线程正常并发出问题缺少原子操作或事务检查扣减、计数、状态流转测试环境正常线上偶发错误时区或环境变量差异统一时间策略核对配置程序不报错但结果不对默认值掩盖了漏传参数审查业务函数默认值异常日志里看不到原始错误异常被吞或被过度包装沿调用链查异常传递内存持续增长资源未释放或缓存无上限检查连接、句柄、缓存策略边界输入导致崩溃只判断了空集合没判断空元素补充边界测试除了这张表再分享几个我踩坑之后总结的小技巧。第一个是让AI重构代码之前先让它给出现有代码的行为说明然后对比它的重构结果和这份说明是否一致。AI重构时经常顺手改掉一些它觉得多余的逻辑而这些逻辑可能正是处理某个罕见场景的。第二个是重要的AI生成代码我会刻意隔一天再审一遍。当天审的时候我的脑子已经被AI的思路带跑了容易顺着它的逻辑往下走隔一天再看更容易用陌生的眼光发现不对劲的地方。这个方法听起来土但救过我好几次。第三个是养成问AI这段代码在什么情况下会失败的习惯。它给出的答案往往能帮你发现它自己没处理好的场景因为模型知道很多失败模式只是生成时不一定都实现出来。你一问它就会把那些被省略的边界补上这比你自己一个个想快得多。5. 顺着这条线往下走AI应用开发与Agent开发该怎么入手5.1 从用AI写代码到用AI搭产品的技能栈前面聊的是用AI辅助编程这属于工具层面的使用。如果你想把AI当成产品的一部分也就是做AI应用开发那需要补的东西就完全不一样了。用AI写代码你面对的是模型生成文本我来判断对错做AI应用你面对的是模型生成结果系统要处理它、约束它、评估它。后者的工程复杂度高一个量级。我梳理了一下这条路的技能栈大致分三层。最底层是编程基础Python 或 Java 加上一门脚本语言基本够用重点是你会写工程化的代码会做依赖管理、日志、测试、部署。中间层是AI相关的基础能力包括怎么调用大模型接口、怎么设计提示词、怎么处理结构化输出、怎么做流式返回、怎么管理上下文长度、怎么做 token 成本的估算。最上层是应用架构能力包括怎么把模型调用编排进业务流程、怎么设计 fallback模型不可用时怎么办、怎么对输出做校验和过滤、怎么评估效果、怎么监控线上表现。我个人的建议是别一上来就追求复杂的 agent 架构。很多新手觉得 agent 才是正题于是上来就搭多智能体的复杂系统结果连一次稳定的模型调用都没做扎实。更务实的路径是先把一个单轮的、边界清晰的AI功能做稳比如一个文档摘要、一个字段抽取、一个意图分类把提示词、输出校验、错误处理、成本监控这几件事做到位再往上叠更复杂的编排。5.2 Agent开发里容易被忽略的工程细节Agent 这个词被炒得很热但真正落地时决定成败的往往不是模型多聪明而是那些枯燥的工程细节。我把几个最容易被忽略的列一下。第一是工具调用的幂等性。Agent 调用外部工具时可能因为重试或循环导致同一个操作被执行多次如果这个操作是下单扣款发消息重复执行的后果很严重。所以工具层必须做幂等设计比如带业务唯一键服务端去重。第二是循环终止条件。Agent 的自主性来自于它能自己决定下一步做什么但这意味着它可能陷入循环一直调用工具却达不成目标。必须设置最大步数、最大耗时、最大 token 消耗这几个硬性上限超了就中断并返回可控的结果而不是让它无限跑下去烧钱。第三是输出校验。模型输出的东西永远要当成不可信输入来对待尤其是当它要被用来拼接命令、查询、或者渲染到前端时。结构化输出要用 schema 校验字段类型、取值范围、必填项都要检查不合法就重试或走降级绝不能信任模型说我返回了合法 JSON。第四是可观测性。Agent 的决策链路长出问题时如果只看到最终结果根本没法定位是哪一步跑偏的。所以每一步的输入、输出、耗时、消耗都要记下来最好能按一次会话串起来。这块做得好不好直接决定了你线上出问题时是十分钟解决还是三天解决。第五是成本控制。模型调用是按 token 计费的Agent 多轮调用特别容易失控。我的做法是给每个任务设预算上限超预算降级到更便宜的模型或者直接返回任务过于复杂的提示。上线前一定要做一次真实流量的成本估算别等账单出来才发现。第六是评测。AI 应用不像传统软件输入输出不是确定的你不能只靠单元测试判断对错。需要建一套评测集覆盖典型场景和边界场景每次改提示词或换模型都跑一遍看效果有没有退化。这套评测集是AI应用开发里最值钱的资产之一值得花时间慢慢养。最后回到开头那条消息。我个人对暂停这件事没有立场可言那是行业层面的大问题。但从我天天写代码的角度看它至少提醒了我一件事AI 让写代码变得前所未有的快可写完之后那些确认它真的对的工作速度并没有跟着一起变快。这个落差才是每个用 AI 写代码的人真正要面对的东西。我自己现在的做法就是凡是让 AI 生成的核心逻辑都要能用自己的话讲清楚一遍讲不清楚就重写。这个方法很笨但用下来它让我在享受效率的同时还保留了对自己代码库的掌控感。