
1. 为什么我坚持让AI写完代码先过我这道人工闸门团队里最近有个挺有意思的变化以前是我追着开发要代码审查现在反过来了AI助手写完一坨代码主动往我这儿一丢说你审吧。标题里那句AI的代码交给我审说的就是这个场景。听着像是玩笑但真落到日常协作里它其实是一套挺严肃的工程流程——AI负责产出人负责把关中间靠几条硬性清单卡住质量。先把话说清楚这篇不是教你怎么让AI写代码那玩意儿现在谁都会。我要聊的是AI产出代码之后人工审查这一环到底该怎么审、审什么、卡在哪几条线上。关键词里提到的代码审查、Claude Code、沙箱、权限基本就是我这套流程的四个支点。适合谁看适合已经在用AI辅助编码、但发现AI写得挺快、坑埋得挺深的开发者也适合团队里负责代码质量、被AI产出搞得审查压力陡增的技术负责人。我自己的体感是AI写的代码有个很鲜明的特点表面工整暗处随意。命名规范、注释齐全、结构清晰第一眼看上去比很多人类写的还漂亮。但你只要往深里挖两层就会发现它在边界条件、权限处理、异常路径、资源释放这些不显眼但要命的地方经常给你埋雷。所以审查AI代码不能按审人类代码那套看逻辑对不对来得换一套更偏防御性的思路。标题里四条清单两次打回不是修辞。四条清单是我实际在用的四类检查项两次打回是这套流程跑下来最常见的节奏——第一轮打回基本都栽在权限和沙箱上第二轮打回多半是边界和资源问题。下面我把这套东西完整拆开讲包括为什么这么设计、每条清单具体查什么、以及那些只有真审过才懂的坑。2. 四条清单的由来AI代码的失效模式决定了审查重点2.1 先搞清楚AI代码到底容易在哪翻车要设计审查清单得先知道审查对象会怎么坏。我前后审了大概两三百个AI产出的代码片段和模块把翻车点归了归类发现它们高度集中在四个区域而且这四个区域有个共同特征——都是运行时才暴露、静态看很难发现的问题。第一个区域是权限与访问控制。AI特别爱写直接访问的代码比如直接读某个路径、直接调某个系统接口、直接假设当前进程有管理员权限。它不会主动去想如果这个操作没权限会怎样。第二个区域是沙箱与隔离边界。AI生成的代码经常默认自己跑在一个什么都能干的环境里对容器边界、文件系统隔离、网络隔离毫无敬畏。第三个区域是边界条件与异常路径。空值、超长输入、并发竞争、超时重试这些它要么不处理要么处理得极其敷衍。第四个区域是资源生命周期。文件句柄、数据库连接、锁、临时文件开了不关是常态。你看这四个区域恰好对应我标题里的四条清单。这不是我硬凑的是失效模式倒推出来的审查结构。审查清单的本质是把AI最可能忽略的东西变成人必须逐条确认的东西。2.2 四条清单分别是什么为什么是这四条我把四条清单命名为权限清单、沙箱清单、边界清单、资源清单。每一条都对应一类具体的检查动作而不是笼统的检查安全性。权限清单查的是这段代码假设自己有什么权限这个假设成立吗。沙箱清单查的是这段代码在隔离环境里还能不能跑会不会越界。边界清单查的是输入极端、并发极端、时间极端时会发生什么。资源清单查的是开出去的东西有没有收回来。为什么偏偏是这四条而不是五条六条因为审查是有成本的。清单太长审查者会疲劳最后变成走过场。四条是我实测下来覆盖了绝大多数致命问题、又不至于让人审到崩溃的平衡点。每条清单下面大概三到五个具体检查项加起来十几项一个中等规模的模块审下来大概二十到四十分钟这个成本团队能接受。提示清单不是越多越好。我早期试过搞八条清单结果审查者前三条认真看后面全靠感觉。砍到四条之后反而每条都能落到实处。2.3 两次打回是怎么形成的两次打回不是我定的规矩是流程跑顺之后自然形成的节奏。第一次打回几乎必然发生在权限和沙箱这两块。因为AI产出的代码在能不能跑起来这个层面经常就有问题——它假设的权限环境和你实际的运行环境对不上或者它在沙箱里访问了不该访问的东西。这一轮打回改的是能不能安全地跑。第二次打回通常发生在边界和资源这两块。第一轮改完代码能跑了但一上压力测试或者跑长时间边界问题和资源泄漏就冒出来了。这一轮打回改的是能不能稳定地跑。我一开始还想着能不能一轮改完后来发现不行。因为权限和沙箱的问题不解决你根本测不到边界和资源问题——代码在第一步就崩了。所以这个两次打回其实是被问题的依赖关系逼出来的先解决跑得起来再解决跑得稳顺序不能反。3. 权限清单AI最爱假设自己有权限这是第一打回区3.1 AI代码里的权限假设有多离谱我审过一段AI写的文件处理代码它上来就是一句删除某个系统临时目录下的文件。逻辑没问题但它默认当前进程对这个目录有写权限。实际部署的时候这个进程跑在一个受限账户下直接报权限不足。AI写的时候压根没考虑这茬因为在它的训练经验里删个临时文件天经地义。这类问题在AI代码里极其普遍。它倾向于写理想路径——假设所有资源都可访问、所有操作都被允许。而真实系统里权限是一层一层卡出来的。你可能会遇到你需要来自administrators的权限才能删除这种提示也可能遇到更隐蔽的应用程序特定权限设置并未向在应用程序容器中运行的地址开放这类问题。AI不会主动帮你处理这些它甚至不知道这些存在。所以权限清单的第一条就是逐行找出代码里所有需要特权的操作然后问一句这个特权在当前运行环境下有吗。需要特权的操作包括但不限于写系统目录、改注册表、访问其他用户的文件、绑定低位端口、加载内核模块、修改系统时间。3.2 权限清单的具体检查项我把权限清单落成了五个具体检查项审查时逐条过检查项具体动作常见问题特权操作识别标出所有需要 elevated 权限的调用AI默认有管理员权限运行账户确认确认代码实际以什么账户运行开发用管理员生产用受限账户降级路径无权限时是否有优雅降级AI通常直接抛异常权限申请时机是否在真正需要时才申请AI爱在启动时一次性申请全部错误信息权限失败时提示是否可操作AI的报错信息对用户毫无帮助这里重点说降级路径。AI写的代码在遇到权限不足时基本就两种反应要么直接崩要么抛一个用户看不懂的异常。但好的工程代码应该有降级方案——比如写不了系统目录就写到用户目录改不了注册表就存配置文件。这个降级逻辑AI几乎不会主动写必须人工补。还有权限申请时机这一项很多人会忽略。AI喜欢在程序启动时就把所有可能用到的权限一次性申请了图省事。但这在安全审查里是大忌——权限应该按需申请、用完即释放而不是一上来就要一大把。你审AI代码时看到启动阶段一堆权限申请基本可以判定这块要打回重写。3.3 一个真实的权限打回案例说个具体的。有次AI写了个配置管理模块需要在程序启动时读取一个系统级的配置文件如果不存在就创建一个。代码逻辑很顺但它创建文件时用的是绝对路径指向系统配置目录。在开发机上跑没问题因为开发账户权限高。一上测试环境进程以服务账户运行创建文件直接失败整个模块起不来。第一次打回我给的修改意见是三条第一读取系统配置失败时回退到用户级配置目录第二创建文件前先检查目录可写性不可写就明确报错并给出替代方案第三把需要写系统目录这个假设从代码里彻底拿掉改成优先系统目录、回退用户目录的双路径策略。改完之后这个模块在受限账户下也能正常跑只是配置存到了用户目录。这就是权限清单的价值——它逼着你去质疑AI那些想当然的假设。4. 沙箱清单代码在隔离环境里还活不活得下去4.1 沙箱边界是AI最没有概念的东西如果说权限问题是AI假设自己有权限那沙箱问题就是AI假设自己在一个没有边界的世界里。它写的代码经常默认可以随意访问文件系统、随意发起网络请求、随意调用系统命令。但在现代部署环境里代码大概率跑在某种沙箱或容器里文件系统是隔离的、网络是受限的、系统调用是白名单的。我审过一段AI写的代码它需要调用一个外部命令来处理数据。代码里直接用了系统调用去执行那个命令。逻辑上没错但它没考虑这个命令在沙箱里可能根本不存在或者沙箱禁止了进程创建。结果就是代码在本地跑得好好的一进容器就挂。沙箱清单的核心问题就一个这段代码依赖的所有外部资源在目标沙箱环境里都存在且可访问吗外部资源包括文件路径、环境变量、网络端点、系统命令、设备节点、共享内存。4.2 沙箱清单要查的四件事第一件文件系统假设。AI代码里出现的每一个路径都要确认在沙箱里是否可访问。特别注意那些硬编码的绝对路径比如/tmp、/var/log、C:\Windows\Temp这类。沙箱里的临时目录往往是映射过的跟宿主机不是一回事。第二件网络假设。AI代码如果发起了网络请求要确认沙箱是否允许出网、允许访问哪些地址。很多沙箱默认禁止所有出网或者只允许白名单。AI不会管这些它只管把请求发出去。第三件进程与命令假设。代码里如果有执行外部命令、创建子进程的操作要确认沙箱是否允许。容器环境里进程创建经常是被限制的。第四件环境变量与配置假设。AI代码可能依赖某些环境变量但沙箱里的环境变量跟宿主机完全不同。这个坑特别隐蔽因为代码在本地跑的时候环境变量都在一进沙箱就全没了。注意沙箱问题最坑的地方在于它在开发环境里几乎不会暴露。开发机权限全开、网络全通、命令齐全AI代码跑得飞起。只有进了真正的隔离环境问题才集中爆发。所以沙箱清单必须在接近生产的环境里验证本地跑通不算数。4.3 沙箱打回的典型场景有个模块AI写的时候用了一个第三方命令行工具做格式转换。代码里直接subprocess调用那个工具。本地测试全过。部署到容器里容器镜像里根本没装那个工具直接报命令未找到。这是第一次打回。我给的方案是要么把工具打进镜像要么改用纯代码库实现转换逻辑。团队选了后者因为把外部工具打进镜像会增大镜像体积、增加攻击面。改完之后代码不再依赖任何外部命令沙箱里跑得很稳。这个案例说明一个原则AI代码对外部环境的依赖越少越好。每多一个外部依赖就多一个沙箱里可能不存在的风险点。审查时看到AI引入的外部依赖第一反应应该是这个能不能去掉。5. 边界清单极端输入和并发才是真正的照妖镜5.1 AI处理边界条件的方式就是不处理前两条清单解决的是能不能跑从边界清单开始解决的是跑得对不对、稳不稳。AI在边界条件上的表现用一句话概括就是它只处理它想到的情况想不到的一律不管。空输入、超长输入、非法格式、并发访问、超时、重试这些在AI代码里要么完全没有处理要么处理得极其表面。比如一个解析函数AI会写正常的解析逻辑但不会写如果输入是空字符串怎么办如果输入超长怎么办如果输入包含特殊字符怎么办。它默认输入永远是正常的。但真实世界里输入永远不正常。用户会输入空值网络会超时并发会撞车。边界清单就是要把这些不正常一个个拎出来逼着代码给出明确行为。5.2 边界清单的检查维度我把边界分成四个维度来查输入边界空值、超长、非法字符、类型错误、编码问题时间边界超时、时钟回拨、时区、长时间运行后的状态漂移并发边界竞态条件、死锁、资源争抢、顺序依赖容量边界内存上限、磁盘上限、连接数上限、队列长度上限每个维度下审查时要问的是这个边界上代码的行为是什么。注意不是问代码有没有处理而是问行为是什么。因为有些代码没处理本身就是一种行为——比如空输入时直接崩溃这也是一种行为只是不可接受。我审AI代码时有个习惯动作给每个函数都脑补一个最坏输入然后看代码会怎么反应。这个习惯帮我抓出了大量边界问题。AI写的函数十个里有六七个在最坏输入下会崩或者给出错误结果。5.3 并发问题是AI代码的重灾区边界清单里并发问题最值得单独拎出来说因为AI在这块栽得最狠。AI写的代码经常有共享状态但它不会加锁或者加了锁但锁的粒度不对。更麻烦的是AI写的并发问题往往在单线程测试里完全看不出来一上并发就炸。我审过一个缓存模块AI写的逻辑是先查缓存没有就计算然后写缓存。单线程跑完美。多线程一跑同样的 key 被计算了好几次因为两个线程同时发现缓存没有同时开始计算。这是典型的 check-then-act 竞态。AI完全没意识到这个问题。修复方案是加锁或者用原子操作。但这里有个细节加锁的粒度要控制好锁太大会影响性能锁太小又保护不住。AI如果被要求加锁它倾向于加一把大锁把整个函数锁住简单粗暴但性能差。人工审查时要根据实际并发压力调整锁的粒度。5.4 边界打回从能跑到跑得对第二次打回基本都发生在边界这一块。第一轮权限和沙箱改完代码能跑了测试一上强度边界问题就冒出来。我印象最深的一次一个数据处理模块正常数据跑得好好的一上生产遇到一条超长记录直接内存溢出。AI写的代码里读取记录时没有长度上限检查默认记录不会太长。打回意见是加长度校验和分块处理。改完之后超长记录会被截断或分块不再撑爆内存。这个改动不大但如果没有边界清单这个问题会一直潜伏到生产环境才爆发那时候代价就大了。6. 资源清单开了不关是AI的肌肉记忆6.1 资源泄漏为什么在AI代码里这么常见资源清单是四条里最朴素的一条查的就是文件、连接、锁、内存这些资源有没有正确释放。但就是这么朴素的一条AI代码的通过率低得惊人。原因是AI写代码时注意力全在主逻辑上资源释放这种收尾工作它经常忘。文件句柄开了不关、数据库连接用了不还、锁加了不解、临时文件创建了不删这些在AI代码里是常态。更麻烦的是资源泄漏在短时间测试里看不出来只有长时间运行或者高并发时才暴露。所以它特别适合放在最后一轮审查因为前面几轮的问题不解决你根本跑不到能暴露资源泄漏的阶段。6.2 资源清单的检查方法资源清单的检查方法很直接找出代码里所有获取资源的操作然后确认每个获取都有对应的释放且释放路径覆盖所有分支。具体来说要查这几类资源资源类型获取方式释放方式AI常见问题文件句柄openclose异常路径不关闭数据库连接connectclose/归还连接池忘记归还锁lock/acquireunlock/release异常时死锁临时文件createdelete从不删除内存allocfree/GC循环引用重点看异常路径。AI写的代码正常路径下的资源释放往往是对的但一旦中间抛异常释放逻辑就被跳过了。这就是为什么资源清单要特别关注 try-finally 或者类似的结构——资源释放必须放在 finally 里保证无论是否异常都能执行。6.3 资源打回与两次打回的收尾资源问题通常是第二次打回的一部分。第一轮改完权限和沙箱第二轮改边界时资源问题往往一起暴露。我一般会把边界和资源放在同一轮审查里因为它们经常交织在一起——比如并发场景下资源没释放既是边界问题也是资源问题。有个连接池模块AI写的获取连接后如果业务逻辑抛异常连接就不归还了。跑一段时间连接池就耗尽。这是典型的资源泄漏。打回意见是所有连接获取必须配 try-finallyfinally 里归还连接。改完之后连接池再也没耗尽过。到这里两次打回的完整节奏就清楚了第一次打回解决能不能安全地跑权限沙箱第二次打回解决能不能稳定地跑边界资源。这个节奏不是硬性规定但它符合问题的依赖关系所以实践中反复出现。7. 审查AI代码时我踩过的坑和攒下的经验7.1 别被AI代码的表面工整骗了这是我踩过的第一个坑也是最贵的一个。刚开始审AI代码时我看它命名规范、注释齐全、结构清晰下意识就觉得这代码质量不错审查时放松了警惕。结果上线后一堆问题。后来我才明白AI代码的工整是表演性的它把表面功夫做足恰恰掩盖了深层的随意。所以现在我审AI代码第一件事就是无视它的外表直接跳到权限、沙箱、边界、资源这四个维度去查。外表再漂亮这四个维度不过关一律打回。这个心态转变很重要不然你很容易被AI的礼貌迷惑。7.2 审查要带着恶意去审审人类代码时我们通常假设作者是善意的、逻辑是通的审查重点是找疏漏。但审AI代码我建议换个心态假设这段代码在每一个可能的点上都会出错然后逐个验证它没出错。这种有罪推定式的审查听起来累但对AI代码特别有效因为AI代码的失效模式就是到处都可能出错。具体做法是对每个函数、每个分支、每个外部调用都问一句这里如果出问题会是什么问题。这个习惯养成之后审查效率反而提高了因为你不再纠结作者为什么这么写而是直接验证这么写会不会坏。7.3 清单要落地成可勾选的形式四条清单如果只是记在脑子里审查时很容易漏。我的做法是把每条清单做成一个可勾选的检查表审查时逐项打勾。这个检查表不用很复杂就是每个检查项一行审完打勾。团队里现在审AI代码都是对着检查表过的。这个做法还有个好处它让审查结果可追溯。打回的时候我能明确指出你这段代码在权限清单第3项没过而不是笼统地说权限有问题。AI或者用AI的人拿到具体条目改起来也更有方向。7.4 打回意见要具体到改哪里、怎么改打回不是目的改对才是。我早期打回时经常只写权限处理有问题改一下结果对方或者AI改出来的东西还是不对因为有问题太笼统了。后来我改成具体意见指出具体行、说明具体问题、给出具体改法。比如第42行创建文件前没有检查目录可写性改成先检查、不可写则回退到用户目录。这种具体意见AI拿到之后基本能一次改对。笼统意见则要来回好几轮。所以打回的成本很大程度上取决于意见的具体程度。7.5 不是所有AI代码都值得审最后说个反直觉的经验有些AI代码不值得审直接重写更快。如果一段AI代码在权限和沙箱这两条清单上大面积不过关说明它的基础假设就是错的修修补补不如推倒重来。我现在的判断标准是如果第一次打回的意见超过五条或者涉及架构层面的调整那就别改了让AI按新的约束重写一遍。这个判断帮我省了大量时间。早期我总想着能改就改结果在一个错误的架构上反复修补越修越乱。后来学会该重写就重写效率反而高了。AI重写一段代码的成本很低没必要在错误的代码上死磕。8. 把这套流程跑顺之后我的真实体会这套四条清单、两次打回的流程我跑了大概半年最大的体会是它把审AI代码从一件凭感觉的事变成了一件有章法的事。以前审AI代码我心里没底不知道重点在哪审完也不确定有没有漏。现在有了四条清单审查有了明确的靶子审完心里踏实。另一个体会是这套流程反过来也提升了AI产出的质量。因为我知道会被这四条清单卡所以在让AI写代码时会提前把这些约束写进提示里——注意权限降级注意沙箱兼容注意边界处理注意资源释放。AI带着这些约束写出来的代码第一次打回率明显下降。审查清单不只是审查工具它还是给AI的需求说明书。当然这套流程不是万能的。它覆盖的是AI代码最常见的失效模式但不可能覆盖所有问题。业务逻辑的正确性、算法的合理性这些还是得靠人来看。清单解决的是工程健壮性不是业务正确性。这两者要分开。最后分享一个我最近在用的技巧把四条清单直接喂给AI让它自己先审一遍自己的代码。具体做法是AI写完代码后我让它对照这四条清单逐条自查把不过关的地方标出来。实测下来AI自查能抓出大概一半的问题剩下的一半还是得人来。但这一半已经省了不少事。这个技巧的关键是清单要写得足够具体AI才能对照着查。笼统的检查安全性它查不出东西具体的检查所有文件操作是否有权限降级路径它就能查。这套东西还在迭代。最近在考虑加第五条清单专门查依赖管理——AI引入的第三方库、外部服务、系统命令这些依赖的版本、可用性、安全性。但还没想好怎么把它做得足够轻不至于让审查成本失控。等跑顺了再分享。