Skip to main content

LLM with Vim

这是一个简单有用的LLM Restful API访问引擎。

最初的时候,我主要用它实现从vim对ollama进行访问的,所以叫做llm_with_vim。后来 慢慢增加其他LLM API的支持,又增加vscode和micro的插件。2.0版本重构引入了新的 插件化引擎架构(我们称之为ContextForge架构,见 架构设计),但项目名保持为 llm-with-vim。本质上,它是一个可以让你在编辑器中不断编辑和LLM的交互历史的另类 “智能体”(Agent)。

也许很多人都知道,但也许也有很多人不知道:现在的大语言模型都是没有记忆的。它完 成训练以后,内容就固定了,你给它说什么,它就基于你说的话决定返回什么。

所以我们一般理解的和LLM的“对话”,其实是每次都把所有的对话都过程都告诉LLM,LLM都 当作是“一次对话”。这样给了我们一个玩技巧的空间:你不需要为你原来说过的话负责, 你随时可以修改,你也不需要接受LLM说过的话,你也随时可以修改。

各种所谓“提示词工程”,本质都是控制这个输入给LLM的“对话”是什么,控制了这个对话的 上下文,就控制了LLM的输出。

比如说你让AI给你写一份简历,AI写完以后你基本上都满意,但你希望你换一个名字,并 且调整一下你的工作经历。你反复给它说明白这个问题其实不容易,还不如你直接编辑它 的回答,让它把这个当作它自己说的,之后你直接提其他更复杂的修改要求。你如果对它 整个回答都不满意,你也可以删掉他这个回答,然后在问题上增加你的额外要求。这样整 个沟通效率就会高得多。

这种方法最大程度扩展了你对AI的控制能力。现在各种智能体,本质都是软件在你输入以 后,帮你按某种方式编排这个“上下文”,而我们这个工具反过来,我们把这个上下文整个 呈现在编辑器里,让你自己看到整个上下文,决定怎么安排你和LLM的“共同历史”,完全按 你的要求提交给LLM。

使用截图

当然,因为这个工具的目的是人为接入交互过程,所以它也没有一般AI编程智能体那种自 动循环的能力,它最多支持LLM那边要求工具调用的时候自动回复,其他时候,LLM回复以 后,我们就需要用户的介入,你要自己决定下一步把什么上下文提交给它。

安装

最简单的方式是从 PyPI 安装(支持 pip / uv):

pip install llm-with-vim          # 或 uv tool install llm-with-vim
llm-with-vim-setup                # 检测并安装 Vim/VSCode/micro 插件

也可以只装部分编辑器插件:

llm-with-vim-setup --vim          # 仅 Vim 插件(--vscode / --micro 同理)

从源码安装或了解可编辑安装、uv 等方式,参考安装指南。

安装后 vim 插件未激活的排查

安装成功(vimfiles/plugin/llm.vim 确实存在)但打开 .llm 文件没有高亮和热键时, 在 vim 里按以下顺序检查,可以快速定位断在哪一环(Windows 用户尤其容易遇到)。

第一步,二分定位——插件到底加载没有:

:LLMHelp

这是插件加载时注册的全局命令,不依赖文件类型检测。能弹出帮助说明插件已加载, 问题在文件类型识别(跳到第三步);提示“不是编辑器命令”说明脚本根本没被 加载(继续第二步)。

第二步,确认脚本在 runtimepath 上(Windows 上最常见的两类原因):

:scriptnames
:echo expand('~')
:set runtimepath?

scriptnames 输出里应能看到 <你的家目录>\vimfiles\plugin\llm.vim。

特别地:如果 llm.vim 在 scriptnames 列表里,但 :LLMHelp 却不存在——说明脚本 加载到一半出错中断了(命令定义在文件末尾),执行 :messages 查看报错,常见于 编码问题(见本节末尾)或 vim 版本过老缺少某功能。

llm.vim 不在 scriptnames 里时:

  • HOME 不一致:安装脚本写入的是 %USERPROFILE%\vimfiles,而 vim 的 ~ 解析到了别处 (比如环境变量 HOME 指向网络盘)。对比 expand('~') 与安装输出路径,把 vimfiles 拷到 vim 实际的家目录即可。(新版 install.py 已按 vim 的规则解析家目录, 并会主动询问 vim 确认,两处不一致时两处都装——重跑安装脚本通常可直接修复。)
  • 运行的 vim 和安装目标不是同一个:Neovim 不读 ~/vimfiles(Windows 上配置在 ~/AppData/Local/nvim);WSL 里的 vim 读的是 Linux 家目录;微软商店版 vim 的 路径也有差异。

第三步,确认 vimrc 和文件类型检测:

:echo $MYVIMRC
:set filetype?
:filetype
  • $MYVIMRC 为空说明你以为在改的 vimrc 根本没被读取。Windows 上 vimrc 放在家 目录,名字 _vimrc 或 .vimrc 都行;注意记事本的“隐藏已知文件扩展名”可能 让它实际变成 _vimrc.txt。
  • vimrc 里需要 filetype plugin on(它同时开启检测和插件加载,本插件的 ftdetect 钩子就是在这一步生效的)。编辑 .llm 文件时 set filetype? 应显示 llm。
  • 如果你的 vimfiles 里有自定义的 filetype.vim,它会整体替换内置版本,需要自行 保证 ftdetect 目录仍被加载。

最后看一眼加载期报错::messages。中文 Windows 上若 vim 默认 cp936 编码,脚本里 的 UTF-8 中文注释偶发解析问题,可在 vimrc 开头加 set encoding=utf-8。

用法

这里主要介绍原理。

我们的应用的主体是一个python程序,命令名为 llm-with-vim(旧名 llm_engine / llm-engine 仍可用,是同一个程序的过渡别名)。如果你本地已经安装了ollama,简 单的用法是这样:

echo "User: hello" | llm-with-vim

或者

llm-with-vim < chat.llm

这个程序的使用方式就是把你输入的对话提交给LLM,然后把对话本身,以及 LLM的回答,一起返回给你。

当然,这种用法并不方便,我们设计的使用方式是通过插件来调用这个命令,这样,你一 边编辑一个文本文件,一边就可以提交请求给LLM,然后基于回答继续编辑这个上下文,再 做下一次提交。这样你就会成为你和LLM这段因果的时空控制者,你不是和它不断创建“未 来”,而是反复改变“过去”和“当下”。

下面我们主要用vim为例来介绍这个程序的用法,vscode和micro的对应用法可以参考 vscode/README.md,和 micro/llm_micro/help/llm-micro.md。

安装vim插件后,vim会把.llm后缀名的文件特殊对待(特有的语法高亮,特有的热 键,特有的缩写等等)。

.llm就是个普通文本文件,里面的内容要求是这样的:

Configure:  {'engine_type': 'ollama', 'server_addr': 'default',
             'model': 'qwen2.5:0.5b', 'keep_alive': 36,
             'max_tokens': 512, 'temperature': 1}
System: 你是一个中学数学老师,协助我解决一些中学数学问题。
@// 这是临时注释,提交时会被忽略;在替换模式下会被清理
@## 这是长久注释,不提交给AI,但会保留在文件中
User: 解如下方程:2x^2 + 3x -10=0。
不需要过程解释。
Assistant: ...
User: 有没有其他解法?
....

llm文件中以“xxx: ”开头的行称为一个“角色发言”。当前实现也兼容 System:、 User:、Assistant:、Tool:、Configure: 后面不带空格,以及使用全角冒号 : 的写法。角色发言后的每一行,在遇到新的角色发言前,都是这个角色发言的一部分。

除了User的发言,其他部分都是可选的。当前实现要求提交内容中至少有一条 User: 消 息;没有角色前缀的普通文本会被视为无效上下文并给出模板提示。

下面是支持的角色:

  • User: 这表示你的发言

  • System:这通常用于给整个对话定调,它的地位比其他角色的地位稍高,通常LLM会对这 个发言偏袒一些,在上下文过长的时候也会优先保护它。但它也不是绝对控制所有发言 的。

  • Assistant:这是LLM的发言

  • Configure:这是给Agent(也就是llm_engine)说的话,就是用来配置用的,通常你需 要这个选择引擎,配置服务器地址,选择工具等等。

给LLM发送的上下文从文件开头算起,一直到你发送的位置为止,后面的内容自动忽略。发 送方法通常是在最后一个User发言后输入jj,或者按<F1>,这整个上下文都会发给LLM, 并且把它的回答补充到User后面,而原来在User发言后面的内容都不会改变。

所以,如果你临时不想发什么东西出去,你可以暂时放在别的文件中,或者直接留在本文 件的最后。

这不改变这仍是一个文本文件,你可以用所有你喜欢的vim编辑功能来编辑这个文件。你可 以继续决定向下开辟你和LLM的未来,也可以编辑你和它交往的过往。每段这样的历史都可 以独立存盘,独立重新使用,可以用git进行版本管理。

如果在输出的过程中需要中止请求,可以按K键(原K键功能被禁止了)。注意这里不要用 Ctrl-C,Ctrl-C仍是打断当前vim操作的功能,这不会中止请求任务。

<F2>映射到outline功能,可以显示一个新窗口给出所有User的位置,你可以通过这个来 定位到特定的问题,只要移动到特定的行然后回车就可以跳转到那个问答上。

完整的命令列表可以通过:help llm查看。

如果一时忘了快捷键,可以在 Vim 里执行 :LLMHelp 或按 <F4> 查看快速帮助。

如果你在micro中使用,对应快捷键是:F1 提交、Shift+K 中断、F2 大纲、F3 图片预览。

扩展模式和替换模式

llm_engine默认工作在替换模式,就是你输入的上下文,它会做一些过滤会产生一些调试 日志,替换你原来的上下文。这是llm_engine的完整功能。但这种功能在部分编辑器上面 上下文变得很长的时候会影响速度,因为可能要一下提供数千甚至上万行的文本。所以我 们现在也提供--append模式,加上这个参数,llm_engine不过滤输入,而是直接输出LLM的 响应。

我们现在在vim和vscode上默认使用扩展模式,以提高显示效率,你仍可以使用全功能的替 换模式,在vim上使用JJ输入或者F5提交请求,就是对应的模式。其他插件可以参考其他参 加的帮助。

引用图片和调用工具

如果LLM支持(即模型具备视觉能力),对话中可以引用本地图片和调用工具。其中引用 本地图片的方法是在上下文中增加“$$图片本地路径$$”这样的描述,代理会把图片附加 到这个角色的发言中。细节:

  • 语法仅在 User 角色的发言中生效,其他角色中出现同样的记号不会被解释
  • 相对路径基于 llm 文件所在目录(由编辑器插件自动传入)解析
  • 提交时引用处会替换为“[IMAGE:路径]”占位符一并发给 LLM;如果文件不存在, 占位符变为“[IMAGE_NOT_FOUND:路径]”,从提交结果中可以直接看出来

工具是个全局的概念,这可以在Configure角色中告知LLM有那些工具可用。像这样:

Configure: {'model': 'qwen3:1.7b', 'tools': ['read', 'bash', 'search']}
User: 请读取 /etc/hostname 并告诉我这台机器的名称

当前可用工具:

工具名 功能
read 读取文件内容
write 写入/创建文件
edit 精确字符串替换编辑文件
bash 执行 bash 命令
search grep 正则搜索文件内容
list_dir 列出目录内容
python 执行 Python 代码片段
web_fetch 获取网页文本内容
view_image 查看图片

每个工具是 llm_engine/plugins/tools/ 下的一个插件文件,由 llm_engine/plugins/plugins.json 清单选择加载;用户也可在 ~/.config/llm-with-vim/plugins.json 清单中登记自己的工具插件或排除内置工具 (详见 docs/plugin_architecture_design.md)。通过max_tool_rounds(默认10)可以 控制单次对话中工具调用的最大轮数。

在vim插件中,allTools缩写会展开成所有工具的配置,可以用这种方法建立工具的基 础配置模板。

使用思维链

思维链可以优化模型的输出,这需要模型支持。如果你使用的是ollama,通过ollama show 命令可以查看模型是否有thinking这个Capability。如果有,在llm_with_vim的 配置中可以用'thinking': True激活。

思维过程会以一次性注释的形式输出,下次提交的时候会被全部忽略并且丢弃,所以如果 你需要保留,就把它拷贝出提交范围。

Configure

User/System/Assistant都是LLM或者人说的话,所以是自然语言的。而Configure是给Agent 说的话,Agent就是我们的传统电脑,所以必须是“结构化”的。这里支持 JSON 或 Python 字典格式(内部会按结构解析) 的定义。

通常你不用学,一开始可以完全不写,让llm_engine自动给你生成一个,然后修改就行。 我们的插件也通过vim的缩写(:ab[breviate])提供了一组模板,比如:

  • configO 本地ollama配置模板
  • configAli Ali云配置模板
  • configHj 海鲸配置模板
  • configZp 智谱配置模板

本地之外的模型通常需要配置apikey,模板中通常只有一个占位符,需要自己修改成你自 己的API key。如果你不想每个llm文件都出现这个信息,也可以写到用户目录配置中: ~/.config/llm-with-vim/config.json(新路径,推荐)。 当前仍兼容旧路径 ~/.config/llm_engine/llm_engine.json:首次运行时会自动把 旧配置迁移到新路径并在输出中提示(原文件保留,确认无误后可删除)。

引擎名、服务器地址和模型名都支持别名,比如 server_addr 写 'default' 会解析为 localhost,model 写 'qwen' 会解析为 'qwen2.5:0.5b'。可用别名定义在各引擎的 provider 描述文件中(见下文“自定义引擎”),你也可以在描述文件里增加自己的 别名。

其他辅助语法

llm文件还提供一些辅助语法,以便更容易维护代码逻辑。这包括:

  • 头文件:llm支持类似C语言的头文件机制,可以通过@@inc前缀说明其他包含文件。具体 语法也很简单,在某行顶格写"@@inc xxx.h"就可以了,xxx.h中的内容会无条件包含到所 处的位置。具体细节参考@@inc原语设计思路。

  • 临时注释:@// 开头的语句被看作临时注释,提交请求给llm_engine时会被忽略,不会进 入LLM上下文。是否会在文件中“自动消失”取决于提交模式:替换模式会清理,追加模式会 保留原文本(但仍不会提交给LLM)。

  • 长久注释:@## 开头的语句被看作长久注释,这种语句也会被llm_engine忽略。这个地方 有个风险是:如果AI返回的语句中也有这样的行,也会被忽略。

  • 引用图片:语法为 $$图片路径$$,仅在 User 角色发言中生效,详见前文 “引用图片和调用工具”一节。

这些辅助语法最大的风险是如果角色也有一样的输出,可能导致llm_engine误解释,我们 已经尽量使用比较特殊的符号组合了,但不能保证LLM会怎么返回,所以,只能请你自行注 意。

全局配置

除了通过单个llm文件配置单个会话的参数,llm-with-vim也可以通过独立配置文件进行全 局配置(推荐 ~/.config/llm-with-vim/config.json,旧文件 ~/.config/llm_engine/llm_engine.json 会被自动迁移),这些配置分三层:

  • 默认配置:这个内嵌在llm_engine的模块目录中
  • 用户配置:推荐放在$HOME/.config/llm-with-vim/config.json,兼容旧目录 $HOME/.config/llm_engine/
  • 文件配置:这个就是前面Configure角色的定义

后面的配置会覆盖前面的配置,通常来说,全局的配置,比如API Key,可以放到用户配置 上,单个会话专门的配置,则可以配置在单个文件中,至于默认配置,这是工具自身的一 部分,可以拷贝出来当作用户配置的模板。

你也可以用这个部分设置一个默认的引擎和模型,避免每次创建llm都要写配置。

{
  "config": {
    "engine_type": "ollama",
    "server_addr": "localhost",
    "model": "qwen2.5:0.5b",
    "keep_alive": 3600
  }
}

环境变量

  • LLM_ENGINE_CONFIG:指定用户配置文件的完整路径(默认 ~/.config/llm-with-vim/config.json)
  • LLM_ENGINE_DEBUG:等价于 Configure 里写 'debug': True
  • LLM_FILE_DIR:工具和图片的相对路径基准目录,编辑器插件会自动设置为当前 llm 文件所在目录
  • LLM_FILE_ENCODING:重设标准流编码(主要供 Windows 使用,默认 utf-8)

一些技巧

  • 如果你不知道当前环境有什么可用,把Configure的engine_type设置为TELLME再提交 一次,引擎会输出一份环境清单:可用引擎(含用户自建的)、可用工具、当前生效的 三层合并配置。首次输错提交时的模板提示也会指引到这里。想知道某台服务器上具体 有什么模型,再把model设置为TELLME提交,支持查询的引擎(比如ollama)会返回 模型列表。

  • ollama第一次使用某个模型会比较慢,可能因此会超时,可以通过如下Configure参数调 整超时时间(以秒为单位):

    • keep_alive=60。定义模型使用后,在内存中保留多久
    • connect_timeout=30。定义连接超时时间
    • read_timeout=1800。定义请求超时时间
  • 如果你不知道引擎是什么协议,可以尝试用通用的OpenAI协议去尝试一下,只要引擎的 名字和标准模型不一样,就会尝试用OpenAI的协议去访问。如果你不确定你的请求是否 和请求的一致,可以在Configure中加一个"'verbose': True"的参数,这样输出中会包 含每次llm请求的信息,让你看看是否符合期望。

  • 需要看发送给LLM的原始报文时,可以在Configure中加一个"'debug': True"参数(或 设置环境变量LLM_ENGINE_DEBUG),引擎会把每次请求的完整JSON(URL、headers、 消息内容)打印出来;响应制式同样收齐后聚合成一个等价的完整响应对象一次性输出 (分片content拼接、tool_calls重组、保留finish_reason/usage等元数据),不再是 每个流式分片一行。图片base64会截断、auth密钥会脱敏。若只需要看请求不看响应, 可用"'debug': 'request'"减少输出。该输出在vim的追加模式下同样可见,调试完删 掉这些@//注释行即可(它们不会回传给LLM)。

当前支持的引擎

  • Ollama,参考配置:Configure: {'engine_type': 'ollama', 'model': 'qwen2.5:0.5b', 'keep_alive': 3600}

  • DeepSeek ,参考配置:Configure: {'engine_type': 'deepseek', 'model': 'deepseek-chat', 'auth': 'Bearer sk-xxx'}

  • 智谱 AI,参考配置:Configure: {'engine_type': 'zhipu', 'model': 'glm-4.7', 'auth': 'Bearer your-api-key'}

  • 阿里云百炼 ,参考配置:Configure: {'engine_type': 'ali', 'model': 'deepseek-r1', 'auth': 'Bearer sk-xxx'}

  • 海鲸 AI ,参考配置:Configure: {'engine_type': 'haijing', 'model': 'DeepSeek-R1', 'auth': 'Basic xxx'}

  • llama.cpp ,参考配置:Configure: {'engine_type': 'llama', 'server_addr': 'localhost:8080'}

  • 对于任何兼容 OpenAI API 格式的服务(如 vLLM)或者llama-server(这个连模型的 名字都可以随便写),可以使用任意引擎名称,参考配置:Configure: {'engine_type': 'vllm', 'server_addr': 'localhost:8000', 'model': 'Qwen/Qwen2.5-7B-Instruct'}

自定义引擎

每个引擎的行为由一个 JSON 描述文件(provider 描述)驱动,内置描述在 llm_engine/providers/ 目录(ollama、deepseek、zhipu、ali、haijing、llama、 openai_compat)。上条“任意引擎名称”的原理就是:引擎不认识这个名字时,自动回 退到 openai_compat 描述用 OpenAI 协议访问。

你也可以增加自己的引擎:拷贝一份内置描述到 ~/.config/llm-with-vim/providers/ 下修改(服务地址、默认模型、鉴权方式、模型查询地址、别名等都是字段化的),文件 名即引擎名,用户描述会覆盖同名的内置描述。字段含义和设计思路参考 ContextForge架构设计。

已知问题

  • micro在Windows下不支持异步模式,需要提交请求后只能等待全部回答返回后才能看到 显示。这会造成一种“服务器迟迟没有响应”的错觉。如果你是因为不熟悉vim的键位操作 所以用micro,我建议使用evim(vim包里自带),这用起来也是和记事本差不多的。

  • 部分LLM服务平台可能会拒绝非通用AI引擎发的请求,所以通过本应用发送请求,有可能 被这些平台拒绝。

Metadata

Release files for llm-with-vim 2.0.20261111

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for llm-with-vim 2.0.20261111
File Size Uploaded Provenance Actions
llm_with_vim-2.0.20261111.tar.gz 88.2 kB With provenance Details for llm_with_vim-2.0.20261111.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for llm-with-vim 2.0.20261111
File Interpreter ABI Platform Actions
llm_with_vim-2.0.20261111-py3-none-any.whl Python 3 none any Details for llm_with_vim-2.0.20261111-py3-none-any.whl

Total release size: 178.4 kB

Release files / llm_with_vim-2.0.20261111.tar.gz

Download URL llm_with_vim-2.0.20261111.tar.gz
Size 88.2 kB
Tags Source
SHA-256 checksum
How to use checksums
d953d7f59e253af9bb53bd296b41d2b10be2e70d449ef57542e76d5cf9b0ffa4
BLAKE2b-256 checksum
How to use checksums
631149d8e8ba7ad00aa902020440c4de46359316a93e818cc8a02a49fa4ff454
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

Release files / llm_with_vim-2.0.20261111-py3-none-any.whl

Download URL llm_with_vim-2.0.20261111-py3-none-any.whl
Size 90.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d33aeecc38e7dde463ef247f55876d899c48c15a5223a51221a5b1a51260334a
BLAKE2b-256 checksum
How to use checksums
053b72e286cba4040fc802845ccd88e09a5f39e4ad443330eb02196f88232468
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

Security for llm-with-vim 2.0.20261111

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for llm_with_vim-2.0.20261111.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page