使用 gptel-request 写的一个翻译函数

gptel-translate

最近用 LLM 挺多的,尝试用英文写 prompt,但有时我不知道怎么写(英文菜)就想着翻译一下,当然翻译的工具很多。

这个函数只是分享一下 gptel-request 的一种实现。(本来是想玩玩 gptel-tool,但还没玩明白)

逻辑都是 LLM 写的,大体的逻辑是:

  1. 找到当前选中区域,或者基于当前 point 获取对应的行/段落,作为要翻译的内容
  2. 绑定一些本地变量,如 model,backend,因为翻译只需要一个快速便宜的模型
  3. 调用 gptel-request 发起请求,得到响应后覆盖第 1 步选中的内容
(defun spike-leung/translate-region-to-english ()
  "Translate the selected region to English using gptel and replace it in the buffer.
If no region is active, try to guess the sentence or paragraph at point."
  (interactive)
  (let* ((has-region (use-region-p))
         (bounds
          (cond
           (has-region
            (cons (region-beginning) (region-end)))
           ((use-region-p)
            (cons (region-beginning) (region-end)))
           ((and (fboundp 'bounds-of-thing-at-point))
            (or (bounds-of-thing-at-point 'sentence)
                (bounds-of-thing-at-point 'paragraph)
                (bounds-of-thing-at-point 'line)
                (cons (point) (point))))
           (t (cons (point) (point)))))
         (start (car bounds))
         (end (cdr bounds))
         (text (buffer-substring-no-properties start end))
         (model 'openai/gpt-4.1-nano)
         (backend (gptel-make-openai "OpenRouter"
                    :host "openrouter.ai"
                    :endpoint "/api/v1/chat/completions"
                    :stream t
                    :key (spike-leung/get-openrouter-api-key)
                    :models spike-leung/openrouter-models)))
    (if (string-blank-p text)
        (user-error "No text to translate")
      (let ((gptel-backend backend)
            (gptel-model model)
            (gptel-use-tools nil)
            (gptel-use-context nil))
        (gptel-request
            (format "Translate the following text to English:

%s" text)
          :callback (lambda (response _)
                      (when response
                        (save-excursion
                          (delete-region start end)
                          (goto-char start)
                          (insert response)))))))))

定义一个方法包装 prompt 和 gptel-request,感觉可以利用 LLM 解决很多重复的小任务。

(顺便水了篇博客 https://taxodium.ink/use-gptel-request-to-translate.html

4 个赞

gptel里有个gptel-rewrite,我是把它绑定到embark 的region action中,然后mark一个region之后embark-act+<绑定的快捷键>就能实现你上面类似的功能了

借贵贴请教一个问题 之前想写一个用gptel proofreading英语的function 想法是根据选中的区域加上proofreading指令发给gptel 之后根据返回的结果来后处理产生smerge block 这里需要callback + 后处理 请教怎么做比较稳定可靠?谢谢

gpt-rewrite 确实本身就很适合这样的工作,它就是做重写的。

但我有几个点不太明白:

  1. gpt-rewrite 是否可以单独设置模型?因为有时我我可能会切换一个好一些的模型进行问答,但翻译感觉用比较便宜快速的就足够了。

  2. rewrite 默认应该是不知道你要翻译的,你是如何让它去翻译?每次需要改 rewrite 的 prompt?

  3. 我不太清楚 embark 是否能基于 point 附近的内容直接操作,虽然选择 region 并不麻烦,但我有时我希望直接将 point 下内容翻译,可以免去一点选择 region 的动作。

触及盲点了,proof reading 我还没用到过,我会去看看。

目前我用 gpt-request 有时也会不稳定,取决于模型,函数里也需要做一些错误处理,给出一些反馈。

你的问题看看有没有大佬知道怎么搞👀

  1. 应该可以单独给翻译设置模型,不过我没试过
  2. 自定义一个命令,这个命令调用 gptel-rewrite

我是这样实现的

(defun gptel-translate-to (lang)
  "Translate the selected region or buffer to the specified language.
LANG is the full name of the target language (e.g., `French')."
  (interactive
   (list (completing-read "Target language: " '("English" "French" "Spanish" "German" "Chinese" "Japanese" "Russian" "Italian" "Korean"))))
  (unless (use-region-p)
    (error "You should select a region to perform such action"))
  (let ((prompt (format "Translate the following text to %s. Do not explain your changes." lang)))
    (gptel--suffix-rewrite prompt)))

(transient-define-prefix gptel-translate-to-menu ()
  "Translate text to a specified language."
  [:description "Select target language:"
   ("e" "English" (lambda () (interactive) (gptel-translate-to "English")))
   ("f" "French" (lambda () (interactive) (gptel-translate-to "French")))
   ("s" "Spanish" (lambda () (interactive) (gptel-translate-to "Spanish")))
   ("g" "German" (lambda () (interactive) (gptel-translate-to "German")))
   ("c" "Chinese" (lambda () (interactive) (gptel-translate-to "Chinese")))
   ("j" "Japanese" (lambda () (interactive) (gptel-translate-to "Japanese")))
   ("r" "Russian" (lambda () (interactive) (gptel-translate-to "Russian")))
   ("i" "Italian" (lambda () (interactive) (gptel-translate-to "Italian")))
   ("k" "Korean" (lambda () (interactive) (gptel-translate-to "Korean")))])

(map! "M-p t" #'gptel-translate-to-menu)
1 个赞

这个是不是有点像 gptel-rewrite 这个 inline-diff 的例子

1 个赞

如果在写文档的时候遇到一些不会用英语表达的情况,我一般用 gptel-menu(就是 gptel 自己糊的一个 transient) 然后选 m,直接从 minibuffer 输入中文,返回结果后直接插入到当前光标下。

一个类似的例子是在 vterm 里面写一些复杂命令,不过 vterm 是只读的,糊了个 tool 让它返回后弹出 y-or-n-p,显示并询问是否插入命令到 vterm。工作流大概是:gptel-menu,按 me,然后输入要求,等待弹出是否插入命令的提示就好。

感觉 gptel-menu 还蛮好用的,已经常驻到 <f5> 了,因为 gptel-directives 能接受函数作为 prompt ,还糊了个动态检测环境的 prompt :rofl:

1 个赞

这个不错,很简洁。

美中不足的是 gptel-rewrite 要求要先选一下 region 才行 :smiling_face_with_tear:

也是个不错的方法 :grinning: