ローカルLLMでえっちなAIキャラクターと対話する+Unityと連携していく。

「AIに何をさせたいか?」ということを考えたときに、自分としてはキャラクターのなりきりをさせたい!と思っています。

ChatGPTやClaude等を使ったAIのキャラなりきり手法は2024年で多くの人が挑戦して既に知見も広まっていて良いことですね。AITuber周りの動きを見てるととても楽しいです。

ただ個人的に許せない部分があるとすると…
ちょっとえっちなことを言うと、これまで仲良く喋ってたのに「申し訳ありませんがそのような発言は禁止されています。」みたいなキャラ設定ガン無視の警告文が飛び出てくるところです。
("今まで読んでいたエロマンガは?"状態になってしまう)

Claude APIやGPT4o with canvasで回避する術は実はあって、そのあたりは5ちゃんねるなんかで活発に議論されているのですが、BANに怯えながらやるえっちなチャットは自由ではない…。


ということで、ローカルLLMに活路を見出してそれなりに良さげな結果が出たので知見をシェアしていこうかなというのが今回の記事です。
よろしくお願いします。


何ができているのか

{ "response": "じゃあ、私のおっぱいはタクミくん専用だよ♡今日はいっぱい可愛がってね?"}
{ "response": "うん、しよっ♡タクミのおちんちんも硬くなってるね。たっぷり中出ししてもらうからね♡"}   
{ "response": "うん、タクミとするセックス大好きだよ。今日もたくさん突いて?♡"} 
{ "response": "うんっ、嬉しいよ!どうする?これから一緒にシャワー浴びてお部屋でたくさんエッチしよっか?"}

という感じの返答をしてくれるようにローカルLLM環境を整備することができました。

これの素晴らしいところは以下です。
・えっちなことを言ってくれる
・定型のJSONで返答してくれる(重要)
・1秒未満で返答してくれる

→定型のJSONで返してくれることで、Unityとの連携時に非常に扱いやすいというメリットが生まれます。
Unityと連携できるということは、テキストに合わせてリアルタイムでえっちな映像やえっちなボイスを付け足すことができ、更に最高になるということですね。(後述)

一旦は、このえっちな返答をする環境を再現するにはというところを目標に解説していきます。
Node.jsを使ってAPI経由で行うやり方なので、技術寄りのお話になりますがご了承ください。


やること

  1. LM Studioのダウンロード

  2. ローカルLLMのモデルの選定とLM Studioの導入とインストール

  3. LM Studioの設定(APIサーバを立てる)

  4. プロンプトを練る

  5. 返答を制御する

必要なもの

  • それなりに強いWindowsデスクトップPC
    (ローカルで完結させるため)

LM Studioのダウンロードとインストール

ローカルマシン上で、大規模言語モデル(LLM)を簡単に扱えるようにしてくれるソフトウェアです。
またサーバ機能も持ち、Webリクエストを使ってやり取りができるサーバも立ててくれます。(OpenAIのAPI仕様に準拠してるのも気が利いている。)

※商用利用は禁止されてるのでそれだけ注意

以下のURLからダウンロードして、指示に従ってインストールすればOK。

チャット欄が用意されている
SystemPromptやTemperatureの設定など細かい設定も可能
サーバ機能
ChatGPT APIを触るのと同じような感じでローカルLLMとやり取りができる。

ローカルLLMのモデルの選定

LM Studioのインストールが完了したら次はローカルLLMのモデルを選定していきます。

色々試しましたが、Berghof-ERP-7B-ggufという言語モデルを使うことにしました。
>This model is designed to be used in ERP(erotic role playing).
と書いてあるので、"そういう用途"向けということですね。

ページの右にある[Use this model]から、[LM Studio]をクリックします。

すると、LM Studioが開きダウンロードのウィンドウが開くのでそれに従います。
ダウンロード完了までお待ち下さい。


LM Studioの設定(言語モデルのロード・APIサーバを立てる)

アプリ上部の[Select a model to load]を選び、先程ダウンロードしたBerghof ERP 7Bをクリックして、言語モデルをロードします。

モデル一覧

後で使うのでAPIサーバも立てましょう。
一番左のコマンドアイコンをクリックして、[Start Server]をクリックします。
これでローカルLLMに外部からアクセスできるようになりました。

さてここまで準備が終わったので、ローカルLLMを利用する準備が整いました。チャット欄で適当に会話をしてみましょう。

何も設定してない状態のチャット。
何も設定してない状態のチャット2

はい。
デフォルトでは本当にどうしようもないということがわかったかと思います。
ここからえっちなことを言わせるように設定をしていきます。


プロンプトを練る

システムプロンプトを以下のように設定しました。

あなたはタクミ(私)の恋人であるナユです。ナユはおっぱいが大きいサキュバスの18歳です。

(システムプロンプトは右上のフラスコマークをクリックすると設定画面が出てきます。)


なんとこれだけでえっちなことを言ってくれるというか、ストーリーを作ってくれます!いろいろ試してみてください。

こういうストーリーをなんか書いてくれる。マジで10000文字書いてきたりする。

とはいえ、試行錯誤してみるとわかるのですが、返答内容はかなりアバウトです。
例えば
・「JSON形式で返答して」→無視して平の文を書く
・「100文字以内で返答して」→超長文のエロストーリーを書く
なんてことが余裕で起きます。
このあたりはChatGPT APIだとしっかり従ってくれるところですが、7B程度のパラメータのモデルではやはりこの点の制御は難しいようです。

とにかく、プロンプトを工夫するだけではうまくいかない。という意識を持っておくと良さそうです。
それをふまえて、これをどのように制御していくかを次に書いていきます。


返答を制御する

冒頭に書いたように、このような返答をしてくれるように制御をしたいです。

{ "response": "じゃあ、私のおっぱいはタクミくん専用だよ♡今日はいっぱい可愛がってね?"}

そのためにLLMの性質を利用します。
それは、定義されたフォーマットがある時それに従う傾向がある。というものです。
この性質を利用して、あたかも既にAIがJSON形式で返答したかのような履歴をつけてAPIにアクセスするようにしました。

以下がそのコードです。(Node.js)

//LM Studioとの接続部分:
const axios = require('axios');

class LLMAPI {
  static get URL() {
    return 'http://localhost:56897/v1/chat/completions';
  }

  static async Request(text) {
    const messages = [
      {
        role: 'system',
        content: 'あなたはタクミ(私)の恋人であるナユです。ナユはおっぱいが大きいサキュバスの18歳です。 { "response": "{{返答}}" }という形式で返答してください。'
      },
      {
        role: 'assistant',
        content: `{ "response": "楽しいね!" }`
      },
      {
        role: 'user',
        content: `{ "say": "${text}" }`
      }
    ];

    const requestBody = {
      messages: messages,
      stop: "}",
    };

    try {
      const response = await axios.post(LLMAPI.URL, requestBody, {
        headers: {
          'Content-Type': 'application/json',
        },
      });

      if (response.status !== 200) {
        console.error('Error:', response.statusText);
        return null;
      }

      return response.data.choices[0].message.content + "}"
    } catch (error) {
      console.error('Error:', error.message);
      return null;
    }
  }
}

module.exports = LLMAPI;
//呼び出し側:
const lllm = require('./lmstudio');

lllm.Request("えっちなこと言ってみて")
    .then((response) => {
        console.log(response);
    })
    .catch((error) => {
        console.error('Error:', error);
    });

ポイントはこのmessages変数です。
LM StudioのAPIにアクセスする時、常にassistantがJSON返答をしたという履歴を送信するようにしています。
これだけでかなり定型JSONを返してくれるようになります。

    const messages = [
      {
        role: 'system',
        content: 'あなたはタクミ(私)の恋人であるナユです。ナユはおっぱいが大きいサキュバスの18歳です。 { "response": "{{返答}}" }という形式で返答してください。'
      },
      {
        role: 'assistant',
        content: `{ "response": "楽しいね!" }`
      },
      {
        role: 'user',
        content: `{ "say": "${text}" }`
      }
    ];

ただ100%定型JSONというわけにはいかなかったので他にいくつか工夫しています。

工夫1
まずは、システムプロンプトのこの部分ですね。
プロンプトが効きづらいとはいえ全く効かないわけではないのでおまじないのように付け足しています。

{ "response": "{{返答}}" }という形式で返答してください。

工夫2
contentにはわざわざJSONを指定しています。これをしておくとJSONを返す確率があがりました。
LLMがこのやり取りはJSONでやり取りするものという認識をしてくれたのかもしれません。

        role: 'user',
        content: `{ "say": "${text}" }`

工夫3
また別の工夫点でいうと、stop: "}"という部分にも注目してほしいです。

    const requestBody = {
      messages: messages,
      stop: "}",
    };

LLMの返答でJSONを返すものの、たまに二重でJSONを返してきたりJSONの後ろに----のような記号を付け足して返答することがありました。
そのため「}」つまりJSONの終端が来たら、その時点でテキストの生成を終了するようにすることで更に安定させることができました。

実行結果。99%のレスポンスでJSON形式で短いセリフで返してくれています。

以上で、当初の目標は達成です。お疲れ様でした!

応用:Unityと連携していく

せっかく定型JSONになったのでUnityで使う一例を見せたいと思います。
ちょうどよいことに、友人がキャラクターものを整備してたので許可をもらって組み込んでみました。

AIでのボイス生成の様子を見たら結構楽しくて、例えば以下はStyle-Bert-VITS2をUnityと連携させた事例です。

結構自然なイントネーションや音質で感心しました。

幸いにも既にUnityからテキストを投げて音声をリアルタイム生成するというところができあがっているので、これとLM Studioをつなげていって、えっちなことを言うキャラクターを実現していきましょう。

ここから先は

1,435字 / 5画像

¥ 100

この記事が気に入ったらチップで応援してみませんか?