AI agent融入Emacs:打造会自我进化的编程工作台

发布时间:2026/10/29 6:42:38
AI agent融入Emacs:打造会自我进化的编程工作台 1. 为什么我把 AI agent 搬进 Emacs工作台选型的底层逻辑先说说我最近的一段真实体验。过去半年我一直在折腾各类 AI 编程工具从 IDE 插件到独立的 agent 终端都试过一轮。最终留在日常主力环境里的不是那些开箱即用的商业产品而是 Emacs 里一个叫 agent-shell 的自建工作台。听起来有点复古但当你真正把一个 AI agent 放进 Emacs 这个四十多岁的编辑器里会发现它做的事情远不止帮你写代码这么简单——它能让整个工作台具备自我进化的能力。1.1 为什么是 Emacs 而不是 IDE 插件市面上主流的 AI 编程插件本质上都是围绕补全和对话设计的。它们运行在 IDE 提供的封闭扩展点里你只能在编辑器划出的对话框里提问、接受代码建议偶尔让它跑一下测试。问题在于这种交互模型把 AI 当成一个顾问而不是一个执行者。Emacs 的底层逻辑完全不同。它的核心是一个 Elisp 解释器所有编辑功能、窗口管理、模式切换都是 Elisp 代码。这意味着你可以在运行时加载一段新代码让编辑器立刻获得新能力——不需要重新编译不需要重启进程。如果把 IDE 比作一台焊死电路板的设备Emacs 就是一块可以随时插拔模块的试验台。agent-shell 正是踩中了这个特性。它本质上是一个 Emacs 小工具minor mode把 AI 模型的对话能力、Shell 命令执行能力、Elisp 代码求值能力三者串成一条流水线。AI 理解你的意图后生成动作Emacs 替你执行动作再把结果反馈给 AI 继续迭代。这个闭环跑通之后AI 就不再是回答问题的聊天框而是一个真的能干活、能试错、能修改自身工具的工程师。1.2 所谓进化的技术前提标题里自我进化这个词可能有人觉得夸张。但拆开看它依赖三个技术前提恰好 Emacs 全部具备第一可热更新的运行环境。Elisp 支持eval-last-sexp、load-file这类运行时求值手段。AI 生成一段 Elisp 代码Emacs 可以直接加载运行这个过程不需要离开当前会话。我实测过从模型生成代码到函数生效延迟基本在秒级。第二统一的工具入口。Shell 是操作系统的万能接口Emacs 通过shell-command、make-process等函数可以无缝调用外部程序。AI agent 要读文件、跑脚本、查日志、执行 git 操作全部可以归结为生成一段 shell 命令并执行。Emacs 对这些调用有完整的输出捕获机制AI 能看到结果进而调整下一步动作。第三可持久化的配置体系。Emacs 的配置就是一个含多个 Elisp 文件的目录agent 每次学到的经验、沉淀的工具函数都可以写入配置目录中的文件。下次启动 Emacs 时这些能力自动加载相当于 AI 的记忆被固化进了工具本身。这三个条件凑齐就形成了一条进化链AI 执行任务 - 发现重复操作 - 将操作固化成 Elisp 工具 - 工具被持久化加载 - AI 下次用更短路径完成任务。这篇文章后面会具体讲这条链路怎么建立。2. agent-shell 的工作机制从对话到操作的桥梁理解了选型逻辑接下来拆解 agent-shell 的核心机制。很多人第一次用这类工具时最大的困惑是AI 模型只是一个文本生成接口它怎么知道该执行什么命令又是怎么把命令跑通的2.1 一次完整任务的三段式流水线一次对话驱动的 shell 操作在 agent-shell 里被拆成三段。第一段是意图解析。模型收到用户输入例如看看当前 git 仓库里有哪些未提交的改动顺便列出最近改动的文件后模型依据预设的工具描述把它拆解为具体的动作序列。这一步在模型内部完成agent-shell 只需要按照 OpenAI 兼容格式准备好 system prompt告诉模型当前工作目录、可用工具和输出要求。第二段是动作生成与执行。模型返回一个结构化的 JSON包含工具类型比如shell、read_file和参数命令字符串、文件路径等。agent-shell 解析 JSON 后调用 Emacs 的进程管理函数执行对应操作。这里有一个关键设计工具名称集合在 prompt 里以严格的 schema 声明模型只能从中选择。只要 prompt 写得清楚模型极少胡来。第三段是结果回填与继续。shell 命令的输出被截断到合理长度默认 4000 字符可配置连同退出码一起拼进对话上下文再发给模型。模型看到结果后决定是继续下一步、修正刚才的命令还是直接结束任务。这个循环可以持续多轮直到模型认为任务完成返回一个 FINAL 标志agent-shell 才退出执行状态。2.2 进程调用与输出捕获的 Elisp 实现真正动手做这个工具时最费精力的部分不是 AI 接口对接而是进程管理。Emacs 执行 shell 命令有几种方式每种都有取舍方式特点适用场景shell-command同步执行阻塞编辑器输出直接插入 buffer快速跑一条命令不关心并发make-process异步执行不阻塞编辑通过 sentinel 回调捕获结束事件常规 agent 命令执行start-process底层接口需要手动管理 buffer 和过滤函数需要精细控制输出的场景async-shell-command异步但输出进临时 buffer无法直接拿到字符串看日志、启动服务我最终用的是make-process 自定义过滤函数。核心逻辑是先创建一个临时 buffer 作为输出容器make-process的:filter参数传入一个函数每收到一段新输出就追加到这个 buffer 里:sentinel参数在进程退出时被触发此时读取整个 buffer 内容、检查退出码、把结果回传给模型请求函数。这个方案的优点是异步非阻塞命令执行期间用户还可以继续在 Emacs 里做别的事这在跑长时间构建任务时特别重要。缺点是代码复杂度高一些要处理好输出被截断、进程被杀掉、连续执行多条命令时的状态切换。下面是一段简化后的核心代码(defun agent-shell--run-command (cmd optional timeout) 异步执行 CMD返回 (output . exit-status) 风格的 alist。 (let* ((buf (generate-new-buffer *agent-shell-output*)) (output ) (proc (make-process :name agent-shell-task :buffer buf :command (list bash -c cmd) :connection-type pipe :sentinel (lambda (process event) (unless (process-live-p process) (let ((full-output (with-current-buffer buf (buffer-string)))) (setq output full-output) (kill-buffer buf) (agent-shell--on-finish output (process-exit-status process))))) :filter (lambda (proc str) (when (buffer-live-p (process-buffer proc)) (with-current-buffer (process-buffer proc) (goto-char (point-max)) (insert str))))))) proc))这只是一个最朴素的版本。实际使用中我加了超时处理定时跑kill-buffer和kill-process、输出压缩只保留头尾各 1000 字符以及敏感命令确认钩子。后面讲护栏配置时再展开。2.3 为什么把 JSON 作为模型和执行的中间格式是必要的这里值得多说两句设计决策。为什么不让模型直接输出 shell 命令字符串而要输出 JSON早期版本我试过让模型直接返回命令我拿到字符串就执行。看起来简单但很快发现两个问题。第一模型经常在一个回复里塞好几条命令夹着解释文字解析起来很不可靠第二一旦命令执行失败模型看不到失败状态因为它不知道退出码只能靠输出文本猜测这会导致它重复尝试同一条错命令。改成结构化 JSON 之后一切清晰了。模型输出的工具调用层可以用函数调用模式来约束每一轮只干一件事成功失败都有显式状态回传。JSON 里还带thought字段模型可以写下它的判断理由这既方便调试也让 agent 的行为可解释。代价是每轮多了一些 token 消耗以及 prompt 里得塞一份完整的工具 schema 定义——但这部分开销换来的是系统稳定性的显著提升。我自己的项目从改成 JSON 协议后多轮任务的失败率至少下降了一半。3. 让 agent 会干活工具集设计与任务分发有了执行通道接下来要解决的是agent 到底能调用哪些工具。只留一个 shell 窗口远远不够现实中的任务往往需要读文件、查找代码、运行测试、查询 git 状态…… 工具集的丰富程度直接决定了 agent 的上限。3.1 最小可用工具集我推荐从这六个开始如果你也要做一个类似的工作台我建议从以下六个工具起步不多不少刚刚好工具名功能说明底层命令shell执行任意 shell 命令bashread_file读取文件内容并可选定位到指定行范围cat / sedwrite_file写文件、创建新文件cat heredocglob查询目录结构、文件名匹配find / fdgrep在项目中搜索文本rg / grepgit_status查看当前仓库状态和 diffgit status / git diff设计这个工具集时遵循一个原则让 agent 具备查看现状 - 修改文件 - 验证结果闭环所需的最小能力。多一个用不上的工具prompt 就更长模型误选工具的概率也更高。read_file和grep这两个工具要特别注意结果截断的配置。有一次 agent 在处理一个大型项目时glob扫出了一个包含几万个文件的node_modules目录直接把上下文撑爆了。后来我在每个文件相关工具里都加了路径排除列表默认忽略.git、node_modules、dist这类目录同时在 prompt 里明确告诉模型使用前尽量先 glob 确认目标存在不要盲目读取大文件。3.2 工具选择不靠猜prompt 里的 schema 声明方法当前主流模型都支持函数调用function callingagent-shell 利用的就是这个能力。关键实现是把工具定义写成 JSON schema塞进 system prompt然后要求模型在需要调用工具时返回一个特殊的 JSON 结构。举例定义shell工具的 schema 片段大致这样{ type: function, function: { name: shell, description: 在项目目录下执行一条 shell 命令获取输出。, parameters: { type: object, properties: { command: { type: string, description: 要执行的命令必须是开发者预设白名单允许的。 }, timeout_seconds: { type: integer, description: 命令超时时间默认 30最长 120。 } }, required: [command] } } }schema 里最好写明每个字段的作用和约束这能显著减少模型的误用。比如我在command描述里写必须是白名单内命令或明显的文件读取/修改操作模型的违规调用一下就少了很多。实际对话时的模型返回格式是这样{ tool_calls: [ { id: call_123, function: { name: shell, arguments: {\command\: \git status --short\} } } ] }agent-shell 拿到这个结构后根据function.name找到对应的 Elisp 处理函数然后把arguments解析成 plist分派执行。如果模型在回复里没有tool_calls字段说明它认为任务已经完成agent-shell 就把文本内容作为最终回答展示给用户。3.3 多工具协作让 agent 按项目地图工作单一工具调用看起来平平无奇但多个工具串起来就完全不一样了。我日常用得最多的一个 pattern 是这样用户输入给这个项目的 README 补一段架构说明。agent 会依次调用glob查看目录结构read_file读取几个核心入口文件可能还会grep搜一下函数定义最后write_file生成新的 README 段落。全程不需要用户介入每步都会输出执行结果。这里有个体验优化技巧agent-shell 在每轮工具调用后会短暂显示正在执行 xxx 工具的状态行用户能看到它在干什么如果 agent 跑偏了可以立即打断。这个反馈机制在早期试错阶段特别重要能帮你快速发现工具定义和 prompt 里的问题。4. 真正的自我进化循环让 agent 修改自己的工作台现在来到本文的重头戏——标题里所说的自我进化。如果你的 agent 只是会跑命令、写文件那它还只是一个好用的工具。真正让它进化需要建立一个循环agent 在干活的过程中发现未来还会用到类似操作就把操作固化成新的工具函数写回 Emacs 配置。下次再遇到同类需求一行命令搞定不需要重新对话。4.1 把重复劳动 固化 成工具函数我在 agent-shell 里设计了一个agent-shell:learn机制触发条件很简单当 agent 连续两次以上使用同一段多步命令完成类似任务时agent 会在回复中主动建议这个流程要不要我固化成一个新工具。比如我的一个项目里经常需要找出最近的失败日志并提取异常堆栈。第一次 agent 是这样干的先grep -rn ERROR logs/再过滤时间戳再tail -n 50然后人工整理成一段总结。这个流程跑了三遍之后agent 在某一轮对话里提出了固化建议。我同意后agent-shell 会调用一个agent-shell:learn-from-history函数把最近几轮对话里提取的命令行序列和工具调用参数折叠成一个模板函数自动生成 Elisp 代码追加到我的~/.emacs.d/lisp/agent-tools.el文件里然后load-file热加载。生成的函数类似这样(defun agent-tools:last-error-log () 提取最近一次错误日志的堆栈信息。 (interactive) (let ((cmd grep -rn ERROR logs/ | tail -n 50)) (shell-command-to-string cmd)))从这以后用户再输入看看刚才的报错agent 会直接引用这个工具函数而不是从零开始跑命令。能力就这样沉淀下来了。4.2 配置热加载Elisp 让它真正活起来这个过程里最核心的技术支撑是热加载。Elisp 中函数可以被重新定义load-file会在运行时重新评估整个文件把新函数装进当前会话。这意味着 agent 生成的函数体立即生效不需要重启。(load-file ~/.emacs.d/lisp/agent-tools.el)运行这行代码后agent-tools:last-error-log函数对当前会话可用而且因为 Emacs 的配置本质是加载的代码总和下次启动时init.el里(require agent-tools)也会自动把这些函数加载进来。这比传统程序改完代码要重新构建部署的循环短得多——AI 的产出物当场就能成为工具的一部分。需要注意一个坑自动生成的函数要特别小心命名空间冲突。Elisp 没有真正的局部函数隔离导出的符号会全局可见。如果 agent 生成一个名为my-helper的函数很可能和别的包冲突。我在learn机制里强制所有自动生成的函数带上固定的agent-tools:前缀并且每次生成前扫描一次obarrayEmacs 内部符号表查重从源头避免冲突。4.3 对话历史与经验的分层沉淀自我进化不只是生成工具函数还包括经验知识的沉淀。我做了另一个设计每次 agent 完成一个复杂任务后把任务描述、步骤序列、最终结果总结为一个配方卡片追加到一个 markdown 文件里。这个文件同时满足两个需求一是给人看——我偶尔翻一下能回忆起这个工作台最近帮我完成了哪些任务二是给模型看——每个配方卡片的开头注明了任务类型和关键词下一次 agent 收到类似任务时会先搜索这个文件把相关配方当作 few-shot 示例塞进 prompt。这个做法的效果非常显著。我做过对比在沉淀了二十多个配方卡片后agent 处理相似任务时工具调用轮次平均减少了 40% 左右。因为模型不需要每一次都从头推理怎么做直接在历史经验上做小修小改就行了。这就是进化最直观的体现——不是模型本身变聪明了是它可调用的上下文、可复用的工具变多了解决问题的能力自然就上去了。5. 筑好护栏权限、职责与失败的边界讲完了进化的路径必然要讲约束。一个能自己执行命令、修改文件甚至写代码加载进编辑器的 agent听起来很强大如果不加控制它也能把环境搞得一团糟。我在实际使用中踩过的坑基本都集中在权限和资源控制上——AI 没有常识你必须在 prompt 之外用硬代码给它画好边界。5.1 命令白名单与默认拒绝策略最简单也最有效的护栏是命令白名单。agent-shell 有一个agent-shell:allowed-commands全局变量是一个命令前缀列表。只有当命令以某个前缀开头时才允许执行否则直接拒绝并告知模型。以下是我的基础配置(setq agent-shell:allowed-commands (git cd ls cat rg grep find sed awk curl python python3 node npm run npx make cmake bash))策略很明确默认拒绝、显式放行。模型的工具描述里也写明了这个限制所以它生成命令时会主动收敛到白名单内。遇到像rm -rf这类危险操作我加了关键词黑名单即使命令以白名单开头也会被拦截。比如git clean -fdx这种命令因为包含clean关键词会被单独弹窗要求人工确认。提示这里的关键不是阻止所有危险命令而是确保危险操作必须经过人工确认。AI 犯错的概率不高但一旦发生成本可能极高。5.2 工作目录隔离与路径约束除了命令维度路径维度同样重要。我最开始没有做隔离agent 可以在家目录甚至根目录随意读写文件。某次它为了搞清楚一个配置文件直接打开了~/.ssh/id_rsa.pub还好只是公钥这让我出了一身冷汗。解决办法很简单为每个 agent 会话绑定一个工作目录默认就是当前项目根目录。所有文件读写在传递前都要经过一个路径校验函数如果绝对路径不是工作目录或其子目录拒绝执行并返回错误信息给模型。跨目录操作需要显式声明例如在 prompt 里写清楚你可以读取 /etc/hosts 但绝不能修改它。这个设计也让多项目并行成为可能。我开了三个 Emacs 框架frame分别对应三个项目每个会话的 agent 相互独立不会乱窜。5.3 超时、并发上限与输出长度控制最后一个容易忽视的环节是资源消耗控制。模型生成的命令可能是个死循环也可能是故意拖延的构建任务。我在进程执行层加了三个保险命令默认 30 秒超时超时强制kill-process返回 timeout 提示同一时间只允许一个 shell 任务运行新的调用排队等待避免并发命令产生不可预期状态输出截断到最多 10 万字符超过部分折叠为一个摘要信息。这些限制可以防止agent 看起来好像什么都不干但其实在后台疯狂消耗资源的情况。我有一次深夜跑任务agent 生成的命令里有个while true死循环如果没有超时保护可能要到第二天早上才会发现。加了这些护栏之后类似事故没有再发生过。6. 实测下来最值得复用的几个配方最后分享几个我在日常使用中高频使用、且稳定复现的 agent 对话配方。每一个都可以直接复制到你的 agent-shell 里试试。6.1 重构当前缓冲区的 import 顺序用户把这个文件里所有 import 按字母序重新排列保持注释不变。这一步 agent 会先用read_file读取缓冲区内内容我配置了可以让 agent 直接读取当前缓冲区而不是磁盘文件这样未保存的修改也能感知然后用python3跑一段小脚本解析 import 区块并重排最后write_file写回文件。整个过程大约 20 秒比我自己手动整理快得多。有一个细节要注意如果文件很大超过几百行建议让 agent 先只看 import 区块附近的 30 行避免它把整个文件读进上下文后生成过度修改。6.2 帮我写一个补丁并跑本地测试用户给 src/parser.py 里的 parse_line 函数加一个支持空行的逻辑然后跑 pytest test/test_parser.py如果挂了就自动修一遍再跑。这是一个典型的迭代任务。agent 流程是read_file读函数实现write_file写入改动shell跑测试看到失败输出后回到第一步修改再跑。我在 prompt 里加了一个规则重复修改最多 3 次超过次数就停止并汇报最可能的根因。这样不会无限循环卡在那也不会把明明走偏的修改一直试下去。实测效果这种小规模单函数修改agent 的成功率大概在八成左右剩余两成通常是测试断言格式或环境问题导致但即使失败了agent 给的失败信息也帮我缩小了排查范围。6.3 总结这个项目的架构并生成文档用户用五分钟快速看一遍这个项目的结构和核心依赖生成一份 200 字的架构说明存到 docs/architecture.md。这是最能体现多工具协作的配方。agent 会先用glob拿到整棵目录树快速识别入口文件和配置文件package.json、pyproject.toml、go.mod等然后针对性地read_file三四个核心文件最后生成 markdown 并写入。我第一次跑这个时agent 读了 11 个文件才写总结耗时约 40 秒。我给 prompt 加了一条约束只读取你判断为最关键的 5 个文件其他用 glob 扫描来推断之后耗时降到 15 秒左右质量几乎没有下降。这告诉我们给 agent 限定工作规模有时结果是更好的因为限制迫使它抓重点。7. 现在可以考虑的扩展方向这个工作台用了一个多月后我已经把文档写读、代码重构、日志分析、测试驱动这几类常规任务完全交给 agent 了。它并未取代我的判断力但确实消灭了大量重复机械动作。尤其让我满意的是进化这个属性——每完成一次此前没做过的复杂任务agent 就拥有了曾经做过一次的经验下次再做就是轻车熟路。如果你也要搭一个类似的自己工作台我给你一个建议不要一开始就贪多求全。先把read_file、shell、write_file这三个工具跑通形成一个最小闭环让 agent 能读文件、能跑命令、能改代码。一个功能闭环跑通后再考虑扩展。早期版本把工具做得越多prompt 越冗余调试时定位问题越费劲。另外强烈建议给 agent 的操作留一个撤销机制。我最初没在意这个问题有次让 agent 改了配置文件结果没备份折腾了很久才恢复。现在 agent-shell 会在每次write_file前自动生成一份.bak备份避免改坏无法回退。这应该成为类似工具的标配。搭好这个循环之后你会感觉到自己的工作台确实在慢慢进化。隔一两周翻看一下配置目录里自动生成的工具函数能看到它帮你沉淀了不少原本要反复操作的东西。这份沉甸甸的积累感可能就是最让人继续折腾下去的理由。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询