代码生成 / AI 编程
让模型写代码,从补全一行到实现整个功能
Code Generation · AI 编程 · Coding
- 是什么——让模型写代码,和让它写文章有何不同。
- 为什么这么成功——为什么「写代码」成了大模型最成功的应用之一。
- 怎么进化的——从补全一行,到自主编程 Agent。
- 关键搭档——光会写还不够,还差什么。
- 能直接信吗——AI 写的代码有哪些坑。
- AI 编程让模型把意图翻译成能跑的代码,从补全到实现功能。(§1)
- 它成功,因为代码是文本(海量训练)、可执行验证(对错能测)、模式多。(§2)
- 它沿补全→对话→仓库级→自主编程 Agent 进化,高级形态就是把 Agent 用在代码上。(§3)
- 关键搭档是代码执行:写→跑→看报错→改的闭环让它能自我纠正。(§4)
- 但在大项目里,上下文喂不对就会瞎写,故核心是上下文工程。(§5)
- 风险:编不存在的 API、隐藏 bug、安全漏洞、过度信任——必须审必须测。(§6)
1什么是 AI 编程直觉
让模型写代码和让它写一段文案,表面上都是「生成文本」,但两者有一个本质区别:代码不是给人读着舒服就行的文字,它必须能运行,而且对错分明。一段文案写得平庸只是效果差一点;一段代码少一个符号、用错一个接口,程序就会崩溃或产生错误结果,这在绝大多数情况下是可被验证、可被判定的事实。这个区别决定了 AI 编程这项任务的根本性质:它不是自由创作,而是把一个「意图」翻译成「能跑的代码」的生成任务。
具体来说,AI 编程的输入包括三样东西:用自然语言表达的(或隐含在注释、函数签名里的)需求、仓库里已有的代码、以及验收条件。输出则是候选补丁、对新代码的解释,或者一个全新的程序。模型要做的,是理解这些输入之后,产出一段机器能够执行、并且符合意图的代码。
AI 编程覆盖从小到大的一整条谱系。最小的一端是补全:你正在打这一行,模型猜出后半截。往上一级是按注释生成一个函数:你写一句「把列表里所有正数加起来」,模型生成对应的实现。再往上是实现一整个功能:跨函数、跨模块地把一个需求变成可工作的代码。最大的一端是跨多个文件的重构:在保持行为不变的前提下重新组织代码结构。这些任务规模不同,但内核完全一样——都是把意图翻译成能运行的代码,机制上也没有本质区别。
这里有一条必须记住的边界:代码能够运行,只说明语法正确、并且被测试到的那些执行路径成立。运行通过并不证明需求全部满足、安全无漏洞、性能也达标。一个程序可能跑起来没有任何报错,却把边界条件处理错了,或者在极端输入下耗尽资源。所以「能跑」是 AI 编程的必要条件,远远不是充分条件;它只是验证链条上的第一环,而不是终点。
2为什么代码特别适合大模型直觉
同样是生成,为什么「写代码」偏偏成了大模型落地最成功的方向之一?答案在于代码同时具备三个对模型极其友好的性质。
第一,代码本身是文本。预训练阶段,模型在海量公开数据里见过数量惊人的开源代码,各种语言、各种风格的仓库都被它读过。写代码不是模型被强加的新技能,而是它原本就在做的事——续写一个函数、补全一个循环,正是它在预训练时反复练习过的行为模式。
第二,代码可以执行、可以验证。这一点最为关键。代码对不对,跑一下就知道:编译报错、测试挂掉,都是明确、客观、不依赖人主观判断的反馈信号。这与散文形成鲜明对照——一段散文「好不好」没有客观标准,模型得不到可衡量的反馈,改进方向模糊;而代码一旦跑失败,错误就变成了一个具体、可定位的反例,模型可以据此修正。可验证性把「模糊的错误」压缩成了「具体的反例」,这是代码生成能够形成高效闭环的根本原因。
第三,代码模式重复且结构严格。语法有明确规则,套路高度复现——增删改查、循环遍历、错误处理、资源清理,这些模式在无数项目里以相近的形态反复出现。模型擅长「按模式续写」,而代码恰好就是模式密度最高的文本之一。
但「可验证」有一个容易被忽视的边界:验证只覆盖已经表达出来的规格。编译器、测试和静态分析工具能给出客观反馈,但它们只能检查你是否实现了「已写下来的要求」。如果测试本身缺少边界条件,或者根本没有把真实需求转成可检查的验收条件,那么一段错误代码完全可以做到「测试全绿」——所有测试都通过,但需求从一开始就被理解错了。要形成真正可靠的闭环,不能只靠「跑一下」,而要先花力气把需求翻译成可检查的验收条件,再配合单元测试、集成测试、安全扫描和人工审查一起使用。换句话说,代码适合大模型,是因为训练语料丰富、语法结构重复、并且能用编译和测试产生外部反馈;输入是需求与代码模式,输出是可被工具检验的候选实现。而验证的真正价值在于把错误逼成具体反例,它做不到的,是替你把「规格」补完整。
3从「补全」到「编程 Agent」工程
AI 编程不是一步到位的,它沿着「管得越来越宽」的方向逐级进化。理解这条谱系,关键是看每一级把多宽的决策面交给了模型。
最早的一级是补全。你打字时,模型实时补出下一行、下一段,早期的 Copilot 就是这种形态。它只负责「你正在写的这行后面跟什么」,输入是光标前的一小段上下文,输出是一小段续写代码,不需要理解整个项目,也不需要自己做任何决策。
往上一级是对话式。你可以用自然语言让它生成一段代码、解释一段代码、或者改动一段已有代码。这时模型开始理解需求语句,但工作范围通常仍局限在你指给它看的片段。
再往上是仓库级。模型要读懂整个项目,跨文件地做改动——找到所有相关的调用点、同步修改接口和它的使用者。这需要检索和搜索能力,而不仅仅是续写。
最宽的一级是自主编程 Agent。你给它一个任务,它自己读代码、改多个文件、跑测试、看报错、再改,循环往复直到任务完成。这一步的本质,是把「Agent」这一整套机制用在了代码上:规划、调用工具(读文件、跑命令)、观察结果、再调整。
这条谱系的因果链很清楚。补全只需要「模型会写」;对话式需要「模型听懂并写对一段」;仓库级需要「模型会搜、会定位」;而自主编程需要模型像 Agent 一样进入一个闭环——规划、调工具、看结果、再改。越往后,模型负责的工作面越宽,系统随之增加搜索、规划、执行和恢复这些能力。
因此,能力阶段的输入不再是单纯的代码片段,而是任务范围、仓库上下文、可用的工具和授予的自主权;输出也相应地从补全、对话修改,扩展到仓库级补丁,再到完整的 Agent 轨迹。有两点边界必须说清。第一,阶段更高只表示模型负责的工作面更宽,不代表模型本身变得更聪明——一个在补全上平庸的模型不会因为被包装成 Agent 就突然擅长规划。第二,更宽的负责面不应当自动换来更大的权限;让一个仓库级工具获得删库、发布、打款的权限,是不由能力阶段支撑的越权。
| 阶段 | 能做什么 |
|---|---|
| 补全 | 你打字时实时补下一行/一段(如早期 Copilot) |
| 对话式 | 用自然语言让它生成、解释、改一段代码 |
| 仓库级 | 读懂整个项目、跨文件改动(见「AI 编程工具」) |
| 自主编程 Agent | 给个任务,自己读代码、改多个文件、跑测试、看报错再改(见「AI Agent」「Claude Code」) |
4关键搭档:代码执行运行示例工程
光会「写」代码还不够。要让 AI 编程真正靠谱,还差最重要的一步:把代码真的跑起来。让模型写完就执行,把运行结果和测试结果拿回来,它就能发现自己写错了、并据此修改。「写 → 跑 → 看报错 → 改 → 再跑」这个闭环,正是让代码生成区别于其他文本生成的关键搭档。
用一个缓存功能的例子能看清整个过程。第一步是把模糊需求变成可检查的验收条件:需求说「让查询更快」,要把它改写成三个可检查的条件——相同参数第二次调用时不走后端;不同用户之间不能共享结果;原有测试保持通过。这一步决定了后面所有验证是否有效。
第二个补丁,模型用 query 作为全局缓存键。单元测试通过了,但安全审查发现缓存键里缺少 user_id——这意味着不同用户会命中同一个缓存槽,互相看到对方的结果。
于是引入失败测试 test_cache_isolated_by_user:它让用户 B 得到用户 A 的结果,把一个「逻辑正确」的假象改写成「缓存键不完整」的明确结论。关键在这里——闭环之所以比「再生成一次」强,是因为第二个补丁不是因为模型随机换了个写法,而是失败测试提供了一个具体反例:同一个 query 在两个不同的 user_id 下,必须产生两个缓存槽。可执行反馈把模糊的「可能有 bug」收缩成可定位、可复验的约束。
接着是最小修复:把缓存键改为 (user_id, query),并且只缓存成功响应。目标测试与全量测试都通过,而 diff 显示没有改动任何测试本身——这保证模型是通过修实现来过关,而不是靠篡改测试来过关。
最后是交付:报告改动内容、测试命令与剩余风险,但不自动合并。这里要记住,测试全绿只支持已被测试覆盖的行为,它不替代代码审查,也不授予自动合并或发布的权力。
图 1 可验证带来的闭环:写完就跑,用真实的报错和测试结果来纠错,再改再跑。正是这个客观反馈,让 AI 编程能够自我纠正——这是写文案等任务给不了的。整个运行示例的输入是缓存需求、首个补丁、用户隔离测试和项目回归,输出是修正后的最小补丁与运行证据。系统先把需求变成三条验收条件,再用失败测试定位缓存键缺少 user_id 的问题,修复后由窄到宽地验证。
| 运行示例 | 证据怎样改变下一步 |
|---|---|
| 需求与验收 | 把“更快”改写成三个可检查条件:相同参数第二次不调用后端;不同用户不能共享结果;原测试保持通过。 |
| 首个补丁 | 模型用 query 作全局缓存键;单元测试通过,但安全审查发现键缺少 user_id。 |
| 失败测试 | test_cache_isolated_by_user 显示用户 B 得到用户 A 的结果,把“逻辑正确”改成“缓存键不完整”。 |
| 最小修复 | 键改为 (user_id, query) 并只缓存成功响应;目标测试与全量测试通过,diff 未修改测试。 |
| 交付 | 报告改动、测试命令与剩余风险,不自动合并;测试全绿不替代代码审查和发布授权。 |
5上下文决定成败工程
为什么同一个模型,写一个小函数时很强,一放进你的大项目里就频繁出错?最直接的原因:它没看到项目的全貌。在真实代码库里,一段改动往往要顾及别处的接口、命名约定、依赖关系,而这些都散落在其他文件里。如果没把相关的上下文喂给它——相关文件、函数签名、项目约定——它就只能靠猜,于是写出「单看没错、放进项目就崩」的代码。
所以仓库级 AI 编程的核心,是上下文工程:把对的上下文,在有限的窗口里喂对。输入包括当前失败的表现、相关接口、调用者、测试、项目约定和依赖版本,输出则是完成修改所需的最小相关代码包。系统从复现问题和符号关系出发逐步扩展材料——先看报错在哪,再看它调用了什么、被谁调用、有什么约定——而不是把整个仓库无差别地塞进窗口。无差别塞入只会撑满上下文窗口,稀释真正有用的信息。
这里有一条边界要记清:上下文充分,只表示关键约束被看见了,不表示模型一定做对。模型仍可能误解约束的含义,或者遗漏那些没有被加载进来的路径。上下文工程解决的是「看不见」的问题,它无法消除「看见了却理解错」的问题。
6能直接信吗:风险安全
AI 写的代码,能闭眼合并吗?不能。它带来的风险具体有几种。
幻觉编 API:模型可能一本正经地调用一个根本不存在的函数或库——名字看着合理,其实压根没有。这是幻觉在代码里的典型形态,编译或运行到那一步才会暴露。
看着对、其实有 bug:能跑通不代表逻辑对。边界情况、并发、数值精度这些地方藏着隐患,测试没覆盖到就发现不了。
安全漏洞:模型可能写出有注入、越权等问题的代码,或者照抄了训练数据里的坏范例。它学到的写法里,本身就混着大量不安全的历史代码。
过度信任:代码越长、越像模像样,越容易让人放松审查——而这恰恰是最危险的一点。格式工整和逻辑正确是两回事,而人很容易把前者误当成后者。
正确的定位,是把它当作「很强但会犯错的初级工程师」:高产、好用,但产出必须审、必须测。AI 编程的正确用法是「人把关的加速器」,不是「无人监督的替代品」。
落到操作上,代码风险控制的输入是候选补丁、依赖、测试、安全规则和审查者,输出是接受、修正、拒绝或待验证项这几类结论。编译、测试、静态分析和人工 diff 分层检查:编译挡掉幻觉 API,测试挡掉已覆盖的逻辑错误,静态分析挡掉已知的漏洞模式,人工审查挡掉那些机器规则表达不出的问题。有一点要诚实面对:把模型当高产初级工程师是一种责任分配方式,它并不意味着人类审查天然不会漏错——审查者自己也可能疲惫、误判,所以这个把关过程同样需要被认真对待。
6.5怎么评估:片段题不等于真实仓库数学工程
一个模型在小函数基准上很强,能否直接推出它会修真实项目?不能直接推出。这背后是两套测量逻辑的差异。
函数生成常用 pass@k 这个指标:采样 k 个候选,只要其中至少一个通过隐藏测试,就算成功。它衡量的是「多试几次能否命中」,允许用采样次数去换成功率。pass@1 则只关注单次生成就命中,不含采样预算带来的收益。所以同一个模型,报 pass@k 和报 pass@1,数值含义完全不同。
仓库级任务的要求要高得多:它还要模型能定位相关文件、理解依赖、做最小范围的修改、并通过回归测试。SWE-bench 一类的评测更接近这种真实流程,它检验的不只是「会不会写这段」,而是「会不会在别人的项目里找到该改的地方、改对、并且不碰坏别处」。
比较结果时,还必须固定几个变量,否则分数不可直接比较:测试集、工具权限、采样预算、以及是否允许重试。任何一个不同,数字就没有可比性。
最后一条边界:无论 pass@1 还是 pass@k,测试通过只证明代码通过了现有测试,不证明它安全、性能合格,或者完全符合用户意图。指标的输入是固定任务、隐藏测试、工具权限、采样数 k 和重试预算,输出是 pass@1、pass@k 或仓库级任务成功率;它们各自覆盖的范围,恰恰决定了它们各自证明不了的东西。
7把整条因果链连起来综合
把前面几节的线索串起来,可以看到一条完整的因果链,从「代码为何适合语言模型」一路推到「为什么测试全绿仍不是无人审查合并的许可证」。
起点是:AI 编程让模型把意图翻译成能跑的代码,从补全到实现功能。它之所以成功,是因为代码同时具备三个性质——它是文本,模型在海量开源代码里受过训练;它可以执行验证,对错能用编译和测试测出来;它的模式高度重复,适合按模式续写。
由此,工具沿着补全、对话式、仓库级、自主编程 Agent 逐级进化,高级形态本质上就是把 Agent 用在代码上。而让这个进化真正闭环的关键搭档,是代码执行:写、跑、看报错、改、再跑,模型靠真实反馈自我纠正。
但进入大项目后,新的约束出现了:上下文喂不对,模型就瞎写,所以仓库级编程的核心是上下文工程。即使上下文和验证都做好了,风险依然存在——编不存在的 API、隐藏 bug、安全漏洞、过度信任,因此产出必须审、必须测。
最终这条链落到一个结论上:可验证性让代码成为大模型最成功的应用之一,但它只验证「已表达的规格」;仓库级的成功则高度依赖把对的上下文喂对。两者加在一起,解释了为什么「测试全绿」是必要的一环,却永远不是跳过人工审查、自动合并发布的许可证。
10概念依赖与延伸学习路线
这一页的知识不是孤立的,它站在几个更基础的概念之上,又为若干更深入的主题铺路。
先修概念:要理解 AI 编程,先要理解大语言模型本身、工具调用,以及 AI Agent 的基本机制。这三者决定了「模型为何会写代码」以及「高级形态为何是 Agent」。
本页的核心,是可执行验证、写-跑-改的闭环、从补全到编程 Agent 的进化,以及上下文决定成败。这四个点合起来,构成了理解 AI 编程的内核。
紧邻的延伸是:代码执行与沙箱(跑代码的安全环境从哪来)、AI 编程工具(具体工具如何实现前面讲的机制)、上下文工程(如何系统地把对的上下文喂给模型)、幻觉(编出不存在的 API 这类错误的根源)、人在回路(为什么必须由人把关)。
更远的方向还有:规划与任务分解、工作流编排、评测。这些属于把单个编程任务放大成更大系统时才会遇到的问题,可以在掌握本页内核之后再深入。
| 学习层级 | 涉及概念 |
|---|---|
| 先修 | 大语言模型、工具调用、AI Agent |
| 本页核心 | 可执行验证、写-跑-改闭环、从补全到编程 Agent、上下文决定成败 |
| 紧邻延伸 | 代码执行与沙箱、AI 编程工具、上下文工程、幻觉、人在回路 |
| 更远 | 规划与任务分解、工作流编排、评测 |
- Chen et al., Evaluating Large Language Models Trained on Code:代码模型训练与 pass@k 评测。
- Jimenez et al., SWE-bench:真实仓库级软件工程任务评测。
- Yao et al., ReAct:工具交互与反馈循环,可迁移到编码 Agent。