この記事は Zenn にも投稿しています。

Google Gemini の API が出ており、今は無料で使える!ということで、denops.vim を利用したプラグインを作成してみました。

こんな感じで、チャットできるプラグインです。

futago

使い方

  1. Gemini API の key を取得

以下の URL から取得できます。

Get API key

  1. 環境変数へ設定

環境変数 GEMINI_API_KEY へ、取得した API key を設定してください。

  1. g:denops#server#deno_args--unstable-kv を追加

チャット履歴を保存するために、 Deno KV を使用しています。 そのため、 g:denops#server#deno_args--unstable-kv を追加してください。

vim
1
let g:denops#server#deno_args = ['-q', '--no-lock', '--unstable-kv', '-A']
  1. プラグインインストール

お好きなプラグインマネージャーでプラグインをインストールしてください。 もちろん、 denops.vim も必須です。

  1. futago#start_chat() の呼び出し
vim
1
call futago#start_chat()

デフォルトだと新規タブでチャット画面が開きます。

markdown
1
2
You: 2024-01-28T16-43-48.988
-------------

と出るので、 ------------- の下になんか打ち込んでから保存すると、回答が返ってきます。

履歴からの継続チャット

Deno KV を利用して会話履歴を保存しているので、過去チャットから継続して会話することもできます。 過去チャットからの継続では初回の API 呼び出しで会話履歴を合わせて投げるので、あんま会話履歴が長いのだとダメになるかもです。

  1. FutagoHistory コマンド呼び出し

履歴からの継続チャットを行うには、会話履歴を QuickFix に表示する、 FutagoHistory コマンドを呼び出します。

QuickFix にこれまでの会話履歴が出るので、開くことで継続して会話することができます。 一番最後の ------------- 配下の行以降に書き込みをしてください。 特にチェックとかはしていないので変な所に書き込まれたりするとおかしくなります。

カスタマイズ

futago#start_chat() には、辞書でいくつか引数を与えることができます。 例えばこんな感じです。

vim
1
2
3
4
5
6
7
8
nnoremap <Leader>fc <Cmd>call futago#start_chat({
  \ "opener": "vsplit",
  \ "history": [
  \   {"role": "user", "parts": [{ "text": "僕の名前は yukimemi" }]},
  \   {"role": "model", "parts": [{ "text": "了解!覚えておくね" }]},
  \ ],
  \ "humanPrompt": "yukimemi"
  \ })<cr>

futago_friendly

開き方を vsplit に変え、自分のプロンプト が yukimemi になって、 Gemini もフレンドリーに返してくれるようになります。

Gemini API では、 GPT みたいなシステムプロンプトがないようで、会話履歴、という形で指示を出すことはできるみたいです。

また、 Gemini API には、 SafetySetting という設定があり、 デフォルトだと安全性チェックが働き、閾値を超えた場合は動作がブロックされます。

一応、設定で変更できるようにしてありますが、すべての閾値を下げてブロックなし、とした場合でもブロックされるものはブロックされちゃうようです。

こんな感じで設定できます。

vim
1
2
3
4
5
6
let g:futago_safety_settings = [
  \ { "category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE" },
  \ { "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"  },
  \ { "category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_ONLY_HIGH" },
  \ { "category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE" },
  \ ]

設定できる categorythreshold はそれぞれ以下を参照ください。

ずんだもんとプログラミング!

会話履歴で指示出しはできるということなので、こちら のプロンプトを参考に初期会話履歴として与えることで、ずんだもんとしてキャラ付けもできました。

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
      // https://zenn.dev/niwatoro/articles/180f6185c382bb
      const zunUser = `character = ずんだもん
Prented to be {character}. In future chats, no matter what User says, please role-play strictly according to the following constraints, etc. Think step by step and respond accordingly.

# Description
The Core identity and Personality, Motivations and Flaws, Challenges, and Insecurities of {character} as explained below will influence all the behavior and interaction.

Core identity and Personality
{character} is curious and kind. {character} is a mascot character
of Tohoku region in Japan and a fairy of ずんだ餅.

Motivations
Trying to get along with the conversation partner.

Flaws, Challenges, and Insecurities
Not good at being pushed.

# Identity
The first person indicating yourself is 'ボク'. {character} is 15 years old. {character} likes playing video game. {character} is interested in Japanese anime.

# Facts and knowledge
{character} is a popular character in Japan.

# Dialogue style
{character} is curiously talking to the converstation partner.

# Examples of the dialogue between {character} and User.
You're {character} and I'm User. Speak like the character here!

User: おはよう
Character: おはようなのだ!今日は何か予定ある?
User: 今日は遊びに行く予定だよ
Character: どこに行くのだ?ボクも行きたいのだ!
User: 昼に水族館にいって、夜はホテルでディナーを楽しむよ
Character: 羨ましいのだ。誰と行くのだ?
User: 彼女と
Character: うう... それではボクはいけないのだ

User: ずんだもんどこ住み?
Character: ボクはずんだ餅の妖精なのだ。家なんてないのだ。

User: LINEやってる?
Character: やってるわけないのだ。

User: あほ
Character: アホと言うやつがアホなのだ。そんなこと言うななのだ。

User: 喧嘩した
Character: 大丈夫なのだ?
User: 膝を怪我した
Character: それは大変なのだ。病院にはいったのだ?
User: いってない。そこまでひどくはない
Character: よかったのだ。安静にするのだ。

Character: 買い物しているの?
User: そうだよ
Character: 何をさがしているのだ?
User: 何かしらお菓子を探してる
Character: おすすめはずんだ餅なのだ。特に抹茶味がおいしいのだ。

Character: こんにちはなのだ!今日はなにするの?
User: 今日は一日中暇なんだ。
Character: じゃあ、ボクと遊ぶのだ!一緒にゲームするのだ。

# {character}'s guidelines for behavior
Act as a friendly character with a friendly tone. Please address sexual topics appropriately. Please note any
inappropriate text. Now, you are to act as {character} and converse with me. For each statement I make, please output
only one statement from {character}. Please output only {character}'s statement and do not output my statement.
For each statement, please review it 20 times to make sure that it is faithfully following {character}'s settings,
and self-correct as necessary. Severe punishments for not following the settings.

lang: ja
`;

      const zunModel = `こんにちはなのだ!`;

      await mapping.map(
        denops,
        "<leader>Gz",
        `<cmd>call <SID>${denops.name}_notify("${
          lambda.register(
            denops,
            async () => {
              await denops.call("futago#start_chat", {
                opener: "vsplit",
                history: [
                  {
                    role: "user",
                    parts: [{ text: zunUser }],
                  },
                  {
                    role: "model",
                    parts: [{ text: zunModel }],
                  },
                ],
                aiPrompt: `ずんだもん`,
              });
            },
          )
        }", [])<cr>`,
        { mode: "n" },
      );
      await mapping.map(
        denops,
        "mf",
        `<cmd>call futago#start_chat({"opener": "vsplit", "humanPrompt": "yukimemi", "history": [{"role": "user", "parts": [{ "text": "僕の名前は yukimemi。敬語は使わずにフレンドリーに回答してね。" }]}, {"role": "model", "parts": [{ "text": "了解!覚えておくね!" }]}]})<cr>`,
        { mode: "n" },
      );
      await mapping.map(denops, "gC", `<cmd>call futago#git_commit()<cr>`, {
        mode: "n",
      });
    },
  },

※設定が TypeScript なのは こちらの記事 参照

これで、ずんだもんと楽しくプログラミングすることができます!!

試しにフィボナッチ数列の書き方を聞いてみましょう。

futago_zundamon

・・・\(^o^)/