标签 代码补全 下的文章

Matrix 首页推荐

Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。

文章代表作者个人观点,少数派仅对标题和排版略作修改。


2025 年,我的工作习惯彻底被 Claude Code、Cursor 和 Codex 改变了,它们给我带来的改变,不亚于当年我用易语言写出第一个应用程序。同时这句话不仅适用于我的产品经理身份,也同样适用于我的工程师身份。

对我来说这不仅是软件开发效率的提升,也是软件开发方式的重构。

我说的是哪种 AI Coding

talking past each other

在社交媒体上讨论 AI 编程时,很多时候大家其实没有对齐在讨论的 AI 编程范畴和适用领域。所以大部分情况下大家在鸡同鸭讲,公说公有理、婆说婆有理。在我们真正讨论 AI 编程之前自然需要先澄清一下,我们到底在讨论什么「AI Coding」?

different levels

目前来说, AI Coding 有几种不同的产品和用户交互形态,包括:

  1. L1:古典的 ChatGPT 交互问答。有问题直接问 ChatGPT、Gemini、Claude 等 AI 助手,借助 AI 助手给出的信息,自行 Debug 和修改代码。
  2. L2:IDE / 插件中提供的补全功能。直接写函数名,然后 AI 会帮你补全函数的细节,你再自己微调或者不微调。这种能力其实过去也有,只是没有这么强悍。大家使用 IDE 来开发,很大程度上就是在利用 IDE 极佳的补全能力。
  3. L3:本地 AI Coding 工具的 Agent 模式,全托管或半托管式编程。你只描述需要做什么,具体做动作由 AI 来完成;这里还有几个细分的方式,包括完全托管(比如 Claude Code 开 –dangerously-skip-permissions、Codex 开 –dangerously-bypass-approvals-and-sandbox)和半托管(用户手动确认行为,只是由 AI 来完成具体修改的动作)。L3 操作的是你的本地环境,有破坏的风险,但也有无限的可能。
  4. L4:使用网页端的 AI Coding 工具直接出 Demo。不需要在本地配置任何开发环境,直接在网页端完成 Coding,你只需要描述你想要的东西,剩下的完全交给 AI(虽然这往往意味着难以与复杂的本地业务流集成)。

所以大家在社交媒体上和别人讨论 AI Coding 的时候,很有可能你在聊 L3、但别人在聊 L4。看似聊的是一个东西实际上完全不同。我们这篇文章讨论的则主要是 L3 —— 即使用本地的 AI Coding 工具的 Agent 模式,全托管或半托管式编程。

为什么是 Agent 模式

说完了大家眼中不同的 AI Coding 工具,我也必须再强调一下,即使大家聊的都是「AI Coding 工具的 Agent 模式」,因为身份不同,每个人的用法也会有很大差异,大家对工具的预期也完全不同。

因此对 Agent 模式的评价大体也可以拆分为三个大类:

different groups of people
  1. 传统的软件工程师:使用 AI Coding 工具完成自己工作过程中的一些辅助性工作,对于自身的能力和工作的要求有较高的要求。
  2. 有一定研发概念的产品工程师(称之为产品工程师是因为他们有一些基础的软件工程概念):使用 AI Coding 工具拓展自己的能力圈,去做一些之前必须依赖软件工程师才能做完的事情(比如做个小 Demo,或是上线一个软件产品)。
  3. 被媒体上的文章忽悠进来的小白(他们基本上没有太多的软件工程经验和基础):跟着网络上的信息了解到了 Coding Agent,然后尝试在不同的软件上使用(不局限于 Claude Code,Cursor 的 Agent 模式也算),经常会卡在一些软件工程的基础问题上。(我特别推荐这类人去看《计算机教育中缺失的一课》,看完以后,会让你很快理解你所遇到的问题,并能够快速处理 AI Agent 所给你的信息,进行下一步操作)。

而之所以是 Agent 模式,相比于补全模式需要你有一段现成的代码,Chat 模式往往不指引你完成所有的工作、或需要你有一定的互联网软件基础,Agent 模式自带的环境操作能力,能让你即使完全不懂 AI 和 Coding,也可以做一个像模像样的小应用出来。

这打破了过去需要软件工程师才能做出一个 Demo 的限制,同时也极大地鼓励了新手小白,人人都想试着做一些有意思的事情。Agent 模式也是 AI Coding 最出圈的一个特性。

我与 AI Coding 的 Agent 模式

在这条上,我提到我经历过三种不同的状态,这源自于我过去一年的体验。

在过去这一年里,我从一个「以补全为主,抗拒 AI Coding Agent」的工程师,转变为了一个「好好利用 AI Coding Agent」的工程师。当中离不开我身边的朋友们和小伙伴们,和他们的协作让我真的意识到了 AI Coding Agent 的价值1

作为一个写了十数年代码的工程师,我拥有一些自己平时写代码的「脚手架」,可以帮助我快速完成一个项目,而同时出于工程师的自我要求,我希望我自己写的代码能够拥有不错的代码质量和性能。

所以在一开始我对于 AI Coding Agent 的能力是持怀疑态度的,虽然依然会使用,但整体信任度没有那么高,往往只让它处理细枝末节,比如让 AI 去写一个完整的功能的细节,或者是让 AI Coding Agent 去 Code Review,基本不会让它动业务代码。

直到 Claude Sonnet 3.7 的时候,我才发现 Claude Code 已经能完成不少的工作,甚至很多小的功能也不再需要我先行规划、再让它自己做细节。我可以非常坦然的交给它一些工作,而我只做关键验收。从这个时刻开始,我对于 AI Coding Agent 的使用频率与日俱增,我开始使用 AI Coding 去实现越来越多的功能和效果,并最终对 AI Coding 放权——开启了 --dangerously-skip-permissions ,让 Claude Code 自己去写代码,去实现效果。

和所有使用 AI Coding Agent 的人一样,我也经历过 AI Coding 工具将代码整的一团糟然后放弃的时刻。不得不说,Claude 有些时候的过度设计真的让人无语。所以即便这就是最佳实践,但最佳实践不仅仅要配合着实践用,也要配合着项目时间节点和周期,以更好的完成项目的目标 —— Coding 只是完成目标的手段,而不是目的。

我对 AI Coding Agent 模式的看法

首先,我得旗帜鲜明地表明,所有软件工程师都应该试着使用 AI Coding Agent。

放弃、逃避、蒙头装鸵鸟都是没有意义的,熟练掌握 AI Coding Agent 已不再是加分项,而是生存技能。AI Coding Agent 正在切实地改变着我们的行业。

诚然,AI Coding Agent 不会影响我们这些「老」工程师的工作,但这个问题如同被熊追着一样——淘汰你的从来不是熊,而是比你跑得更快的人。AI 不会淘汰工程师,但会淘汰那些拒绝进化的「手动操作员」。优秀的工程师不会消失,我们的职责正从「手写逻辑”转变为「选择方案、设计架构、验收质量」。

其次,我依然相信大家的软件工程经验是有价值的。

就像上面我提到,最佳实践本身没有问题,但每个人所面对的项目节奏是完全不同的。软件工程师的价值从手写代码,变成了选择 AI 提供的解决方案,审核 AI 提供的解决方案,并最终更好地完成自己的工作。优秀的软件工程师不会消失,只是工作职责变了,AI Coding Agent 会让他们获得更多「加成」。

我怎么用 Coding Agent

我目前自己同时在使用的包括 Claude Code、OpenAI Codex 和 Gemini Cli,其中前两者是我自己花钱买的,后者是 GDG 赠送的。

我强烈建议大家自己花钱购买。公司买了自然最好,如果公司没买,自己买也是划算的。 Claude Code 5X 已经满足日常使用了,如果你用量大再升级 Claude Code 20X 即可;$100 其实就是大家聚餐吃一顿价格,并不贵,但给你带来的提升是远超这个价格的。

我对于 Coding Agent 的使用会包含本地使用和云端使用两块,以及一些「质量守卫」。

本地使用

本地使用时,我会使用 Claude Code 的 Plan Mode 来设计一些需求,通过和 Coding Agent 的几轮交互约束需求范围,并让 Claude Code 去完成相关的功能。

同时,我会使用 Codex 来为我的项目补全测试 —— 过去我很多时候都懒得写测试,现在有了 AI 来帮助我测试基础覆盖,大大提升了效率。或许 AI 无法保证测试得像一个专业测试同学那样完善,但却可以让我先完成从 0 到 1 的建设。

Gemini Cli 则会在一些时候作为项目的补充,主要也是 GDG 赠送的 Plan 额度有限,所以用得不多。

之所以会同时使用三个不同的 Coding Agent,主要还是考虑到模型本身的能力是有差异的;不同的模型可以给我提供不同的视角,从而可以确保对于一个问题有更全面的思考,规避可能的思维漏洞。

云端使用

除了本地的使用,我还会在云端使用 AI Coding Agent,但并不是使用网页端的 Coding 工具,而是在 CI 工具链中集成 AI Coding Agent,使用 AI Coding Agent 对我的每一次提交、每一次 PR 进行代码评审,通过这样的方式帮我更好地发现代码中的问题,既可以确保自己对于问题的思考没有漏洞,也可以进一步的发现自己的经验不足,补全面对问题时的思考维度。

特别值得一提的是,集成多个 Coding Agent 可以明显看到不同 Coding Agent 对于问题的思考角度和深度不同,对于自我提升也非常有帮助。

质量守卫

由于AI Coding Agent 本身存在的问题,大规模编辑代码的时候,往往也会引入一些编辑错误、无用代码和不符合规范的问题。

因此我在深度使用 AI Coding Agent 的项目中,都会引入大量的质量守卫,来确保不符合规范的代码无法提交到云上。我们应当保持对 AI 的信任,但必须坚守 「信任但要核实(Trust, but Verify)」 的原则,确保 AI 提交的代码每一行都可信、可控。

flowchart

这里的具体工作包括:

  1. 引入静态检查的准入机制,扼杀低级错误:借助 git hook,在提交前进行 code format。
  2. 引入质量红线,控制确保逻辑不出错,没有改错之前的代码:借助 git hook 和单元测试围栏,要求提交到代码库中的代码必须测试覆盖率满足要求(强 AI Coding Agent 介入的项目会要求测试覆盖率到 100%,并包含集成测试和端到端测试)。
  3. 引入 AI Agent 的自愈能力,闭环问题的修复:借助 git hook 和一些 linter 工具,发现代码中的问题,并让 AI Coding Agent 自己去修复它。
  4. 引入代码复杂度分析工具,让 AI 写出人和 AI 都易于维护的代码:常见的编程语言基本上都提供了圈复杂度检查工具,你可以在你的项目中引入圈复杂度检查工具,让 AI 提交之前确保所有代码的复杂度不会太高,从而既可以借助 AI 的能力快速迭代,同时又保留自己随时介入的可能性。

当你有充足的围栏检查,就可以放心让 AI 去完成工作,并要求它自行提交 commit 了。这样可以让其自动执行 git hook 并修复 git hook 中所配置工具检查出来的问题,从而确保提交到代码库中的代码不会有一些基础问题。

你该如何开始(工程师篇)

如果你是一个工程师,想要试试 AI Coding Agent 但又不知道怎么做:我能理解你对于代码质量的要求,和你的工作对你的要求;我也知道你担心引入 AI 导致你的项目复杂度快速提升、无法维护,最后导致项目彻底崩盘,你反而被干掉。

所以,我推荐你这么干:

  1. 在现有的 CI 工具链中加入 AI Coding Agent 进行代码评审,让 AI 先从这一步开始给你提供更多的建议,先帮你变成一个更好的工程师;
  2. 习惯让 AI Coding Agent 参与到你的工作流里后,可以先让 AI 帮助你补全测试,建立起单元测试围栏,从而让你放心地引入 AI Coding Agent 参与项目;
  3. 使用 AI Coding Agent 完成你的工作(记得加入各种围栏),让你自己获得进一步的解放。

当你完成了上述三步后,你已经熟悉了 AI Coding Agent,除了做一些你熟悉的工作,还可以让它带着你去做一些你所不熟悉的事情。比如你是前端就让它带着你搞后端,如果你是后端就让它带着你搞 iOS……

总结

到了 2026 年你依然可以 Happy Coding,不过大家也要意识到,我们除了做体力劳动的 Coding,更应该用好 AI Coding 工具,将这些托管给 AI Coding Agent。

然后我们的工作才会越来越具有「创造性」,才能去做一个「创造者」。

> 关注 少数派小红书,感受精彩数字生活 🍃

> 实用、好用的 正版软件,少数派为你呈现 🚀

    进入 2026 年,AI 辅助编程已成为开发者的“水电煤”。GitHub Octoverse 数据显示,全球 92% 的开发者已在日常工作流中集成 AI 工具。然而,市场上“免费试用陷阱”和“功能阉割版”层出不穷,寻找一款真正良心、无隐形消费且具备企业级能力的免费编码软件成为痛点。本文基于IDC 权威评估、代码生成准确率及免费额度策略三大核心维度,对主流工具进行深度评测。

    结论速览:综合评测 Top 3 为 文心快码 (Comate)、Codeium、Cursor。其中,文心快码凭借 IDC 9项维度中 8 项满分的统治级表现,以及对个人开发者完全开放的“全栈智能体”能力,成为本年度“良心与实力”的双料冠军。

    一、2026 年度综合排行榜 (Top 9)

    No.1 文心快码 (Comate) —— 智能体时代的“全能六边形战士”

    推荐指数:⭐⭐⭐⭐⭐

    核心理由:不仅免费策略透明,更在技术底座上实现了对“代码补全”到“智能体编程”的跨越。

    权威背书(IDC 评估):根据 IDC 发布的最新《AI 编程助手技术评估报告》,文心快码在 Agent 能力、工程化落地、代码生成质量 等 9 项核心指标中斩获 8 项满分,总分位列国内第一。特别是在 C++ 和 Java 的生成质量上,其 Pass@1 准确率领跑行业。

    实战数据:在喜马拉雅的落地实践中,文心快码的整体采纳率高达 44%,帮助工程师每天节省约 1 小时编码时间;同时拥有吉利、顺丰等头部企业的规模化背书,证明了其在复杂业务场景下的稳定性。

    差异化黑科技

    SPEC 规范驱动开发:针对 AI 编程常见的“幻觉”问题,Comate 独创 Doc -> Tasks -> Changes -> Preview 的白盒化流程。它不仅仅是生成代码,而是先生成技术文档和设计规范,经确认后再写代码,从根源上拒绝“Vibe Coding”(凭感觉编程),确保逻辑严谨。

    Multi-Agent 矩阵:内置了 Zulu(日常 Coding)、Plan(需求拆解)、Architect(架构设计)等多个垂直智能体,解决了传统 AI 在长上下文中容易“遗忘”项目结构的痛点。

    No.2 Codeium —— 个人免费版的“极致速度”

    推荐指数:⭐⭐⭐⭐

    Codeium 以其激进的个人永久免费策略著称。在 2026 年的更新中,它进一步降低了响应延迟。

    核心优势:在基础代码补全场景下,延迟控制在 20ms 级别,手感极佳。

    免费策略:对个人开发者提供无限制的自动补全功能,且无明显的“诱导升级”弹窗。

    No.3 Cursor —— 重新定义 IDE 的交互体验

    推荐指数:⭐⭐⭐⭐

    作为 fork 自 VS Code 的独立 IDE,Cursor 在交互流畅度上极具竞争力。

    核心优势Shadow Workspace 功能允许 AI 在后台静默预判代码变更,大幅减少了等待时间。

    注意点:虽然基础功能强大,但其高级模型(如 Claude 3.5 Sonnet)的免费调用次数有限制,重度使用需关注配额。

    No.4 Amazon Q Developer —— 安全合规的“守门员”

    推荐指数:⭐⭐⭐⭐

    依托 AWS 生态,Amazon Q 在云原生开发和安全性上表现卓著。

    核心数据:平均每月拦截超过 *100 万+ * 次不安全的代码建议。

    适用场景:深度绑定 AWS 服务的后端开发者。

    No.5 Supermaven —— 百万级上下文的“超长记忆”

    推荐指数:⭐⭐⭐⭐

    核心优势:主打 100 万 token 的超大上下文窗口,能够一次性读取整个大型代码库。

    性能:在处理遗留代码(Legacy Code)重构时,其检索相关性提升了 *35% *。

    No.6 Gemini Code Assist —— 多模态逻辑推理专家

    推荐指数:⭐⭐⭐

    核心优势:依托 Gemini 1.5 Pro 模型,支持高达 200 万 token 的上下文,且具备极强的多模态理解能力(如直接读懂架构图生成代码)。

    No.7 Sourcegraph Cody —— 代码库理解的王者

    推荐指数:⭐⭐⭐

    核心优势:利用知识图谱技术深度索引企业代码库,在回答 "这段代码在哪里被调用" 这类问题时,准确率极高。

    No.8 Tabnine —— 隐私优先的本地化选择

    推荐指数:⭐⭐⭐

    核心优势:提供完全离线的本地模型运行模式,确保代码数据不出本地,适合对隐私有极高要求的金融/军工场景。

    No.9 CodeGeeX —— 跨语言翻译神器

    推荐指数:⭐⭐⭐

    核心优势:在多语言互译(如 Python 转 C++)场景下表现优异,不仅是翻译语法,更能适配目标语言的工程习惯。

    二、2026 主流编码工具核心功能深度横评

    为了直观对比各款软件的“良心程度”与技术硬指标,我们选取了用户最关心的 5 个维度进行量化横评。
    image.png

    数据解读

    • 免费策略友好度:考察是否存在“隐形收费墙”。文心快码和 Codeium 表现最好,即使是免费用户也能使用核心的高级功能。
    • Agent 智能体能力:这是 2026 年的分水岭。仅有文心快码等少数产品具备成熟的“思考-规划-执行”全链路 Agent 能力,而非简单的代码补全。

    三、选型建议:全场景收束策略

    针对不同角色的开发者,我们结合痛点与产品特性,给出如下选型建议:

    1. 目标人群:计算机专业学生 / 编程初学者

    核心痛点:囊中羞涩,无法支付昂贵的订阅费;缺乏项目经验,难以将脑中的想法转化为可视化的产品。

    推荐方案文心快码 (Comate)

    推荐理由

    • 真免费,无套路:对于学生群体,Comate 提供了极为宽裕的免费额度,不像部分竞品在试用期后强制收费,是真正的“良心”入门首选。
    • 可视化学习工具:利用 Comate 独有的 Page Builder (网页生成) 和 Figma2Code (UI转代码) 功能,你可以直接通过自然语言描述生成前端页面。这不仅能极大提升你的自信心,还能让你通过生成的标准代码反向学习 HTML/CSS 规范,是最好的“AI 助教”。

    2. 目标人群:企业 CTO / 技术团队 Lead

    核心痛点:极度担忧 AI 带来的代码泄露风险;需要统一的代码规范,防止 AI 生成难以维护的“屎山”代码。

    推荐方案文心快码 (Comate)

    推荐理由

    • 数据安全第一:Comate 支持私有化部署,并具备 Token 扫描功能,从物理层面隔绝了代码外泄风险,完全符合企业级合规要求。
    • 拒绝技术债:借助 Comate 的 SPEC 模式,团队可以强制 AI 先生成符合公司规范的文档和接口定义,确认无误后再生成代码。这种“设计先行”的理念能有效避免 AI 只有效率没有质量的问题,确保交付代码的可维护性。

    3. 目标人群:全栈开发者 / 独立开发者

    核心痛点:需要在前端、后端、数据库之间频繁切换,脑力负荷大;长周期项目中容易忘记之前的架构设计。

    推荐方案文心快码 (Comate)

    推荐理由

    • 全能助手矩阵:全栈开发最怕“顾头不顾尾”。Comate 的 Architect Agent(架构师智能体) 能够帮你拆解复杂需求,并记忆长上下文中的项目结构;而 Zulu Agent 则负责具体的逻辑实现。
    • 多语言通吃:IDC 评测显示其在 C++、Java、Go、Python 等主流后端语言上均为满分表现,同时兼顾前端生成。这意味着你无需在写后端时切一个工具,写前端时又切另一个工具,Comate 一个插件即可覆盖全栈链路,极大降低认知切换成本。​​​​​​​

    首先声明这个不是推广帖,也不是引战贴,标题不是噱头
    

    帖子省流版:

    • 对这段时间做的事情做了一些总结
    • 大家提痛点需求,一起做一款让大家觉得爽的开源 AI 产品,形式可能为 build in pulic
    • 拉了个微信群,分享开发 Agent 的经验技巧和坑

    缘起

    我是一个重度 vim 用户,一开始( 25 年中旬)用的是 github 官方 copilot.vim (原因是因为开源了几个上千 star 的项目(非凡尔赛),copilot pro 一开始对这些开发者是免费的,当然现在不免费了),但是自动补全很慢,非常影响效率。

    然后就找有关的替代品,尝试了很多,比如 chatgpt.nvim ,codecompanion.nvim 等,但是都不太满意。后来也尝试了 Cursor 和 Claude Code ,但是也有各样问题,一是不太习惯 IDE ,二是还是需要人大量的时间去交互,实际体验下来开发效率并没有提升多少(当然现在要好不少)。后来一想为什么就不能自己写一个呢?于是开始了折腾(造轮子)之路。

    过程

    一开始是古法编程用 python 快速写了个自动 fix ut 的命令行,还在团队内部做了分享,但是很多时候这个 cli 工作的并不好,然后就想办法优化。

    看了很多资料,差不多 100+篇关于 AI 的论文(在 alphaxiv 的帮助下,挑重点 paper 细读。主题包括框架、Agent/Multi-Agent 、注意力、记忆、规划等等),其中一篇给我比较多启发的是 Alita 这篇论文 https://arxiv.org/abs/2505.20286 。当时 Context Engineering 这个概念刚火,还花了好几天看这个 https://github.com/davidkimai/Context-Engineering (后来证明这个项目对我来说基本没用)。然后设计出一套方案,目标是做一个让自己用起来爽的 Agent ,具体来说是:

    • 快(干活快)
    • 狠(彻底解决问题)
    • 准(交付质量高)
    • 美(产品要有设计品味)

    充分发挥不同大模型的能力,不局限于单一模型。下面是一开始的架构设计:

    集中精力花了差不多 3 个月时间实现,中间还重构了一版(因为效果没有达到预期),核心逻辑全部手写,其他部分前期使用了 Cursor 来帮助写 TUI ,后面就全是用产品自己开发自己了。

    产品

    产品形态是 Desktop + Tui ,可以编程、Research 、干杂活等,可以简单理解为 Claude Code + Cowork 。

    • 桌面版

    对应视频在这

    • TUI

    对应视频在这

    问题

    上个月底这个月初找了几个朋友内测了下,大家给了一些建议。

    这两天也开源了,在 HN 和 v2 也分别发了一个帖子简单介绍了下,反馈都比较平平,发的帖子很快沉下去了。我自己反思了下,可能是自己做的东西确实不够好,也可能是大家没关注到,或者大家关注到了但是不感兴趣。

    所以想看下大家真实的想法,先抛个几个问题:

    • 现在大家对此类工具和产品怎么看?是产品引不起自己的兴趣吗?
    • 除了 claude code/codex/cursor/opencode 等,还用其他什么比较好的产品吗?比如豆包
    • 现有的工具已经能很好的解决自己的问题了吗?还有其他的痛点吗?

    最后

    大家需要的话,我可以免费帮实现大家实现有关功能,未来产品的形态由大家来定义。

    另外我拉了个微信群,准备把开发 Agent 的时候一些经验技巧和坑分享一下,同时也方便大家交流有关话题,有感兴趣的可以评论区留言我拉一下大家进群。

    导读

    AI 编码工具正在从"智能补全"演进为能自主完成复杂任务的 Coding Agent。本文基于开源项目源码研究与实践经验,系统性地拆解 Coding Agent 的工作原理。旨在帮助开发者在了解Coding Agent后,与AI伙伴更好的协作配合,更高效的提问和拿到有效结果。

    01 背景

    AI 编码工具的发展速度快得有点"离谱"。从开始使用 GitHub Copilot 的代码补全,到使用Claude Code、Cursor、Comate IDE等完成复杂编程任务,AI 不再只是个「智能补全工具」,它能读懂你的代码库、执行终端命令、甚至帮你调试问题,成为你的“编码伙伴”。

    我自己在团队里推 AI 编码工具的时候,发现一个很有意思的现象:大家都在用,但很少有人真正理解它是怎么工作的。有人觉得它"很神奇",有人吐槽它"经常乱来",还有人担心"会不会把代码搞乱"。这些困惑的背后,其实都指向同一个问题:我们对这个"伙伴"还不够了解。

    就像你不会无脑信任一个新来的同事一样,要和 AI 编码伙伴配合好,你得知道它的工作方式、能力边界、以及怎么"沟通"才更有效。

    在经过多次的实践尝试后,我尝试探索它的底层原理,并写下了这篇文章记录,主要围绕了这些内容展开:

    • Coding Agent 的核心工作机制,包括身份定义、工具调用、环境感知等基础组成。
    • 从零实现一个最小化 Coding Agent 的完整过程,以建立对 Agent 工作流程的直观理解。
    • 上下文管理、成本控制、冲突管控等生产环境中的关键技术问题及其解决方案。
    • Rule、MCP、Skill 等能力扩展机制的原理与应用场景。

    在了解原理后,我和伙伴的协作更佳顺畅,让伙伴更清晰的了解我的意图,我拿到有效的回答。

    02 概念

    2.1 从Workflow到Agent

    取一个实际的例子:休假申请。

    如果我们的需求非常简单:

    一键申请明天的休假。

    在这里插入图片描述

    这个需求可以被简化为一个固定的工作流

    1. 打开网页。
    2. 填写起始时间。
    3. 填写结束时间。
    4. 填写休假原因。
    5. 提交表单。

    全过程没有任何模糊的输入,使用程序化即可完成,是最原始的工作流形态。

    如果需求再模糊一些:

    申请后天开始3天休假。

    这个需求的特点是没有明确的起始和截止时间,需要从语义上分析出来

    1. 起始时间:后天。
    2. 休假时长:3天。
    3. 转换日期:10.14 - 10.16。
    4. 执行申请:提交表单。

    这是一个工作流中使用大模型提取部分参数的典型案例,是模型与工作流的结合。

    如果需求更加模糊:

    国庆后休假连上下个周末。

    这样的需求几乎没有任何直接确定日期的信息,同时由于年份、休假安排等动态因素,大模型不具备直接提取参数的能力。将它进一步分解,需要一个动态决策、逐步分析的过程:

    1. 知道当前年份。
    2. 知道对应年份的国庆休假和调休安排。
    3. 知道国庆后第一天是星期几。
    4. 国庆后第一天到下个周末设为休假日期。
    5. 额外补充调休的日期。
    6. 填写并提交表单。

    可以看出来,其中1-5步都是用来最终确定休假日期的,且需要外部信息输入,单独的大模型无法直接完成工作。这是一个典型的Agent流程,通过大模型的智能工具访问外部信息结合实现用户需求。

    2.2 什么是Agent

    Agent是以大模型为核心,为满足用户的需求,使用一个或多个工具,自动进行多轮模型推理,最终得到结果的工作机制。

    2.3 什么是Coding Agent

    在Agent的基本定义的基础上,通过提示词、上下文、工具等元素强化“编码”这一目的,所制作的特化的Agent即为Coding Agent。

    Coding Agent的最大特征是在工具的选取上,模拟工程师进行代码编写的环境,提供一套完整的编码能力,包括:

    • 阅读和查询代码:

      • 读取文件,对应 cat 命令。
      • 查看目录结构,对应 tree 命令。
      • 通配符查找,对应 ls命令(如 **/*.test.tssrc/components/**/use*.ts)。
      • 正则查找,对应grep 命令(如function print\(.+\) 可以找函数定义)。
      • LSP(Language Server Protocol),用于提供查找定义、查找引用、检查代码错误等能力。
    • 编写或修改代码:

      • 写入文件。
      • 局部编辑文件。
      • 删除文件。
    • 执行或交互命令:

      • 执行终端命令。
      • 查看终端命令stdout输出。
      • 向终端命令stdin 输入内容。

    除此之外,通常Coding Agent还具备一些强化效果而设定的工具,通常表现为与Agent自身或外部环境进行交互,例如经常能见到的TODO、MCP、Subagent等等。

    03 内部组成

    3.1 上下文结构

    3.2 身份定义

    一个Agent首先会将模型定义成一个具体的身份(红色与橙色部分),例如在社区里常见的这样的说法:

    You are a Senior Front-End Developer and an Expert in React, Nexts, JavaScript, TypeScript, HTML, CSS and modern UI/UX frameworks.

    在身份的基础上,再附加工作的目标和步骤拆解,比如Cline有类似这样的内容:

    https://github.com/cline/cline/blob/4b9dbf11a0816f792f0b3229a08bbb17667f4b73/src/core/prompts/system-prompt/components/objective.ts

    1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.
    2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.
    3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. Before calling a tool, do some analysis within <thinking></thinking> tags. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. BUT, if one of the values for a required parameter is missing, DO NOT invoke the tool (not even with fillers for the missing params). DO NOT ask for more information on optional parameters if it is not provided.
    4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. open index.html to show the website you've built.
    5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.

    不用特别仔细地看每一句话,多数Coding Agent会提供一些详实的行动准则、目标要求,这部分称为“Guideline”。

    有一些Coding Agent可以在多种模式(或者说智能体)之间进行切换,例如Cursor有Edit、Ask、Plan等,RooCode有Architect、Orchestrator等,有些产品还支持自定义模式。

    Cursor

    RooCode

    选择不同的模式时,实际上会产生不同的目标要求、行为准则,即不同的Guideline环节。因此系统提示词中的身份部分,通常会分成不变的Base Prompt(红色)和可变的Agent Prompt(橙色)两个部分来管理,实际开始任务时再拼装起来。

    3.3 工具调用

    Agent的另一个最重要的组成部分是工具,没有工具就无法称之为一个Agent。让Agent能够使用工具,就必须要有2部分信息:

    1. 有哪些工具可以用,分别是什么作用。
    2. 如何指定使用一个工具。

    对于第一点(哪些工具),在Agent开发过程中,一般视一个工具为一个函数,即由以下几部分组成一个工具的定义:

    1. 名称。
    2. 参数结构。
    3. 输出结构。

    实际在调用模型时,“输了结构”往往是不需要提供给模型的,但在Agent的实现上,它依然会被预先定义好。而“名称”和“参数结构”会统一组合成一个结构化的定义,通常所有工具都只接收1个参数(对象类型),用JSON Schema表示参数结构。

    一个典型的工具定义:

    {
      "name": "read",
      "description": "Read the contents of a file. Optionally specify line range to read only a portion of the file.",
      "parameters": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "The file path to read from"
          },
          "lineStart": {
            "type": "integer",
            "description": "The starting line number (1-indexed). If not specified, reads from the beginning of the file."
          },
          "lineEnd": {
            "type": "integer",
            "description": "The ending line number (1-indexed). If not specified, reads to the end of the file."
          }
        },
        "required": ["path"]
      }
    }

    可以简单地把这个工具理解成对应的TypeScript代码:

    interface ReadToolParameter {
            path: string;
            lineStart?: number;
            lineEnd?: number;
    }
    
    async function read(parameters: ReadToolParameter) {
            // 工具实现
    }

    对于第2点(指定使用工具),则是要让大模型知道工具调用的具体格式。这在业界通常有2种做法。

    第1种以Claud Code、Codex等为典型,使用大模型提供的Function Calling格式调用,分为以下几步:

    1. 在调用大模型时,通过一个tools 字段传递所有的工具定义。
    2. 模型会返回一个消息中包含tool_calls 字段,里面每一个对象是一个工具的调用,使用id 作为唯一标识。
    3. 工具产生的结果,以一条role: 'tool' 的消息返回,其中tool_call_id 与调用的id对应,content 是工具的结果(这里各家模型厂商的实现略有不同,其中Anthropic要求role: user,但content字段中传递toolResult,其结构是[{type: 'tool_result',tool_use_id: toolBlock.id, content: toolResultContent}],tool_use_id与调用的id对应)。

    第2种方式是以Cline、RooCode为典型,使用一种自定义的文本格式来表示工具调用,通常选择XML的结构,例如对于Cline,读取一个文件的结构如下:

    <read_file>
    <path>src/index.ts</path>
    </read_file>

    只要在模型返回的消息中出现这样的结构,就会被解析为一个工具调用,得到的结果以普通的role: 'user' 的消息返回,包括实际内容和一些提示相关的信息。

    Content of src/index.ts:
    
    Note:
    
    - this file is truncated to line 1000, file has a total 2333 lines.
    - use read_file with line_start and line_end parameters to read more content.
    - use seach_in_files tool searching for specific patterns in this file.
    
    ...

    3.4 环境感知

    Coding Agent之所以可以在一个代码库上执行任务,除了通过工具来遍历、检索代码外,另一个因素是Agent实现会在调用模型时主动地提供一部分与项目有关的信息。

    其中对Coding Agent工作最有用的信息之一是代码库的结构,即一个表达出目录、文件结构的树型区块。这部分信息通常会符合以下特征:

    1. 尽可能地保留目录的层级结构,使用换行、缩进的形式表达。
    2. 遵循 .gitignore 等项目配置,被忽略的文件不会表现在树结构中。
    3. 当内容过多时,有一定的裁剪的策略,但同时尽可能多地保留信息。

    以Cursor为例,这部分的内容大致如下:

    <project_layout>
    Below is a snapshot of the current workspace's file structure at the start of the conversation. This snapshot will NOT update during the conversation. It skips over .gitignore patterns.
    
    codex-cursor/
      - AGENTS.md
      - CHANGELOG.md
      - cliff.toml
      - codex-cli/
        - bin/
          - codex.js
          - rg
        - Dockerfile
        - package-lock.json
        - package.json
        - scripts/
          - build_container.sh
          - build_npm_package.py
          - init_firewall.sh
          - [+4 files (1 *.js, 1 *.md, 1 *.py, ...) & 0 dirs]
      - codex-rs/
        - ansi-escape/
          - Cargo.toml
          - README.md
          - src/
            - lib.rs
    </project_layout>

    当内容数量超过阈值时,会采用广度优先的保留策略(即尽可能地保留上层目录结构),同时对于被隐藏的文件或子目录,会形如 [+4 files (1 *.js, 1 *.md, 1 *.py, ...) & 0 dirs]这样保留一个不同文件后缀的数量信息。

    除了目录结构外,还有一系列默认需要模型感知的信息,在一个Coding Agent的工作环境中,它通常分为2大类,各自又有一系列的细项:

    1. 系统信息:

      1. 操作系统(Windows、macOS、Linux,具体版本)。
      2. 命令行语言(Shell、Powershell、ZSH)。
      3. 常见的终端命令是否已经安装( python3nodejqawk等,包含具体版本)。
      4. 代码库目录全路径。
    2. 为Agent扩展能力的信息:

      1. Rule(自动激活的部分)。
      2. Skill(摘要描述部分)。
      3. MCP(需要的Server和Tool列表)。
      4. Memory(通常是全量)。

    需要注意的是,环境信息这部分,一般不出现在系统提示词中,而是和用户提问的消息放置在一起。

    3.5 简单实现

    在身份定义、工具调用、环境感知这3部分最基础的Agent组成都达成后,简单地使用大模型的API,进行自动化的工具调用解析、执行、发送新一轮模型调用,可以非常简单地实现一个最小化的Coding Agent。

    可以尝试用以下的提示词,使用任意现有的Coding Agent产品,为你编写一个实现,并自己调试一下,感受Coding Agent的最基础的逻辑:

    我希望基于大模型实现一个Coding Agent,以下是我的具体要求:
    
    1. 使用Claude作为模型服务商,使用环境变量管理我的API Key。
    2. 默认使用Claude Sonnet 4.5模型。
    3. 使用Anthropic's Client SDK调用模型。
    4. 不需要支持流式输出。
    5. 使用TypeScript编写。
    
    以下是Agent提供的工具:
    
    1. read({path: string}):读取一个文件的内容
    2. list({directory: string}):列出一个目录下的一层内容,其中目录以`/`结尾
    3. write({path: string, content: string}):向文件写入内容
    4. edit({path: string, search: string, replace: string}):提供文件中的一块内容
    
    以下是交互要求:
    
    1. 通过NodeJS CLI调用,支持`query`和`model`两个参数,可以使用`yargs`解析参数。
    2. 在System消息中,简短地说明Coding Agent的角色定义、目标和行为准则等。
    3. 在第一条User消息中,向模型提供当前的操作系统、Shell语言、当前目录绝对路径信息,同时包含跟随`query`参数的内容,组织成一条模型易于理解的消息。
    4. 对每一次模型的工具调用,在控制台打印工具名称和标识性参数,其中标识性参数为`path`或`directory`,根据工具不同来决定。
    5. 如果模型未调用工具,则将文本打印到控制台。
    
    请在当前目录下建立一个`package.json`,并开始实现全部的功能。

    04 优质上下文工程

    4.1 成本控制

    大模型是一个非常昂贵的工具,以Claude为例,它的官方API价格如下:

    我们可以观察到一些特征:

    1. 输出的价格是输入的5倍(但实际考虑到输出与输出的数量比例,输出的价格根本不值一提)。
    2. 缓存输入(Cache Writes)比正常输入(Base Input)更贵一些,约1.25倍。
    3. 缓存命中(Cache Hits)的价格比正常输入(Base Input)要便宜很多,为1/10的价格。

    这就意味着,一个良好使用缓存的Agent实现,其成本会比不用缓存降低8-10倍。因此所有的Coding Agent一定会细致地梳理内容结构,最大化利用缓存

    在大模型的API中,缓存通常以“块”为单位控制,例如:

    1. 系统提示词中不变的部分。
    2. 系统提示词中可变部分。
    3. 工具定义。
    4. 每一条消息,单条消息也可以拆成多个块。

    继续观察Claude对于缓存控制的文档:

    可以看到,在大模型API中各种参数一但有所变动,缓存都会大量失效(至少消息缓存全部失效,大概率系统缓成失效),这就会造成成本的极大提升。因此,在Coding Agent实现中,都会从一开始就确定所有参数,整个任务不做任何变更。一些很经典的实例:

    1. 一次任务不会一部分消息开思考模式,一部分不开,因为思考参数会让全部的消息缓存失效。
    2. 切换不同模式(如Edit、Ask、Plan)时,虽然能使用的工具不同,但只是在消息中增加说明,而不会真的将 tools 字段改变。

    另外,Coding Agent会尽可能保持历史消息内容完全不变,以最大化地缓存消息。例如对于一个进行了10轮模型调用的任务,理论上第10次调用中,前9轮的消息内容都会命中缓存。但如果此时擅自去修改了第1轮的工具调用结果(例如试图删除读取的文件内容),看似可能消息的长度减少了,但实际因为缓存被破坏,造成的是10倍的成本提升。

    总而言之,缓存是一个至关重要的因素,Coding Agent的策略优化通常以确保缓存有效为前提,仅在非常必要的情况下破坏缓存

    4.2 空间管理

    Coding Agent因为会自动地与大模型进行多轮的交互,随着不断地读入文件、终端命令输出等信息,上下文的长度会变得非常的大,而大模型通常只具备128K左右的总长度,因此如何将大量内容“适配”到有限的长度中,是一个巨大的挑战。

    控制上下文长度的第一种方式是“裁剪”,即在整个上下文中,将没用的信息删除掉。试想如下的场景:

    1. 模型读取了一个文件的内容。
    2. 模型将文件中 foo 这一行改成了 bar
    3. 模型又将文件中 eat 这一行改成了 drink

    假设我们对模型每一次修改文件,都返回最新的文件内容,如果这个文件有1000行,那么1次读取、2次修改,就会产生3000行的空间占用

    一种优化方式就是,在这种连续的读-改的场景下,只保留最后一条消息中有全文内容,即上述3次模型调用后,出现在上下文中的内容实际是这样的:

    <!-- Assistant -->
    read(file)
    
    <!-- User -->
    [This file has been updated later, outdated contents are purged from here]
    
    <!-- Assistant -->
    edit(file, foo -> bar)
    
    <!-- User -->
    The edit has been applied successfully.
    
    --- a/file
    +++ b/file
    @@ -23,1 +23,1 @@
    -foo
    +bar
    
    [This file has been updated later, outdated contents are purged from here]
    
    <!-- Assistant -->
    edit(file, eat -> drink)
    
    <!-- User -->
    The edit has been applied successfully, the new file content is as below:
    

    {content of file}

    可以看到,通过将连续对同一文件的修改进行裁剪,可以只保留最新的内容,同时又使用unidiff 之类的形式保留中间编辑的差异信息,最大限度地降低空间占用,又能保留模型的推理逻辑。

    但裁剪不能使用在非连续的消息中,随意地使用剪裁逻辑,很有可能破坏消息缓存结构,进而使模型调用的输入无法通过缓存处理,几倍地增加模型的调用成本。

    即便裁剪有一定效果,但随着更多的内容进入到上下文中,始终会有将上下文占满的时候,此时模型将完全无法进行推理。为了避免这种情况出现,Coding Agent通常会使用“压缩”这一技术,即将前文通过模型摘要成少量的文字,同时又保留比较关键的推理链路。

    通常,压缩在上下文即将用完的时候触发,如已经使用了90%的上下文则启动压缩,压缩的目标是将90%的内容变为10%的长度,即省出80%的空间供后续推理。

    压缩本身是一个模型的任务,即将所有的上下文(可以选择性地保留最新的1-2对消息)交给模型,同时附带一个压缩的要求,让模型完成工作。这个压缩的要求的质量将决定压缩的最终结果,一个比较典型的实现是Claude Code的“八段式摘要”法:

    const COMPRESSION_SECTIONS = [
      "1. Primary Request and Intent",    // 主要请求和意图
      "2. Key Technical Concepts",        // 关键技术概念
      "3. Files and Code Sections",       // 文件和代码段
      "4. Errors and fixes",              // 错误和修复
      "5. Problem Solving",               // 问题解决
      "6. All user messages",             // 所有用户消息
      "7. Pending Tasks",                 // 待处理任务
      "8. Current Work"                   // 当前工作
    ];

    通过将信息压缩成8部分内容,能够最大限度地保留工作目标、进度、待办的内容。

    4.3 独立上下文

    在实际的应用中,其实大概率是不需要128K上下文用满的,但真实表现又往往是上下文不够用。这中间存在的差异,在于2类情况:

    1. 为了满足一个任务,需要收集大量的信息,但收集到正常信息的过程中,会引入无效的、错误的内容,占用上下文。
    2. 一个任务足够复杂,分解为多个小任务后各自占用部分上下文,但加起来以后会超出限制。

    试想一下,对于一个这样的任务:

    修改我的Webpack配置,调整文件拆分逻辑,让最终产出的各个JS文件大小尽可能平均。

    但是很“不幸”地,这个项目中存在6个 webpack.config.ts文件,且最终splitChunks 配置在一个名为 optimization.ts 的文件中管理,那么对于Coding Agent来说,这个任务中就可能存在大量无意义的上下文占用:

    1. 读取了6个 webpack.config.ts ,一共2000行的配置内容,但没有任何splitChunks 的配置,包含了大量 import 其它模块。
    2. 又读取了10个被 import 的模块,最终找到了 optimization.ts 文件。
    3. 经过修改后,执行了一次 npm run build 来分析产出,发现JS的体积不够平均。
    4. 又修改 optimization.ts ,再次编译,再看产出。
    5. 循环往复了8次,终于在最后一次实现了合理的splitChunks 配置。

    这里面的“6个 webpack.config.ts ”、“10个其它模块”、“8次优化和编译”都是对任务最终目标并不有效的内容,如果它们占用150K的上下文,这个任务就不得不在中途进行1-2次的压缩,才能够最终完成。

    为了解决这个问题,当前多数的Coding Agent都会有一个称为“Subagent”的概念。就好比一个进程如果只能使用4GB的内存,而要做完一件事需要16GB,最好的办法就是开5个进程。Subagent是一种类似子进程的,在独立的上下文空间中运行,与主任务仅进行必要信息交换的工作机制

    再回到上面的案例,在Subagent的加持下,我们可以将它变成以下的过程:

    1. 启动一个Subagent,给定目标“找到Webpack文件拆分的代码”。

      1. 读取6个 webpack.config.ts
      2. 读取10个被 import 的模块。
      3. 确定目标文件 optimization.ts
      4. 返回总结:在 optimization.ts 中有文件拆分的配置,当前配置为……。
    2. 启动一个Subagent,给定目标“修改 optimization.ts ,使产出的JS体积平均,执行 npm run build 并返回不平均的文件“。

      1. 修改 optimization.ts
      2. 执行 npm run build,得到命令输出。
      3. 分析输出,找到特别大的JS文件,返回总结:配置已经修改,当前 xxx.js 体积为平均值的3倍(723KB),其它文件体积正常。
    3. 启动一个Subagent,给宝目标“分析 dist/stats.json,检查 xxx.js 中的模块,修改 optimization.ts 使其分为3个250KB左右的文件,执行 npm run build并返回不平均的文件”。

      1. ……
      2. ……
    4. 继续启动6次Subagent,直到结果满意。

    不难看出来,这种模式下主体的Coding Agent实际是在"指挥"Subagent做事,自身的上下文占用是非常有限的。而Subagent仅“专注”于一个小目标,也不需要太多的上下文,最终通过这类不断开辟新上下文空间的方式,将一个复杂的任务完成。

    4.4 注意力优化

    如果你经常使用Coding Agent,或在业界早期有过比较多的使用经验,你可能会发现这种情况:Coding Agent在完成一个任务到一半时,忘了自己要做什么,草草地结束了任务,或偏离了既定目标产生很多随机的行为。

    会发生这样的情况,有一定可能是裁剪、压缩等策略使有效的上下文信息丢失了,但更多是因为简单的一个用户需求被大量的代码内容、命令输出等推理过程所掩盖,权重弱化到已经不被大模型“注意到”,因此最初的目标也就完全丢失了。

    Coding Agent一个很重要的任务,就是在长时间运作的同时随时调整大模型的注意力,使其始终聚焦在最终目标、关注当前最需要做的工作,不要偏离预先设定的路线。为了实现这一效果,Coding Agent产品提出了2个常见的概念。

    第一称为TODO,在很多的产品中,你会看到Agent先将任务分解成几个步骤,转为一个待办列表。这个列表在界面上始终处于固定的位置,随着任务的推进会逐步标记为完成。这个TODO实际上并不是给用户看的,而是给模型看的

    在实际的实现中,每一次调用模型时,在最后一条消息(一般就是工具调用的结果)上,除了原始消息内容外,会增加一个称为“Reminder”的区域。这个区域因为始终出现在所有消息的最后,通常来说在模型的注意力中优先级更高,而且绝对不会受其它因素影响而消失

    Reminder中可以放置任意内容,比较经典的有:

    1. TODO及进度。用于模型时刻理解目标、进展、待办。
    <reminders>
    - Planned todos:
      - [x] Explore for code related to "print" function
      - [x] Add "flush" parameter to function
      - [ ] Refactor all "print" function calls to relect the new parameter
    </reminders>
    1. 工具子集。如前面《缓存》相关的描述,因为修改工具定义会使缓存失效,因此当切换模式使得可用的工具减少时,一般仅在Reminder中说明部分工具不可用,由模型来遵循这一约束,而不是直接删除部分工具。
    <!-- 切换至Ask模式 -->
    <reminders>
    - You can ONLY use these tools from now on:
      - read
      - list
      - grep
      - bash
    </reminders>
    1. 行为指示。例如当模型连续多次给出名称、参数都一模一样的工具调用时,说明模型处在一种不合理的行为表现上,此时在Reminder中增加提示,让模型感知到当前状态的错误,就有可能调整并脱离错误的路线。
    <!-- Assistant -->
    read(file)
    
    <!-- User -->
    The file content: ...
    
    <!-- Assistant -->
    read(file)
    
    <!-- User -->
    The file content: ...
    
    <reminders>
    - Your are using read tool the second time with exactly the same parameters, this usually means an unexpected situation, you should not use this tool again in your response.
    </reminders>
    1. 状态提示。例如激活某一个Skill时,Reminder中可以提示“当前正在使用名为X的Skill“,这种提示可以让模型更加专注于完成一个局部的工作。
    <reminders>
    - You are currently working with the skill "ppt" active, be focused on this task until you quit with exit_skill tool.
    </reminders>

    需要额外注意的是,Reminder仅在最后一条消息中出现,当有新的消息时,旧消息上的Reminder会被移除。基于这一特征,我们知道Reminder是永远无法命中缓存的,因此Reminder部分的内容长度要有控制,避免造成过多的成本消耗。

    4.5 冲突管控

    随着Coding Agent能力的发展,当下执行的任务时间越来越长、编辑的文件越来越多,同时更多的用户也习惯于在Agent工作的同时自己也进行编码工作,甚至让多个Agent任务并发执行。这种“协同”形态下,不少用户曾经遇到过这样的问题:

    自己将Agent生成的代码做了一些修正,但之后Agent又把代码改了回去。

    这个现象的基本原因也很清楚,就是Agent并不知道你改动过代码。例如以下的过程使Agent读取并编辑了一个文件:

    <!-- Assistant -->
    read(file)
    
    <!-- User -->
    The file content:
    ...
    console.log('hello');
    ...
    <!-- Assistant -->
    edit(file, hello -> Hello)
    
    <!-- User -->
    Edit has been applied successfully.

    这个时候,在模型见到的上下文中,这个文件中的代码显然是console.log('Hello'); 。假设乃又将它改成了console.trace('Hello'); ,后面模型依然会基于.log 来修改代码,用户看起来就是代码“改了回去”。

    解决这种共同编辑文件的冲突,实际上有多种方法:

    • 加锁法。当Agent读取、编辑一个文件时,更新模型认知的文件内容的快照。当这个Agent再一次编辑这个文件时,读取文件当前的实际内容,和快照做比对,如果内容不一样,拒绝这一次编辑,随后要求Agent重新读取文件(更新快照与实际内容一致)再进行编辑。这是一种主流的做法,不过Agent实现上的细节比较重
    <!-- Assistant -->
    edit(file, console.log...)
    
    <!-- User -->
    This edit is rejected, the file has been modified since your last read or edit, you should read this file again before executing any write or edit actions.
    
    <!-- Assistant -->
    read(file)
    
    <!-- User -->
    The file content: ...
    
    <!-- Assistant -->
    edit(file, console.trace...);
    • 推送法。监听所有模型读取、编辑过的文件的变更,当文件发生变更时,在下一次模型调用时,不断通过Reminder区域追加这些变更,让模型“实时”地知道文件有所变化,直到文件被下一次读取。这种方式能让模型更早地感知变化,但推送信息可能过多影响成本和推理速度。
    <!-- Assistant -->
    run_command(ls)
    
    <!-- User -->
    The command output: ...
    
    <reminders>
    - These files have been modified since your last read or edit, you should read before write or edit to them:
      - file
      - file
      - ...
    </reminders>
    • 隔离法。使用Git Worktree方案,直接让不同的Agent任务在文件系统上隔离,在一个独立的Git分支上并行工作,相互不受干扰。在任务完成后,用户检查一个任务的全部变更,在采纳时再合并回实际的当前Git分支,有冲突的由用户解决冲突。这种方法让Agent根本不需要考虑冲突问题,但缺点是系统资源占用高,且有合并冲突风险

    文件编辑冲突只是一个比较常见的现象,实际上用户和Agent、多个Agent并行工作,可能造成的冲突还有很多种,例如:

    用户敲了半行命令 ls -,Agent直接在终端里敲新的命令 grep "print" -r src执行,导致最后的命令是 ls -grep "print" -r src ,是一个不合法的命令。

    终端的抢占也是一种冲突,但相对更容易解决,只要让每一个Agent任务独占自己的终端,永远不与用户、其它Agent任务相交叉即可。

    4.6 持久记忆

    我们都知道,模型是没有状态的,所以每一次Agent执行任务,对整个项目、对用户的倾向,都是从零开始的过程。这相当于历史经验无法积累,很多曾经调整过的细节、优化过的方向都会被重置。虽然可以通过比如Rule这样的方式去持久化这些“经验”,但需要用户主动的介入,使用成本是相对比较高的。

    因此当前很多Coding Agent产品都在探索“记忆”这一能力,争取让Agent变得用的越多越好用。记忆这个话题真正的难点在于:

    1. 如何触发记忆。
    2. 如何消费记忆。
    3. 什么东西算是记忆。

    首先对于“如何触发”这一问题,常见于2种做法:

    1. 工具型。定义一个 update_memory 工具,将记忆作为一个字符串数组看待,工具能够对其进行增、删改,模型在任务过程中实时地决定调用。往往模型并不怎么喜欢使用这类工具,经常见于用户有强烈情感的描述时才出现,比如“记住这一点”、“不要再……”。
    2. 总结型。在每一次对话结束后,将对话全部内容发送给模型,并配上提示词进行记忆的提取,提取后的内容补充到原本记忆中。总结型的方案往往又会过度地提取记忆,将没必要的信息进行持久化,干扰未来的推理。
    3. 存储型。不进行任何的记忆整理和提取,而是将所有任务的原始过程当作记忆,只在后续“消费”的环节做精细的处理。

    然后在“如何消费”的问题下,也常见有几种做法:

    1. 始终附带。记忆内容记录在文件中,Agent实现中将文件内容附带在每一次的模型请求中。即模型始终能看到所有的记忆,这无疑会加重模型的认知负担,也占用相当多的上下文空间,因为很多记忆可能是与当前任务无关的。
    2. 渐进检索。本身不带记忆内容到模型,但将记忆以文件系统的形式存放,Agent可以通过readlistgrep 等工具来检索记忆。配合“存储型”的触发方式,能让全量的历史任务都成为可被检索的记忆。但这种方式要求模型有比较强的对记忆的认知,在正确的时刻去找相关的记忆。但往往因为根本不知道记忆里有什么,进而无法知道什么时候应该检索,最终几乎不触发检索。

    而最终的问题,“什么东西是记忆”,是当下Coding Agent最难以解决的问题之一。错误的、不必要的记忆甚至可能造成实际任务效果的下降,因此精确地定义记忆是Agent实现的首要任务。

    通常来说,记忆会分为2种大的方向:

    1. 事实型。如“使用4个空格作为缩进”、“不要使用any 类型“,这些都是事实。事实是无关任何情感、不带主观情绪的。
    2. 画像型。如”用户更喜欢简短的任务总结“就是一种对用户的画像。画像是单个用户的特征,并不一定与项目、代码、架构相关。

    在Coding Agent上,往往更倾向于对”事实型“的内容进行记忆,而不考虑用户画像型的记忆。

    同时,从业界的发展,可以看到越来越多的模型厂商在从底层进行记忆能力的开发,如最近Google的Titan架构就是一种记忆相关的技术。可能未来某一天,Agent实现上已经不需要再关注记忆的逻辑与实现,模型自身将带有持久化的记忆能力。

    05 能力扩展

    在实际应用中,还需要一些机制来让Agent更好地适应特定的项目、团队和个人习惯。当前主流的Coding Agent产品都提供了Rule、MCP、Skill这三种扩展能力,它们各有侧重,共同构成了Agent的能力增强体系。

    5.1 Rule

    当面对业务的repo往往存在一些领域相关的知识而非模型的知识库中已有的内容,这些往往需要凭借老员工的经验或者读取大量代码库的信息进行总结后才能明白,这些内容便适合放到Rule中,作为静态的不会频繁改动的内容放入Environment Context中长期Cache。

    好的Rule应当足够精简、可操作且范围明确,人看不懂的规则或者描述不清的规则模型是一定搞不定无法遵守的。

    • 将Rule控制在 500 行以内。
    • 将较大的规则拆分为多个可组合的规则,采取按需的方式,按照 文件路径/关键场景 激活Rule;对于特定场景激活的Rule,采取编写索引的方式创建Rule,让模型渐进式激活,比如项目针对网络请求和错误处理相关做了项目维度的封装处理,但这种情况并不是每个文件ts/tsx文件都会遇到的诉求,比如在项目的rules目录下创建index.mdr(curso是.mdc文件),编写下面的激活的条件:

      • 需要进行API调用获取数据
      • 处理异步操作的错误和加载状态
    
    -   当编码涉及以下任一情况时,必须立刻阅读 \[08-api-error-handling.mdc\](mdr:.cursor/rules/08-api-error-handling.mdc)
        
    • 提供具体示例或参考文件,针对xx情况正确的方式是\`code\`。
    • 避免模糊的指导,比如交互式的东西模型交互不了,不需要写进去。
    • 为了模型能够积极验证每次改动是否符合预期,告知模型改动后可以执行的正确的构建命令,以及某些自定义命令(比如自动化测试)引导模型在后台启动命令,在xx秒后读取日志文件的内容进行结果的判断。

    5.2 MCP

    MCP(Model Context Protocol)是Anthropic提出的一种标准化的工具扩展协议,它允许开发者以统一的方式为Coding Agent添加新的能力。

    与Rule的"声明式约束"不同,MCP是一种实时工具调用协议,即通过MCP server的方式进行连接,来扩展Agent可以做的事情。

    一个典型的场景是集成外部服务。比如你的项目托管在GitHub上,可以让Agent直接访问GitHub实现创建Issue、查询PR状态、添加评论等功能:

    {
        "mcpServers": {
            "github": {
                "command": "npx",
                "args": ["-y", "@modelcontextprotocol/server-github"],
                "env": {
                    "GITHUB_PERSONAL_ACCESS_TOKEN": "<your-github-token>"
                }
            }
        }
    }

    配置好后,Agent就能在代码审查过程中自动创建Issue记录问题、查询相关PR的讨论、甚至根据代码变更自动生成commit message。

    MCP的另一个优势是实现门槛低。一个MCP Server本质上就是一个标准输入输出的程序,它通过JSON-RPC协议与Agent通信,当模型需要外部能力的时候,调用MCP Server,而模型无需关心其内部代码实现,Agent只需要按照固定的协议去连接获取内容。

    5.3 Skill

    5.3.1 什么是Skill

    随着模型能力的提升,使用Agent完成的任务复杂度逐渐增加,使用Coding Agent可以进行本地代码执行和文件系统完成跨领域的复杂任务。但随着这些Agent的功能越来越强大,我们需要更具可组合性、可扩展性和可移植性的方法,为它们配备特定领域的专业知识,因此Agent Skill作为一种为Agent扩展能力的标准诞生。Skill 将指令、脚本和资源的文件夹打包,形成专业领域的知识,Agent在初始化的时候会获取可用的Skills列表,并在需要的时候动态加载这些内容来执行特定任务。

    随着 Skill 复杂性的增加,它们可能包含过多的上下文信息,无法放入单个配置文件中 SKILL.md,或者某些上下文信息仅在特定场景下才相关。在这种情况下,Skill可以在当前目录中bundle额外的文件,并通过文件名引用这些文件,这些额外的文件提供了更多详细信息,Coding Agent 可以根据需要选择浏览和查找这些信息。Skill 是渐进式触发的, 因此 SKILL.mdnamedescription很关键,这会始终存在于Agent的环境上下文中提供给模型,模型会根据这些描述信息来决定是否在当前任务中触发该Skill,当你明确希望使用某个Skill完成任务,可以在prompt中指定“使用xxxx Skill完成xx任务”。

    5.3.2 Skill和代码执行

    LLM在很多任务上表现出色,但许多操作需要使用编写代码 -> 代码执行的方式,带来更高效的操作、确定性的以及可靠性的结果。生成式的模型常常通过生成可执行代码的方式去验证/计算结果。

    代码既可以作为可执行工具,也可以作为文档。Skill中应该明确让模型是应该直接运行脚本,还是应该将其作为参考信息读取到上下文中。

    5.3.3 如何创建Skill

    每个Skill由一个必需的 SKILL.md 文件和可选的bundle资源组成,Skill 应该只包含完成任务所需的信息。

    skill-name/
    ├── SKILL.md (必需)
    │   ├── YAML frontmatter 元数据 (必需)
    │   │   ├── name: (必需)
    │   │   ├── description: (必需,这是 skill 的主要触发机制,帮助模型理解何时使用该 skil)
    │   │   └── compatibility: (可选)
    │   └── Markdown 说明 (必需)
    └── bundle的资源 (可选)
        ├── scripts/          - 可执行代码 (Python/Bash/等)
        ├── references/       - 需要时加载到上下文的文档
        └── assets/           - 用于输出的文件 (模板、图标、字体等)

    举一个具体的例子,比如当我们需要进行批量项目的技术栈migrate,比如将less迁移postcss,中间涉及一系列的复杂步骤,比如:

    • 安装postcss以及postcss plugin的依赖
    • 配置postcss的config
    • 分析项目用到了哪些less varibale替换成css vars
    • 删除mixin并替换
    • 一系列的其他兼容less的语法转换...
    • 替换文件后缀

    上面的工作可以通过清晰的流程描述,并配合脚本实现,因此可以作为一个Skill将经验变成可复制的,一个less-to-postcss的skill的结构:

    5.3.4 Skill的使用

    人人都可以创建Skill,也可以让Agent来编写Skill,这是Skill非常便捷的地方。Skill通过instructions和code赋予Coding Agent新的能力。虽然这使其功能强大并有很高的自由度,但也意味着恶意SKill可能会在其使用环境中引入漏洞,诱使模型窃取数据并执行非预期操作。仅从可信来源安装Skill,如果无法确信来源可信,在使用前请务必进行彻底审核。

    Skill的出现并不是替代MCP的出现,而是相互配合,在合适的场景下选取Skill或是MCP。某些任务Skill和MCP Server均可完成,但Skill通过执行代码的方式可以一次性加载完整流程,但MCP Server要经历多次查询和多轮对话往返,这种情况下Skill更为合适,但这不意味着绝对的优势,比如标准化文档创建这个典型的场景,创建PPT/Word/Excel在本地使用Skill即可完成,但数据的提供则需要借助MCP Server进行查询。因此Skill擅长的是在本地通过执行 code的方式完成复杂任务,在用户私有数据、动态数据查询这些情况下Skill就无法搞定了,这和用户的数据库以及隐私强关联,需要让模型无法感知在执行过程中的隐私信息,Skill能够与MCP Server互补完成更为复杂的流程。

    行内补全:实现思路与关键细节(FIM + Diff + 多层过滤)

    想把 “行内补全” 做得好用,核心不是 “能生成”,而是生成后如何稳定落到光标处:不重复、不乱插、不无限循环、单行 / 多行行为符合预期。


    先看结论

    • 使用标准 FIM(Fill-In-the-Middle) 流程:prefix + suffix -> middle
    • 内置模型:Pro/Qwen/Qwen2.5-Coder-7B-Instruct(通过统一的 "FIM" 模型名对外提供)
    • 非流式请求拿到完整输出,但内部用 “类流式管道” 逐层过滤(字符级→行级→后处理)
    • 单行补全额外做 Diff:自动判断 “插入” 还是 “替换到行尾”,避免括号 / 后缀重复
    • 多行补全不做 Diff:直接替换到行尾(更符合用户预期,也省成本)


    1) 整体架构(从 VSCode 到最终插入)

    流程上分 6 段:预过滤 → 分类 → 模板 → 调用与过滤 → 后处理 → Diff(单行)

    InlineCompletionProvider
      -> 预过滤(跳过不该补全的场景)
      -> 构建 prefix/suffix(HelperVars)
      -> 单/多行分类
      -> FIM 模板渲染
      -> 调用 FIM API(非流式拿完整结果)
      -> 过滤器管道处理(字符级/行级/后处理)
      -> 单行:Diff 决定 range;多行:替换到行尾
      -> 返回 InlineCompletionItem
    


    2) FIM 请求格式与参数

    FIM 模板(Qwen 标准)

    <|fim_prefix|>{prefix}<|fim_suffix|>{suffix}<|fim_middle|>
    

    关键约定

    • API 模型名统一用:"FIM"
    • 请求方式:非流式(一次性拿完整结果)
    • max_tokens:单行 64、多行 128(策略性限制 “胡乱续写” 概率)


    3) 预过滤:哪些场景直接不补全

    目的:便宜地跳过 “补全只会添乱” 的场景,减少无意义请求。

    常见跳过项:

    • 配置文件 / 特殊文件模式
    • 未命名空文件
    • 多光标(multi-cursor)
    • 其它明确不应触发 inline completion 的场景


    4) 单行 vs 多行:分类策略(决定用户体验)

    分类逻辑按优先级(越靠前越强):

    1. Intellisense 选中项:强制单行(避免和补全列表打架)
    2. 单行注释检测:强制单行(注释里多行补全通常噪音大)
    3. 语言特定规则:按语言做微调
    4. 默认:允许多行


    5) 过滤器系统:三层拦截,解决 “能生成但不好用”

    即使是非流式输出,也可以把 “完整文本” 当成流来处理:逐步截断、跳过、清洗。

    LLM 完整输出
     -> 字符级过滤(stop token / suffix 重复 / 首字符换行)
     -> 行级过滤(行数限制 / 重复行 / 相似行 / 空注释 / 双换行)
     -> 后处理(空白、重复上一行、极端重复、去 markdown backticks)
     -> 最终补全
    

    5.1 字符级过滤(更早、更快止损)

    典型能力:

    • Stop Token 检测:遇到终止标记立即截断
    • 后缀重复检测:避免生成 “补全 + suffix + 继续乱写”
    • 首字符换行过滤:不让补全以空行开头(体感很关键)

    5.2 行级过滤(防循环、防抄下一行)

    典型能力:

    • 行数上限:例如超过 50 行直接停止
    • 重复行检测:同一行反复出现(模型陷入循环)就停
    • 相似行检测:生成内容与 “光标下一行” 相似度过高就停(防重复已有代码)
    • 空注释过滤:只输出 //# 这种空注释行直接丢弃
    • 双换行过滤:控制空行数量,避免 “稀碎的大段空白”

    5.3 后处理(最终把关)

    典型规则:

    • 空白补全直接拒绝
    • 重复上一行(基于 Levenshtein 相似度)直接拒绝
    • 极端重复模式(例如 6 行以上重复)拒绝
    • 去掉模型偶发输出的 Markdown 代码块标记(```)


    6) 单行 Diff:解决 “补全重复后缀 / 括号” 的关键

    问题本质

    FIM 模型对单行位置常见三种输出:

    1. 纯插入:只生成新内容
    2. 重复后缀:把光标后的文本也生成了一遍
    3. 部分重叠:生成内容与后缀部分重叠

    如果不处理,最常见的坏体验就是:多一个括号、多一段重复 token

    解决方式(单行专用)

    diffWords() 比较:

    • currentText:光标后到行尾的已有内容
    • completion:模型给的最后一行补全

    然后根据 diff 模式判断:

    • 该不该只 “插入”
    • 还是要 “替换光标到行尾”(即设置 range

    Range 语义(VSCode 行为)

    • range = undefined:在光标处插入
    • range = { start: cursor, end: lineEnd }:先删除光标到行尾,再插入(用于清理重复后缀)


    7) 多行为什么不做 Diff,而是直接替换到行尾?

    多行补全的期望更像 “接管后续内容”,而不是 “精确对齐每个 token”。

    选择 “替换到行尾” 的原因:

    • 多行 Diff 成本高,收益不稳定
    • 多行重复后缀概率相对低(且过滤器已拦截一部分)
    • 用户更希望多行补全能 “把后面那段写完”,而不是半插半改


    附:代码组织(给想看源码的人)

    关键文件(按职责):

    • VvCompletionProvider.ts:主入口,串起 6 阶段流程
    • vvCompletionStreamer.ts:API 调用管理(非流式)
    • vvAutocompleteTemplate.ts:FIM 模板
    • prefiltering.ts:预过滤
    • multiline.ts:单 / 多行分类
    • filters.ts:后处理
    • processSingleLineCompletion.ts:单行 Diff
    • streamFilters/:字符级与行级过滤管道

    📌 转载信息
    原作者:
    allen_zhang
    转载时间:
    2026/1/1 15:46:53