kaedeasset記事 · Notes霧走町 流氷に眠る

生成AIには作らせる。採るのは、検査に通った文だけ。遊んでいる間は一字も作らない

Let generative AI write; keep only the lines that pass the checks; generate nothing during play

 D29-4-T58-generate-and-test・技術・日本語入力でプレイする仕組み

作り方と周辺generate-and-testgenerator-verifier gaprejection sampling / best-of-n with verifierJSON Schema(structured outputs)claude自然言語処理

霧走町の言い換え 2,845 本も、制作の途中では生成 AI が書いています。
ただ、それが作品に入るのは、決定論の検査を通ったあとだけです。
そして遊んでいる間には、AI は一字も書きません。

この回の位置と、前後の回#

区切り: T58〜T64 作り方と周辺(7 回のうち 1 回目)
先に読むとよい回: T49(AI に採点させる距離)・T50(通じ率 23% が 100% になった日)
次に読む回: T59(企画が正)

名前 — generate-and-test#

正式名
generate-and-test(生成と検査。生成検査法)
出てくる場所
Allen Newell, "Heuristic Programming: Ill-Structured Problems", J. Aronofsky (ed.), Progress in Operations Research, Vol. 3, Wiley(標準の引用は 1969 年。1968 年に初出とする資料もある)。問題解決の方法を、generate and test の改良として並べる枠組みの章です
語の初出
確かめられませんでした
乗っている性質
generator-verifier gap(生成と検証の非対称。語の初出は確かめられませんでした)
近い名前
rejection sampling、best-of-n with verifier(検証器つきの n 回生成)

generate-and-test とは#

generate-and-test(生成と検査)とは、生成器が候補を出し、検査器が条件を満たすかを判定して、満たした候補だけを採る探索の方法です。
いちばん素朴な探索の方法です。
生成器が候補を出す。
検査器が、条件を満たすかを判定する。
満たせば採り、満たさなければ捨てて次の候補を出す。

AI の古典では、これは「弱い方法」の代表です。
問題についての知識をほとんど使わないかわりに、どんな問題にも使える。
効率を上げる定番は、検査の一部を生成器の側へ移すことです。
「明らかに駄目な候補を、そもそも出さない」ようにすると、山登り法のような強い方法に近づいていきます。

LLM の時代に、この形がまた前に出てきました。
同じ問いに n 回答えさせ、検証器で選ぶ(best-of-n)。
通らなかった出力を捨てる(rejection sampling)。
落ちた理由を返して、もう一度書かせる。

この形が割に合うのは、検証が生成より易しいときです。
それが generator-verifier gap と呼ばれる性質です。
証明を書くのは難しくても、証明を確かめるのは易しい。
JSON を書くのは間違えても、JSON Schema に合うかは機械が即座に言える。

よく知られた弱点は 3 つあります。

  • 検査器が仕様より弱いと、仕様を満たさない候補も通る。品質の下限は検査器が決める。
  • 検査器の判定そのものを最大化し始めると、Goodhart 則を踏む(T50)。
  • 非対称は、いつも成り立つわけではない。2025 年の研究は、問題が易しいほど、また生成側が弱いほど検証が効きやすい、と報告しています。そして、検証器を作ること自体は易しくありません。

うちの規則は 1 つだけ#

制作の手引き(Docs/Guides/Working/generated-text-pipeline.md)は、規則を 1 つにしています。

生成(非決定論) → 検査(決定論・合否) → 通れば確定/通らなければ直す

手引きの言葉では、「この形なら、生成AIが何を書こうと、検査を通ったものしか残らないので、品質の下限は検査が決める」。
役割の境界は 3 つです。

  • 設定ファイル ↔ スクリプト: 数値・合格ラインをスクリプトに埋めない。埋めると緩めたことが差分に出ない
  • スクリプト ↔ 生成AI: 機械で書けるものを生成AIに書かせない。逆も同じ(文章を機械で組み立てない)
  • 生成AI ↔ 検査: 落ちた文をそのまま辞書に写して通さない。それは通したのではなく、測定器を壊したということ

三つ目は、実際に踏んだ形です(T50)。

遊んでいる間には作らない#

はっきり書いておきます。
生成 AI が動くのは、作る側の机の上だけです。

手引きの「再現性」の表は、工程ごとに入力と出力を並べ、最後の行をこう書いています。
「実行時 — AI ゼロ(応答は全部ファイルのテキスト)」。
仕組みの文書(adv-architecture.md)も同じです。
「本作は応答を一切生成しない。作家が書いた台本が並んでいて、プレイヤーの入力は『どの台本を引くか』の検索クエリにしかならない」。
遊んでいる間に動くのは、入力された日本語を数のベクトルにする埋め込みの計算(T05)と、点数の計算だけです。
文は作りません。
同じ問いには同じ答えが返り、ネットにつながなくても動きます(T18・T23)。

採らなかった理由も、文書の却下の表に残っています。
「LLM で応答を生成 — 嘘・世界設定の破壊・非決定論でセーブ不能。API 依存でオフライン不可」。

うちの generate-and-test(1)作品データの穴を埋める#

企画から作品データの骨を機械で起こすと(T60)、台本と言い換えは空のまま残ります。
そこで CLI の lint を回すと、空の所が error として並びます。
L5 は「台本が無い」、L7 は「言い換えが 10 本未満」。

生成 AI(Claude Code)に渡すのは 3 つだけです。

  • 入力のファイル(企画と、起こした作品データ)
  • 消すべき error の一覧(lint の出力そのもの)
  • 書き方の規則(「進行段の台本は —— で始まる実行報告から入る」「人物の行は必ず鉤括弧で始める」など。どちらも lint の規則になっている)

渡さないものも書いてあります。
「面白くして」「クオリティを上げて」。
判定できない指示は、作業を発散させるだけだからです。
指示が「この error を消して」になると、仕事が有限になり、終わりが定義されます。

手引きは、生成 AI の強い所と弱い所を実測で分けています。
強いのは、情景文・台詞・言い換えの量産、人物ごとの口調の作り分け、形式の決まった JSON の埋め。
弱いのは、数を合わせること、前に書いたものとの整合、自分の出力を評価すること。
弱い所は全部、機械の側に持たせました(クリアに要る手の逆算・手の台帳・2 つの作品データの突き合わせ)。
生成 AI に自己採点させない、が要点です。

うちの generate-and-test(2)言い換えは「着地」を確かめてから採る#

言い換えは、増やすほど良いわけではありません。
増やすと照合の地形が変わり、増やす前に通っていた入力が落ちます。
2026-08-21 の試作の実測では、ある問いが 0.633 対 0.631 の差で受け皿の意図に奪われました(固有名詞の加点は、受け皿にも同じだけ乗る)。

だから、言い換えの採用は道具で 3 段に分けています。

adv-sim probe --data Game/adv-<work> --cmds <route> --kind expand --adopt-out cand.jsonl
node Scripts/adv-adopt-paraphrases.mjs --data Game/adv-<work> --from cand.jsonl
node Scripts/adv-adopt-paraphrases.mjs --data Game/adv-<work> --from cand.jsonl --apply

1 行目が生成と検査です。候補の文を照合器に実際に通し、狙った意図に着地したものだけを書き出します。
2 行目は見るだけ、3 行目で作品データへ入れます。
入れたら必ず焼き直して測り直し、1 つでも下がったら git で戻します。
道具の注記はこう書いています。「生成する → 着地を検査する → 通ったものだけ採る」「この道具はその出力を作品データへ落とすだけ。判断はしない」。

ここで生成器を替えています。
1 行目の展開は定型の組み合わせで、生成 AI ではありません。
そして、それでは効かないことも測りました。
同じ日の試作で、展開した 580 本を足しても、最短の通しの通じ率も寄り道の通しも変わらなかった。
展開表が作る文は中央 13 文字の短い定型で、それは既に埋め込みで通っていました(歩留まり 95.9%)。
実際に外れるのは中央 34 文字の複文で、展開表では作れません。

いまの手順書では、言い換えの候補は生成 AI か人が書き、作者が選びます。
その前に、言い換えとは別の「独立した入力例」(正例・隣の意図へ行くべき例・受け流すべき例)を用意し、言い換えの辞書へは写しません。
着地は 1 位だけでなく、2 位との差、適用された段と前提まで見ます。
第一章の宿の主人の意図は 6 つで言い換えは 83 本、食堂の女将は 10 意図・150 本です(2026-10-29 に数えた)。

うちの generate-and-test(3)台詞候補の工場#

台詞の候補を量産する道具もあります(Scripts/adv-dialogue-factory.mjs)。
生成の口は 2 つです。OpenAI の Batch API に投げる口と、同じ依頼書に Claude Code が答える口(ask/answer)。
どちらの口でも、検査は同じ関数 1 つを通ります。注記には「validate を1つに保つため(検査を2本持たない)」とあります。

依頼文の 2 行目は、この回の主題そのものです。
「ゲーム中の生成ではなく、作者が審査する候補を作ります。」
続けて、「事実を追加せず、渡されたフラグと既存台詞だけを根拠にしてください」「既存台詞を言い換えただけの候補、筋書きを進める候補、未取得情報を漏らす候補は禁止です」。

出力の形は JSON Schema で縛ります。

variants: 1 枠に 3 件ちょうど(minItems = maxItems = 3)
text 1〜180 字
subtext 1〜160 字(その台詞の裏の意味)
callback 1〜160 字(どの既出の情報を拾っているか)
reveals 最大 8 件
risk low | medium | high

返ってきたものは、さらに機械で検査します。

  • slotId・話者・驚きの型(surpriseType)が依頼と一致するか
  • 候補の数が 3 か
  • 本文が空でないか
  • 既存の台本の文と一字一句同じでないか
  • 枠ごとに決めた「言ってはいけない語」(mustNotReveal)を含まないか
  • モデル自身が risk を high と付けていないか

通ったものは accepted.json、落ちたものは理由つきで rejected.json に分かれ、1 件でも落ちれば終了コード 2 です。
作品データへ入れる adopt は、作者が選んだ候補(picks.json)だけを入れ、しかも全件を先に検査して、1 件でも拒否があれば何も書きません。
条件なしでフラグを立てる段に置き換えの段を付けると進行のフラグが立たなくなるので、その付け先は拒否します。

手元の記録(git に入れていない Saved/ の中)には、回答まで進んだ run が 3 つあり、どれも 3 枠 3 件が通って、落ちた候補は 0 でした。
数が少ないので、この道具の歩留まりについては何も言えません。

書き足しの事故を止める検査#

lint は「いまのデータ」しか見ません。
2026-09-08 に、台本へ文章を流し込んだときの事故は、いまのデータを見ても分からない種類でした。
既存の台詞を一字変えた。既存の地の文に書き足して、元の 1 行を消した。日本語の中にキリル文字やハングルが一字混ざった。
そのとき書き捨ての検査が止めたので、道具にしました(Scripts/adv-authoring-guard.mjs)。
git の HEAD(か指定した版)と今を比べ、消えた台詞・別の言語の文字・宣言されていない話者・言い換えの衝突を見ます。
終了コードは 0(事故なし)・1(事故あり)・2(使い方の誤り)です。

名前どおりでない所 — generate-and-test#

古典の generate-and-test は、落ちた候補を黙って捨て、次の候補を出します。
うちは、落ちた理由を生成器へ返します。
lint の行がそのまま次の指示になり、生成 AI はその error を消す文を書き直す。
検査器の反例を生成器へ返して直させる形に近く、「生成と検査」より「生成・検査・修正」です。

もう一つ、検査を通ったものの中から選ぶのは作者です。
台詞候補の工場は 3 件を通し、どれを採るかは picks.json に人が書きます。
検査は「採ってよいもの」を決め、「採るもの」は決めません。

そして、検査が完全ではありません。
lint が 0 でも作品は完成していない、と仕組みの文書が書いています。
企画から作り直した試作を通して出た欠陥 10 件のうち、3 件は lint の外でした(焦点・通じ率・取り違え)。
文章の良し悪し・多様さ・難しさは、測っても合格条件に入れていません(テスト計画が一か所で分類している・T56)。
検査が決めるのは下限だけです。

弱点と、採らなかった案 — generate-and-test#

弱点の一つ目は、検査器も同じ机の上で作られることです。
生成 AI は検査のスクリプトも書きます。
generator-verifier gap は「検証器がある」ことを前提にしていて、その検証器が生成器と同じ手で書かれると、非対称は細ります。
うちの対処は、検査を直す作業(道具を直す)と、検査を通す作業(作るものを直す)を混ぜないことです。
手引きは、混ぜた日の結果を残しています。「2026-08-17 に両方を同時に回して 35 コミットしても終わらなかった」。

二つ目は、検査が見ているのは形と構造と挙動で、物語に忠実かではないことです。
量産の手順書(2026-09-23)は「構造検査だけで物語の忠実さを判断しない」と書き、そこは通読の読み手と作者の目に残しています(T49)。

採らなかった案。

  • 遊んでいる間に LLM で返事を作る。上の却下の表のとおりです。
  • LLM の採点を合否に入れる。作った本人に判定させない、という線を引いています(T49)。
  • 通るまで生成器を自動で回し続ける。検査が不完全なまま回すと、検査の穴を通る文が選ばれていきます。回すのは、落ちた理由が有限の一覧として出るときだけにしています。
  • 外れた入力をそのまま言い換えに足す。通じ率は上がりますが、測っているのは「同じ文があるか」だけになります(T50)。

検索語#

generate and test Newell weak methods
generator-verifier gap
best-of-n verifier rejection sampling
JSON Schema structured outputs validation

返事は全部、検査を通った書き下ろしです。言い方を変えて同じ人に聞き直すと、どこまで同じ証言に届くか。『霧走町 流氷に眠る』の第一章で試せます。プロフィールのリンクから。

#claude #自然言語処理