Gptel 是一个运行在 Emacs 内部的 AI 客户端。它没有独立的聊天窗口,而是把 LLM(大语言模型)对话直接放进普通文本缓冲区里。只要你熟悉 Emacs 的缓冲区操作、region 选中、org-mode 缩进和组织方式,就可以用同样的习惯和 AI 对话。这也是 Gptel 和网页端 ChatGPT、独立桌面客户端最本质的差别:它不是让用户切换到另一个界面里聊 AI,而是让 AI 变成 Emacs 编辑体验的一部分。
这篇文章会围绕“Gptel 到底怎么用起来”展开。先解释 Gptel 的技术定位和核心设计,再走一遍安装、最小配置、启动对话的过程;然后扩展到多后端、多模型和上下文管理,把 Gptel 接进 org-mode、region 请求和异步调用;最后给出常见报错的排查路径和生产环境最佳实践。适合已经在用 Emacs 写代码、想在编辑器里使用 AI 助手,但又不想牺牲 Emacs 编辑习惯的开发者阅读。
1. Gptel 是什么,它和独立 AI 应用的核心差别
1.1 Gptel 解决的核心问题
使用 AI 模型时,最常见的交互方式是复制代码、打开网页、粘贴进去、再把回复复制回来。这个过程的问题是上下文在编辑器之外断裂了:你选中的代码、报错信息、历史对话、模型回复,都不在同一个地方,模型无法稳定地理解你当前的编辑现场。
Gptel 解决的就是这个问题。它把模型提供商封装成一个 Emacs 后端,通过 API 请求把当前缓冲区里的内容发送给模型,并把模型回复写回同一个缓冲区。这样,输入、输出、上下文、历史记录都保留在本地的普通文本文件里,你可以用 Emacs 编辑它们、折叠它们、保存它们,也可以随时改一改再重新发送。
换句话说,Gptel 不是一个单独的聊天软件,而是一个协议层:左边是 Emacs 的编辑能力,右边是任意模型 API。你的代码、问题、上下文,都作为普通文本流过这个协议层。
1.2 对话即缓冲区:底层设计思路
Gptel 有一个很关键的设计:对话内容不是存在私有数据库里的,而是存在于一个普通 Emacs buffer 中。你可以用M-x gptel创建一个新对话缓冲区,也可以在一个已有的 org-mode 文件里继续对话。
因为对话是普通文本,所以很多 Emacs 原本的能力可以直接复用:
- 可以用
C-x C-s保存对话记录。 - 可以用
C-x C-w把它另存为普通文件。 - 可以用 org-mode 的副主题、折叠、导出能力来组织思考过程。
- 可以用 region 选中一部分内容,只把选中的区域发送给模型。
- 可以手动编辑模型回复,修正答案后继续追问。
这种设计带来的直接后果是:Gptel 的上下文不是记忆在某个后台进程里,而是“你当前缓冲区里有什么,模型就看到什么”。如果你把之前的提问删除,模型在下一次请求时就不会知道这段历史。这既是干净、可预测的特性,也是新手容易踩坑的地方。
1.3 Gptel 与其它 Emacs AI 方案的差别
Emacs 生态里出现过不少 AI 客户端,比如chatgpt.el、llm.el、ellama等。它们的目标看起来相同,但设计粒度不一样:
| 方案 | 设计定位 | 使用方式 | 扩展性 |
|---|---|---|---|
| chatgpt.el | 偏 ChatGPT 网页交互模拟 | 固定后端、固定提示词 | 较弱 |
| llm.el | 提供统一的 LLM 调用接口 | 面向开发者的 API 封装层 | 较强,但不自带交互界面 |
| ellama | 偏端到端 AI 助手 | 提供命名交互命令和缓冲区 | 中等 |
| Gptel | 以缓冲区为核心的客户端 | 对话、region、org-mode、异步请求 | 强,适合深度嵌入 Emacs 工作流 |
Gptel 的优势不在于“能调用模型”,而在于“调用模型这件事和编辑行为是一体的”。你可以为一个独立函数写注释、生成文档、解释报错、补测试用例,整个流程都在同一个缓冲区里完成。
2. 环境准备与安装:先把依赖跑通
2.1 安装 Gptel 的最低环境要求
在正式安装之前,先确认 Emacs 环境满足要求:
- GNU Emacs:建议使用较新的稳定版本。Gptel 依赖 transient、map 等包,新版本 Emacs 对这些包的兼容性更好。
- 网络连接:Gptel 本身是客户端,访问模型 API 需要能够连通对应的 HTTPS 端点。如果你使用的是本地模型(如 Ollama),则只需要能访问
127.0.0.1。 - 包管理基础:至少能使用
package.el,或者已经配置了straight.el。 - API 密钥或本地模型服务:如果使用在线模型提供商,需要提前准备好 API key;如果使用本地模型,需要先跑通模型服务。
检查 Emacs 版本和包管理器:
emacs --version进入 Emacs 后执行:
(package-initialize) (require 'package) (package-refresh-contents)如果包刷新这一步出现超时,通常是网络访问问题,和 Gptel 配置本身无关,先排查网络连通性。
2.2 安装方式对比
Gptel 的安装方式主要取决于你现有的 Emacs 配置管理习惯。下表列出了三种常见方式:
| 方式 | 操作路径 | 适用场景 | 注意事项 |
|---|---|---|---|
| MELPA 安装 | M-x package-install RET gptel | 大多数使用package.el的用户 | 需要先配置 MELPA 源 |
| straight.el 安装 | (use-package gptel :straight t) | 已经使用 straight 管理所有包 | 可以获得最新提交 |
| 手动 clone | git clone后配置 load-path | 二次开发或离线安装 | 需要手动跟踪上游更新 |
对于大多数用户,推荐直接用 MELPA。MELPA 源配置示例:
(require 'package) (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) (package-initialize)然后:
M-x package-refresh-contents RET M-x package-install RET gptel RET如果你的配置全部写在init.el里,更推荐用use-package声明式管理:
(use-package gptel :ensure t :config ;; 后续配置写在这里 )2.3 配置目录与文件组织建议
学习 Gptel 和投产 Gptel 时,配置应该分成不同层次:
- 一个单独的配置文件,例如
~/.emacs.d/gptel.el,存放 Gptel 专属配置。 - 在
init.el中require这个文件。 - API key 不要直接写在配置里,而是通过环境变量或 Emacs 的 auth-source 读取。
- 为不同的后端准备独立的配置段,避免修改一个模型时影响其他模型。
目录结构可以参考:
~/.emacs.d/ ├── init.el ├── lisp/ │ └── gptel-config.el └── authinfo.gpg这种组织方式看起来多了一层文件,实际排查问题时非常有价值:Gptel 相关设置集中在同一个地方,不会散落在 init.el 的各个角落。
3. 最小配置启动第一个 AI 对话
3.1 API key 配置:不要直接硬编码
Gptel 需要访问模型服务的密钥或者端点信息。API key 是敏感信息,最不应该做的就是直接写在 init.el 里并提交到仓库。
推荐做法有三种:
- 环境变量:在 shell 配置中设置变量,然后在 Emacs 中通过
getenv读取。 - auth-source:使用
~/.authinfo.gpg统一管理密钥。 - 动态函数:设置
gptel-api-key为一个函数,需要时再读取。
最小示例,使用环境变量:
(setq gptel-api-key (getenv "GPTEL_API_KEY"))使用 auth-source 的方式:
(setq gptel-api-key (lambda () (auth-source-pick-first-password :host "api.openai.com")))这里的核心原因是:gptel-api-key可以是一个字符串,也可以是一个返回字符串的函数。Gptel 会在发起请求时读取它。用函数读取的好处是,密钥不会长期以纯文本形式留在 Emacs 变量里。
注意:不要把自己的 API key 硬编码在配置文件中。哪怕只是个人私有仓库,也有泄露风险。建议至少使用环境变量或者密文 authinfo。
3.2 最小 use-package 配置
以 OpenAI 兼容接口为例,最小配置如下:
(use-package gptel :ensure t :config (setq gptel-model "gpt-4o-mini") (setq gptel-backend (gptel-make-openai backend :key (getenv "GPTEL_API_KEY") :endpoint "https://api.openai.com/v1/")) (setq gptel-default-mode #'org-mode) (setq gptel-directives '((default . "You are a helpful assistant.") (coding . "You are an expert software engineer. Provide concise and practical solutions."))))关键点:
gptel-model:默认使用的模型名。gptel-backend:当前使用的后端,gptel-make-openai会构造一个后端对象。gptel-default-mode:新对话缓冲区的默认主模式,设置为org-mode后,可以用 org 的标题组织对话。gptel-directives:系统提示词列表,每个条目对应一种上下文角色。
如果你用的模型服务支持 OpenAI 协议,但端点不是官方地址,只需要改:endpoint。OpenAI 生态的兼容服务非常多,这个字段是接入时最常调整的参数。
3.3 启动会话与验证结果
配置完成后,重新加载配置:
M-x eval-buffer RET然后启动一个对话:
M-x gptel RET这会在一个新 buffer 中打开 Gptel 对话界面。输入问题后可以按C-c C-c或根据 transient 菜单提示发送请求。
验证是否成功的几个标志:
- 如果配置的是远程模型,第一次请求会等待几十秒到几分钟不等,取决于模型和网络。
- 请求成功后,模型回复会出现在缓冲区中。
- 如果出现
401 Unauthorized,说明密钥无效。 - 如果出现
404,说明模型名或端点路径不对。 - 如果出现超时,需要检查网络连通性和请求体大小。
3.4 核心配置参数说明
Gptel 配置看似只有几行,但有几个参数会影响日常使用,整理成速查表:
| 参数 | 含义 | 常见取值 | 错误配置表现 |
|---|---|---|---|
gptel-model | 默认模型名 | 提供商支持的模型 ID | 模型名不对会直接 404 |
gptel-backend | 当前请求使用的后端 | 由gptel-make-*构造 | 后端地址错误会连接失败 |
gptel-api-key | 认证密钥 | 字符串或函数 | 401 Unauthorized |
gptel-default-mode | 对话缓冲区主模式 | org-mode、markdown-mode、text-mode | 不会报错,但影响组织体验 |
gptel-directives | 系统提示词集合 | 不同角色的 prompt | 模型回答不贴合场景 |
这里最容易被忽略的是gptel-directives。它控制的是系统提示词,不是用户输入。如果你希望模型扮演“代码审查者”“SQL 优化者”“面试官”,应该在 directives 里切换,而不是在每次提问时反复补充说明。
4. 多后端、多模型与对话上下文控制
4.1 什么是 Gptel 的后端
后端(backend)在 Gptel 里是一个“如何和某个模型服务通信”的配置集合。它包含端点地址、密钥、请求格式、支持的模型列表等信息。
Gptel 支持多种后端构造方式,常见的有:
gptel-make-openai:用于 OpenAI 官方 API。gptel-make-anthropic:用于 Anthropic 系列模型。gptel-make-curl:用于任意自定义 HTTP API。- 本地模型服务:通过 OpenAI 兼容接口接入 Ollama 等本地模型。
多后端的价值在于:你不需要在 OpenAI、本地模型、公司内部模型之间反复切换配置,只需要定义好每个后端,然后通过 transient 菜单或快捷键切换。
4.2 用 gptel-make-curl-backend 接入本地 Ollama
本地模型是保护隐私和降低调试成本的有效方式。如果你已经安装了 Ollama,并拉取了模型,例如qwen2.5,可以在 Emacs 中这样配置:
(use-package gptel :ensure t :config (gptel-make-curl backend :name "Ollama" :host "127.0.0.1:11434" :endpoint "/v1/chat/completions" :stream t :protocol "http" :models '("qwen2.5" "llama3.1")) (setq gptel-backend (gptel--backend-name "Ollama")))这个配置的含义是:让 Gptel 向http://127.0.0.1:11434/v1/chat/completions发起请求,并在本地模型的模型列表中允许qwen2.5和llama3.1。
使用本地模型的好处:
- 不消耗在线 API 额度。
- 数据不出本机。
- 便于在没有稳定外网的环境下验证 Gptel 功能。
需要注意:本地模型的上下文窗口和响应速度与硬件性能直接相关。如果模型过大导致响应慢,不一定是 Gptel 的问题,而是本地推理资源不足。
4.3 OpenAI 兼容端点的接入方法
很多企业内部模型网关、云厂商模型服务都提供 OpenAI 兼容接口。接入时不需要特殊代码,只需要构造一个 OpenAI 后端,并修改 endpoint 和模型名。
(gptel-make-openai "InternalGateway" ; 后端名称 :host "llm-gateway.example.com" ; 网关地址 :endpoint "/v1/chat/completions" :key (getenv "INTERNAL_LLM_KEY") :models '("internal-model-v2"))这里建议把 endpoint 拆开理解::host是域名或 IP,:endpoint是路径。不同网关的路径差异很大,接入失败时先确认文档里的完整 URL 到底是怎么组成的。
还要注意:如果公司网关要求自定义User-Agent、额外 header,或者有特殊认证方式,可能需要查看 Gptel 是否支持在gptel-backend中附加请求参数。不同版本支持的程度不同,使用前先查C-h f gptel--backend-name对应的结构定义。
4.4 上下文管理:directives、角色与多轮对话
Gptel 的多轮对话和网页端一样,是通过把历史消息一起发送给模型实现的。但 Gptel 对“历史消息”的处理有一个关键区别:它只发送缓冲区中符合当前会话结构的消息。
因此,控制上下文的手段主要是:
- 使用
gptel-directives定义不同角色。 - 使用 region 限制发送范围。
- 手动删除不相关的历史内容。
- 重新开启一个新 buffer,开始全新会话。
定义一个用于代码审查的 directive:
(add-to-list 'gptel-directives '(code-review . "You are a strict code reviewer. Focus on bugs, security issues, and performance problems. Reply in Chinese."))之后在 Gptel 缓冲区中,可以通过 transient 菜单选择这个 directive,模型的行为会立刻切换。
不少用户把多轮对话和上下文长度混为一谈。实际上,多轮对话越长,token 消耗越大,也越容易触发模型上下文窗口上限。如果问题已经明显偏离最初主题,不需要继续累计上下文,新建 buffer 往往更高效。
5. 把 Gptel 装进日常 Emacs 工作流
5.1 在 org-mode 里组织 AI 对话
Gptel 默认可以配置为使用org-mode作为对话模式。这样做的好处是:对话历史本身就是一份 org 文件,可以用标题、列表、引用块来组织。
例如:
* 需求背景 我们有一个接口响应时间过长,需要定位原因。 * 排查过程 请帮我分析下面的代码为什么慢。 #+begin_src python def slow_function(items): result = [] for item in items: result.append(process(item)) return result #+end_src由于这是普通 org buffer,你可以把代码块、日志、报错信息直接放在当前上下文里,然后让 Gptel 基于整个 buffer 或选中的 region 回答。
5.2 用 region 选中代码或报错内容发送请求
这是 Gptel 最实用的日常用法之一。选中一个 region,然后发送请求,模型只会看到你选中的内容,而不是整个 buffer。
操作思路:
- 在代码缓冲区里选中一段函数或报错片段。
- 调用 Gptel 请求命令。
- 模型基于选中的 region 生成回答。
这样做的业务价值是上下文隔离。你不用把整个文件发给模型,只需要把关键代码段发过去,既节省 token,也避免无关代码干扰回答。
如果你希望把 region 发送到一个已经存在的 Gptel 缓冲区,就需要查看 Gptel 是否提供对应的 region 传递命令。不同版本命令名可能不同,用M-x gptel后弹出的候选命令列表可以帮你确认。
5.3 用 gptel-request 做异步请求
Gptel 不只是一个交互式客户端,它还提供了可编程的请求函数。gptel-request允许你在自己的 Emacs Lisp 代码中发起 AI 请求,并在回调中处理结果。
一个最小示例:
(gptel-request "Using one sentence, what is this function doing?" :context "def add(a, b):\n return a + b" :callback (lambda (response) (message "Gptel replied: %s" response)))实际使用前,需要用C-h f gptel-request确认当前版本的参数签名。因为不同版本的回调参数可能有差异,有的回调会传入完整响应对象,有的只会传入文本。
利用gptel-request,可以自己写一些自动化小工具,例如:
- 为当前 diff 生成 commit message。
- 将选中的英文注释翻译成中文。
- 对一段报错信息给出排查建议。
这种异步请求方式比交互式对话更适合批处理场景。请求发出后,Emacs 不会阻塞,你还可以继续编辑。
5.4 一个生成 git commit message 的小思路
把 Gptel 接入 git 工作流很有价值。核心思路是:读取出git diff --cached的内容,发送给模型,返回提交信息。
示例代码框架:
(defun my/gptel-commit-message () (interactive) (let ((diff (shell-command-to-string "git diff --cached"))) (gptel-request "Generate a concise git commit message based on the following diff:" :context diff :callback (lambda (response) (with-current-buffer (get-buffer-create "*gptel-commit*") (erase-buffer) (insert response) (pop-to-buffer (current-buffer)))))))这里的思路可以复用,但生产使用时要注意:
- 不要把整个大 diff 全量发送,超长 diff 会消耗过多 token。
- 可以只发送修改文件列表和每个文件的关键变更摘要。
- 结果不要直接当作最终 commit message,要人工检查后再使用。
6. Gptel 常见报错与排查路径
6.1 API key 无效
现象:请求后模型没有回复,*Messages*或请求响应中提示401 Unauthorized。
排查顺序:
- 确认
gptel-api-key是否设置了值:C-h v gptel-api-key RET。 - 如果值是函数,确认函数能返回非空字符串。
- 确认环境变量在 Emacs 启动时已经加载:
M-x getenv RET GPTEL_API_KEY RET。 - 确认 key 没有复制多余空格或换行。
解决方案:
(setq gptel-api-key (lambda () (string-trim (getenv "GPTEL_API_KEY"))))这里用string-trim是为了避免复制密钥时带入换行符。
6.2 认证相关报错
如果你看到类似client does not support authentication protocol的报错,需要先确认这个报错的来源。这种错误在数据库客户端中出现更常见,但如果在 Gptel 场景中出现,通常说明后端端点要求使用的认证方式与当前配置不匹配。
Gptel 场景下的处理方式:
- 确认后端构造时是否使用了正确的 key 字段。
- 检查请求是否被网关拦截,部分内部网关要求额外的 header。
- 用
curl手动请求同一接口,验证服务端期望的认证格式。
手动验证示例:
curl -X POST https://api.openai.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $GPTEL_API_KEY" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "hello"}] }'如果 curl 请求成功而 Gptel 失败,问题通常在 Emacs 配置侧;如果 curl 也失败,问题在网络、密钥或服务端。
6.3 模型名或端点 404
现象:请求返回404 Not Found。
可能原因:
- 模型名不存在。
- endpoint 路径写错。
- 服务商接口版本升级。
处理方式:
- 查看提供商文档,确认模型 ID 准确。
- 查看 endpoint 是
/v1/chat/completions还是/v1/responses,不同接口版本差异很大。 - 使用
curl验证模型列表接口,看实际可用的模型名。
例如查看 OpenAI 模型列表:
curl https://api.openai.com/v1/models \ -H "Authorization: Bearer $GPTEL_API_KEY"把返回结果和gptel-model对比,就能定位是不是模型名拼写错误。
6.4 请求超时与 token 限制
现象:请求长时间没有响应,或者模型回复被截断。
排查方向:
| 现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 长时间无响应 | 网络不通、服务端响应慢、模型过大 | 用 curl 测试同一接口 | 检查网络、更换小模型 |
| 回复截断 | max_tokens 设置过小 | 查看 Gptel 的 max_tokens 配置 | 增大 max_tokens |
| 出现 400 | 上下文超出模型窗口、请求结构错误 | 减少上下文、检查请求体 | 清理历史消息、换大窗口模型 |
| 掉线或连接重置 | 网关限制长连接 | 开启更简短的请求 | 缩短 prompt、避免超大请求 |
这里最容易被忽视的是上下文过长问题。Gptel 会把当前缓冲区中的历史内容一股脑发送给模型,如果历史里包含了大量代码块和日志,很容易超过模型的上下文窗口。遇到 400 或者超时时,优先考虑把历史缩短,而不是盲目调整网络参数。
6.5 Gptel 排错顺序清单
按这个顺序排查,大多数问题能在几分钟内定位:
- 输入是否正确:prompt 是否写错、region 是否选错。
- key 是否存在:
C-h v gptel-api-key、getenv。 - 模型名是否正确:对照文档和模型列表接口。
- endpoint 是否可达:用 curl 手动请求。
- 配置是否生效:修改配置后是否执行了
eval-buffer。 - 请求体是否过大:缩短上下文重试。
- 日志是否有明确异常:检查
*Messages*和后端响应内容。
7. 生产环境中的 Gptel 最佳实践
7.1 API key 安全管理
Gptel 在生产环境中的第一安全风险是 API key 泄露。不要把密钥写进 init.el、不要上传到公开仓库、不要轻易截屏发到团队群聊。
推荐的做法:
- 使用环境变量加载。
- 使用
~/.authinfo.gpg管理。 - 定期轮换密钥。
- 给不同环境分配不同 key,避免一把 key 到处用。
如果你的公司有统一的密钥管理平台,可以写一个小函数读取平台上的密钥,再赋值给gptel-api-key。这样 Emacs 配置里不会出现任何明文密钥。
7.2 费用控制、日志与可观测性
使用在线模型时,费用和 token 消耗是必须考虑的生产问题。建议:
- 设置最大响应长度,避免模型生成超长无意义内容。
- 限制上下文长度,不要让历史消息无限增长。
- 对重要操作使用便宜的模型先验证,再用昂贵模型做精调。
- 记录每次请求的模型、token 估算和耗时,便于月底核对账单。
Emacs 中的日志主要看两个地方:*Messages*和 Gptel 缓冲区本身的返回内容。如果请求失败,优先看*Messages*里有没有请求错误摘要。如果只有静默失败,检查配置中是否忽略了错误回调。
7.3 扩展方向:把 Gptel 当成 Emacs 里的 AI 基础设施
当多后端和异步请求都跑通后,Gptel 就不再只是一个聊天工具,而是一个可以嵌入到任意 Emacs Lisp 程序中的 AI 基础设施。
典型的扩展方向:
- 在
compilation-mode中自动分析编译错误。 - 在
org-mode中根据任务标题生成 TODO 分解。 - 在
minibuffer中快速生成文件名或代码片段。 - 结合
git-gutter查看当前文件变更并请求注释。 - 写一个小的
after-save-hook,保存文件时自动总结变更。
需要注意的是,加入自动触发逻辑时要控制频率。不要在每次按键或每次保存时都发请求,否则 API 消耗和请求延迟都会影响正常使用。建议做成显式命令,或者只在特定模式下自动触发。
把 Gptel 用成自己的工具,关键是理解它只是 Emacs 里另一种形式的缓冲区编辑。所有 AI 交互都可以落到普通文本上,因此可保存、可编辑、可组合。遇到问题时,从 key、端点、模型名、上下文这四件事查起,大多数问题都能在几分钟内定位。如果已经能完成一个多后端会话,就可以继续研究 org-mode 导出、角色提示词和异步请求,把这套能力沉淀成日常写代码时的固定动作。