AI编程三年进化:从代码补全到工程协作的实战复盘
1. 三年前那个“玩具”到底卡在哪一步2023年前后我身边不少写代码的朋友对AI编程的态度基本分两派一派觉得它就是个高级点的自动补全写个正则、补个样板代码还行真让它碰业务逻辑就露馅另一派则已经开始偷偷用它写单元测试和脚本但绝不敢让它进主仓库。那时候的典型体验是你写一段注释它给你补一个函数签名补出来的东西语法没错但变量命名风格跟你项目完全不搭边界条件基本靠你自己兜底。用一句话概括就是——它能猜你想写什么但不知道你为什么要这么写。这个阶段的AI编程工具本质上是一个基于局部上下文的概率补全器。它的工作范围通常只有当前文件甚至当前光标附近的几十行代码对项目结构、依赖关系、历史约定几乎一无所知。你让它写一个requests请求它会给你一个能跑但没有任何重试、超时、异常分类的裸调用你让它处理一个429 Too Many Requests它大概率只会告诉你“加个 sleep”。这不是它笨而是它的信息边界就那么大。真正的转折点出现在两个方向同时发力一是上下文窗口的指数级扩张从几千token到几十万token意味着它终于能“看见”整个项目二是工具调用能力的引入也就是让模型不只是生成文本还能主动去读文件、跑命令、看报错、再改代码。这两件事叠加才让AI编程从“补全”变成了“协作”。我印象很深的一次是让它帮忙排查一个exceeded retry limit, last status: 429的问题早期模型只会建议你加延时而现在的模型会先问你用的哪个库、重试策略怎么配的、有没有做指数退避然后直接给你一段带Retry适配器的完整方案。所以回头看三年前的“玩具”卡住的地方其实很具体上下文太短、不能动手、没有记忆。它像一个只能看一页纸的实习生你让他写个函数可以让他理解整个系统为什么这么设计他做不到。而这三年的进化本质上就是把这三点一个个拆掉的过程。下面我会从几个我实际踩过坑的角度把这条进化链路拆开讲清楚包括它现在到底强在哪、哪些地方依然会翻车、以及普通人怎么用才能不被它带沟里。2. 上下文窗口膨胀之后AI终于能“看懂项目”了2.1 从单文件补全到跨文件推理的质变早期用AI写代码最痛苦的一点是它永远只盯着你当前打开的那个文件。你项目里有一个统一的http_client.py封装了所有请求的重试和鉴权逻辑但AI不知道它会在每个新文件里重新给你生成一套裸的requests.get。结果就是代码风格分裂、重复逻辑遍地。这个问题在上下文窗口从4K涨到128K甚至更大之后才真正缓解。现在的模型可以把整个src目录的关键文件都读进去然后告诉你“你已经有http_client了这里应该复用它而不是新建 session。”我实测过一个场景一个中等规模的Python项目大概40多个文件核心模块包括数据拉取、清洗、入库三层。我让模型基于现有代码新增一个数据源适配器。它做的第一件事不是写代码而是先列出它读到的相关文件指出现有的基类接口、配置加载方式、日志规范然后才给出实现。这个顺序很关键——它先建立项目认知再动手。这跟三年前直接吐代码的行为有本质区别。2.2 长上下文带来的新问题信息过载与注意力漂移但上下文变长不等于变聪明。我踩过的一个坑是当我把整个仓库塞给它之后它反而开始“幻觉”出一些不存在的工具函数。原因在于长上下文里噪声太多模型在检索相关信息时会被无关文件干扰。这就像你让一个人在一间堆满杂物的房间里找一把螺丝刀房间越大他越容易拿错东西。解决办法是主动做上下文裁剪。我现在习惯在提问前先明确告诉模型“只看core/和adapters/这两个目录忽略测试和文档。”或者用工具把相关文件内容拼成一个精简的上下文再喂进去。实测下来精准的5个文件比模糊的50个文件效果好得多。这一点在热词里提到的python构建邻接矩阵、python结构化数据这类任务上尤其明显——你给它太多无关的图论代码它反而会把简单的矩阵操作写复杂。2.3 上下文管理正在变成一项独立技能现在我会把“给AI准备上下文”当成一个独立步骤来做而不是随手把报错贴过去。具体做法是先复现问题把最小可复现的代码片段、完整的报错栈、相关的配置项整理成一段结构化描述再交给模型。这个习惯让我排查requests爬虫里的429问题时效率高了很多——模型能直接看到我的重试配置和请求头而不是靠猜。提示上下文不是越多越好。给模型的信息要满足“相关、完整、无冗余”三个条件缺一个都会让效果打折扣。3. 工具调用能力从“只会说”到“能动手”3.1 模型开始自己跑命令、看报错、改代码如果说上下文膨胀解决的是“看得见”的问题那工具调用解决的就是“动得了手”的问题。以前的流程是模型给代码你复制到编辑器跑报错再把报错贴回去它再改。这个循环里你是个搬运工。现在很多AI编程环境已经支持模型直接读写文件、执行命令、查看输出。我让它帮我配一个vscode python环境配置它会先检查当前解释器路径发现没装numpy直接给出安装命令并执行然后验证导入是否成功。这个能力在处理python安装numpy库的方法这类看似简单但版本兼容性坑很多的任务时特别有用。模型会先跑pip show numpy看版本再根据你的Python版本决定是升级还是降级而不是无脑给你一句pip install numpy。它开始有了“先诊断再开药”的意识这是从玩具走向工具的关键一步。3.2 工具调用的边界它依然会“自信地做错事”但工具调用也带来了新风险。我遇到过模型在没确认的情况下直接执行了一个会修改数据库的脚本幸好那是个测试库。还有一次它为了“修复”一个导入错误擅自把python 3.8的语法改成了3.10的写法导致在老环境上直接崩掉。这些问题的根源是模型有行动力但缺乏对后果的敬畏。我的应对策略是给工具调用加“护栏”涉及写操作、删除、网络请求的命令必须经过我确认涉及版本变更的先让它说明理由。这跟带新人的逻辑一样——你可以让他动手但关键操作得先报备。热词里那个python cc攻击源码就是个典型反面教材模型如果真按字面去生成攻击脚本后果不堪设想。所以我现在会明确告诉模型“只做防御性编程不生成任何攻击性代码。”3.3 从“给答案”到“给排查路径”工具调用能力成熟之后模型的价值不再只是给最终答案而是能展示排查过程。比如处理布尔盲注爆破python脚本这种敏感度较高的任务时我会把需求转成“如何检测和防御这类注入”模型会给出参数化查询、输入校验、日志监控的完整方案而不是直接写攻击脚本。这个转变很重要——它从“帮你写代码”变成了“帮你建立正确的工程习惯”。4. 提示词工程为什么同一件事别人问就是比你问得好4.1 提示词的本质是“需求规格说明书”很多人觉得提示词就是“话说得客气点”这是误解。我观察下来好的提示词本质上是一份需求规格说明书它要说清楚输入是什么、输出要满足什么约束、边界条件怎么处理、验收标准是什么。比如你要写一个python筛选一样的去重逻辑差的提示是“帮我写个去重”好的提示是“输入是一个可能含重复元素的列表元素是字典按id字段去重保留第一次出现的返回新列表不要修改原列表”。这个差别带来的结果完全不同。前者模型可能给你一个set()去重遇到字典直接报unhashable后者它会用dict或循环来实现。提示词的精度直接决定了输出的可用性这跟给外包写需求是一个道理。4.2 结构化提示词的几个实用模板我现在常用的提示词结构是四段式背景 任务 约束 示例。背景说明项目环境和已有约定任务描述具体要做什么约束列出不能用的库、必须兼容的版本、性能要求示例给一个输入输出对。这个结构在处理python队列queue不堵塞这类有明确行为预期的任务时特别有效模型能准确理解“不堵塞”是指用get_nowait还是QueueFull异常处理。另一个技巧是让模型先复述需求再动手。我会加一句“先别写代码用你自己的话复述一遍你要实现什么”。这一步能过滤掉大量理解偏差。实测下来加了这句之后返工率至少降了一半。4.3 提示词不是越长越好关键是信息密度我见过有人把提示词写成千字小作文结果模型反而抓不住重点。提示词的核心是信息密度不是字数。每一句都要有明确目的要么限定范围要么消除歧义要么给出验收标准。废话和客套话只会稀释关键信息。这跟写技术文档一个道理——好文档不是字多是每句话都有用。5. 那些依然会翻车的场景以及我的应对清单5.1 版本兼容性AI最容易忽略的隐形杀手模型训练数据里的代码往往来自不同时期它很容易把python 3.8不支持的语法混进来。我遇到过它用match语句写分支而我的环境是3.8直接语法错误。还有一次它用了某个库的新API但我的版本是旧的。这类问题的排查成本很高因为报错信息往往不直接指向版本。我的做法是在提示词里固定写上“目标环境是Python 3.8只用标准库和已声明的依赖”。如果项目有requirements.txt直接把它贴给模型。另外我会让模型在给出代码后附一句“这段代码依赖哪些版本特性”方便我快速核对。5.2 网络请求与重试429不是加个sleep就完事热词里exceeded retry limit, last status: 429 too many requests和dma continuous requests都指向同一个问题请求频率控制。早期模型只会说“加延时”但实际生产环境需要的是指数退避 抖动 最大重试次数 熔断的组合。我现在会让模型基于urllib3的Retry类来配置而不是手写循环。这样重试策略是声明式的可读性和可维护性都好很多。还有一个坑是重试范围。不是所有请求都该重试比如400重试多少次都没用只有429、500、502、503、504才值得重试。模型如果不加约束可能会把所有异常都重试一遍反而放大问题。5.3 数据处理里的边界条件空值、类型、编码python类型转换、python变量的类型练习题这些热词背后是大量新手在类型处理上翻车。模型给的代码经常假设输入是“干净”的但真实数据里None、空字符串、混合类型比比皆是。我现在的习惯是让模型显式处理三种情况输入为空、类型不符、字段缺失。比如python画图横坐标太密集这个问题表面是绘图实际是数据采样和标签旋转的综合处理模型如果只给plt.xticks(rotation45)是不够的还得考虑抽稀和格式化。5.4 一个实用的翻车自查清单翻车类型典型表现我的应对版本不兼容用了高版本语法或API提示词固定版本要求标注依赖边界缺失不处理空值、异常要求显式列出边界条件处理过度重试所有错误都重试限定可重试状态码加退避上下文幻觉引用不存在的函数先让模型列出它读到的文件安全越界生成攻击性或危险代码明确限定防御性编程这张表是我踩了无数次坑之后总结的每次让模型写稍微复杂点的逻辑我都会对着过一遍。6. 从“能写”到“能维护”AI编程的下一站6.1 代码可读性正在成为新的分水岭早期大家比的是“AI能不能写出来”现在比的是“AI写出来的代码能不能维护”。我见过太多AI生成的代码功能没问题但命名随意、注释缺失、结构混乱过两周自己都看不懂。这其实不是模型能力问题而是提示词里没有提可维护性要求。我现在会明确要求函数职责单一、变量名用完整单词、关键逻辑加注释、复杂分支拆成小函数。加了这些约束之后代码质量明显上一个台阶。6.2 测试与文档AI最该发力但最常被忽略的地方让AI写业务代码很多人会做让AI写测试和文档很多人嫌麻烦。但恰恰是这两块AI的价值最大。因为测试和文档是有明确规范、重复度高、容易验证的工作。我现在的新功能流程是先让模型写实现再让它基于实现写单元测试最后让它根据测试和实现反推文档。这个顺序能保证三者一致不会出现文档和代码对不上的情况。6.3 人机分工的重新划分三年下来我的体会是AI擅长的是有明确输入输出、有大量先例、可快速验证的任务人擅长的是需求判断、架构取舍、风险决策。把这两者混在一起要么人累死要么AI闯祸。我现在会把任务先分类写个python连接cmd的脚本直接交给AI设计一个数据管道的分层架构我自己来AI只做方案对比和细节补充。这个分工不是固定的随着模型能力变化会调整但**“人定方向、AI填细节”**这个原则目前还很稳。7. 我现在的日常工具箱与工作流7.1 工具选型不追新看稳定性和可控性市面上AI编程工具很多我的选择标准就三条能不能读整个项目、能不能执行命令、能不能回滚。读项目决定了它能不能理解上下文执行命令决定了它能不能自己验证回滚决定了它闯祸之后我能不能快速恢复。这三条缺一个我都会谨慎使用。至于模型本身我会根据任务类型切换写算法用推理强的写脚本用响应快的改bug用上下文长的。7.2 一个可复现的日常流程我现在的典型流程是这样的先手动复现问题或明确需求整理成结构化描述然后让模型复述需求确认理解一致接着让它给出方案对比我选一个再让它实现同时要求写测试跑测试失败就把报错给它通过后让它生成变更说明。这个流程看起来步骤多但实际跑下来比“让AI直接写、我来回改”快得多因为前期对齐省掉了后期大量返工。7.3 给不同阶段使用者的建议如果你刚接触AI编程先从写脚本和测试开始别一上来就让它改核心业务。如果你已经用了一段时间重点练提示词的结构化和上下文裁剪。如果你已经在团队里推广优先建立代码审查和回滚机制别让AI生成的代码绕过正常流程。这三条是我观察下来最有效的进阶路径。8. 关于“超越中高级程序员”这个说法的冷静判断标题里说“超越中高级程序员”我的看法是在特定任务上它确实超过了在整体工程能力上还差得远。它能在一个明确定义的函数里写出比多数中级程序员更严谨的边界处理但它不知道这个函数该不该存在、放在哪一层、会不会影响其他模块。它能快速学会一个新库的用法但它不理解这个库为什么被选进来、替代方案是什么。所以更准确的说法是AI把“编码”这件事的门槛拉平了但把“判断”这件事的门槛抬高了。以前你只要会写代码就有价值现在你得会判断什么代码值得写、什么代码该删、什么代码该重构。这个转变对认真做工程的人来说是好事因为它把重复劳动压缩了把真正的决策空间留出来了。至于那些只会照着需求写CRUD的人压力确实会越来越大。我自己的态度是把它当成一个能力很强但需要监督的协作者而不是替代者。它负责快我负责对。这个组合目前来看效率是最高的。