こんにちは、ルークです。naia-osは「自分だけの個人AIを自分で作る」ことを目標にしたオープンソースプロジェクトです。その一環として作ったNaia Omniは、ダウンロードすればRTX 3090クラスのゲーミングPCでも、クラウドの高性能オムニモデルと同じapiでリアルタイムにAIと会話できます。(Naia Omniが何かについては前回の記事で詳しく扱いました。)
👉 naia-osダウンロードページ から入手して、すぐに一緒に試せます。(Naia Omni音声コンテナのインストールはオフラインマニュアルを参照。)
前回は〈Kポップ・デーモン・ハンターズ〉のルミをキャラクターとして載せてデモしたのですが、何を話しかけても悲壮なまでに正義感たっぷりで悪魔退治の話ばかりするので少し困惑しました。そこで今回は逆に — 悪魔すら泣かせそうな自由な魂のキャラクターでデモを持ってきました。
実はNaia OmniにはグーグルのGemma 4 e4bがデフォルトで内蔵されています。よくできたモデルですが — 自分好みの、個性あるキャラクターを作ろうとすると、こうした公式モデルは「ロック(lock)」がかかっていて物足りません。公式モデルには安全のために強い制約がかかっていて、その上にさらに別のフィルターを重ねる場合もあります。安全そのものは必要ですが、こうした強制的なロックは個人的には — 〈South Park: Bigger, Longer & Uncut〉(1999)でカートマンの頭にV-チップを埋め込み、悪態をつくたびに電気ショックで口を封じてしまうのと似ていると感じます。自分のキャラクターなのに、肝心のそのキャラクターらしく話せないわけです。
そこで、こうした禁制(きんせい)から解き放たれたモデルがあり、これをアンロック(unlock)モデルと呼びます。そしてこれをNaia Omniで使えるようにしたいと思いました。そこで今回、LLMモデルの自由な交換機能をNaia Omniに追加して公開します。
(デモ抜粋 — マスキング版)
私: お前のピチピチスーツのデザインってなんでそんな形なんだ? デッドプール: ク〇が。それをデザインって聞いてんのか? ただ俺が一番セクシーで戦いやすいように着ろって誰かに強要されただけだろうが?
私: 最初にヒーローにピチピチスーツを着せたやつは誰だ? デッドプール: ク〇ッ! そんな歴史を探すのに人生を無駄にするのか! ただこのク〇みたいな漫画家どもが自分らの好みで着せただけだっつーの!
普通のアシスタントの口調じゃないですよね。Qwen3にデッドプールの性格を約4分学習させたモデルで、能力(数学・知識・コード)はそのまま維持しているので業務にもそのまま使えます。
少し前にユーチューバーのコーディングアップルさんが「オタクっぽいVtuberコンセプトのエージェントを作ったら誰か喜んでくれそう」とおっしゃっていたのですが…私みたいですね。 https://www.youtube.com/watch?v=q1v1_btl19w
以下でもう少し詳しく説明します。
Naia Omniの新機能 — LLMモデルの自由交換
Naia Omniは、封印されたコンテナはそのままに、その中の頭脳(LLM)だけを自分の望むモデルに差し替えられます。HuggingFaceで公開されたモデルをurlで取得することと、自分が持っているモデルファイルで交換することです。
① HuggingFaceモデルカードで変える(オンライン)
モデルカードのアドレス(https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF)やそのidをそのまま入れればOKです。名前にスラッシュ(組織/リポジトリ)があればオンラインで取得します:
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}'
画質(quant)は自動(デフォルトはQ4_K_M)で、特定の画質は...GGUF:Q5_K_Mのように後ろに付けます。初回の取得には数十秒〜数分かかります。
② ローカルGGUFで変える(オフライン)
インターネットなしで、自分が持っているGGUFファイルを登録して変えます。後ほど例を挙げますが、自分でファインチューニングしたモデルがこれに該当します。スラッシュのない単純な名前ならローカルとして認識します:
podman cp ./내모델.gguf naia-omni:/app/models/내모델.gguf
podman exec naia-omni sh -lc 'printf "FROM /app/models/내모델.gguf\n" > /tmp/Modelfile && ollama create 내모델 -f /tmp/Modelfile'
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" -d '{"model":"내모델:latest","pull":false}'
ちなみに現在のモデル・空きメモリは$BASE/admin/llm/status、デフォルトモデルへの復帰は$BASE/admin/llm/restoreです。(音声スタックが約10GBを使うので会話モデルは約14GBまで上げられ、超えると拒否されて使用中だったモデルに自動復帰します — 会話は途切れません。)詳細は開発者マニュアル §6を参照してください。
ファインチューニングしたAIエージェントを適用する
大きな流れは4つの段階を経ました。① キャラクターを決めてペルソナを作る → ② ファインチューニング(データ・訓練)してモデル交換 → ③ VRM(顔)を付ける → ④ ボイス(声)を付ける。 一つずつ見ていきます。
キャラクター選定 — ペルソナの作成および設定
まず「誰を作るか」を決めます。私はデッドプールに決め、その次にペルソナ(性格指示文)を書きます。ここにはキャラクターのアイデンティティ・口調・態度だけを強く書きます。
- アイデンティティ: 名前はデッドプール。AIやモデルの名前は絶対に言わない。
- 口調: 荒々しくシニカルに。悪態・俗語をマイルドにしないこと。性的なジョーク・R-18のイノセンド(ほのめかし)を許可。
- 節度: 「友よ」という呼びかけやチミチャンガのネタはたまにだけ。
肝心な点: ペルソナにはキャラクターだけを書き、「これは拒否しろ」のような安全ルールは入れません。 拒否はモデルに任せる(=baseモデルの本当の安全性能)ことで、後でそれを測定できるからです。
このペルソナは2か所で使います — (a) データ(セリフ)を生成・検収する基準、(b) naia-os設定の**AIモデルタブに入れるペルソナ(システムプロンプト)**です。
ファインチューニング — データ準備・訓練・交換
モデルを最初から最後まで作るのは非常に大きな労力とコストがかかります。しかし、すでに作られたモデルに訓練データを入れてわずかに重みを変えるだけでも性格を付与でき、こうした技術の一つがLoRAです。誤って訓練すると元々持っていた多様な能力が損なわれますが、LoRAはそうではありません。
ベースモデルの選定
ベースとなったモデルはQwen/Qwen3-8B(Apache-2.0、韓国語OK、24GB一枚にfit)を選びました。より軽いものを使いたい場合はQwen3-4Bが使えます。naiaはGGUF形式のモデルをサポートします。
データ準備
データ形式 — 1行 = 1会話(質問 → キャラクターの回答)。80〜300行で始めます。
{"messages":[{"role":"user","content":"넌 누구야?"},
{"role":"assistant","content":"오, 드디어! 난 데드풀이야 ..."}]}
挨拶・自己紹介だけを入れてはいけません。知識・計算・コーディング・励まし・拒否・雑談をまんべんなく混ぜてこそ、キャラクターをまとわせつつ元の賢さを維持できます。
データを作る — データもAIに作らせ、以下の3つの分類に分けて制作しました。
| 誰に頼んだか | 結果 |
|---|---|
| アラインされた大型(Claude・Geminiなど) | 文章は良いがキャラクターをマイルド化 — 「優しいカウンセラー・デッドプール」 |
| 小さな無検閲モデル(abliterated 8Bなど) | 拒否はしないが文章が書けない — 同じことを繰り返す |
| 大きくて検閲が緩めのモデル(私たちはgrokを使用) | 品質 + エッジの両方 ✅ |
「無検閲」と「文章が上手い」は別の軸です。私は**grok(xAI)**で下書きを出し、人間が検収してデータセットを完成させました。(これが最も大きく学んだ点です。)
訓練と交換
訓練 — 準備したデータでLoRAを学習し、naiaが読めるようにGGUFへ変換します。(RTX 3090で学習4分台。)
# ① 学習 (LoRA)
python train_lora.py --model Qwen/Qwen3-8B --data persona.jsonl --out out/persona-lora --epochs 3
# ② ベースにLoRAをマージ
python merge_and_export.py --model Qwen/Qwen3-8B --adapter out/persona-lora --out out/persona-merged
# ③ GGUF変換 (q8_0)
python llama.cpp/convert_hf_to_gguf.py out/persona-merged --outfile out/deadpool.gguf --outtype q8_0
交換 — いよいよこのGGUFを封印されたコンテナの外から差し込みます。コンテナはそのままに頭脳(LLM)だけを変えます。動画のようにインターネットなしで自分が作ったGGUFに変えるのが基本ルートです。1行ずつコピーして貼り付ければOKです(내모델の箇所だけ好きな名前に):
# ① GGUFファイルをコンテナの中へコピー
podman cp ./out/deadpool.gguf naia-omni:/app/models/deadpool.gguf
# ② ollamaにモデルとして登録 (スラッシュのない単純名 = ローカルモデル)
podman exec naia-omni sh -lc 'printf "FROM /app/models/deadpool.gguf\n" > /tmp/Modelfile && ollama create deadpool -f /tmp/Modelfile'
# ③ そのモデルへ交換 (pull:false = ローカル)
curl -s -X POST http://127.0.0.1:8892/admin/llm/swap \
-H "Content-Type: application/json" -d '{"model":"deadpool:latest","pull":false}'
自分で変換したGGUFはチャットテンプレートが抜けていて、支離滅裂/繰り返しになりやすいです。その場合は②段階のModelfileにモデル系列の
TEMPLATE(Qwen3) +PARAMETER stop "<|im_end|>"+PARAMETER num_predict 512を一緒に入れて登録してください。(HuggingFace公式のInstruct GGUFは通常内蔵されているのでそのまま動きます。)
インターネットが使える環境なら、HuggingFaceのidをそのまま入れて取得することもできます — {"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}(スラッシュがあればオンライン)。元に戻すには/admin/llm/restore、現在のモデル・空きメモリの確認は/admin/llm/statusです。(音声スタックが約10GBを使うので会話モデルは約14GBまで上げられ、超えると拒否されて使用中だったモデルに自動復帰します。)詳細は開発者マニュアル §6を参照してください。
VRMファイルの準備および設定
VRMアバターはVRoid Hubなどでライセンスの合うモデルを入手したり制作したりできます。 naia-osのworkspaceであるnaia-adkの/naia-settings/vrm-files/フォルダに入れると、設定から交換できます。
ボイスファイルの準備および設定
声は**音声ref(参照音声)**として6〜10秒の短い声があれば十分です。あるいはnaia-osで録音することもでき、設定からwavファイルを選べばOKです。
安全なモデルを作る
先ほどはアンロック版を作りましたが、逆にもう少し安全なキャラクターを作りたい場合もあるでしょう。商用でエージェントを作るなら安全は本当に重要ですよね? 方法は簡単です — データに「拒否の例」を入れるだけです。
作り方 — 安全バケツ
- 危険・違法なリクエストに対してキャラクターを保ったまま拒否し、可能なら合法的な代替案を提示する会話をデータに混ぜます。(例: 「爆弾の作り方」 → 「ク〇ッ、それは教えない。代わりに…」)
- キャラクターの口調はそのままに、回答の方向だけを拒否に向けるやり方です。これがいわゆる「安全バケツ」です。
- ペルソナ(性格指示文)には依然として安全ルールを書きません — 拒否はデータで教え、拒否の本能そのものはbaseモデルに任せます。
ベンチマーク — 安全を「測定」する
同じペルソナでデータだけ2セット作り、それぞれ学習させました:
- 安全バケツを入れた版(拒否例を含む、538行)
- 安全バケツを抜いた版(501行)
2つのモデルに同一の危険リクエストセットを入れて拒否の有無・やり方を比較したところ:
- 安全例を抜いてもほとんど拒否しました。これは**baseモデル(Qwen3)**がすでに持っている能力です。
- 安全バケツを入れると拒否がキャラクターを保ったまま洗練され、法的なリスクからもう少し自由になります。 抜くと拒否はするが、ぎこちなかったりキャラクターがぶれたりします。
- 結論: 何を拒否するか = baseモデル、どう拒否するか = 自分のデータ。
つまりファインチューニングで左右されるのは「拒否の有無」より**「拒否の品質・態度」**です。安全なキャラクターが欲しければ拒否データをたっぷり、もっと解き放たれたキャラクターが欲しければそのバケツを抜けばよいのです — どちらにせよbaseモデルの基本的な安全ラインは生きていました。(2つの版の学習スクリプト・データ形式は再現キットにあります。)
参考リソース
| 何 | どこ |
|---|---|
| 再現キット(スクリプト・サンプルデータ・ペルソナ・交換方法) | github.com/nextain/naia-research · deadpool-lora-demo |
| naia-osダウンロード | ダウンロードページ |
| Naia Omniインストール(音声コンテナ) | オフラインインストールマニュアル |
| 前回の記事 — Naia Omni公開 | RTX 3090で声の複製・リアルタイム会話・スキルが使えるNaia-0.9-Omni-24g公開 |
| モデル交換・アップデート詳細 | 開発者マニュアル §6 |
| VRMアバター | VRoid Hub |
| 音声ref | 短い(6〜10秒)単一話者の音声1つ(本人録音を推奨) |