最早进入公众视野的AI编程工具大概是GitHub Copilot或者Tabnine,使用方式类似一个扩展版的IDE补全:工具根据光标位置的上下文猜测用户想输入的代码,以虚色形式提示,可以按Tab落实。DeepSeek的API至今仍保留了这个功能。这个功能今天看来有点简朴甚至落后,但在ChatGPT诞生前对程序员生产力的影响是突破性的。

当然,时间没有过很久,2022年底ChatGPT诞生。很快人们发现,编程语言说到底也是一种语言,既然大语言模型擅长人类语言,那它也很容易用来写代码。从2023年我的亲身体会看,当时的ChatGPT已经可以一次性输出上百行的脚本,并且将输入的代码翻译到另一种语言,不过没办法一次跑通,还需要人做些修改。

显然大模型在未来会向传统编程发起革命,但彼时的模型上下文太短(16K甚至4K),遵循指令和理解人类意图的能力较差(JSON输出的格式也未必能保证),只能用来做一次性辅助用途。甚至有人打趣说未来所有的代码都会变成Perl一样的write-once。这个阶段还诞生了Cursor等被誉为「下一代编辑器」的产品。

同样,时间也没有过太久,模型能力迅速增长,突破某个临界点以后,Agent自主根据命令规划和完成任务的能力可用。以Claude Code为代表的工具展现出一种新的编程模式——我只需要对话就行了。

最自然的产品演进当然是在已有编辑器里加一个Agent面板。这当然没错,但它留下了一个尴尬的问题:编辑器和Agent仍然是平行的两套系统。人操作的是文件、命令、日志输出,Agent却在另一边操作shell、patch,两者只能靠硬盘上的文件勉强同步。

这让Copilot、Cursor以及某些跟在Cursor后面的编辑器非常难受。它们想到的办法是什么呢?在编辑器里面加上一个面板,把类似Codex的功能集成进去就行了。毕竟,可以蹭就行了,谁管它别扭与否呢?

但是,如果重新思考人使用编辑器的流程呢?最经典的编辑器 (如Windows记事本) 模型是:

   (Command)
Human ---> Editor ---> File

IDE以及装备了LSP的编辑器中,模型变成了这样:

             +----> File
             |
Human ---> Editor/Document <----> Language Service
             ^
             |
       Completion/Diagnostics

Copilot等基于大模型的补全工具并没有根本上改变这个流程,只是把最右侧基于确定性逻辑的代码分析工具换成了模型 (或者让两者同时存在,LSP给模型提供辅助)。

而Codex这样的工具的工作模型更类似这样:

    (Prompt)
Human ---> Agent ---> File/Project

明显能看出区别:Agent的存在将人类 (以点击、编辑、快捷键为形式) 的命令集成进来,同时让AI自驱动一个循环来反复完成编辑和构建等操作。这样的模型和传统编辑器自然是不兼容的,因为编辑器不存在一套将Agent视为一等主体的语义模型。Agent修改文件、完成构建直接通过Shell命令,走了一条完全不同于编辑器的路,这就是强行集成Agent的编辑器让人觉得别扭的原因。

OK,有人说现在有了ACP (Agent Client Protocol),可以让编辑器像Agent暴露能力。但ACP解决的是Agent与编辑器互操作的基本问题,而不是编辑器内部的统一操作模型。这导致ACP的能力只能涵盖主流编译器的交集。即使ACP继续增加能力,也很难感知到编辑器完整的内部时间流和状态变化。

让我们看看作为编辑器的Emacs设计:

  • 用户编辑的基本单元是buffer,一个buffer可以关联具体的文件,也可以不关联 (虚拟buffer、REPL、日志)
  • 对于一个buffer,存在一个major mode和若干minor mode。major mode代表了buffer的文件类型 (如C++源码文件) 或虚拟buffer的交互模式 (如临时解密后的加密文本),minor mode代表若干附加功能 (如是否显示行号)
  • 几乎所有快捷键和鼠标能触发操作 (包括最基本的光标移动),都对应一个特殊的Emacs Lisp函数,这个函数被注册为命令。快捷键M-x (对应的函数名叫execute-extended-command) 可以手动执行任意命令。在有丰富配置的Emacs环境中,M-x的命令数量可以达到数千上万个
  • 用户可以在一个buffer里实时eval一段Emacs Lisp代码,组合已有的命令 (只是普通的Lisp函数而已) 并构造一个新命令。最关键的是,整个Emacs的上层交互几乎都是Lisp实现的,这意味着编辑器运行时可以动态生成代码修改一切

再总结一下,就是:绝大多数用户可主动触发的操作最终都会进入统一的命令或函数抽象,而这些命令又和编辑器内部逻辑共享一个Lisp环境。因此Emacs的模型类似这样:

Human ---> Key/Mouse ---> Command ---> Buffer
       +-> M-x -------+      ^            |
                             +------------+

对比观察,这个架构显然对Agent更加友好。或者说,在Emacs中,Agent可以完美取代上面Human->Key/Mouse这一段,像人一样使用命令完成交互。Emacs因为是一个纯粹的Lisp环境,模型通过eval/execute-extended-command调用命令不会有任何障碍 (和Agent调用Bash非常相似)。而因为编辑器可以动态执行Lisp代码,Agent在调用任何工具前都可以动态感知编辑器的环境状态,筛选出可用的工具。

当然,不是所有人都适合Emacs,Emacs也的确有它自身的若干缺陷。但是,这种架构本身依然是Emacs最大的生命力所在。其实截至目前,Emacs世界本身也还没有如此灵活的Agent诞生。

但是,如果一个编辑器能够像Emacs一样,不强行割裂用户交互和内部逻辑,而是把命令以一种统一、可编程的形式暴露出接口,Agent的设计会有极大的想象空间。

甚至,这样的编辑器本身可以运行在headless的状态,当用户不需要主动编辑代码的时候,它的状态类似Codex;当用户需要打开某个具体的代码做修改的时候,Agent和用户不会有冲突,而更像两个结对编程的人由同一个进程做协调。

除此之外,如果这个编辑器从一开始就把命令设计在一个受控执行环境之中,那么每个命令都可以拥有一份详细的权限列表,这会极大增强Agent执行的审计和撤销空间。

     Human / UI
         |
         v
+------------------+
|                  |
|   Editor Core    |
|                  |
| Buffer / Command |
| Event / History  |
| Diagnostic / AST |
| Permission       |
+------------------+
         ^
         |
       Agent
         |
+--------+--------+
|        |        |
LSP     Build     Git ...
         |
        File

很多人都在复读一句话:AI时代最重要的东西是品味。但是,品味究竟是什么?恐怕不是界面是否漂亮,而是以前的产品设计隐含了哪些假设,在AI冲击下这些假设还是否成立。

今天Agent极大降低了各方面的试错成本,希望更多的团队能认真思考品味究竟代表了什么,而不是想当然做一个功能大杂烩的模仿产品出来。