最近这段时间我几乎没有再打开过自动补全档位的 AI 编程工具。不是补全不好用而是真正意义上的编程代理Agentic Coding已经完全是另一个物种了它不再等你打完半行代码再猜下一段而是会自己去翻仓库、改文件、跑测试、看报错再决定下一步怎么动手。这种从补全到代理的转变不是把模型换大一号就能实现的背后是一整套架构设计的变化。这篇文章我不想空谈概念而是选了三个在开源社区里讨论度最高、也最有代表性的编程代理项目来拆。三个项目我都实际部署跑过也读过它们的源码和设计文档。它们恰好代表了编程代理架构的三个典型方向一个把简洁即可靠做到极致一个按完整软件工程师来设计还有一个在 IDE 生态里找到了平衡。把这三个拆完之后你会发现所谓 Agentic Coding 的架构本质其实就是几个核心问题谁在控制循环、模型怎么看见代码仓库、动作怎么执行落地、以及执行之后如何反馈。把这些搞明白编程代理在你眼里就不再是黑盒了。1. Agentic Coding 不是更强的补全先从能力边界说起1.1 从补全到代理发生了什么质变传统的 Copilot 式工具本质上是一个条件概率生成器给定左侧的上下文预测右边的 token。它没有目标没有多步推理也不具备调用工具的能力。而 Agentic Coding 的核心是让模型进入一个感知-推理-行动-观察的循环模型每走一步都可以选择调用某个工具读文件、写文件、执行命令工具执行完把结果返回给模型模型根据新的观察继续推理直到任务收敛。这个循环听起来简单但它带来了三个连锁变化。第一错误容忍度大幅提升。补全工具写错一行代码错误就留在了文件里因为没有人去验证。代理则可以在写完代码后主动运行测试、编译或者 lint把报错作为下一个观察喂给自己。也就是说代理具备试错-修正的能力哪怕第一次改错了它也能从反馈里爬起来。第二任务边界从一个函数扩大到整个仓库。补全是局部的代理可以先用搜索工具找到相关文件再读取文件内容再跨文件修改。它不受你当前打开的文件限制这是完全不同的工作模式。第三交互方式从人按一次 Tab变成人发一个目标。你需要说把这个项目的登录接口改成 JWT 认证并更新所有调用方剩下的搜索、修改、验证代理逐步自己完成。这也是为什么很多人把 Agentic Coding 称为给大模型装上了手和脚。1.2 编程代理的三大能力支柱规划、工具、反馈拆了多个项目之后我发现所有编程代理的架构差异最终都落在这三根支柱上。规划能力模型怎么把一个高层目标拆成可执行的步骤。有的项目完全靠模型自由发挥有的项目则引入显式的 Plan 阶段先输出方案再动手。规划越强代理在多步任务中的稳定性越好规划越弱代理容易陷入改一个地方坏两个地方的循环。工具能力代理能碰哪些东西。文件读写、目录搜索、终端执行、浏览器操作、Git 操作每一种工具都对应一个接口。工具表面上是能力范围本质上是代理的行动边界——它能干什么不能干什么全由工具集决定。反馈能力行动之后环境如何把结果回传给模型。测试通过与否、编译报错、lint 警告、文件内容变化这些反馈的质量和速度直接决定了代理收敛的效率。我见过不少团队自己用大模型 API 拼接读文件写文件就号称做了编程代理结果跑两步就失控。原因不是模型太弱而是这三根支柱之间缺少衔接没有规划约束、工具接口不统一、反馈路径断裂。所以拆开源项目时我最关注的就是这三件事分别是怎么实现的。1.3 为什么拿开源项目来拆市面上的编程代理宣传铺天盖地但真正能让你看清架构的还是开源代码。闭源产品你只能看到效果开源项目你能看到设计决策为什么这个项目选择用 diff 格式编辑而不是整文件重写为什么那个项目把事件全部序列化存储这些决策背后都是作者踩过坑之后的取舍。我选的三个项目分别代表了三种设计取向。第一个是把轻量做到极致的命令行结对编程代理它证明了一个人一个终端一个模型也能形成高效的 Agentic Coding 闭环。第二个是试图构建全自动软件工程师的大型框架事件流架构、沙箱运行、浏览器与终端的全工具覆盖是学院派和工程派结合的产物。第三个是深度集成在 IDE 里的自主代理它解决的是如何在现有开发环境里自然落地的问题Plan/Act 双模式的设计非常值得借鉴。2. 精简到极致的 CLI 结对编程代理项目 A2.1 核心循环编辑请求、应用 diff、提交验证项目 A 是目前开源社区里使用门槛最低的编程代理之一。装好后你在仓库目录下启动它然后直接说把main.py里那个find_user函数的空指针问题修掉它就进入工作循环读取相关文件 → 生成编辑 → 以 diff 形式返回 → 程序应用修改 → 自动创建一条 git commit。整个过程你可以随时介入改得不对就说回退重来改得满意就继续下一步。它的核心循环简单到可以用一张图说清用户消息进入对话 → 模型生成结构化编辑 → 程序把编辑应用到工作区 → git 提交 → 结果返回模型 → 进入下一轮。这里有个被很多人忽略的细节它把每一次修改都交给 git 管理所以代理的每一次改动都是可回退的。这意味着即使模型生成了完全错误的修改你也可以无损撤销。在很多大型代理项目里回退能力甚至需要单独设计而项目 A 用最朴素的 git 就解决了。为什么这个设计有效因为 git commit 不仅是版本管理还充当了**行动记录**模型后续的推理可以从 commit message 里看到自己刚才做了什么改动用户也可以在对话里看到每次提交的 diff。这个自带审计日志的特性让项目 A 在几乎零附加架构的情况下实现了可追溯性。2.2 代码库地图让代理用最短 Token 看见项目全貌但项目 A 最让我服气的不是 git 集成而是它的代码库地图repo map机制。编程代理绕不开一个问题大模型的上下文窗口有限不可能把整个项目所有文件都塞进去。那代理怎么知道要改哪个文件怎么知道相关函数在哪里项目 A 的答案是在每次请求时它用语法解析器扫描整个仓库生成一棵符号级别的仓库地图里面记录着每个文件有哪些类、哪些函数、函数之间的调用关系。然后它根据当前任务涉及的文件和符号只把最相关的那部分 API 签名和高频使用片段拼进上下文。整个地图可能只需要几千个 token却能覆盖一个中型仓库的骨架。我把这个机制理解成一张城市交通图你不需要记住每一栋楼里所有房间的布局你只需要知道主干道在哪、某个小区大概在什么位置。代理真正需要看到详细文件时再通过读取文件工具去拿具体内容。这种**先给骨架再按需取肉**的策略是解决上下文窗口限制最优雅的实践之一后来不少 IDE 插件里的语义索引都借鉴了类似思路。2.3 为什么省反而可靠少即是多的架构哲学项目 A 还有一个反直觉的设计选择它默认采用 diff 格式来做文件修改而不是让模型输出整个文件然后覆盖。通俗地说它要求模型只输出删除哪几行、新增哪几行由程序合成最终文件。这样有几个具体好处上下文里不需要重复粘贴整个文件模型只需要聚焦变更部分改动范围小引入无关错误的风险低而且代码审查时一眼就能看到模型到底动了什么。当然这个设计也有代价。diff 格式对模型的要求更高部分模型用它反而表现不稳定。所以项目 A 做了一件聪明的事根据你所用的模型能力自动选择 diff 编辑还是整文件重写模式。它把这个决定从用户手里接了过去变成了一个模型级别的自动配置。拆完项目 A我的感受是编程代理不一定非要复杂才能有用。把循环做短、把反馈做密、把上下文做省一个几十人维护的开源项目也能支撑起完整的 Agentic Coding 体验。它适合的恰恰是大多数日常开发场景不是让你把整个项目丢给 AI 全自动做完而是让 AI 在每一步都和你确认、反馈、迭代。3. 以AI 软件工程师为目标的完整代理系统项目 B3.1 事件流架构把智能体动作变成可回放的事件总线如果说项目 A 是精巧的瑞士军刀那项目 B 就是一艘全尺寸航母。它给自己的定位不是帮你改代码的助手而是自主完成软件工程任务的智能体。它的核心架构创新是引入了事件流Event Stream。项目 B 里所有事情都变成事件模型产生一个意图就是一个 Agent Action 事件执行完动作环境返回结果就是一个 Observation 事件用户发来一条消息是一个 Message 事件状态切换也有对应的状态事件。这些事件全部追加写入同一个事件流中带有顺序和时间戳。UI 只是事件流的可视化视图模型读到的是事件流的历史工具执行器消费的是 Action 事件。这个设计给我最大的启发是它把代理程序的运行过程和交互界面彻底解耦了。你可以用浏览器 UI 看代理干活也可以用命令行甚至可以通过 API 让另一个程序订阅事件流。调试的时候你可以把事件流导出来逐条回放看到底是哪一步决策出了问题。这一点对开发编程代理的人来说非常关键——没有事件流的代理出错了就是一团黑箱有了事件流代理的每一步都有迹可循可观测性直接拉满。3.2 行动接口的宽度文件、终端、浏览器一网打尽项目 B 在工具设计上走的是宽覆盖路线。它给代理配置了三类核心工具文件操作读取、写入、编辑、目录列举支持按路径和模糊搜索定位文件。终端执行代理可以在沙箱环境里运行 shell 命令包括编译、测试、安装依赖。浏览器操作代理能打开页面、点击元素、填写表单、读取渲染后的 DOM。官方意图是让代理能做需要打开浏览器才能验证的任务比如前端联调或者爬取页面信息。这三大类工具合在一起意味着代理几乎拥有了一个真实开发者工作台的全部能力。它的一个 Agent 实现甚至直接把推理过程和动作合并成一整段 Python 代码模型生成的不是调用某个函数的单步指令而是一段可以顺序执行的脚本脚本里可以混入文件读写、shell 调用甚至可以打印中间结果给模型自己看。这种代码即行动的思路大大提高了代理执行复杂序列时的效率因为模型不需要一轮一轮地为了一个多步操作来回切换工具而是可以一口气写出一段完成多步操作的程序。3.3 沙箱隔离自治与安全之间的取舍工具越宽风险就越大。当代理能在终端里执行任意命令时你怎么保证它不会把项目目录删了不会把环境变量搞坏不会连到外部网络干些意料之外的事情项目 B 的做法是把所有工具执行放进隔离的容器沙箱里。代理的终端操作、浏览器操作、甚至文件系统的初始状态都在一个可随时销毁重建的隔离环境里进行。你给代理的任务它在沙箱里实验、修改、验证直到跑通了你才把最终的改动拿回来审阅。这里有一个特别值得注意的取舍沙箱提高了安全性但同时也限制了代理访问你本机环境的真实信息。代理在沙箱里跑不通某个依赖往往是因为沙箱里没有你本地那份特殊的配置。所以项目 B 设计了多种运行模式可以完全在本地运行高权限高风险也可以在容器沙箱运行安全但环境与生产有差异。我个人的经验是如果只是让它做代码生成和结构化修改本地模式问题不大只要涉及自动化高风险的依赖安装或批量文件操作务必切换到沙箱模式。项目 B 的复杂度和它的野心是匹配的。它不是拿来写个函数补丁的而是适合做多文件重构跨模块修改和需要反复验证的工程任务的研究级工具。社区里甚至有人拿它跑单次多小时的自主开发任务这在项目 A 的交互模式下是很难想象的。4. 在 IDE 里落地的自主编码代理项目 C4.1 工具集设计读、写、执行、搜索的权限划分项目 C 走的是第三条路不是让你在终端里和代理对话而是把编程代理直接嵌进你每天都在用的编辑器里同时兼顾自主和可控。它的工具集设计和项目 B 很不一样B 是堆工具宽度C 是讲权限划分。它把工具分成几大类每一类的触发时机都不同。读取类工具打开文件、查看目录、搜索代码在代理思考时自由调用而会改变系统状态的操作写文件、执行终端命令、安装依赖则要在经过你授权之后才能执行。在 IDE 的交互模型里代理每发起一个写文件或执行命令动作界面都会弹出一个提示让你确认。这个设计解决了一个很现实的矛盾纯自主代理如项目 B效率高但风险大纯交互代理如项目 A安全但每步都要人盯着。项目 C 的分权限工具模式把高风险动作变成需要你确认的动作把低风险动作留给代理自由裁量在效率和安全感之间找到了一个非常实用的折中点。4.2 Plan 与 Act 双模式思考优先还是行动优先项目 C 在工作模式上做了一个十分值得借鉴的决策把代理分成Plan 模式和 Act 模式。在 Plan 模式下代理只能使用只读工具。它可以读文件、搜索、查看依赖关系可以给出详细的分析和实施方案但不能改任何一个文件也不能执行任何命令。这个模式适合做任务规划和代码走查你要做一个涉及多个文件的改造先让代理把改动方案说清楚你看了没问题再切换模式执行。在 Act 模式下代理才获得全部工具权限可以自主修改代码、跑测试、迭代验证。你可以在两种模式之间随时切换甚至可以在 Act 模式中途切回 Plan让代理先停下来重新分析。为什么这个设计有效因为大量研究表明让模型边想边做很容易在上下文里累积错误决策越到后面越容易偏离方向。而显式地隔离开分析和执行等于在机制层面强制模型先形成方案再动手。如果说项目 A 的成功在于把循环做短项目 C 的成功就在于把思考和执行拆开。这个模式后来被很多编程代理产品学习现在你在很多工具里看到的先出计划确认后执行最初的架构原型就来自这类设计。4.3 上下文瘦身Token 预算管理与自动压缩机制项目 C 还有一个很细节但很关键的设计上下文管理。IDE 场景下代理会不断读取文件、记录搜索结果对话轮次一多上下文窗口很快就会被撑满。项目 C 的策略是给上下文设一个预算上限当接近上限时它不是简单粗暴地截断最早的消息而是自动把早期低价消息压缩成摘要比如把五轮关于某个函数实现的讨论浓缩成已确定将parse_data函数重构为返回生成器。这个摘要保留在上下文里这样代理仍然记得结论但省下了大量 token 空间。在我用过的同类工具里项目 C 的上下文管理算是最让我省心的。它还会根据当前打开的文件和选中的代码主动把最相关的文件加入上下文而不是让所有历史消息平权竞争位置。这些细节听起来不性感但决定了代理能不能在长时间任务里保持高质量输出。很多自己拼装的 Agent 跑着跑着就失忆本质就是没做过类似的上下文治理。拆完项目 C我最大的感受是它证明了编程代理不需要在全自动和纯手动之间二选一。通过权限分级、模式切换和上下文压缩一个编辑器插件也能承载相当强的自主编码能力同时不夺走开发者的控制权。5. 把三个项目放在解剖台上架构本质的横向对照5.1 控制权归属谁在主导循环把三个项目摆在一起看第一个要比较的就是控制权谁来决定代理下一步干什么。项目 A 是人主导的协作循环每一步行动都从用户的输入开始模型响应后等用户确认再进入下一步。这种模式的好处是可控性极高缺点是自动化程度低模型很难连续自主地推进一个大型任务。项目 B 是代理主导的自主循环模型拿到目标后自己规划、自己执行、自己根据观察迭代用户主要看最终结果。好处是自动化程度高坏处是中间过程一旦出错排查和介入都比较费力。项目 C 是混合主导你在 Plan 模式里掌握决策权切到 Act 模式后代理自己跑你可以在关键动作处打断和授权。这个设计本质上把控制权做成了一条连续光谱你可以按任务风险等级滑动控制点。这个差异表面上是产品定位的不同实际上是架构上的选择控制权越靠模型你对循环稳定性的要求就越高必须有事件流或日志系统来兜底控制权越靠人循环越简单但对交互设计的要求就越高。5.2 记忆与上下文数据从哪里来、到哪里去第二个核心维度是代理怎么记住它工作的环境。项目 A 的记忆是两层短期的对话历史和长期的代码库地图。它不试图理解仓库里所有代码的细节只维护一张符号级别的骨架图按需加载。项目 B 的记忆依托于事件流所有行动、观察、消息都在流里模型在任何时刻都可以回溯此前任意一步的执行结果。相当于代理有一个完整审计记忆代价是上下文很容易膨胀所以它也引入了摘要压缩策略。项目 C 的记忆是动态组装的根据当前任务实时决定读哪些文件、留哪些摘要试图用最小的 token 成本维持对任务的最优理解。三者的差异背后是一个权衡上下文信息的完整性与经济性不可兼得。想要代理不丢上下文就要付出 token 成本想要省 token就必须接受代理在对项目理解上的盲区。没有绝对正确的选择只有适合场景的选择。5.3 改动落地方式diff 增量与全量重写之争第三个维度是文件修改的粒度。项目 A 强制走 diff 编辑模型只输出变更由程序合成最终文件。这种方式对 token 友好、diff 清晰、容易审查但对模型的精准编辑能力要求高。项目 B 因为采用代码即行动的模式很多时候直接生成新的文件内容并覆盖改动粒度可以非常大。它追求的是执行效率一个脚本可以同时改十个文件但结果的可审查性和精细度就要靠事后 diff 来补。项目 C 的工具集里同时提供整文件写入和局部替换两类编辑工具让模型按需选择。它还引入了查找-替换式的精确修改当模型只需要改一个函数体时不大费周章重写整个文件。我的体会是diff 增量更适合日常结对场景因为改动小、好 review全量重写更适合自动化重构场景因为让模型在旧结构上打补丁反而别扭。编程代理的架构设计里以什么粒度落地修改是一个影响非常大的决策它同时牵扯上下文消耗、错误传播和审查体验。5.4 一张表看清三种架构的取舍维度项目 ACLI 结对项目 B自主工程师项目 CIDE 代理循环主导人主导代理主导人机混合上下文策略代码库地图 对话历史事件流 摘要压缩动态组装 自动压缩修改粒度以 diff 增量为主整文件与脚本化大改整写/局部替换按需选工具边界文件编辑 Git文件 终端 浏览器文件 终端 搜索权限分级安全控制靠 git 回退容器沙箱隔离用户确认高风险动作典型场景日常结对编程多文件自动化重构IDE 内深度开发架构复杂度低高中这张表基本就是编程代理架构的全部内核了。你会发现所谓看透架构本质其实就是回答五个问题谁来决策、模型怎么看仓库、改动怎么落地、执行怎么隔离、历史怎么管理。这三个项目各自交出了不同的答卷没有标准答案只有不同约束下的最优解。6. 从拆解到实践落地选型与避坑体验6.1 场景选型不同需求匹配不同架构拆了架构最终还是要落地。我给不同场景的建议是这样的如果你是一个人维护中小型项目希望在日常编码里有一个随叫随到、能改 bug、能补测试的结对搭档项目 A 的轻量交互模式最舒服。它安装快、心智负担小每一次改动都能在 git 里看到完全不需要额外的隔离措施。如果你要做大范围重构——比如把整个项目从回调改成 async/await或者给几十个类统一补上异常处理——项目 A 这种逐轮交互的模式会让你点到手软这时候项目 B 的自主规划和多工具并行才是正解。它可以在沙箱里自己探索、自己改、自己跑测试一口气推进完整任务。如果你是重度 IDE 用户写代码的大部分时间都花在编辑器里那项目 C 的嵌入体验最好。选一段代码让它先 Plan 后 Act风险可控又不打断工作流。还有一种常见需求是团队协作多个开发者共享一个编程代理服务。这种情况项目 B 的事件流架构优势明显因为事件流天然支持多人订阅和协作代理的状态不是某一个人的私有对话。6.2 踩过的坑循环失控、上下文污染与权限失控用编程代理用得越深踩的坑也越多。我挑三个最有代表性的说一下。第一个坑是循环失控。自主代理跑多步任务时经常出现反复改同一个文件但始终不满意的死循环。根因通常是反馈信号太弱——测试没有明确报错模型就不知道自己的改动是对是错于是反复试。我的办法是给任务设置明确的验收条件和最大迭代步数比如pytest 全绿最多改 5 轮代理到点不收敛就停下来向你汇报而不是无限空转。第二个坑是上下文污染。很多 DIY 者想让代理看到更多于是把一堆文件一股脑塞进上下文。结果模型被无关信息淹没改出各种张冠李戴的逻辑。正确的做法是向项目 A 学习只给骨架和摘要代理需要细节时再主动去读而不是预先把整条街的店铺都塞给一个问路人。第三个坑是权限失控。我给项目 B 跑过一个自动化任务它在一个本该只读的目录里创建了十几个临时文件还把环境变量改了整个环境弄得一团乱。从那以后我养成了习惯凡是会执行命令的代理场景一律先开沙箱或至少备份工作区。权限下放要一点点来宁可让代理多问你一次也别让它一次把所有权限都拿到。6.3 自己动手用 30 行代码搭一个最小编程代理看再多架构不如自己搭一遍。下面是我手工构造过的极简编程代理的核心逻辑也就三十来行能让一个模型通过工具调用完成读文件-改文件-跑命令的闭环。大模型服务的接入部分替换成你惯用的 SDK 即可。import json import subprocess # 这里替换为你所选大模型服务的客户端 client None TOOLS [ { type: function, function: { name: read_file, description: 读取指定文件的全部内容, parameters: { type: object, properties: { path: {type: string, description: 文件路径} }, required: [path], }, }, }, { type: function, function: { name: write_file, description: 把内容写入指定文件, parameters: { type: object, properties: { path: {type: string, description: 文件路径}, content: {type: string, description: 要写入的完整内容}, }, required: [path, content], }, }, }, { type: function, function: { name: run_command, description: 执行命令行命令并返回输出, parameters: { type: object, properties: { command: {type: string, description: 要执行的命令} }, required: [command], }, }, }, ] def call_tool(name, args): if name read_file: with open(args[path], r, encodingutf-8) as f: return f.read() if name write_file: with open(args[path], w, encodingutf-8) as f: f.write(args[content]) return 文件已写入 if name run_command: result subprocess.run( args[command], shellTrue, capture_outputTrue, textTrue ) return result.stdout or result.stderr def agent_loop(goal, max_steps10): messages [ { role: system, content: f你是一个编程代理。目标{goal}。 你可以调用工具推进任务全部完成后回复DONE并简述改动。, } ] for _ in range(max_steps): resp client.chat.completions.create( model你的模型名, messagesmessages, toolsTOOLS ) msg resp.choices[0].message messages.append(msg) if not msg.tool_calls: print(代理回复, msg.content) break for tc in msg.tool_calls: result call_tool(tc.function.name, json.loads(tc.function.arguments)) messages.append( {role: tool, tool_call_id: tc.id, content: result} ) else: print(达到最大步数任务可能未收敛请检查日志。) # 示例让代理修复 main.py 中的语法错误并跑测试 agent_loop(修复 main.py 的语法错误并运行 python -m pytest 确认测试通过)这个迷你代理没有仓库地图、没有沙箱、没有上下文压缩但你跑一遍就能直观感受到 Agentic Coding 的核心机制模型输出工具调用宿主程序执行并返回结果模型根据结果调整下一步。把这三段逻辑跑通之后再看项目 A 的代码库地图、项目 B 的事件流、项目 C 的权限分级你就知道它们各自解决的是上面这个骨架里哪一环的什么问题了。我自己折腾完这三个项目最大的体会是编程代理的架构进步很少来自某个大模型的突然变强更多来自这些看似琐碎的工程细节——用 diff 还是整写、上下文摘不摘要、沙箱隔离到哪一层、谁来点确认键。这些决定堆叠在一起才塑造出了一个代理靠谱还是失控的天壤之别。如果你也想在自己的项目里引入 Agentic Coding不用一上来就追求最全的工具和最宽的权限从一个小而清晰的闭环开始把每一环反馈做扎实再慢慢往里面加你想要的能力这条路会比直接部署一个大而全的系统稳得多。