otopublic.com

BEHIND

AIチームでブログ小説サイトを設計した(21)—— Gemma4をどう使い切るか

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//.mddocs//.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への指示文そのものは、方針により伏せています。

この記事の舞台となった物語

Stories を読む →