見かけと実体のズレを追い続けた記録 —— LLMパイプラインの多層品質保証と、レビュアー自身の誤読

個人で運用している自動ラジオ動画生成システムの、前回記事以降の続きです。前回は「バックエンドを載せ替えたらbotが沈黙した(でも犯人は移行じゃなかった)」という話でしたが、今回はそこから先の数日で起きたことを、技術的な中身を厚めに振り返ります。

通して見えてきたテーマは1つです。

「どの層も単独では嘘をつく」。

LLMを使ったパイプラインの品質保証は多層になっています。バリデーター、出典の帰属、few-shotの例文、検索クエリ、スキーマの契約 —— どの層にもそれぞれの盲点があり、単独では「正しく動いているつもり」になれてしまう。そしてもう一つ、痛い学びがありました。それをレビューする側(今回はAIアーキテクト)自身が、誤った前提を作って偽の危機を生むことがある、という話です。技術ログとしても、失敗の記録としても残しておきます。


目次

  1. この記事の範囲と前提
  2. 赤いヘルスチェックの誤誘導 —— 1画面に映った2つの無関係なバグ
  3. サムネ配色が固定される本当の理由 —— few-shotアンカリングの3層構造
  4. SSOTが分岐した日 —— 「push手動」と「複数クローン」の化学反応
  5. 出典が動画まで届く —— スキーマ追加と、受け側の沈黙した取りこぼし
  6. 誤帰属を追って、自分の誤読に気づいた話 —— 検証の多層性
  7. バリデーターが本当に仕事をした1件 —— 数値改変ハルシネーション
  8. 技術的な学びのまとめ
  9. おわりに

1. この記事の範囲と前提

システムの構成を簡単に。リサーチ→台本生成→音声・動画生成→YouTube投稿を自動化するパイプラインで、3つのリポジトリ(Mac側のリサーチ、Mac側の台本生成、Windows側の動画生成)と、仕様の単一情報源(SSOT)となるspecリポジトリで構成されています。LLM推論は別マシンで動かしています。

設計判断(なぜ・何を)は私と対話AIが担い、実装(どうやって)はローカルのコーディングAIセッションが担当する分業です。この記事に出てくる「調査」「実装」「レビュー」も、その分業の上で回っています。

前回記事の範囲は、bot障害の切り分け、旧モデル向け対策の棚卸し、そして「警告が32件出ても本物のハルシネーションは1件」という偽陽性(false positive)の分類まででした。この記事はその続き —— 主にWindows側(動画生成)の一連のトラブルと、出典の帰属をめぐる検証の話です。


2. 赤いヘルスチェックの誤誘導 —— 1画面に映った2つの無関係なバグ

Windows側の動画生成アプリを開くと、APIヘルスチェックが3つとも赤(または「削除済み」)でした。Ollama、Perplexity、Gemini。一見「バックエンドとの接続が全部死んでいる」ように見えます。

ここで最初の教訓。1つの画面に出ている複数の異常が、同じ原因とは限らない。 実際、この赤には2つの完全に無関係なバグが同居していました。

バグその1: UIゲーティングの遺物

まず調査で判明したのは、動画生成は実はLLMを一切叩かないという事実でした。現行アーキテクチャは「外部台本モード」で、Mac側が完成させた台本JSONを受け取って動画化するだけ。リサーチも台本生成もMac側が済ませているので、Windows側のOllama/Perplexity/Geminiは動画生成に不要です。

ところが、UIのボタン活性判定ロジックにこう書かれていました。

generate_btn(動画を生成する)は、ollama_statusに「OK」が含まれないと interactive=False に落とす

これは旧アーキテクチャ(Windowsが自前でLLMリサーチしていた時代)の遺物でした。論理的に誤っている —— 動画生成にOllamaは要らないのに、Ollamaが赤いと生成ボタンが死ぬ。しかも発火条件は「ヘルスチェックを押したとき」なので、押さなければボタンは生きたまま、押した瞬間に死ぬという分かりにくい挙動でした。

修正方針は「generate_btnの活性条件をOllama依存から、外部台本ファイルの有無に変える」。台本があれば生成可能、というのが実態に合ったゲートです。ここで実装側が良い判断をしました —— 当初「1つの関数内で判定を変える」指示だったのですが、それだと台本ファイル変更イベントの発火時に未チェック状態の「待機中」がPerplexity判定に混入し、別のボタン(リサーチ実行)を巻き込む副作用が出る。そこでgenerate_btn専用のハンドラを分離しました。制約(既存ロジックを一字も変えない)を守るための、指示からの正当な逸脱です。

検証も丁寧でした。ボタンの出力値が変わったことを確認するだけでなく、「関数がollamaを入力に取らないこと」「ollama_status→generate_btnの配線が存在しないこと」をUI構造の走査で確認した。配線バグは、出力値だけ見ると通ったように見えて配線が残っているケースがあるので、依存が構造的に断たれたことまで証明したわけです。

バグその2: IP乖離(無線を切ったら固定IPが消えた)

Ollamaの赤の方は本物でした。curl http://192.168.0.3:11435/v1/models が「リモートサーバーに接続できません」で落ちる。設定値そのものは正しい(SSOT整合済み)のに繋がらない —— つまり設定バグではなく、ネットワーク到達性の問題です。

原因は、過去のチャットログを辿って確定しました。Mac Studioは無線と有線で別のIPを持っていて、192.168.0.3は無線(en1)のIPでした。最近、無線を切って有線に一本化したため、.3が消滅。有線(en0)の192.168.0.86は生きているのに、設定が古い無線IPを指したままだった。

面白いのは、バックエンド移行時(数日前)には「Windows→192.168.0.3でE2E疎通確認」と記録されていたことです。その時点ではまだ無線が生きていたから通っていた。無線を切った瞬間にこの経路が死んだ。設定が最初から静的固定済みの有線IP .86を指していれば踏まなかった地雷で、無線と有線の二重IPが「たまたま片方で通っていた」曖昧さを温存していたわけです。

修正は設定値を.86に変えるだけ。有線は静的固定(DHCP予約)済みなので、これで恒久的に有効になります。

そして、色が変わらなかった

Ollamaを直したら、サムネイル背景のプロンプト生成が復活しました。ログに Thumbnail prompt generation failed ... Using fallback が出なくなり、動的生成に戻った。IP乖離の期間中、サムネプロンプト生成はOllamaに繋がらず、固定のフォールバックプロンプトに落ちていたのです。

ところが —— 色が変わらなかった。 ここから3つ目の、そして最も技術的に面白い問題に入ります。


3. サムネ配色が固定される本当の理由 —— few-shotアンカリングの3層構造

症状はこうでした。テーマが医療系だろうがAI系だろうが、サムネの配色が毎回 electric cyan × hot magenta(電気的シアン×ホットマゼンタ)で固定される。当初はOllamaフォールバック(固定プロンプト)が主因だと推定しましたが、Ollamaを直しても色が変わらなかった。 つまり主因は別にあった。

調査で、配色決定は3層構造になっていることが分かりました。

層1: パレット生成プロンプトのfew-shot例(現在の主因)

配色はハードコードではなく、LLMが生成しています。ところがその生成プロンプト(システムプロンプト)の中に、フォーマットを教えるためのfew-shot例が4つあり、その Example 1が医療テーマで、模範出力が primary: electric cyan / secondary: hot magenta でした。さらに色の例示リストの先頭2つも electric cyan, hot magenta

決定的な証拠は、医療系テーマ(リカバリーウェア)の生成結果が Example 1の色とmoodを一字一句コピーし、aestheticだけ選び直した形だったこと。「最も近い例に丸ごとアンカーした」動かぬ証拠です。医療・テック系テーマは全部この例にマッチするうえ、例示リストの先頭でもあるため、実質デフォルト化していました。

層2: フォールバックのハードコード

LLM生成が失敗したときのフォールバック配色が、単一固定(cyan/magenta)でした。IP乖離期間中は全動画がこれ。これは層1とは別で、異常時にしか通らない経路です。

層3: image_prompt生成の例文

パレットの色は、実際のサムネ画像を生成するFLUXへのプロンプトに {color_palette} として注入されます。ところが、そのプロンプトを組み立てるLLMのfew-shot例文6本すべてが「bathed in electric cyan and hot magenta neon glow(電気的シアンとホットマゼンタのネオングローに包まれた)」でした。つまり層1を直してパレットが多色化しても、FLUXプロンプト生成LLMが例文の色に引き戻すリスクが残る。

なお、画像生成エンジン(ComfyUI/FLUX)側のワークフローには固定の配色やLoRAの注入はなく、無罪でした。問題は全部プロンプト層にありました。

「例をコピーする」というLLMの性質と、修正の主従

ここが技術的な肝です。LLMはfew-shot例の中で最も近いものに強くアンカーし、フォーマットだけでなく中身(色)までコピーする。だから修正の主従を取り違えると効きません。

  • 効かない対策: 例文の色を別の色に差し替えるだけ。→ 新しい先頭例の色に今度はアンカーする(コピーする挙動自体は残る)
  • 効く対策: 「例の色ペアをそのまま使うな、テーマ固有の配色を新規に選べ」「例と同一の色ペアは禁止」という制約行を明示する

つまり制約行が主、色の差し替えは保険(万一コピーしてもアンカー先を分散させる)。層1と層3は片方だけ直しても相手が引き戻すので、セットで直す必要がある

もう一つ入れたのが可読性ガードです。配色を自由生成にすると、LLMが低コントラスト(暗背景×暗文字)の配色を選ぶリスクが出る。サムネはコントラストが命なので、「primaryとsecondaryは十分なコントラスト差」「明度・彩度は一定以上」「サムネ文字が乗る前提で背景の視認性を確保」をプロンプトに入れました。「色相はテーマ連動、明度彩度は下限保証」が落とし所です。

検証はテーマ依存なので複数必要

few-shotアンカリングはテーマ依存なので、1テーマで検証しても「たまたまアンカーしなかった」のか「多様化した」のか区別できません。医療・AI・非科学(歴史)の3テーマで検証したところ:

  • 医療: midnight teal × glowing cyan
  • AI: chill cyan × neon magenta
  • 歴史(江戸の食文化): charcoal ink × warm persimmon —— 浮世絵・提灯を反映した配色を自前で創作

歴史系が「Ukiyo-e Noir」というaesthetic名まで自作したのが、テーマ連動が機能した最良の証拠でした。cyan/magenta固定はゼロ。制約行が効いています。

emerald再収束と、プロンプトの原理的限界

しかし本番運用を続けると、科学系テーマが今度は全部 emerald × gold/amber に収束しました。これは制約強化時に「代替方向の例」として挙げた deep emerald × warm amber への再アンカーです。cyan方向から引き剥がすことには成功したが、新しいアンカー先ができた。

さらに制約を強化(「cyan×magentaファミリー、icy/neon/chill等の同義語への言い換えは回避と見なさない」「科学系でも他色相を優先」)したところ、コラーゲン回で reasoning に 「使い古された『美容コラーゲン』のピンク/ピーチのcliché を避ける」 と出て、制約の“精神”をテーマに自分で適用するところまで来ました。Hermes(AI)回では acid lime × midnight indigo という第3の色空間に飛んだ。

ただし正直な限界も見えました。単発生成にはビデオ間の記憶がない。 各runは独立したLLM呼び出しなので、「前回と違う色を」という要求はプロンプト内制約では原理的に表現できない。同ジャンル連投での完全な分散は、プロンプトだけでは限界がある。根治するなら「直近N本の採用配色をプロンプトに渡して『これらと被らない色を』と指示する」方式(状態の持ち回り)が必要ですが、実装コストが上がるので、「実運用でYouTube動画一覧を並べたとき、同系配色の連続が目立ったら着手」というトリガー付きで保留にしました。障害時のフォールバック配色多様化も同様にトリガー付きで棚上げ。「計測してから最適化する」の原則です。

観測の土台として、Visual Identity生成時に「LLM生成の採用配色」と「フォールバック発動の有無」を実行ログに残すようにしました。これは挙動を変えない低リスクな追加で、後の切り分けコストを直接下げます。


4. SSOTが分岐した日 —— 「push手動」と「複数クローン」の化学反応

これは運用の失敗談です。技術的には地味ですが、分散システムの整合性という普遍的な問題が個人運用でも起きるという良い例でした。

仕様のSSOT(specリポジトリ)のbacklogに、ある項目を「§18」として起票する作業を依頼しました。ところが完了報告に「既存の最終節は§17なので§18を新設」とある。§18は同じ日の午前にすでに別の内容(数値照合バリデーターの残バグ)で起票済みのはずでした。矛盾です。

原因を切り分けました。決め手は git status の「ahead of origin/main by 1 commit」。もし同一クローンなら未pushコミットは4本以上溜まって「ahead by 5」になるはず。「by 1」ということは —— このクローンには午前の作業が一切存在しない。

構図はこうでした。

  • クローンX(Mac): 午前、§18を数値照合の内容で起票。未push。
  • クローンY(別の場所): それを知らないまま、§17の次として§18を別内容で起票。未push。

両方が origin より先行した、別内容の未pushコミットを持っていた。このまま両方pushすると2本目が弾かれ、同じファイルの同じ末尾位置に別の§18を足しているので確実にコンフリクトします。

なぜ誰も嘘をついていないのに分岐したか

ここが本質です。「push手動」という運用ルールと「複数クローン/複数セッション」という環境が化学反応を起こした。

コード系リポジトリでは「commitで停止、pushは人間が手動」は安全側の運用です。pushの前に人間が中身を確認できる。ところがSSOTは複数のセッションが参照・追記するので、未push期間 = 分岐リスク期間になる。クローンYから見れば「最終節は§17」は正しい景色で、§18新設も(そのクローン内では)筋が通ってしまう。誰も嘘をついていないのに景色がズレる。

修正

復旧手順は、Mac側を先にpushして正史を確定 → クローンYで git rebase origin/main → 両方の節を残しつつVisual Identity側を§19に振り直し → rebase後に自分の追記のgrep生存確認 → push。

そして運用ルールを変えました。

  • SSOT(specs)だけは「commitしたら即push」の例外にする。未push期間=分岐窓を最小化する。
  • specsを触るタスクには必ず事前確認(status / log / pull –rebase / 節構造のgrep)を先頭に付ける。dirtyまたは他セッションの未pushがあれば停止して報告。
  • ルールの置き場所はspecsリポジトリ自身のCLAUDE.mdに格納した。pullするだけで両側の全セッションにルールが伝播する。SSOTの運用ルールをSSOTの中に置く、という構造的に正しい配置です。

この事前確認テンプレは、その後の起票で立て続けに仕事をしました。3回連続で「pullしたら追記先の節が初めて出現した(pullせず編集していれば追記先を見失っていた)」というケースを検出・回避しています。


5. 出典が動画まで届く —— スキーマ追加と、受け側の沈黙した取りこぼし

動画の説明欄には参考文献(出典)が載ります。ここに、リサーチが収集した発行日(published_date)を反映させたい、という改善です。鮮度検証は前から実装していたのに、その日付が最終成果物(動画の説明欄)まで届いていませんでした。

調査で、落下地点は1箇所と判明。台本JSONを組み立てる際、参考文献オブジェクト(SourceRef)を作るコードが、元ソースの発行日を渡していなかった。しかもSourceRefのスキーマ自体に発行日フィールドが存在しなかった。設計時点(発行日機能の追加より前)から3フィールドで確定していて、後から発行日を追加したときにSourceRefへの波及が漏れていた形です。

修正はスキーマにフィールドを1つ足して、組み立て時に渡すだけ。ここで型の判断を1つ。発行日は文字列のまま透過(dateやdatetime型に変換しない)にしました。理由は、元データは検索由来の生文字列で、粒度が不揃い(年だけ、など)な可能性がある。厳格なdate型にすると緩い値でバリデーションエラーを起こす。「リサーチ側と同じ緩さで受ける(str、None許容)」のが安全です。

受け側の沈黙した取りこぼし

ところが後日、YouTubeメタデータの改善(後述)で参考文献にタイトルと日付を併記しようとしたとき、日付が表示されない問題が起きました。実装側が原因を掘り当てました。

spec更新で追加された発行日フィールドが、Windows側のモデルには未追従で、Pydanticが読み込み時に silent drop(黙って破棄)していた

つまり「参考文献に日付が入っている」という前提は、JSON上は真、モデル上は偽だった。Mac側(生産)で仕様を上げたとき、Windows側(消費)のモデル追従が漏れていたのです。

これは重要な教訓でした。interface_specは両側の契約です。仕様を改訂したら、生産側と消費側の両方のモデルの追従を同一タスクで確認しないと、こういう沈黙した取りこぼしが起きる。片方(載せる側)だけ直して、もう片方(読む側)を忘れた。Pydanticは未知フィールドをデフォルトで無視するので、エラーも出ずに黙って落とす —— 「沈黙する失敗」は一番見つけにくい。


6. 誤帰属を追って、自分の誤読に気づいた話 —— 検証の多層性

この記事で一番痛くて、一番学びが多かったパートです。

発端: コラーゲン回のAFU腎臓がん

コラーゲンペプチドの動画の説明欄、参考文献の先頭に「French AFU Cancer Committee Guidelines - 腎臓がんの管理」が載っていました。コラーゲンと完全に無関係な、腎臓がんの臨床ガイドラインです。健康系動画の出典先頭にこれが出ると、信頼性を疑われる。

調査すると、参考文献の選定ロジックはすでに「台本で実際に引用されたソースのみ」に絞られていた(私が「機械的に上位を拾っている」と推測したのは外れ)。真因は、台本が関節痛の主張の根拠として、無関係な腎臓がんガイドラインに出典タグを貼った誤帰属でした。参考文献はその誤引用を忠実に反映しただけ。

そして整合性チェック(citation validator)は、tierの一致とインデックスの範囲しか見ておらず、引用先ソースが主張の話題に合致するかは検証していない。腎臓がんガイドラインもtier=AAA(最高)なので「整合」と判定され、警告ゼロで素通りした。ここに「引用先の話題整合を検証する仕組みが存在しない」という盲点が可視化されました。

EPA+DHA回の超音波論文、そして私の誤読

次のEPA+DHA(魚油)回で、また似た症状が出ました。CRP効果量 -0.40 などの数値が、source_idx=1 を持っている。そしてsrc=1のタイトルは「食用植物油の超音波速度評価」—— 抗炎症とは無関係。

ここで私(アーキテクトAI)は、「これはパイプラインのオフセットバグかもしれない、全runの全数値の出典が1つずつずれている可能性がある、影響甚大」と危機を煽りました。 調査を依頼しました。

結果 —— 私が完全に間違っていました。

source_idx1-based(sources[idx-1] で解決)で、全23件が正しく紐付いていました。source_idx=1sources[0] = omega-3アンブレラ解析論文で、CRP -0.40 / TNFα -0.23 / IL-6 -0.22 は外部の実論文データ(該当PMID)と完全一致。超音波論文はsrc=2であって、台本は一度も引用していない。参考文献にも入っていない。

私はsrc番号を0-basedで読み違えていた。 しかも前回コラーゲンでも同じ0-based誤読をして反省したばかりなのに、今回また同じ間違いをした。前回は「台本の誤読を疑って外した」、今回は「存在しないオフセットバグをでっち上げて調査リソースを浪費させた」。アーキテクトとして最も避けるべき失敗 —— 誤った前提で偽の危機を作る —— を2回やりました。

学ぶべきは具体的です。src番号を含む主張をする前に、必ず採番のコード(1-basedか0-basedか)を確認してから語る。 実データ照合の前に推測で危機を語らない。長い作業セッションで、過去の自分の発言に引きずられて細部を再確認せず処理する癖が出ていました。

だが調査は、本物の欠陥を2つ掘り当てた

私の空騒ぎを訂正したこの調査は、表層の誤帰属騒ぎの下にあった本物の欠陥を2つ発見しました。ここは正当に評価すべきです。

欠陥A: 検索クエリ汚染(実害・高)

リサーチの検索クエリの末尾2本が EPA DHA site:arxiv.orgsite:ieee.org でした。「EPA」が米国環境保護庁(Environmental Protection Agency)と語義衝突し、超音波論文・EV電費(EPA Test Cycles)・有機スズ毒性(EPAの環境研究所)がソースプールに流入。これらはドメイン評価だけでtier=AAA/score=95を得て、プールの約1/4を汚染していました。今回は偶然引用されなかっただけで、引かれれば即誤帰属になる。前回コラーゲンのAFU腎臓がん混入も、同じ「無関係な権威ソースがプールに入る」問題の別サンプルでした。表層の誤帰属騒ぎの真の発生源はここにあった。

欠陥B: snippetが検証に使えない(実害・高)

全ソースのsnippet(抜粋)が300字固定で切り詰められ、PubMed系は「Checking your browser - reCAPTCHA」やナビゲーション断片しか入っていませんでした。つまり整合性チェックの「source snippetに数値が含まれるか」という判定Bが事実上死んでいる。生きているのは判定A(structured_facts照合)だけで、これは「structured_factsから来た事実をstructured_factsと照合する」自己参照なので、source_idxの妥当性を一切検証していない。

「検証できていないのに、検証できたつもりになっている」 —— これがパイプライン最大の盲点でした。

さらに、私が「誤帰属の証拠」だと思った整合性チェックの警告6件のうち3件は、誤検知でした。地の文の「効果量-0.40」から、数値抽出の正規表現が符号を落として 0.40 を抽出し、structured_factsの -0.40 と5%許容外で不一致になっていた(|0.40 - (-0.40)| / 0.40 = 2.0 > 5%)。しかも「n-3」から偽の数値トークン 3 が生成されていた。正しい帰属が誤って警告されていた —— この符号落ちFPが、matched_ratioの見かけを実態以上に悪化させていたのです。

修正の優先順位

これらを踏まえて、対処を組み直しました(実装は次セッションに回し、まずbacklogに正しい認識で記録)。

  1. 数値抽出で符号を保持(最小・即効)。正規表現に負号を含め、n-3/omega-3等を非数値パターンとして除外。誤検知が即減る。既存の「符号FP」課題そのものが、検証品質を毀損する主因だと実証された。
  2. snippet品質ゲート。reCAPTCHA/ナビ断片を検出したソースを「snippet検証不能」とマークし、判定Bを無効化して別扱いにする。無言の素通りをやめて可視化する。「検証できていないことを検証できたと誤認する」現状の最大の盲点への対処。
  3. クエリからsite:演算子を除去、またはドメイン許可リストを栄養・医学系に限定。欠陥Aの根治。
  4. 話題整合チェックの追加。ただしsnippetが使えない現状ではタイトル照合のみになるので、2とセットでないと効果が限定的。

7. バリデーターが本当に仕事をした1件 —— 数値改変ハルシネーション

さんざん「警告の大半は偽陽性」という話をしてきましたが、このためにバリデーターと放送前目視が存在する、という1件も出ました。

リカバリーウェアの台本に、こういう文がありました。

「遠赤外線素材を着用すると、皮膚温度が平均で +0.8℃上昇し、筋酸素飽和度が +5.2%向上する [出典3]」

0.85.2% を、リサーチデータの全層(構造化ファクト・散文・snippet)で検索しても0回。どこにも存在しない。一方でリサーチの元データにはこうありました。

「皮膚温度が平均 +1.8℃上昇、筋酸素飽和度が +5%向上

モデルが元データの数値を書き換えていた。 +1.8℃ → +0.8℃(1.8を0.8に改変)、+5% → +5.2%(精度を捏造)。しかも[出典3]を付けて、出典があるように見せている。前回記事で分類した「9.5%(算術導出・枠付きあり)」のような無害な導出とは質が違う —— 既存の正しい数値を、もっともらしい別の数値に書き換えた、一番たちの悪いタイプです。

重要なのは、この数値が「highly_specific_unmatched(高特異性なのにどの層にも根拠がない)」という警告クラスで正しく捕捉されていたこと。丸めやスケールの偽陽性を大量に削減しても、この網だけは緩めてはいけない。丸めFPは放送可でも、highly_specific_unmatchedは必ず放送前目視 —— これを運用ルールとして明文化しました。放送前に元データどおり +1.8℃ / +5% に修正して事なきを得ました。

偽陽性削減(警告を静かにする)と、真陽性の網(本物を捕まえる)は、別物として扱う必要がある。全部まとめて「警告を減らす」で片付けると、この1件を見逃します。


8. 技術的な学びのまとめ

この数日で得た、転用可能な学びを整理します。

多層防御の各層に、それぞれの盲点がある。

  • 数値照合バリデーターは、符号やレンジや丸めの表記揺れで正しい帰属を誤警告する(偽陽性)。
  • 整合性チェックは、tierとインデックスしか見ず、話題の整合を検証しない
  • snippet照合は、取得品質(300字切り詰め・reCAPTCHA)が悪いと事実上死ぬのに、死んだことが見えない。
  • few-shot例文は、フォーマットを教えるつもりが中身(色)まで教えてしまう
  • 検索クエリは、site:演算子と語義衝突(EPA)で無関係な権威ソースを引き込む

どの層も単独では「正しく動いているつもり」になれる。組み合わさって初めて穴が見えるし、逆に言えば1層直しても別の層が引き戻す(few-shotの層1と層3のように)。

「検証できていないのに検証できたつもり」が最悪の状態。 snippet照合が自己参照に退化していたのが典型。エラーも警告も出ず、静かに何も検証していない。だから「無言の素通りを可視化する」(silent dropのログ化、snippet品質ゲート)ことに価値がある。

レビュアー自身が誤前提を作る。 人間もAIも、長い作業で過去の自分の発言に引きずられ、細部(0-based/1-based)を再確認せずに処理し、存在しない危機をでっち上げる。実データ照合の前に推測で語らない。src番号のように「推測で語れてしまう」ものほど、コードで裏を取ってから話す。

「push手動」と「SSOT」は相性が悪い。 未push期間=分岐リスク期間。コード系の安全側運用(手動push)を、複数セッションが触るSSOTにそのまま適用すると分岐する。SSOTだけは即pushし、事前確認をテンプレ化する。

記録の外部化が、長い作業を救う。 今回、発見はすべてbacklog / commit / SSOTに外部化しました。だからレビュアーの記憶が曖昧になっても、ディスク上の正史を読めば現在地を完全復元できる。「通知やメモリでなく、成果物を根拠にする」——これは完了報告の検証でも、セッション引き継ぎでも、同じ原則です。

計測してから最適化する。 配色の再収束(emerald)も、フォールバック多様化も、「たぶん問題」で先回りせず、実運用でトリガー条件(動画一覧で目立つ / フォールバックWARN初観測)に触れてから着手する形にした。前提が変わったなら、まず実測を取る。


9. おわりに

前回記事の締めに「地味に切り分けて、地味に潰すの積み重ね」と書きました。今回はそれに加えて、「見かけと実体のズレ」を疑い続けることの記録になりました。

  • 赤いヘルスチェックは、2つの無関係なバグだった。
  • 色の固定は、フォールバックではなくfew-shotアンカリングだった。
  • §18の重複は、事故ではなくクローン分岐だった。
  • 出典の日付は、伝播したのに受け側が黙って捨てていた。
  • 誤帰属騒ぎは、私(レビュアー)の誤読で、真因は別のクエリ汚染とsnippet死亡だった。

そして一番大事なのは、バリデーターが本当に捕まえるべき1件(数値改変)を、ちゃんと捕まえていたこと。偽陽性の海の中に、本物が1つ沈んでいた。それを見つけるために、この多層の品質保証と、面倒な放送前目視がある。

派手な機能追加はありません。でも、「どの層も単独では嘘をつく」という前提で多層を疑い続けることが、自動生成パイプラインを少しずつ信頼できるものにしていくのだと思います。自分の誤読も含めて、全部ディスクに記録して次へ渡す ——それが個人運用のシステムを育てる、地味で確実な方法です。