拓冰建站拓冰建站
首页 / 资讯中心 / 正文

从零搭建Coding Agent:让AI自动读代码、改代码、跑测试

不用羡慕那些在社交媒体上晒“AI帮我一天写完一个项目”的人真正值得研究的是他们背后那套能自动读代码、改代码、跑测试、看报错、再改代码的Coding Agent。我自己从手搓脚本到搭建完整的编码智能体踩了不少坑也把一套相对成熟的方法论跑通了。这篇是这个系列的第一篇先讲清楚Coding Agent是什么、为什么值得自己搭以及怎么从零开始跑通一个最小可用版本。这个系列适合的人群很明确已经在用Copilot、ChatGPT辅助写代码但觉得“对话式补全”不够爽想让AI真正独立干活的开发者还有那些对Agent开发感兴趣、想入局AI编程工具赛道但不知道从哪下手的同学。看完这篇你会理解Agent工作的底层逻辑并且能亲手搭出一个能自动修复代码Bug的Agent雏形。1. 为什么需要自建Coding Agent——从vibe coding到工程化vibe coding这个词最近火得不行大意是你只需要描述需求、描述感觉AI帮你把代码写出来你像弹吉他一样跟着“感觉”走。但实际用过的朋友应该都有体会vibe coding在小Demo、原型验证阶段确实爽一旦进入真实项目代码量上来、模块耦合复杂、依赖关系混乱AI就经常“一本正经地胡说八道”改一个Bug引入三个新Bug。我自己最初用ChatGPT写代码的模式是“复制粘贴—报错—再复制粘贴”来回几十轮效率低且上下文经常丢。后来我意识到问题不在于模型不够聪明而在于交互方式太原始。我缺的是一个能把“读代码—定位问题—修改—执行—验证”这条链路自动串起来的执行体也就是Coding Agent。1.1 现成工具不够用的几个场景市面上已经有不少AI编程工具比如GitHub Copilot、Cursor、Codex等但它们大多是“辅助驾驶”不是“自动驾驶”。我遇到过几个场景现成工具始终解决不好第一多文件联动修改。当你需要改一个接口定义同时更新调用方、单元测试和文档Copilot这类工具往往只盯着当前打开的文件不会主动去全局搜索所有受影响的位置。第二执行与验证闭环。现成工具能生成代码但不会主动去跑测试、看报错、分析失败原因然后继续修。我经常需要把AI生成的代码粘到终端手动跑发现问题再贴回给AI整个过程还是人在做“翻译官”。第三私有化与安全要求。很多公司代码不能上传到第三方服务你得有个本地部署的方案这时候靠现成云端工具是行不通的。1.2 Agent、Copilot、独立脚本三者的边界在这条路上我走过一段弯路一开始分不清Agent和普通脚本的区别。后来我总结出一个好记的判断方法Copilot是“你说一句它补一句”本质是一个高级补全工具没有自主目标。独立脚本是“你写死步骤它机械执行”没有应变能力。Agent则是“你给目标它自己拆步骤、选工具、看结果、调整策略”有循环、有决策、有记忆。拿修Bug这件事举例普通脚本是“跑测试失败就退出”而Coding Agent是“跑测试失败了去看日志判断是逻辑错误还是环境问题然后修改代码重新跑测试直到通过或者确认自己搞不定”。这个“感知—决策—行动—再感知”的闭环才是Agent的核心价值。2. 核心概念扫盲Agent是怎么工作的想搭好Coding Agent起码得懂它内部的基本工作原理。别急着写代码先花二十分钟把这些概念过一遍能帮你少走一大半弯路。2.1 Agent的四个基本模块我把自己搭建的Agent拆成四个模块规划器、工具箱、记忆体、执行循环。规划器负责拆解目标。比如“修复登录模块的Bug”它会拆成“定位登录代码—阅读相关逻辑—猜测Bug原因—修改—跑测试”。这部分通常由大模型完成本质是思维链的工程化实现。工具箱是Agent能调用的外部功能集合比如读取文件、搜索关键词、执行Shell命令、运行测试等。每个工具就是一个函数有名字、有描述、有参数声明。大模型会“看”这些描述来决定调用哪个工具。记忆体用来保存对话历史、任务状态、中间结论。没有记忆的Agent是“金鱼”每轮都忘了自己刚才干嘛了。短term记忆可以用内存长term记忆可以存向量数据库或直接写文件。执行循环是把上面三者串起来的主干模型产出一个决策—调用工具—把工具结果返回给模型—模型再决策循环往复直到任务完成。这个循环写得健不健壮直接决定了Agent会不会卡死。2.2 大模型之外的“脚手架”是什么很多人以为Agent就是“大模型提示词”这个理解太片面了。大模型本身只负责“想”不负责“做”。真正让Agent能落地的是外面这层脚手架API路由、工具注册、上下文管理、错误处理、执行沙箱。脚手架里有几个容易被忽略的设计点。第一是工具调用结果的大小控制如果工具的返回值超过模型上下文窗口整个Agent直接崩溃。第二是工具调用的权限边界不能让Agent随意执行rm -rf这种命令。第三是并发控制多个工具同时调用时结果怎么合并。我踩过的坑也在这里。最开始写Agent时只注重提示词模型输出经常带幻想给个不存在的文件路径工具调用报错后模型还不自知继续沿着错的方向走。后来我加了“工具调用错误自动反馈模型纠错”的机制准确率才上来。2.3 从coding plan到spec-driven的开发范式变化最近热词里有个GLM Coding Plan还有spec-driven开发这些概念和Agent的规划器直接相关。coding plan是让Agent在动手之前先产出一份详细的代码修改计划相当于让它在脑子里先过一遍方案。spec-driven更进一步先写清楚规格、验收标准再让Agent照着实现。我实际用下来最大的感受是Coding Agent不太适合直接“一把梭”让它生成整个项目更适合“小步快跑”—先拆任务、写计划、逐步执行。有了计划之后Agent的每一步动作都更可控出错时定位也更快。这里有个实操技巧在给Agent的提示词里加上“先输出计划再开始执行”并设置一个“你确定吗”的确认门控。简单的说就是让Agent在动手改代码前先把要改哪些文件、每步做什么、预期结果写清楚你可以人工确认一遍再放行。虽然多了一步但比让Agent直接胡改一通再回滚要省事得多。3. 搭建前的准备环境、模型与工具选型开始动手前先把底层的环境、模型和工具链搞定。这一部分看似琐碎实际决定了你后面会不会频繁返工。3.1 运行环境的坑Coding Agent的推荐运行环境是Linux/macOSWindows会有各种权限和路径兼容问题。我自己主力机是macOS生产服务器是Ubuntu两个平台都跑通了但Windows上跑测试时经常遇到路径分割符、文件锁等奇怪问题。Python版本建议直接用3.11及以上很多Agent框架对3.10以下的支持已经逐渐变差。另外强烈建议用虚拟环境别把Agent的依赖直接装到全局环境里。Agent会频繁安装第三方包一旦依赖冲突整个开发环境就乱了。Docker沙箱是另一个值得考虑的选项。如果Agent要执行不可信的代码或者你要让它跑可能带副作用的操作建议把执行环境隔离在容器里。我开始时嫌麻烦没做沙箱结果有一次Agent执行了pip install把全局环境的版本搞乱了花了半天才恢复。3.2 模型选型的权衡模型是Coding Agent的“大脑”选型直接决定了Agent的编码能力上限。我试过几款主流模型跟大家分享一下实际感受。第一梯队是Claude系列和GPT系列这类模型的工具调用能力、代码理解能力都强但API费用高而且有调用频率限制。做商业化产品可以自己折腾成本偏高。中间梯队是GLM、DeepSeek这类国产模型的中大杯版本性价比不错工具调用能力也在线。我用GLM系列做日常开发辅助比较多尤其在中文需求理解上反而比某些国外模型更自然。开源模型里Qwen系列和DeepSeek的蒸馏版在本地部署场景下是不错的选择。本地部署的好处是隐私、免费、无限制但硬件要求高低显存的机器跑大参数模型会卡到怀疑人生。选型建议是预算充足、追求效果直接上Claude或GPT自己学习折腾、预算有限用GLM或DeepSeek有隐私要求、必须本地部署用Qwen-Coder系列。3.3 工具链选型从零组装还是用现成框架搭建Coding Agent有两条路用现成框架比如LangGraph、CrewAI、MetaGPT等或者从零组装。我自己的建议是如果你是第一次接触Agent开发先用现成框架跑通一条最小链路理解Agent工作流是怎么回事再考虑从零组装。直接一上来就手写循环、手写工具调用、手写上下文管理很容易被细节淹没最后什么都没搭出来。但现成框架也有问题抽象太多出了问题很难定位。我就遇到过LangGraph版本升级导致API变化网上教程全部失效的情况。另一个痛点是框架绑定了特定模型API想换模型要改不少配置。最终我走了第三条路轻量组装不引重型框架只用FastAPI写工具层然后通过OpenAI兼容接口直接调用模型。保持代码自主可控出错也能快速定位。如果你和我一样喜欢掌控感这条路更合适。4. 实操搭一个最小可用的Coding Agent接下来进入动手环节。我先带你跑通一个最小闭环这个Agent能接收“修复代码Bug”的任务自动执行“读代码—分析—修改—验证”的流程。整个代码量不多但麻雀虽小五脏俱全。4.1 项目结构设计我的最小项目结构长这样my_agent/ ├── agent.py # 主循环Agent核心逻辑 ├── tools.py # 工具定义 ├── llm.py # 模型调用封装 ├── memory.py # 简单记忆管理 └── test_files/ # 待修复的代码设计原则就一句话让每一层的职责足够单薄出问题知道去哪个文件找。tools.py只管工具调用memory.py只管上下文存储llm.py只管和模型API对话agent.py把前面几个串起来跑主循环。4.2 核心代码实现先看llm.py用OpenAI兼容接口封装模型调用。我这里用的是GLM的接口换成DeepSeek、通义千问等也类似只要改base_url和api_key# llm.py from openai import OpenAI client OpenAI( api_key你的API密钥, base_urlhttps://open.bigmodel.cn/api/paas/v4 ) def chat(messages, toolsNone): response client.chat.completions.create( modelglm-4-plus, messagesmessages, toolstools, tool_choiceauto, ) return response.choices[0].message注意几个细节。第一tools参数是可选的只在需要工具调用的阶段传入。第二tool_choice设置成auto让模型自己决定是否调用工具如果强制指定某个工具模型就会在不需要的时候也乱调用。再看tools.py定义一个读文件和执行Shell命令的工具# tools.py import subprocess def read_file(path: str) - str: with open(path, r, encodingutf-8) as f: return f.read() def run_command(command: str) - str: result subprocess.run( command, shellTrue, capture_outputTrue, textTrue, timeout30 ) return fexit_code: {result.returncode}\nstdout: {result.stdout}\nstderr: {result.stderr} # 工具注册表把函数描述给模型看 TOOLS [ { type: function, function: { name: read_file, description: 读取指定文件的完整内容, parameters: { type: object, properties: { path: {type: string, description: 文件路径} }, required: [path] } } }, { type: function, function: { name: run_command, description: 执行shell命令并返回输出结果, parameters: { type: object, properties: { command: {type: string, description: 要执行的命令} }, required: [command] } } } ] def call_tool(name: str, args: dict): if name read_file: return read_file(args[path]) elif name run_command: return run_command(args[command]) return f未知工具: {name}工具描述写得精不精准直接影响模型会不会调用。别小看description里那几句话模型能不能理解工具的用途全靠这个。写描述时有几个要点说清楚工具做什么、输入参数是什么格式、返回值长什么样、在什么场景用。然后是最核心的agent.py跑主循环# agent.py import json from tools import TOOLS, call_tool from llm import chat def run(task: str, max_iterations: int 10): messages [{role: system, content: 你是一个Coding Agent负责分析和修复代码问题。}] messages.append({role: user, content: task}) for i in range(max_iterations): message chat(messages, toolsTOOLS) messages.append(message) # 模型没有调用工具直接返回结论 if not message.tool_calls: print(Agent最终回答:, message.content) return message.content # 遍历所有工具调用 for tool_call in message.tool_calls: name tool_call.function.name args json.loads(tool_call.function.arguments) result call_tool(name, args) print(f[第{i1}轮] 调用工具: {name}, 参数: {args}) messages.append({ role: tool, tool_call_id: tool_call.id, content: str(result) }) print(达到最大迭代次数任务可能未完成。) return None if __name__ __main__: run(测试文件在test_files/demo.py请阅读代码找出并修复其中的逻辑Bug。)这段代码的逻辑很直白把用户任务放进消息列表调用模型看模型是否请求工具调用。有就执行工具、把结果返回给模型没有就直接输出结论。max_iterations防止Agent无限循环下去。4.3 第一次运行观察Agent到底在做什么跑起来之后你会在终端看到Agent的每一个动作。第一次跑的时候我建议你仔细观察它的行动轨迹这会帮你理解Agent的“思考过程”。我那次运行大概经历了这么几轮第一轮模型要求调用read_file读test_files/demo.py。返回了一小段代码里面有个函数逻辑明显有问题一个判断条件写反了。第二轮模型要求调用run_command跑python test_files/demo.py看报错。第三轮模型直接输出结论指出错误原因并给出了修复后的代码。整个过程不到一分钟如果人工来做还得打开文件、仔细阅读、猜测问题、运行验证来回至少五六分钟。而且Agent还能明确指出是哪一行、什么逻辑问题省去了人为debug的时间。有个小细节我在测试文件里故意埋了一个“非常规Bug”——不是语法错误而是逻辑错误。这种错误静态检查发现不了只能靠人类理解代码意图才能发现。Agent能定到位说明它的代码理解能力确实在线。4.4 加入记忆与执行计划后的效果对比跑通最小闭环后我在这个基础上做了两个重要升级。第一个升级是加coding plan让Agent在动手前先写计划。我修改了system提示词要求它在每次任务开始时先输出一份“行动计划”包括要读哪些文件、预期找什么问题、修改策略是什么、怎么验证修复成功。从那以后Agent乱走乱撞的情况明显减少。第二个升级是加记忆管理。原始版本里Agent每轮对话都在增长到第8、9轮时经常会忽略很早期的信息。我加了一个简单的摘要机制当消息列表长度超过一定阈值就把前面的对话摘要成一段话替换掉原始消息。这样既省token也防止信息丢失。# memory.py 简化版 MAX_MESSAGES 20 def compress(messages): if len(messages) MAX_MESSAGES: return messages system messages[0] recent messages[-(MAX_MESSAGES - 2):] summary 之前的对话已省略。任务背景: messages[1].content[:200] return [system, {role: user, content: summary}] recent压缩记忆这个操作要小心千万别把关键信息压没了。我踩过坑有次摘要太粗暴把任务目标都给截断了Agent在后半程开始“自由发挥”。后来我把摘要逻辑改成保留任务目标、用户原始需求、当前进度只把无关紧要的中间工具调用过程压掉效果好了很多。5. 常见问题与排查经验搭建Agent的过程中我遇到的坑可能比搭成功的次数还多。这里挑几个高频问题给你做个速查表。5.1 最常见的“Agent执行终止”问题很多人在Agent开发中遇到过“execution terminated due to error”之类的报错这几乎是入门的必经一环。原因大多不在模型本身而在于这几处第一工具函数内部异常没处理。比如read_file传了个不存在的路径直接抛FileNotFoundError整个Agent主循环被中断。解决办法是给所有工具函数包一层try-except异常时返回一段清晰的错误描述字符串让模型自己反思。第二JSON解析失败。模型偶尔会生成不合法JSON的tools调用参数json.loads会直接报错。建议加一个宽容的解析函数去掉多余的反引号、修正缺失的逗号或者干脆重试一次让模型重新生成。第三迭代次数上限设置太短。有些稍微复杂的任务跑十几轮很正常。我开始设的是5几乎天天超限后来改成20才够用。5.2 上下文爆炸怎么处理这是Coding Agent的经典难题。我在工具返回结果时会返回大量代码内容几轮下来模型上下文就被撑满速度和费用都不划算。我的解决方案有三层第一层工具返回内容截断。read_file只默认返回前2000个字符如果需要看完整内容模型会明确要求“读XX文件第YY行到ZZ行”。第二层历史消息摘要就是上面memory模块做的事情。第三层为超大文件建立索引。先让Agent扫描目录结构再读取具体文件的具体片段而不是一次性把所有内容塞进去。这几招组合用下来上下文使用量大概降了70%还没怎么损失效果。记住原则省token就是省钱也是省时间。5.3 工具调用出错的排查顺序如果发现Agent频繁调用同一个错误工具或者调用参数总是错别急着怪模型按这个顺序排查先看工具描述是否清晰描述不清导致模型无法理解工具用途。再看参数声明是否准确type是不是写对了required有没有漏掉必填项。然后看返回内容是否符合预期工具返回格式太乱模型会“看不懂”。最后看模型版本是否支持工具调用个别模型对Function Calling支持不佳换模型效果立竿见影。6. 后续扩展方向这个最小版本搭完之后我发现它能做的事情远超“修Bug”。我把工具扩展了一下加了搜索代码、运行单元测试、读写配置文件等它就能处理不少开发任务。这个系列的后续文章我会逐步展开首先是完整开发任务的拆解与执行从简单Bug修复到多文件功能开发讲解Agent如何规划长任务。然后在多轮对话中的记忆管理与状态同步解决Agent“做了前面忘了后面”的顽疾。再就是工具集的扩展与权限控制兼顾功能丰富度和操作安全性。还有多人协作场景下Agent的上下文共享以及私有化部署的完整方案。搭建Coding Agent这条路我走得不算快但每一步都踩实了。如果你也正在折腾欢迎按这个系列的步骤一步步来。等你的Agent能独立修好第一个Bug时那种感觉比手写一百行代码还爽。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门