不必为每个 AI 编程 Agent 换一套编辑器:用 agent-shell 在 Emacs 里统一 ACP 会话
AI 编程 Agent 的命令行入口越来越多:一个项目里你可能习惯用 Claude Agent,另一个团队偏向 Codex,临时任务又需要 Gemini CLI 或 Cursor。对 Emacs 用户而言,真正麻烦的不只是安装多个程序,而是每个工具都各自拥有会话、认证、快捷键和上下文边界。切换 Agent 时,编辑器往往退化成“开终端的地方”。
agent-shell 给出了另一种做法:它是运行在 Emacs 原生 buffer 中的 ACP(Agent Client Protocol)客户端。它不试图替代后端 Agent,也不把所有模型包装成同一套私有 API;它让 Emacs 负责界面、会话和工作区体验,让各个支持 ACP 的 Agent 保持各自的命令行与认证方式。这特别适合已经把 Emacs 当作项目中枢、却不想为每个 Agent 重新学习一套 UI 的开发者。
先分清:统一的是交互层,不是模型能力
agent-shell 的核心依赖是 acp.el,并通过 ACP 与 Agent 进程通信。官方 README 列出的可接入对象包括 Claude Agent、Codex、Gemini CLI、Goose、Cursor、Qwen Code、OpenCode 等;但“出现在列表里”不等于不需要准备后端。你仍然要按对应工具的官方方式安装、登录或配置 API 凭据,agent-shell 只是启动并承载这些 Agent。
这个边界很重要。比如 Codex 要安装 ACP adapter,Gemini CLI 需要支持 --experimental-acp 的近期版本;Cursor 则需要其 CLI 提供 agent acp。因此排障时应先在普通终端确认 Agent 能启动,再回到 Emacs 检查 ACP 接入。把认证失败误当成 Emacs 配置问题,是此类工作流中最常见的绕路。
从架构上看,这种分层还有两个收益:一是可以在同一编辑器里保留不同供应商的原生能力与账号;二是项目配置不必绑死在某个 Agent 的 GUI。真正会随工具变化的是 ACP adapter 和后端 Agent,编辑器侧的阅读、编辑、窗口管理、搜索与 Org 工作流仍然保持一致。
最小可用安装:先只接一个 Agent
README 给出的 MELPA 安装方式会连带安装 acp.el 和 shell-maker。下面的配置以 Claude Agent 为例;它的目的不是把密钥硬编码进 init 文件,而是先声明包与外部依赖的对应关系:
(use-package agent-shell :ensure t :ensure-system-package ((claude-agent-acp . "npm install -g @agentclientprotocol/claude-agent-acp")))
安装后,先在系统终端执行一次后端 Agent 的登录流程,再从 Emacs 运行 M-x agent-shell。该命令会让你从已知 Agent 配置中选择一个并创建或复用 shell;若使用前缀参数 C-u M-x agent-shell,则会强制创建新的会话。这个区别很实用:日常修复同一个分支时复用会话,准备让另一个 Agent 独立审查时才新开 buffer,避免把两条任务线混在同一上下文里。
不要把“多个 buffer”理解成“多个 Agent 可以无约束改同一工作区”。它们共享磁盘状态,却不共享对彼此计划的理解。比较稳妥的做法是:一个会话负责实现,另一个会话只读审查或生成测试建议;提交前仍由 Git diff、测试和人工判断收口。
把敏感配置放在环境层,而不是提示词里
agent-shell 默认以精简环境启动子进程。若后端需要代理或密钥,可用 agent-shell-make-environment-variables 明确传递;官方文档也支持从 .env 文件加载变量。下面的示例只展示代理设置,密钥应由 auth-source、系统环境或受控的 .env 文件提供:
(setq agent-shell-anthropic-claude-environment
(agent-shell-make-environment-variables
:inherit-env t
"HTTPS_PROXY" "http://proxy.example.com:8080"))
这里的 :inherit-env t 很关键:它让子进程继承 Emacs 进程已有的 PATH、HOME 等环境;没有它时,某些“终端里可运行、Emacs 里找不到命令”的问题会出现。相反,也不应为了省事把整个 .env 内容贴进对话框——提示词、聊天记录和终端滚动区都不是秘密管理系统。
用项目边界管理上下文,而非只管理窗口
在实际仓库中,Agent 的上下文并不只来自对话历史,还来自它启动时所在的目录、可见文件、Git 状态与 MCP 服务。因此,启动会话前最好先把 Emacs 的 default-directory 切到目标项目根目录,再运行 M-x agent-shell。这样,后端 CLI 的相对路径、项目级配置和 Git 根目录判断才会与正在编辑的文件一致。若你在一个 buffer 中临时切到另一个仓库,不要假定已有 Agent 会话也自动切换工作区;更可靠的方式是在新项目中新建会话,并在首条提示中说明任务边界。
可以把一次任务拆成可验证的四步:先让 Agent 只读地总结当前 diff 与测试入口;再要求它提出最小修改方案;确认后才允许写文件;最后由独立命令运行测试并检查 git diff。agent-shell 能让这些交互都留在同一编辑器里,但不会替你建立变更审批。特别是在两个 Agent 同时工作时,应避免让它们并发修改同一组文件;若确实需要并行,按目录、模块或“实现/审查”角色分割,并把最终合并交给明确的一方。
这种约束看似比直接说“修好它”慢,却能显著降低多会话环境中的隐性覆盖:你能知道哪个 buffer 提出了方案、哪个进程执行了命令、哪些改动尚未进入 Git。对需要回溯决策的团队,这比只保存一段聊天转录更有操作价值。
三种界面对应三种工作节奏
agent-shell 并非只有传统终端式交互。默认 shell 基于 Emacs 的 comint 体验,适合连续追问、观察工具调用和快速输入;Viewport 则把单次请求/响应放到专用 buffer,适合需要长篇审阅或精心组织提示的任务。启用偏好后,普通启动入口会优先进入 Viewport:
(setopt agent-shell-prefer-viewport-interaction t)
在 Viewport 编辑状态,C-c C-c 可以发送或排队当前提示;阅读状态中可用 r 回复、b/f 在交互间移动。0.73 更新还引入了默认启用的 chat mode:它在 shell 的连续输出上增加更接近聊天记录的标签与分组,但底层仍是原来的交互模型。若你依赖极简终端视觉,可通过 agent-shell-chat-mode-enabled 关闭;不要把它误认为换了一个 Agent 协议。
选择界面应服务任务,而不是追逐新 UI:短指令和命令回显用 shell,代码评审或设计讨论用 Viewport,需要快速浏览历史时再启用 chat mode。这样能减少“上下文很多却没有决策记录”的错觉。
MCP 配置的复用价值与权限边界
如果团队同时使用多个 ACP Agent,最有价值的往往不是让它们回答同一个问题,而是把共同需要的 MCP 服务集中声明。README 示例使用 Emacs Lisp alist 配置 HTTP MCP server:
(setq agent-shell-mcp-servers
'(((name . "notion")
(type . "http")
(headers . [])
(url . "https://mcp.notion.com/mcp"))))
这能减少在 Claude、Codex、Gemini 等工具中重复录入服务器地址的工作,但不等于自动解决授权问题。每个 MCP 服务的 OAuth、令牌、可写权限和数据范围仍需单独确认。尤其是同时连接代码库、知识库和工单系统时,应先用只读工具验证返回内容,再逐步开放写入操作;把“统一配置”误解为“统一信任”,会放大权限事故的影响面。
适用场景与不适用场景
agent-shell 最适合三类人:长期在 Emacs 中工作、希望试用多种编码 Agent、并且重视文本化配置和会话可控性的人。它也适合把 Agent 放进既有的窗口管理、Org 笔记和 Git 习惯中,而不是围绕某一个厂商客户端重建工作流。
它不适合把 ACP 当成万能兼容层的情况。某个 Agent 若没有可用 ACP 模式、adapter 版本落后,或其独有 GUI 功能没有对应协议能力,agent-shell 就无法凭空补齐。容器运行同样只是实验性支持:命令可以用 agent-shell-command-prefix 加前缀在容器内执行,但宿主机的环境变量不会自动传入容器中的 Agent 进程,密钥和路径必须重新设计。
最终,最稳的实践不是“把所有 Agent 塞进 Emacs”,而是先用一个后端 Agent 验证 ACP、认证与项目工作区,再按任务增加第二个。让编辑器统一交互层,让协议隔离供应商差异,让 Git、测试和最小权限继续承担最后的可靠性边界。这比在多个独立终端里追逐同一段上下文,更接近可维护的开发工作流。