NeovimでLLMを使えるプラグインgen.nvimの使い方
NeovimでAIの力を享受したいと思い、gen.nvimを使ってみました。
gen.nvimとは
gen.nvimは、ローカルでLLMを動かせるOllamaをNeovimで使えるようにするプラグインです。
導入方法
プラグインの他、curl
とOllama、Ollamaで扱うモデルが必要です。
Ollamaのインストール
まずはOllamaをインストールします。OSによって手順は違うため、READMEを確認しましょう。
macOS、Windowsであれば公式サイトからダウンロード、Linuxであれば次のコマンドを実行します。
curl -fsSL https://ollama.com/install.sh | sh
インストール中、次のようにログが表示されました。
>>> Downloading ollama...######################################################################## 100.0%##O=# #>>> Installing ollama to /usr/local/bin...[sudo] password for user:>>> Creating ollama user...>>> Adding ollama user to render group...>>> Adding ollama user to video group...>>> Adding current user to ollama group...>>> Creating ollama systemd service...>>> Enabling and starting ollama service...Created symlink /etc/systemd/system/default.target.wants/ollama.service → /etc/systemd/system/ollama.service.>>> Nvidia GPU detected.>>> The Ollama API is now available at 127.0.0.1:11434.>>> Install complete. Run "ollama" from the command line.
ちなみに筆者の環境はWSLです。上記にNvidia GPU detected.
とあるようにGPUはOllama側が勝手に見つけてくれるようです。
WSL特有の困りごとはありませんでした。
モデルのダウンロード
モデルを選んでダウンロードします。
モデルの選択
今回は「よーしパパ、Ollama で Llama-3-ELYZA-JP-8B 動かしちゃうぞー」 - Qiitaを参考に、日本語のモデルLlama-3-ELYZA-JP-8B-GGUFを用意しました。
ダウンロードリンクの取得
まずGGUFファイルのを開きCopy download link
を押し、ダウンロードリンクをコピーします。

ダウンロードの実行
次のコマンドで、コピーしたリンクからGGUFファイルをダウンロードします。
curl -L -o ~/models/Llama-3-ELYZA-JP-8B-q4_k_m.gguf https://huggingface.co/elyza/Llama-3-ELYZA-JP-8B-GGUF/resolve/main/Llama-3-ELYZA-JP-8B-q4_k_m.gguf
ファイル名やURLは適宜書き換えてください。
Modelfileの用意
Ollamaで使えるようにするための「Modelfileファイル」を用意します。
ファイル名は各自わかりやすい名前にしておきます。筆者は~/models/elyza-model
に配置しました。
前述の「よーしパパ、Ollama で Llama-3-ELYZA-JP-8B 動かしちゃうぞー」を参考に書きます。
FROM ./Llama-3-ELYZA-JP-8B-q4_k_m.ggufTEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
{{ .Response }}<|eot_id|>"""PARAMETER stop "<|start_header_id|>"PARAMETER stop "<|end_header_id|>"PARAMETER stop "<|eot_id|>"PARAMETER stop "<|reserved_special_token"
1行目のFROMの後./Llama-3-ELYZA-JP-8B-q4_k_m.gguf
がダウンロードしたGGUFファイルの名前です。
モデルの作成
オプション-f
にさきほど作成した「Modelfileファイル」の場所を書きます。
ollama create elyza:jp8b -f elyza-model
elyza:jp8b
の部分が、今回のモデルにつける名前です。後で使います。
実行すると次のようなログが出ます。
transferring model data省略writing manifestsuccess
作成に成功したようです。
モデルの実行
モデルの名前を指定して実行します。
ollama run elyza:jp8b
プロンプト>>>
が表示されるまで少し時間がかかります。
適当に質問して確認してみましょう。

終了するにはCtrl+dか/bye
を入力します。
プラグインのインストール
ようやくgen.nvimのインストールです。各自のプラグインマネージャーに合わせて記述します。
{ "David-Kunz/gen.nvim" },
筆者は次のように設定しました。デフォルト値以外の行をハイライトしています。
require("gen").setup({ model = "elyza:jp8b", -- The default model to use. quit_map = "q", -- set keymap for close the response window retry_map = "<c-r>", -- set keymap to re-send the current prompt accept_map = "<c-cr>", -- set keymap to replace the previous selection with the last result host = "localhost", -- The host running the Ollama service. port = "11434", -- The port on which the Ollama service is listening. display_mode = "split", -- The display mode. Can be "float" or "split" or "horizontal-split". show_prompt = true, -- Shows the prompt submitted to Ollama. show_model = false, -- Displays which model you are using at the beginning of your chat session. no_auto_close = false, -- Never closes the window automatically. hidden = false, -- Hide the generation window (if true, will implicitly set `prompt.replace = true`) init = function(options) pcall(io.popen, "ollama serve > /dev/null 2>&1 &") end, -- Function to initialize Ollama command = function(options) local body = { model = options.model, stream = true } return "curl --silent --no-buffer -X POST http://" .. options.host .. ":" .. options.port .. "/api/chat -d $body" end, -- The command for the Ollama service. You can use placeholders $prompt, $model and $body (shellescaped). -- This can also be a command string. -- The executed command must return a JSON object with { response, context } -- (context property is optional). -- list_models = '<omitted lua function>', -- Retrieves a list of model names debug = false, -- Prints errors and the command which is run.})
model
に作成したモデルの名前elyza:jp8b
を指定しました。
表示はsplit
を指定しました。プロンプトの表示させています。
gen.nvimを動かす
:Gen Foo
のようにコマンドで実行します。
たとえば、開いているバッファのコードをレビューしたいならReview_Code
です。
:Gen Review_Code
Neovimを立ち上げてから1回目の:Gen Foo
の実行では、起動のために少々時間がかかる点に注意です。
プロンプトの種類
用意されているプロンプトはgen.nvim/lua/gen/prompts.luaから確認できます。
よく使いそうなものを表にまとめました。
名前 | 内容 | バッファの書き換え |
---|---|---|
Generate | 入力をそのままモデルに聞く | あり |
Chat | 入力をそのままモデルに聞く | なし |
Ask | バッファの内容に関して質問する | なし |
Summarize | バッファの内容を要約 | なし |
Review_Code | バッファの内容をレビューさせる | なし |
Change_Code | バッファの内容を入力に合わせて書き換える | あり |
Make_List | バッファの内容をMarkdownのリストにする | あり |
Make_Table | バッファの内容をMarkdownのテーブルにする | あり |
バッファの内容に関係するなら:Gen Ask
、関係ないなら:Gen Chat
といった具合です。
自分用のプロンプトを設定する
プロンプトは自由に変更可能です。
たとえば、次のようにプロンプトを日本語化できます。
require("gen").prompts["Summarize"] = { prompt = "以下を要約してください:\n$text", replace = true,}require("gen").prompts["Ask"] = { prompt = "以下の文章について、$input:\n$text" }require("gen").prompts["Make_List"] = { prompt = "文章をマークダウンリストに変換してください:\n$text", replace = true,}require("gen").prompts["Make_Table"] = { prompt = "文章をマークダウンテーブルに変換してください:\n$text", replace = true,}require("gen").prompts["Review_Code"] = { prompt = "以下のコードをレビューし、簡潔な提案を行ってください:\n```$filetype\n$text\n```",}require("gen").prompts["Enhance_Code"] = { prompt = "以下のコードを改良し、結果は```$filetype\n...\n```という形式で出力してください:\n```$filetype\n$text\n```", replace = true, extract = "```$filetype\n(.-)```",}require("gen").prompts["Change_Code"] = { prompt = "以下のコードについて、$input、結果は```$filetype\n...\n```という形式で出力してください:\n```$filetype\n$text\n```", replace = true, extract = "```$filetype\n(.-)```",}
「プロンプトを日本語化したほうがいいのか、しない方がいいのか」についてはよく分かっていません。LLM周りで詳しい人いたら教えてください。
次のように独自のプロンプトも追加できます。
require("gen").prompts["Tailwind"] = { prompt = "$input をTailwindCSSで実現する方法を教えていただけないでしょうか?",}
あくまでもモデルの学習の範囲内で答えが返ってきます。
textlintの設定
gen.nvimで開かれるバッファのfiletypeはMarkdownです。textlintを有効化していると不要な実行・通知が出てきてしまいます。
そこで、LSP側でバッファの名前がgen.nvim
だった場合に無効化する設定を書きます。
たとえばnone-ls.nvimなら次のように設定します。
null_ls.setup({ -- ... should_attach = function(bufnr) return not vim.api.nvim_buf_get_name(bufnr):match("gen%.nvim") end,})
以上、gen.nvimの使い方でした。