当我们审视当代 AI 辅助编程工具的演进轨迹时,一个耐人寻味的现象正在浮现:无论是 GitHub Copilot 的上下文感知补全、Cursor 的多模态编辑,还是 Devin、Claude Code 等 AI Agents,这些工具不约而同地呈现出一种向经典致敬的特质 —— 高度可扩展、文本优先、运行时可编程。这种现象被社区敏锐地捕捉到,并冠以一个恰当的名字:软件的 "Emacs 化"(Emacsification)。这不是简单的界面模仿,而是一场关于软件可扩展性本质的深层对话。Emacs 作为诞生于 1976 年的编辑器,用近半个世纪的时间证明了可扩展性不是功能的堆砌,而是一种架构哲学。如今,AI 时代的软件工程师们正从这台 "永恒的 Lisp Machine" 中汲取灵感,重新定义人机协作与软件构建的边界。
可扩展性的哲学根基:为什么是 Emacs
理解 Emacs 在 AI 时代的复兴,需要回到它的设计原点。Emacs 的核心不是某个功能,而是一套关于 "软件应当如何被扩展" 的哲学。这套哲学建立在三个相互关联的原则之上:代码与数据的一体性、运行时修改的能力、以及对统一接口的坚持。Emacs Lisp 作为 Emacs 的灵魂语言,是同像性语言(homoiconic language)的典范 —— 代码即数据,数据即代码。这意味着程序可以读取、修改和生成自身的结构,而这种自省(introspection)能力是真正可扩展性的前提。在传统软件中,用户面对的是编译好的二进制,用户只能通过预设的配置选项与软件交互;而在 Emacs 中,用户面对的是开放的 Lisp 环境,可以通过修改函数、挂载钩子(hook)、重定义行为来改变系统的每一个角落。这种架构哲学的影响力远超编辑器本身,它实际上是在回答一个根本问题:软件应该是一个封闭的产品,还是一个开放的平台?
Emacs 的可扩展性哲学还体现在它对 "一切皆文本"(everything is text)的坚持。在 Emacs 的世界里,所有的源代码、配置文件、甚至邮件都是统一的文本缓冲区。这种看似简单的一致性带来了深远的灵活性:统一的操作语义使得用户可以用同一套技能处理不同的数据类型,模块之间通过文本进行信息交换,避免了紧耦合和专有协议的限制。更重要的是,文本作为通用接口意味着系统天生具备与其他工具协作的能力 ——grep、awk、sed 这些 Unix 工具都可以与 Emacs 无缝集成。当我们将视野投向现代 AI 辅助编程工具时,会发现类似的哲学正在被重新实现:AI 模型处理的是文本形式的代码,上下文窗口本质上是一个可读写的大型文本缓冲区,而 AI Agents 通过自然语言指令修改代码 —— 这一切都不过是 Emacs 哲学在新的技术语境中的变体。
AI 辅助编程工具的 "Emacs 化":从工具到平台
观察近两年 AI 辅助编程工具的演进,可以清晰地识别出一条从功能型工具向可扩展平台演进的路径。早期的 AI 补全工具(如 Tabnine、CodeWhisperer)本质上是一个增强版的自动补全器,它们运行在隔离的环境中,为开发者提供上下文感知的代码片段。这些工具虽然有用,但它们是封闭的 —— 用户无法干预补全逻辑,无法添加领域特定的知识,也无法将补全功能嵌入到自己的工作流中。然而,随着 AI 能力的增强和开发者期望的提升,工具的设计哲学开始发生转变。GitHub Copilot 引入的 Chat 界面允许开发者用自然语言与代码库对话,Cursor 提供的 Composer 模式允许跨文件的多步编辑计划,Claude Code 和 Devin 等 Agents 则更进一步,允许 AI 在代码库中执行复杂的任务序列。这些演进的共同特征是:工具正在变得更像平台,用户正在获得更大的控制权。
这种 "Emacs 化" 趋势具体体现在几个维度。首先是运行时可编程性的回归。在 Emacs 中,用户可以在编辑器中打开一个 Lisp REPL,实时修改函数行为而无需重启程序;现代 AI 编程工具正在引入类似的能力 ——Claude Code 的会话上下文允许在对话过程中定义新的助手指令(system prompts),Cursor 的规则文件(rules)允许在项目级别注入定制行为,GitHub Copilot 的自定义指令(custom instructions)允许为特定代码库配置特定的补全策略。这些机制本质上都是在重新引入运行时可编程性,让用户能够影响 AI 的行为而不被锁死在供应商预设的逻辑中。其次是对语言无关性的追求。Emacs 的 Lisp 核心是语言无关的 —— 它可以编辑 Python、Go、Rust,也可以处理 JSON、Markdown、LaTeX,因为一切都是文本。这种语言无关性在现代工具中的对应物是:AI 模型能够理解和生成多种编程语言的代码,并能够处理跨越语言边界的复杂任务。LSP(Language Server Protocol)的广泛采用进一步强化了这一趋势 —— 通过统一的协议,编辑器可以获得对任意语言的语言服务支持,而无需为每种语言单独实现功能。AI 辅助工具同样受益于类似的标准化:代码补全的上下文格式、AI Agents 的工具调用接口、代码审查的输出规范,都在逐步形成事实上的标准,使得工具之间的互操作性成为可能。
可扩展架构的技术要素:从 hooks 到 AI tool calling
从技术实现的角度,Emacs 的可扩展性建立在几个核心机制之上,理解这些机制有助于我们把握 AI 时代可扩展架构的设计要点。钩子(hook)是 Emacs 可扩展性的基石 —— 它是一种事件驱动的回调机制,允许用户在特定时机插入自定义行为。major-mode-hook 允许在进入特定编辑模式时执行初始化代码,after-save-hook 允许在保存文件后触发外部工具钩子(flycheck、eslint、ruff),window-configuration-change-hook 允许在窗口布局变化时调整界面元素。这种机制的价值在于:核心代码不需要为每一种可能的扩展场景编写分支逻辑,它只需要在适当的时机 "喊一声"(触发 hook),扩展代码自然会响应。这种松耦合的协作模式在现代 AI 编程工具中找到了新的表达:AI Agents 通过 "工具调用"(tool calling)与外部世界交互,而工具注册和调用的机制与 Emacs 的 hook 有着相似的设计哲学。不同的是,AI Agents 的 "hook" 是 AI 模型本身 —— 模型在生成响应时会考虑可用的工具列表,并决定调用哪个工具来完成任务。这种架构将 "什么时候扩展" 的决定权从固定的代码逻辑转移到了更灵活的主体(可以是规则引擎,也可以是 AI 模型本身),从而极大地扩展了系统的适应性。
缓冲区本地变量(buffer-local variables)是 Emacs 架构中另一个值得关注的可扩展机制。在 Emacs 中,变量的行为可以是全局的,也可以是特定于某个缓冲区的。这意味着同一个变量名在不同编辑上下文中可以承载不同的值,从而允许 Emacs 在处理多个文件时保持独立的状态而无需为每个文件单独实例化完整的编辑环境。这种设计在 AI 辅助工具中的对应物是会话上下文(session context)和项目级配置。会话上下文允许 AI 在不同的对话线程中维护独立的记忆和状态,而项目级配置(如 Cursor 的 .cursorrules、GitHub Copilot 的 .github/copilot-instructions.md)则为特定代码库提供了局部定制的能力。更进一步地说,现代 AI 编程工具正在引入的 "上下文协议"(context protocols)本质上就是一种面向 AI 的 "缓冲区本地变量"—— 它们定义了 AI 在处理特定项目时应该遵循的规则、应该调用的工具、以及应该考虑的文件范围。
动态作用域(dynamic binding)是 Emacs Lisp 的另一个独特特性,它允许变量在调用链中临时获得新的绑定,而不影响调用者之外的代码。这种机制在实践中支持了一种优雅的扩展模式:在某个操作的执行过程中临时改变系统的行为,操作完成后自动恢复。在现代 AI 编程工具中,类似的机制正在以不同的形式出现:AI Agents 的 "子任务执行" 允许在主任务的上下文中创建临时的子会话,这些子会话可以携带不同的指令和约束,执行完成后结果被合并到主会话中。Cursor 的 "临时规则"(transient rules)允许为单个编辑操作临时覆盖项目的通用规则,执行完成后自动失效。这些机制的核心价值在于:它们提供了一种可控的、可恢复的状态修改方式,避免了全局状态的污染,同时保持了扩展的灵活性。
AI Agents 与 "Lisp Machine" 的复兴
在所有观察视角中,最具哲学深度的或许是 AI Agents 与 Emacs 作为 "Lisp Machine" 的理念关联。Lisp Machine 是 1970 年代到 1980 年代期间一批专用于运行 Lisp 的高阶计算机,它们不仅能运行 Lisp 程序,还能让用户检查和修改正在运行的 Lisp 系统的每一个部分 —— 包括操作系统本身。这种 "living system"(活的系统)的理念是 Emacs 设计的底层精神:编辑器不是一个静态的程序,而是一个可以生长和改变的活的有机体。当我们审视当代 AI Agents 的发展时,会发现它们正在承袭并扩展这一理念。一个 AI Agent 不再仅仅是一个响应请求的被动程序,而是一个能够感知环境、制定计划、执行行动、评估结果的主动实体。这种从被动工具到主动代理的转变,与 Lisp Machine 的理念一脉相承。更重要的是,现代 AI Agents 正在获得 "自省"(self-reflection)的能力 —— 它们能够审视自己的推理过程,识别错误,并在后续的行动中纠正。这种元认知(meta-cognition)能力正是 Emacs Lisp 环境所提供的那种 "代码可以修改代码" 的哲学在 AI 层面的实现。
这种理念复兴的技术含义值得深入探讨。Lisp Machine 时代的一个核心洞察是:当系统足够透明、当操作原语足够丰富,系统本身就可以成为构建新工具的原材料,而不是需要从外部重新构建。这种 "bootstrapping"(自举)的思想在现代 AI Agents 中表现为 "工具构建工具" 的能力。Claude Code 等 Agents 不仅能够使用工具,还能够生成新的工具 —— 它们可以编写 Python 脚本、生成 shell 命令、甚至创建其他 Agents 可以使用的函数。这种能力的前提是系统的可扩展性足够高:工具调用的接口必须足够通用,允许动态注册新工具;系统的状态必须足够透明,允许 AI 理解当前可用的能力;工具的执行结果必须足够结构化,允许 AI 正确地解析和使用。这种需求与现代软件的 "可观测性"(observability)运动不谋而合 —— 当系统能够被充分地观察和理解,它才能被有效地扩展和协作。
面向可扩展 AI 系统的设计原则
从 Emacs 的历史经验和 AI 时代的需求出发,我们可以提炼出若干面向可扩展 AI 系统的设计原则,这些原则超越了具体的编程语言或框架,关注的是架构层面的深层决策。第一个原则是接口的普遍性(interface universality)。Emacs 的成功很大程度上归功于它选择了一个足够通用的底层表示 —— 文本 —— 作为所有信息交换的媒介。在 AI 时代,这个原则的对应物是选择足够通用的上下文格式。结构化的代码表示(如抽象语法树、语义图谱)可能在某些场景下更精确,但它们缺乏文本的普遍性和互操作性。一个好的可扩展 AI 系统应该能够在文本和其他表示之间灵活转换,既享受结构化表示的精确性,又保持与通用工具的兼容性。第二个原则是层次化的抽象(layered abstraction)。Emacs 的架构清晰地划分了层次:底层是 C 编写的高性能核心,上层是 Emacs Lisp 编写的可修改环境,再上层是用户配置的 init.el 和各个插件。这种层次化确保了核心的稳定性和扩展的灵活性不会相互干扰。在 AI 系统中,这意味着我们应该区分 AI 模型的能力边界和系统的扩展机制 —— 模型负责推理和生成,而工具调用、状态管理、错误恢复等机制应该在模型之外实现,以避免将核心逻辑锁定在模型的权重中。
第三个原则是可组合性(composability)。Emacs 的插件生态之所以繁荣,一个关键原因是插件之间可以通过清晰的接口组合:lsp-mode 提供语言服务,flycheck 提供实时检查,company 提供补全,which-key 提供键绑定提示 —— 这些插件可以独立使用,也可以随意组合,用户可以根据自己的需求构建定制的工作环境。在 AI 系统中,可组合性意味着工具和能力的模块化:代码搜索工具、测试运行工具、代码生成工具、文档查询工具应该能够独立存在,通过标准化的接口相互协作。这种设计允许用户选择性地采用能力,也允许系统在运行时根据任务需求动态组合工具集。第四个原则是运行时扩展(runtime extensibility)。这是 Emacs 区别于大多数传统软件的核心特征:用户可以在编辑器运行时加载新插件、修改配置、重定义函数,而无需重启程序。对于 AI 系统,这意味着系统应该支持动态注册新工具、实时更新系统提示(system prompts)、在线调整行为参数,而不丢失当前的执行上下文。Claude Code 的会话持久化、Cursor 的规则热加载、GitHub Copilot 的上下文注入,都是这一原则的技术实现。
可扩展性哲学的深层启示
当我们从 Emacs 的可扩展哲学回望 AI 时代软件架构的演进时,会发现一些超越技术本身的深层启示。第一个启示关乎控制权的归属。传统软件倾向于将控制权集中于开发者 —— 用户只能在预设的选项中进行选择,系统的行为由代码逻辑固定,用户与软件的交互是单向的。Emacs 反其道而行之,它将控制权尽可能地下放给用户 —— 用户可以改写任何函数,可以添加任何功能,可以改变任何默认行为。这种 "用户主权"(user sovereignty)的理念在 AI 时代获得了新的意义:当 AI 成为软件行为的主要驱动者时,我们应该问的问题不再是 "供应商是否允许我定制这个功能",而是 "用户是否拥有影响 AI 行为的权利和能力"。一个好的 AI 辅助系统应该是一个平台而非一个产品,它应该提供清晰的扩展点和干预机制,让用户能够引导 AI 的行为、定制 AI 的知识、限制 AI 的能力。
第二个启示关乎系统的边界。传统软件工程强调模块化和信息隐藏(information hiding)—— 好的系统应该隐藏内部复杂度,只暴露必要的接口。这种范式在软件作为产品的时代运作良好,但它在可扩展性方面存在局限:隐藏得太深,扩展者就无法理解和干预系统的行为。Emacs 的哲学则相反 —— 系统应该是透明的,所有的内部状态都应该可以被检查,所有的行为都应该可以被修改。这种 "激进透明"(radical transparency)的理念对于 AI 系统尤其有意义,因为 AI 模型的决策过程本身是不透明的 —— 如果我们不能理解 AI 为什么会做出某个决策,我们至少应该能够理解 AI 在做出决策时能够访问什么信息、受到什么样的约束。一个好的 AI 系统应该提供足够的可观测性(observability),让用户能够审计 AI 的行为,并在必要时进行干预。
第三个启示关乎演进的方式。Emacs 保持了近半个世纪的生命力,不是因为它一次性地设计好了所有可能的功能,而是因为它建立了一个可持续演进的框架。核心保持稳定,扩展通过 Lisp 实现,版本之间保持兼容性 —— 这种架构允许生态系统在核心之上持续生长,而不需要每次都重新发明轮子。在 AI 时代,这意味着我们应该区分 "需要稳定的核心" 和 "需要灵活演进的边缘"。核心可能包括:工具调用的协议、上下文管理的机制、错误恢复的策略、多 Agents 协作的框架;而边缘则是:具体的 AI 模型、特定的工具实现、领域的知识库、用户的定制配置。将核心与边缘分离,允许系统在不破坏稳定性的前提下持续演进。
结语:平台之上,再造平台
回望 Emacs 近五十年的历史,它教会我们最重要的一课或许不是某个具体的技术,而是一种关于 "软件应该如何看待用户" 的哲学立场。Emacs 将用户视为有能力、有意愿塑造自己工作环境的主体,而非被动的功能接受者。这种立场在 AI 时代获得了新的紧迫性:当 AI 的能力越来越强大时,我们需要确保这些能力能够被用户所拥有、所控制、所定制。软件的 "Emacs 化" 不是一场复古运动,而是一次螺旋式的上升 —— 旧的哲学在新的技术语境中重新找到了生命力,而新的挑战也在迫使我们重新审视那些被遗忘的智慧。在 AI 与软件工程深度融合的未来,真正的竞争力不在于谁拥有更强的 AI 模型,而在于谁能够构建出最具可扩展性的平台 —— 让 AI 的能力与人类的创造力相互放大,让工具的智能与用户的主权相互尊重。这或许是 Emacs 的可扩展哲学在 AI 时代最深远的启示:在智能日益强大的时代,我们最需要的不是更智能的工具,而是更开放的平台。
参考资料
- 关于可扩展架构与 AI Agents 的 Lisp Machine 理念复兴,可参考 "The Infinite Buffer: Why AI Agents Are Rebuilding the Lisp Machine"(dev.to)。
- 关于 Emacs 可扩展性哲学与现代混合架构实践,可参考 "Debunking the 100% Lisp Myth: Hybrid Approaches Unlock True Extensibility"(WebProNews)。
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。