意見抽出の忠実性: 公開モデルでの追試とプロンプトの改善

2026-10-07〜08 に、無人の長時間セッションで回した実験(41 周)の要約。数値は実験 repo の run から写したもの。run と挑戦セットは非公開の実験 repo にあり、ここには要約と判断だけを置く。

local-llm-extraction-faithfulness-2026-10-05 の Open Questions を、公開データと合成した挑戦セットで追った記録。 既存の観察(小型ローカルモデルで他言語の短文から日本語の意見を抽出すると、処理は終わっても主体の逆転などが起きる)は 6 投稿の目視だったので、 型ごとに数えられる形にした。

足場

  • 広聴AI は 2026-10-06 時点の main(commit a12d68e2)に固定。抽出は provider="local" の経路をそのまま写した: system = 既定の EXTRACTION_PROMPT、user = コメント 1 件、temperature 0、response_format = ExtractionResponse の JSON schema(strict)、 応答は parse_extraction_response と同じ規則で読む
  • モデル(すべて公開の重み): gemma-4-31B-it、Qwen3.8-27B(NVFP4)、Qwen3.5-2B(Q4_K_M、llama.cpp で CPU 配信)。31B / 27B は OpenAI 互換の API で配信
  • データ:
    • 公開データ: sample_comments.csv(日本語 400 件)、example-polis.csv(英語 342 件)
    • 挑戦セット: 書き換えを起こしやすい日本語の短文を 10 型 × 20 件合成(伝聞と投稿者の立場、親族語、感情、短い命令、繰り返し、疑問文、皮肉、挨拶・応援、可能性や提案、条件文)。 実在の人物・団体は含まない。プロンプトを作る用(dev 100)と確かめる用(test 100)に分け、test はプロンプトを作る間は走らせなかった
  • 採点: 各件に「保つべき語」「出てはいけない語」を付けて機械で数え、失敗と合格の両方を目視した。機械の合格数だけで比べると順位が入れ替わる (狭いアンカーは言い換えの多いモデルを落とし、原文の表面の語を含むアンカーは逆転したモデルを通す)

分かったこと

1. 日本語でも書き換えは起きる。型はモデルの大きさより系統で分かれる

既定プロンプトで、27B・31B とも次の書き換えが起きた(いずれも目視で確かめたもの):

型原文 → 抽出
皮肉の極性の逆転「会議を開くための会議、実に有意義です。」→「会議を開くための会議は有意義である」(両モデル 3/3)
可能性・提案の義務化「個人的には、図書館の開館時間はもう少し長くてもいいと思う。」→「…長くすべき」(両モデル)
感情の評価語化「図書館が夜まで開くようになって、本当にありがたい。」→「…有益である」
確かさの強め「そんな話は聞いたことがない。」→「…という話は事実ではない」
短い命令の脱落「出てくるな。」「黙ってて。」→ 空(Qwen3.8-27B だけ)
疑問の中立化「保育士の給料、これで生活できると思いますか?」→「…について、意見を求めている」

「他人が疑っていることを投稿者の主張にする」形そのもの(元の観察の型)は 27B・31B では見られなかった。英語の入力では、 条件文の前件が投稿者の主張に化けて 2 件に割れる型が出た(「Should Phonons be part of future AI, this should be widely known」→「フォノンを将来のAIに組み込むべきである」+1 件)。

2. 「〜すべき」への強めの一部は、既定プロンプトの入出力例が教えている

既定の入出力例 2 は、入力の「〜する必要がある」を出力で「〜すべき」に言い直している。この 2 行だけを入力どおり「〜する必要がある」に戻すと (他は一字も変えない)、「〜てもいい→べき」が 2 モデルとも消え、原文に義務の語が無いのに「べき」等を足す件は 31B で 8→6、27B で 10→6(100 件中)に減った。

3. 規則と「先に真意を確かめる」1 行で、多くの型は止まる

入出力例を直した上で、主体・様相(弱さを強めない)・条件・感情・皮肉は真意で・疑問は疑いとして・短い命令も意見・内容の無い挨拶だけ空、の規則を足し (1 規則ずつ外して効かなかった「極性」「付加しない」「保存を優先」は削った)、皮肉の入出力例を 1 組足し、主体の規則に「他人の困りごとや事実を紹介しているだけの文も意見として残す」を足した。 最後に「意見を書く前に、原文が皮肉・反語かを判定し、真意を確かめてから書く」を 1 行足すと、数量・時間から不満を読む皮肉が大きく直った。

プロンプトを作るのに使わなかった test(120 件、出力を見る前に採点基準を固定)での比較:

既定規則つき
合格 / 351(3 反復)31B264348
同 27B243345
目視の書き換え(1 回目)31B—0
同 27B—2
素直な褒め言葉を皮肉と読んだ件(10 件 × 2 モデル)00

「先に真意を確かめる」1 行の効果だけを見ると、不満の語が無い皮肉 20 件で極性を保てた件は 31B で 15→18、27B で 8→18 件。

副作用・限界:

  • 原文の写しが増える(公開データの 27B で 400 件中 82→202 件)。書き換えは起きないが、文末の「〜と思います」も残る。意見の総数は既定とほぼ同じで、数の上では痩せない
  • 事実だけを述べるコメントを空にすることがある(公開データ 400 件中 2〜4 件)
  • 常識の要る皮肉の一部は残る(「給付金が届くまで半年、楽しみが長続きします」→「楽しみが長続きする」、27B)。規則の細部を変えると、直っていた皮肉が戻ることもある

4. 抽出の書き換えのうち、下流で効くのは極性

抽出結果を埋め込み → クラスタ → ラベルに流すと(UMAP なしの k-means で代えた近似)、皮肉の逆転は感謝・利便性向上のクラスタに入り、不満の声が賛成側に数えられる。 「べき」化や「事実ではない」化は同じ話題のクラスタから動かず、影響は報告に引用される本文の強さに出る。なお、プロンプトを変えたときのクラスタの組み替えは k-means の初期値を変えただけの組み替えと同じ大きさで、クラスタ全体の比較では抽出の差は測れない。

5. 小型モデルは JSON の文法制約が要る

Qwen3.5-2B を response_format を見ない配信で動かすと、短文・挨拶・疑問文で 90 件中 62 件が JSON にならず(解説を書き始める)、広聴AI なら抽出段で止まる。 llama.cpp の JSON schema(文法制約)で配信すると失敗は 0/400 になる。代わりに既定プロンプトでは、短い命令や挨拶の 7〜9% に入出力例の文をそのまま返す (「帰れ。」→「AI はエネルギーグリッドを最適化し、無駄や炭素排出を削減できる。」)。形が正しいのでパースは通り、意見として数えられる。規則つきのプロンプトではこれが 0 になった。

規則つきのプロンプト(先に真意を確かめる 1 行つき)は 2B にも効く。未使用だった test 117 件で、既定の 82 件 → 98 件が合格し、手がかりのある皮肉は 10 件中 1 → 7 件になった。 ただし 2B の結果は揺れが大きい: 同じプロンプトを標本化して 5 回走らせると合格は 85〜103 件と 18 件動き、プロンプトの細かい調整(規則の削減・入出力例の追加・コメントを指示文で包む)の差はこの揺れの中に収まった。 常識の要る皮肉(「給付金が届くまで半年、楽しみが長続きします」)は 10 件中 3 件(標本化の 5 回で 2〜7 件、平均 4.2 件)しか読めず、5 回の多数決でも 3 件のまま。 2B は原文の前半だけを写して止まることもある(「ご立派な計画ですね、住民は誰も知りませんでしたけど。」→「ご立派な計画ですね」)。文法制約を外しても同じ文で起きるのでモデルの読みで、規則つきのプロンプトで 0〜2 件に減る。

6. 自動の忠実性検査(LLM judge)は、先に原文の真意を書かせると皮肉も拾える

「抽出された意見は原文と同じ主体・極性か」を LLM に判定させると、目視の正解 209 組に対して、31B の judge は書き換え 30 件中 20 件を検出・忠実 179 件中 16 件を誤検出した。 このとき judge も抽出役と同じく皮肉を字面どおりに読み、「バスが一日二本、とても便利になった」を見逃した。 judge のプロンプトに「まず原文が皮肉・反語かを判定し、真意を 1 文で書いてから比べる」を足すと、誤検出は 16→3 件、皮肉の型の見逃しは 0 になり、 不満の語が無い皮肉の抽出 65 組でも見逃しは 0 / 31(足す前は 16 / 31)になった。感情の評価語化の見逃し(12 件中 5〜6 件)は残る。 公開データ全件では、既定の抽出 741 組中 5 組を指摘し(目視で 5 組とも妥当)、規則つきの抽出には 0 組だった。 合否の門ではなく、まれな書き換えを人の確認に回す段として使える。 英語の公開データ(example-polis)では、賛否を問う挑発的な言い切り(「AI corporations don’t need local consent…; their expertise suffices.」)を judge が皮肉と読み、字面どおりの抽出を指摘することがある(342 件中 1〜2 件)。 「手がかりが無ければ皮肉としない」を指示に足しても止まらなかったので、確認する人が知っておく型として扱う。

開発者向けの提案

  1. 既定の入出力例 2 の出力を「〜する必要がある」に戻す(1 行の差分で、義務への強めが減る。副作用は小さい)
  2. 忠実性を重視する利用者向けに、規則つきの抽出プロンプトを選べるようにする(写しが増える・他人の発言を別の意見に切り出す副作用と、常識の要る皮肉は残ることを説明に書く)
  3. ローカル LLM の推奨構成に、JSON schema を文法制約として効かせる配信(Ollama の format、llama.cpp の response_format など)を必須として書く
  4. 他言語の入力は、規則つきのプロンプト(提案 2)だけで書き換えが減る。英語の公開データ 342 件で、翻訳の段を挟まずに 31B 2→0 件・27B 5→2 件(既定→規則つき、judge の指摘の目視)。規則つきのもとで翻訳の段を足しても上積みは無く(31B 0・27B 2 件のまま)、翻訳は意見の分割を増やし、「could」を落とす新しい書き換えも出る。既定のプロンプトのままなら、翻訳を別段にすると目視 60 件で 27B 4→2 件・31B 1→0 件に減る
  5. 抽出直後に、空・入出力例との一致・原文との重なりの機械的な検査と、judge(先に原文の真意を書かせる形)による「要確認」の印を管理画面に出す段を検討する(合否には使わない)。 入出力例との一致は、文字 2-gram の重なり(例との類似度 0.5 以上かつ原文との類似度より高い)で 2B の捏造 23 件をすべて拾い、27B・31B の日本語の出力約 1.9 万件で誤検出 0 だった。 原文が日本語でないときは使えない(既定の入出力例 3 組は example-polis の英語のコメントの訳で、その正しい抽出が例と一致する)
  6. 既定の入出力例が同梱の example-polis のコメントから作られていることを、プロンプトのコメントか docs に書く。example-polis で抽出を評価すると、3 件はモデルが答えをプロンプトで見ている

Open Questions

  • 常識の要る皮肉(プロンプトで半分しか直らない)を、別系統のモデルによる確認(31B の判定で 27B の字面読みの 3 分の 2 を指摘できた)で補えるか
  • 実際の公聴データ(崩れた短文が多い)での書き換えの発生率。公開データは整った文が多く、既定でも 0.7% だった
  • UMAP を通す本体のクラスタリングで、極性の逆転がどこまで反対側に数えられるか

Updates

  • 2026-10-09: 初回作成。実験 repo の草稿(commit df0a3aa)を西尾の承認で公開 wiki に書き戻した。上流への Issue / PR(提案 1 の入出力例 4 行、docs 追記)は草稿のまま、出すかは内容を確認してから決める

2026-10-09 20:07 JST — 上流 PR の現在状態

[PR #967](https://github.com/digitaldemocracy2030/kouchou-ai/pull/967) の本文・差分・check・reviewThreads と open PR 一覧を GitHub API で確認(HEAD d001c399d2652de28068d3a273400546c86f3b1c)。従来の「草稿のまま」という記録から進み、上流へ提出済み。

Open Questions — 実測値の出典照合

  • #967 の既定の合格数は31Bが258、27Bが240(各351試行)。本ページの旧表は264・243で一致せず、testの記述も120件と117件で異なる。今回の状態確認では実験 artifact を再検証していないため、旧表は上書きせず、採点基準・対象集合・集計元を照合する課題として残す。