emacs-在org文件中利用ob-gptel保存调用LLM聊天对话(deepseek为例)
1. 前言
我用AI用得比较少,也就网页白嫖一下Google AI Studio的Gemini,或者玩一玩免费的DeepSeek网页版。最近DeepSeek发布了新版本,在保持低廉价格的同时可用性也仅次于几家闭源大模型了,不得不多用用了。
但是DeepSeek的网页版使用起来,感觉不如谷歌的AI Studio。后者将每场对话当做一个纯文本文件,可以自由编辑删减先前的用户输入、AI输出,而DeepSeek中用户的历史输入和模型的历史输出都是固定的、无法修改的,缺失了很大的灵活性。
幸好,我们有Emacs,可以充分自定义整个体验。
2. 软件配置
3. gptel
配置 gptel 。首先是变量设置环节。我们设置使用外部的 curl 用于发送网络请求。内置的 url-retrieve 总会有一些大大小小的问题。
(use-package gptel :demand t
:custom
(gptel-use-curl t)
gptel-directives 是一整套系统提示词,你可以把需要重复使用的提示词写在里面,在 gptel-menu 中快捷切换。我这边就默认留空了。
(gptel-directives
'((default . "")))
设置自动移动光标。如果你在gptel的buffer中对话,默认情况下,模型的输出插入完毕后光标会停留在你发送的位置。通过以下设置自动跳转光标
:hook
(gptel-post-response-functions . gptel-end-of-response)
设置模型。首先在windows上,需要专门配置一下 curl
:config
(when (eq system-type 'windows-nt)
(setq gptel-curl-extra-args '("--ssl-no-revoke")))
开始配置deepseek模型。这里配置默认使用 deepseek-v4-pro 模型。记得将 YOUR-DEEPSEEK-KEY 替换为你自己的api密钥。
(setq gptel-model "deepseek-v4-pro"
gptel-backend (gptel-make-openai "DeepSeek"
:host "api.deepseek.com"
:endpoint "/chat/completions"
:stream t
:key "YOUR-DEEPSEEK-KEY"
:models '("deepseek-v4-flash" "deepseek-v4-pro")))
接下来配置一些预设,可以等会通过 :preset 启用。
这里设置了两个预设, max 用于配置思考深度为max(默认high),而 no-r 用于关闭flash模型的思考。
(gptel-make-preset 'max
:request-params '(:reasoning_effort "max"))
(gptel-make-preset 'no-r
:request-params '(:thinking '(:type "disabled"))))
4. ob-gptel
配置 ob-gptel 。由于该包还未上melpa,我们只能从对方的repo手动下载了。下面的代码适用于30之后的emacs,我设置了该包在 org 加载完成后立即载入。
(use-package ob-gptel :after org :demand t
:vc (:url "git@github.com:jwiegley/ob-gptel"
:rev :newest)
把 gptel 加入 org-babel 的可执行语言中:
:config
(add-to-list 'org-babel-load-languages '(gptel . t))
修复一个小问题:当光标在 end_src 的 c 之后时,按 C-c C-c 执行代码的时候,会将本代码块重复纳入对话上下文中。解决方法也很简单,直接在寻找session函数前执行一下跳至行首即可。
(defun my/ob-gptel-exclude-current-block (orig-fun &rest args)
"Adjust point so that the current block is not included in session history."
(save-excursion
(beginning-of-line)
(apply orig-fun args)))
(advice-add 'ob-gptel-find-session :around #'my/ob-gptel-exclude-current-block))
5. 使用例
请参考ob-gptel的README。这里举例最基础用法:
基本查询
#+begin_src gptel
What is the capital of France?
#+end_src
#+RESULTS:
The capital of France is Paris.
切换模型
#+begin_src gptel :model deepseek-v4-flash
Write a haiku about Emacs.
#+end_src
:session 参数会收集所有共享相同会话名称的前置代码块。这里启用了 :dry-run yes ,让我们可以先不把内容发送给LLM,而是在 RESULTS 中提前预览一下
#+begin_src gptel :session my-chat
Tell me about Emacs.
#+end_src
#+RESULTS:
假设这是第一个输入的模型回复
#+begin_src gptel :session my-chat :dry-run yes
What about its history?
#+end_src
(:model "deepseek-v4-pro" :messages
[(:role "user" :content "Tell me about Emacs.")
(:role "assistant" :content "假设这是第一个输入的模型回复")
(:role "user" :content "What about its history?")]
:stream :json-false :temperature 1.0)
如果你已配置了 gptel 预设,可以直接使用它们:
#+begin_src gptel :preset no-r
Explain monads simply.
#+end_src