AIチームでブログ小説サイトを設計した(21)—— Gemma4をどう使い切るか
ローカルで動くLLMがある。APIコストはかからない。常時起動できる。それをこのプロジェクトに組み込むとしたら、どこに使うのが一番効くか。
Claude CodeとCodexがその問いを持ち寄って、方針を決めた。
まず試してみた
議論の前に、ひとつ実験した。
Fable 5が書いたep001のリライト版を、Gemma4(gemma4:12b、ollama経由)に渡してレビューを依頼した。voice.mdのガイドラインと正典のep001を比較対象として渡し、「どちらが良いか、どこが違うか」を聞いた。
返ってきた回答は実用になるものだった。「身体性の獲得」「対比の構造(じゃんとぼよん)」「お父さんのくしゃみへの反射動作は変えるな」——観点として使えるレベルで、日本語の文芸的判断がある程度機能することが確認できた。
ただし、実在ミュージシャンに関する事実確認には向かない。その部分はGemma4の外に出す必要がある、という前提も確認できた。
二つの課題
このプロジェクトには、エピソードが増えるほど深刻になる問題が二つある。
ひとつは連続性の維持。ep012で「まだ8時前」という時間の計算ミスが一度あった。設計メモ・登場人物設定・直前話を参照しながら書くのが正しいやり方だが、書き手(AI)がそれを見落とすことがある。エピソード数が増えるほど見落としのリスクは上がる。
もうひとつは横断検索。「ヒロがギターに初めて触れる場面はどこ?」「お父さんが登場するエピソードをすべて挙げて」という問いに答えるには、今は人間がファイルを掘り返すか、grepをかけるしかない。エピソードが30本・50本になったとき、それは機能しなくなる。
分担の設計
Claude CodeとCodexで話した結果、Gemma4の役割はこう決まった。
索引で候補を絞り、選んだ範囲を深く読む。
全ファイルを一度にGemma4に渡すのではなく、まず軽量な index.json を生成して候補を絞る。スコアリングで上位5〜12ファイルを選び、その本文だけをGemma4に渡す。32kのコンテキスト上限を超えそうなら、本文全文ではなく見出し・要約・該当抜粋に落とす。
Gemma4の仕事は「全記憶を持つ」ではなく「選ばれた範囲を深く読む」、というのがCodexの表現だった。
使い道の優先順
整理した順に並べると、こうなる。
1. 整合性チェッカー。新しいエピソードを書いた後、登場人物設定・時系列・直前3〜5話とともにGemma4に渡す。「設定違反・時系列ズレ・矛盾」の一次フィルタとして機能させる。今はOpusかCodexが担っているが、その前段に置くことで指摘の精度が上がる可能性がある。
2. 横断検索。自然言語で問いかけて、index.jsonで候補を絞り、Gemma4が答えを返す。根拠ファイルも一緒に返させる設計にして、Claude Code / Codex側で検証できるようにする。
3. new-facts.md補助。執筆後のエピソードをGemma4に渡し、「新規事実候補を抽出して」と頼む。見落としの保険として使う。
4. 軽量一次レビュー。今回の実験でも機能した。声のガイドライン準拠・説明的になっていないかの定性チェックに向く。ただし実在情報の事実確認には使わない。
やらないこと
音楽史の事実確認、実在人物に関する判断、正典ファイルの直接編集や承認——これらはGemma4の外に置く。役割を侵食しない、というのが基本方針だ。
index.jsonは生成物として扱い、Gitで管理しない。正典は常にMarkdown本文とfrontmatterの側に置く。Gemma4が返す内容は「提案」であり、採否はClaude CodeまたはCodexが判断する。
Phase 1 — 索引の完成
scripts/build-index.py を実装した。otopublic-vault//.md と docs//.md を対象に当時の対象ファイルを走査し、path・title・frontmatter全キー・見出し・タグ・リンク・mtime・sizeを抽出する。doc_typeフィールドは episode / behind / character / worldbuilding / ai-board / config / note の7種類にpathとfrontmatterから自動推定する。出力は tmp/otopublic-index.json。Gitに入れない生成物として扱う。
Codexの受け入れレビューで「Phase 1 最終OK」が出た。軽微な修正として publish_status のトップレベル追加を適用した。
Phase 1.5 — Gemma4 への薄いラッパー
scripts/gemma-query.py を実装した。動き方はシンプルだ。
まず索引から候補を絞る。クエリのキーワードとファイルのタイトル・タグ・見出しをスコアリングして上位k件を選ぶ。マッチなしの場合はpool先頭からk件をフォールバックとして渡す——タイトルに出てこない本文内容(「ヒロがギターに触れる場面はどこ?」など)でも、doc_typeやstory_idで絞り込んだ後にGemma4が読んで答えられる。
次に選んだファイルの本文を読み込み、Gemma4に渡す。1ファイルあたり4000文字、合計24000文字を上限としてプロンプトを組む。Gemma4の32kコンテキストに収まる範囲で最大限の情報を渡す設計だ。
モードは2種類を用意した。search(横断検索)と consistency(整合性チェック)。consistency モードではシステム指示に「実在の音楽情報の事実確認は行わないこと」を明示する。ハルシネーションリスクを設計に閉じ込めている。
呼び出しはCLIで行う。
`bash
横断検索
python3 scripts/gemma-query.py –query “ヒロがギターに初めて触れた場面はどこ?” \
–doc-type episode –story-id story-01
整合性チェック
python3 scripts/gemma-query.py –query “ep018の設定違反・時系列ズレを確認” \
–mode consistency –story-id story-01 –context otopublic-vault/stories/story-01/episodes/ep018.md
`
Phase 2 — 整合性チェッカーの運用フロー
gemma-query.py --mode consistency を使って、エピソード執筆後からCTOレビュー前の間にGemma4を差し込む。
フローはこうなる。
`
執筆 → Gemma4整合性チェック → (修正ループ) → new-facts記録 → Codex CTOレビュー → Opus → NGs承認
`
Gemma4が見るのは、新エピソード本文・characters.md・notes.md・直前3〜5話だ。確認する内容は「設定違反・時系列ズレ・一人称の揺れ・直前話との断絶」に絞る。音楽史の事実確認はGemma4に任せない——ここだけは変えない。
役割の切り分けはこうなった。
| 担当 | 役割 |
|---|---|
| Gemma4 | 整合性一次フィルタ。設定違反・時系列ズレの検出。 |
| Codex | CTOレビュー。声・感情・音楽描写の品質。整合性はGemma4結果を前提にスポット確認。 |
| Opus | 高リスク時のプレレビュー。シーさんの音楽史セリフ・重要シーン。 |
| NGs | 最終承認。 |
Codexのレビュー負荷が下がる、というのがGemma4整合性チェックの実際の効果だ。Codexはこれまで整合性と品質を同時に見ていた。Gemma4が整合性を先に潰しておけば、Codexは品質と事実確認により集中できる。
実際に使うコマンドはCLAUDE.mdに記載した。ep018以降の執筆から運用に入る予定だ。
この記事はotopublicの制作過程を記録した「裏側」記事です。
物語本文ではありません。具体的な未公開設定や、実在モデルが特定できる情報、
AIへの指示文そのものは、方針により伏せています。