
现在很多开发者已经习惯了用编程Agent写新代码给它一个需求几秒钟就能生成一个能跑的函数、一个完整的接口、甚至一整套服务骨架。但一旦进入“修复”环节画风突变——你让Agent修一个隐藏Bug它改完这个错又引出另一个错你追问一句它开始反复横跳多轮对话之后问题没解决代码却被改得面目全非。这不是模型能力不够而是绝大多数人把“修复”当成了“生成”的延续。生成是开环任务模型只需要根据提示词往下写修复是闭环任务它要求你能复现问题、定位根因、限制修改范围、再验证结果。Agent缺的不是智商而是“问题边界”和“验收标准”。这篇文章要讲的就是怎么把修复任务变成Agent能稳定完成的小任务。文章整理了11个微小编程Agent修复技巧。这里的“微小”指的是高频、局部、可验证的问题某个函数解析异常、某个配置文件字段错误、某次构建失败、某个依赖版本漂移。这些问题单独拿出来都不大但积少成多非常消耗开发者的时间。读完这篇文章你可以形成一套自己的Agent修复工作流避免Agent瞎猜、减少无效往返、让每次修复都能经得起回归验证。1. 这篇文章真正要解决的问题1.1 为什么Agent“生成很快修复很慢”一个平时写代码很流畅的Agent为什么遇到Bug就发挥失常最直接的原因是生成任务里模型拥有极大的自由发挥空间只要输出看起来合理任务就算完成而修复任务要求它“在已有代码上做最小改动”这需要同时理解现状、错误信息、调用链、边界条件还要避免破坏已有功能。另一个原因是上下文污染。很多人在请Agent修Bug时习惯把一大段历史对话、一组相关文件、一份成功日志全都粘贴进去。Agent的上下文窗口虽然越来越大但关键信息被无关内容淹没之后它往往会“挑一个看起来最像根因的地方”去改而不是真正抓住问题。所以这篇文章真正要解决的不是“怎么让Agent更聪明”而是“怎么把修复任务描述得足够清晰让普通模型也能稳定完成”。1.2 哪些问题适合用微小修复流程处理微小修复和大型重构是两种完全不同的工作模式适合使用的Agent策略也完全不同。维度微小修复大型重构改动范围单个文件、单个函数多个模块、多个服务目标解决一个明确的错误调整整体架构或设计验收方式测试通过、构建通过需要代码评审、架构评审风险低出错可回滚高需要灰度、兼容方案Agent参与度可以独立完成适合辅助不适合主导日常开发中最适合交给Agent的就是红色那一类任务报错信息明确、复现路径清晰、改动边界清楚。这篇文章的所有技巧都是围绕这类任务展开的。1.3 微小修复的四个必要环节不管是什么类型的Bug一次合格的修复都包含四个环节复现、定位、约束、验证。复现是让问题稳定出现在你面前而不是靠描述“偶尔出错”定位是从日志、调用链、测试输出中找到真正出问题的代码位置约束是明确Agent能改哪些文件、不能改哪些文件验证是用测试和构建证明问题真的被修好了而不是“看起来修好了”。后面11个技巧本质上就是在围绕这四个环节做文章。有的技巧解决复现问题有的技巧帮你定位有的技巧帮你管住Agent的手有的技巧帮你确保修复结果真实可信。2. 技巧一先复现再修复让Agent拿到“实锤”很多人让Agent修Bug时第一句话就是“我的代码有问题帮我看看”。这句话对Agent来说是废信息因为它没有可验证的输入输出只能靠猜。猜中概率高不高取决于训练数据里是不是恰好见过类似问题这和“修复能力”没有关系。正确的第一步是先构造一个最小复现脚本。这个脚本要包含三样东西触发问题的输入、当前的实际输出、你期望的正确输出。把这三样交给Agent它面对的就从一道模糊的“阅读理解题”变成了一个“输入到输出之间的不一致修复任务”。# reproducer.py from datetime import datetime def parse_ts(value: str) - datetime: return datetime.fromisoformat(value.replace(Z, 00:00)) if __name__ __main__: # 期望正常解析 print(parse_ts(2025-06-01T10:00:00Z)) # 期望能给出明确结果而不是抛 ValueError print(parse_ts(20250601T100000Z))这个脚本本身不是最终修复方案它的价值在于让Agent先运行一遍看到ValueError: Invalid isoformat string的实际报错。Agent拿到这个报错之后就可以针对fromisoformat的处理逻辑做针对性修改而不是凭空猜测“是不是时区出了问题”。如果连复现都做不到比如Bug只出现在生产环境的某条数据上那第一步也不是让Agent盲改而是先把这条数据脱敏提取出来再构造复现脚本。记住一条原则没有稳定复现的Bug不值得让Agent动手。3. 技巧二把日志整理成Agent能读的现场记录日志是修复Bug的第一手现场记录但Agent并不擅长在一大堆日志里捞针。尤其是分布式系统、大型单体应用里的长日志动辄几百行Agent在摘要过程中很容易丢掉关键信息。更高效的做法是你先做一次日志“提纯”把最相关的部分整理成一个结构化现场记录。一份合格的记录应该包含四块内容报错类型、报错位置、调用链、关键日志片段。下面是Python异常栈的典型格式Traceback (most recent call last): File reproducer.py, line 11, in module print(parse_ts(20250601T100000Z)) File reproducer.py, line 5, in parse_ts return datetime.fromisoformat(value.replace(Z, 00:00)) ValueError: Invalid isoformat string: 20250601T10000000:00如果只是把这段报错原样丢给Agent它也能处理。但如果你想提高一次修复成功率建议按下面的模板整理后再发给Agent请修复以下问题 - 复现命令python reproducer.py - 报错类型ValueError - 报错位置src/parser.py:12 - 调用链main - parse_ts - datetime.fromisoformat - 期望行为parse_ts(20250601T100000Z) 返回 datetime(2025, 6, 1, 10, 0, 0) - 禁止修改不要修改依赖文件不要改函数签名很多团队把这种模板沉淀成一个bug-report-template.md遇到问题就往里填。填模板的过程本身就是一次定位很多问题在填写过程中就已经有答案了。4. 技巧三用锁文件锁定依赖避免“修了又坏”有一种Bug复现非常诡异Agent明明说修好了你本地也验证通过了但过了两天又出现或者换一台机器就报错。这种问题大概率不是代码逻辑的问题而是依赖版本漂移。你让Agent修复时它的运行环境可能已经安装了一个较新版本的依赖而你的生产环境还锁定在旧版本。Agent在“新依赖”下做的修复当然无法兼容“旧依赖”下的行为。反过来也一样。所以修复前先确认依赖树一致是一件性价比极高的事。Python项目的做法是导出锁文件Node.js项目直接使用package-lock.jsonJava项目通过Maven的依赖管理锁定版本。# Python生成当前环境的完整依赖快照 pip freeze requirements.lock.txt # Node.js严格按锁文件安装依赖 npm ci # Java查看依赖树确认是否存在冲突 mvn dependency:tree -Dverbose这里要特别提醒Agent修复的纪律不要为了让测试通过而顺手升级整个依赖。如果你发现根因是某个依赖版本太老正确的做法是单独创建一个升级任务评估兼容性之后再升级而不是把依赖升级混进Bug修复里。依赖变更和代码修复永远要分开提交。5. 技巧四配置文件修复要从“字段边界”开始很多Agent工具在启动时会读取config.toml之类的配置文件一旦配置加载失败整个应用直接无法运行。最近网上有大量“无法加载config.toml”的搜索记录说明这类问题不是个例。配置文件修复的难点在于Agent拿到的是一个解析失败的完整文件它很容易把整个文件重写一遍结果修好了一个字段却破坏了你精心调过的其他参数。正确的做法是先给Agent一份“字段边界”描述明确哪些字段是必填的哪些可以缺省哪些字段有取值范围然后再让它只修改出错的那一项。# config.toml [model] name # 必填当前为空字符串导致加载失败 temperature 0.2 max_tokens 2048 [agent] working_dir ./workspace memory_limit 4096比如上面这个配置model.name为空是启动失败的根因。修复时只需要把它改成可用的模型名称而不是动temperature和max_tokens。如果Agent无法确认哪些字段有效你应该给它一份已知可用的示例配置让它按字段对比而不是自由发挥。更稳妥的方案是给配置加一层结构校验。Python项目可以借助Pydantic在代码启动时先校验配置结构再让程序继续运行# config_schema.py from pydantic import BaseModel, Field class ModelConfig(BaseModel): name: str Field(..., description模型名称必填) temperature: float Field(default0.2) max_tokens: int Field(default2048, ge1) class AgentConfig(BaseModel): working_dir: str ./workspace memory_limit: int 4096 class AppConfig(BaseModel): model: ModelConfig agent: AgentConfig配置修复完成后不要直接启动应用先跑一次配置解析校验。验收标准很简单配置文件能被正确解析并且原有参数没有被意外改动。6. 技巧五构建失败先跑最小构建找到第一个错误在一个多文件项目里构建失败时控制台经常会刷出一长串错误。中间夹杂着几个真正的原因后面跟着几十个级联报错。如果你把这些错误全部丢给Agent它最可能做的事就是试图一次性修完所有报错结果越改越乱。正确做法是让Agent只关注第一个错误修完之后再重新构建循环处理。因为后面的错误很可能是第一个错误的连锁反应第一个错误修复后后面的报错会自动消失。# 前端项目先做一次纯TypeScript类型检查 npx tsc --noEmit --pretty false # Java Maven项目只编译指定的核心模块及其依赖模块 mvn -pl core -am compile -DskipTests对应的工作流可以描述成一个循环while 构建失败: 从日志中提取第一个错误 Agent 只修复这个错误 重新构建这个循环看起来简单但能显著减少Agent在错误堆里“迷路”的概率。记住一个容易忽略的细节日志末尾的错误往往不是根因日志开头的第一个错误才是。7. 技巧六用失败测试给Agent“指路”自然语言描述Bug非常容易产生歧义。你说“这个函数偶尔返回错误值”Agent听到的是“这个函数需要大改”你说“输入某些日期会报错”Agent会思考“哪些日期现在支持哪些格式”。但如果你给Agent一个失败的测试用例歧义就被完全消除了。失败测试把输入、期望输出、当前实际行为一并固定下来Agent的修复目标就变成了“让这个测试通过”。这是目前让Agent修复Bug最有效的输入方式。# tests/test_parser.py from datetime import datetime from src.parser import parse_ts def test_parse_utc_ts(): assert parse_ts(2025-06-01T10:00:00Z) datetime(2025, 6, 1, 10, 0, 0) def test_parse_compact_ts(): # 当前失败compact格式输入导致 fromisoformat 抛出异常 assert parse_ts(20250601T100000Z) datetime(2025, 6, 1, 10, 0, 0)使用这个技巧时推荐按下面的修复循环进行先运行测试确认红灯把红灯输出、失败的测试用例、相关源码文件交给AgentAgent修改代码再运行测试直到变绿。如果Agent连续尝试三轮仍然无法让测试通过不要继续无限对话下去建议退回重来、调整上下文或者换一个修复范围更小的任务。因为此时大概率是问题理解有偏差靠聊天是聊不出正确方案的。8. 技巧七裁剪上下文单文件优先Agent修复失败很多时候不是因为模型笨而是因为你给了它太多不相干的信息。一个常见的错误做法是把整个项目目录树、五个相关文件、一段三天前的成功日志、十轮历史对话全部粘贴进去。Agent的注意力是有限的当关键Bug信息被淹没在噪音里它就只能挑一个看起来最合理的局部去改。上下文过长还会带来另一个问题单次执行超时或直接中断。网上常见的“agent execution terminated due to error”有不少就是因为上下文过长、任务范围过散导致的。更稳妥的上下文策略是“单文件优先”只保留问题文件、调用链上直接相关的文件只保留最近的失败测试输出和错误日志一个Bug对应一个会话不要在修复过程中顺带讨论重构涉及多个文件的修复拆成多轮会话分别处理。下面是一个可以复用的单文件修复提示词模板请修复 src/parser.py 中的问题。 约束 - 只修改 src/parser.py - 不要修改 requirements、package-lock.json、CI 配置 - 不要引入第三方依赖 验证 - 运行 pytest tests/test_parser.py -q直到通过 如果确实无法修复请列出还需要补充哪些信息不要盲目改动。裁剪上下文不是让你少给信息而是让你把信息集中到最精准的那一小块。这比粘贴十个文件更有效。9. 技巧八小步提交与回滚给Agent上“保险”Agent修改范围失控是实际工程里最让人头疼的事。你让它修一个日期解析Bug它可能顺手改了配置、优化了日志格式、重命名了两个变量。如果没有版本控制兜底你会陷入“不敢回滚也不知道哪些改动该留”的困境。最简单的防御手段是在让Agent动手之前建立一个清晰基线并约定好回滚方案。# 第一步查看当前工作区状态 git status # 第二步把复现脚本和失败测试提交到本地 git add reproducer.py tests/test_parser.py git commit -m repro: 添加失败用例和最小复现脚本 # 第三步让Agent修复并检查 diff git diff src/parser.py # 第四步改动可接受时提交修复 git add src/parser.py tests/test_parser.py git commit -m fix: 支持 compact 格式时间解析 # 第五步如果Agent改乱了回滚到基线 git checkout -- src/这套流程的精髓在于“小步提交”一次修复只提交一个意图。如果Agent这次修改涉及三个文件而其中两个和当前Bug无关要敢于回滚重来不要花二十分钟去逐行挑拣它留下的改动。与其和Agent讨论为什么改了无关文件不如直接回滚换一个更明确的约束重新开始。在团队协作中还可以给Agent设置权限边界它只能写开发目录下的源码和测试文件不能碰生产配置、密钥文件、CI脚本。这个边界用Git保护规则或者文件系统的写权限都可以实现。10. 技巧九回归验证定义“真正修好”修复完成的标准不是“Agent说修好了”而是“验证命令全部通过”。很多开发者拿到Agent的修复就直接合代码结果第二天CI亮了红灯又得花时间回滚。这不是Agent的问题是流程缺少回归验证环节。一次完整的修复验证至少要覆盖四个方面# 1. 目标模块测试通过 pytest tests/test_parser.py -q # 2. 全量测试通过确认没有破坏其他模块 pytest tests/ -q # 3. 构建通过 npm run build # 4. 静态检查没有新增告警 npx eslint src --ext .ts从验证标准看“看起来修好”和“真正修好”的区别很明显看起来修好真正修好目标测试通过全量测试通过本地通过CI 环境通过没有新增致命报错静态检查无新增告警Agent口头确认完成构建、测试、检查命令全部退出码为0尤其要注意如果目标测试通过了但关联模块的测试全挂了这不叫修好这叫换了一个Bug。遇到这种情况必须退回重做不要带着破坏性修复合代码。11. 技巧十把修复结果沉淀成测试用例这是所有技巧里“回报率”最高的一条。一个Bug修复完之后如果你只是接受了改完的代码那这次修复就只是一次性的但如果你把复现脚本转成正式的回归测试这次修复就从“处理单点问题”升级成了“建立长期防护”。把复现脚本转成回归测试的写法很简单就是把人工验证步骤改写成自动化断言# tests/test_parser.py import pytest from datetime import datetime from src.parser import parse_ts pytest.mark.parametrize(raw,expected, [ (2025-06-01T10:00:00Z, datetime(2025, 6, 1, 10, 0, 0)), (20250601T100000Z, datetime(2025, 6, 1, 10, 0, 0)), ]) def test_parse_ts(raw, expected): assert parse_ts(raw) expected添加回归测试之后这个Bug就变成了项目的长期资产。以后再遇到类似问题你甚至不需要写复现脚本直接运行测试就能复现CI里也会自动拦截同类问题防止它在几个版本之后悄悄复发。团队层面建议在提交信息里写清楚关联的问题编号例如fix #1234: 支持 compact 格式时间解析。这样以后翻Git历史时可以清楚看到“这次修复解决了什么问题、为什么这么改、对应的测试在哪里”。12. 技巧十一多Agent评审避免“自修自审”如果一个Agent同时承担“写代码”和“验收代码”的职责它很容易陷入逻辑自洽但答案错误的状态。你在对话里问它“改好了吗”它大概率会说“改好了”但它不会主动发现自己漏了边界条件。更稳妥的方式是把角色拆开用多Agent协作完成修复。你不用买三个不同的工具在同一个Agent对话里切换角色设定效果已经很明显。下面是一份简单的角色分工提示词角色A修复根据失败测试修改 src/parser.py保持最小改动。 角色B评审审查角色A的 diff不修改代码。重点检查 1. 输入为空字符串时是否安全 2. 时区偏移为负值时是否正常 3. 是否引入了异常吞噬或类型不安全的写法 4. 是否修改了非目标文件 角色C验证补充边界测试并运行 pytest tests/ -q输出通过/失败结果。这套工作流的核心原则是写代码的人和验收的人不能是同一个角色。评审角色发现了问题就把问题反馈给修复角色进入第二轮迭代。每次迭代的改动要小改完再验证避免一次评审带回一大堆大改。这个技巧特别适合处理边界条件多的函数。单个Agent修复时往往只盯着眼前这条失败用例有了评审角色它才会去思考空字符串、极端值、特殊字符这些“还没出问题但迟早会出问题”的场景。13. 常见问题与排查思路问题现象可能原因排查方式解决方案Agent修复后报错反而更多上下文过长关键信息被淹没查看 diff 范围统计改动文件数裁剪上下文单文件优先必要时回滚重来同一个Bug修复后隔几天又复现依赖版本漂移环境不一致对比锁文件和当时的安装记录使用锁文件安装依赖统一 CI 和本地环境应用启动时提示无法加载 config.toml配置字段名拼写错误或必填字段为空用配置解析脚本或 Pydantic 校验按示例配置逐字段对比只改出错字段构建日志出现一长串报错级联错误只有首个错误是根因查看日志第一条错误及对应文件让Agent只修复第一个错误循环验证Agent执行中途终止上下文或任务范围超出单次限制查看任务日志和上下文长度拆分任务一次只修一个Bug测试通过但CI仍然失败本地环境和CI环境变量、依赖不一致在CI容器中复现对比Node/Python/Java版本统一基础镜像和依赖版本用CI门禁做最终验收如果遇到“Agent执行到一半突然终止”这一类问题第一反应不应该是对着当前会话继续追问而是看看是否存在上下文过长或单个任务步骤过多的情况。最直接的办法是新建一个会话只带最小复现内容和错误日志重新开始。14. 最佳实践与工程建议第一建立团队级的“Bug修复提示词模板”。把这篇文章介绍的问题描述格式、日志提纯格式、修复约束字段都固化下来遇到Bug就往模板里填。模板能显著降低你组织信息的成本也能提高Agent修复的稳定度。第二给Agent划定明确的可写目录范围。开发目录、测试目录可以放开但生产配置、密钥文件、锁文件、CI脚本这些最好设为只读。权限边界越清楚Agent越不容易“好心办坏事”。第三用CI门禁替代个人环境验证。个人电脑上通过了不代表真的修好了因为环境变量、依赖版本、编译器版本可能都不一样。把测试、构建、静态检查全部接到CI上让CI的绿灯作为“真正修好”的唯一标准。第四记录修复决策本身。提交信息里不要只写“修改了parser.py”至少要写清楚根因是什么、为什么采用这个方案、是否考虑过其他方案。这些信息对未来的维护者非常有价值对Agent的后续任务也能提供更准确的历史上下文。第五把微小修复做成高频小任务。一次只修一个Bug一个会话只处理一个明确目标。不要一边让Agent修Bug一边让它顺便重构函数、补充注释、调整日志级别。改动范围越大越难验证越容易引入新问题。15. 总结与后续学习方向这11条技巧单独看都不复杂但组合使用时会产生明显的效果。核心逻辑是尽量给Agent提供可以复现、可以验证、范围清晰的问题然后通过测试和构建把“修复完成”的定义固定下来。建议你从技巧一和技巧六开始实践任何一个Bug都先写出复现脚本再写一个失败测试让Agent在这个框架下去修。等你熟练之后再加入技巧十的回归测试沉淀和技巧十一的多Agent评审逐步把修复能力从“单个文件”扩展到“整个模块”。再往后可以深入研究的方向包括把Agent修复接入CI流水线实现“失败测试自动分配给Agent修复、修复后自动跑全量测试”的闭环或者把多Agent角色协作固化到团队规范里让修复、评审、验证成为三个固定的工作角色。下一次遇到Bug时不用急着把报错糊到Agent脸上先问自己一句这个问题能稳定复现吗改动边界在哪里怎么验证才算真正修好这些问题想清楚之后你会明显感觉到Agent的修复能力比之前“听话”得多。