提案資料からAI臭を消す52のルール — 生成AIの日本語を機械検査する
生成AIに提案資料を書かせると、内容は妥当なのに読んだ瞬間「AIが書いたな」と分かる文章になります。顧客に出す資料でこれが起きると、中身の信頼まで落ちます。手で直せば消せますが、毎回目視で直すのは続きません。
そこで、社内の提案資料生成ツールに日本語のトーンを機械検査するリンターを組み込みました。現在52ルール(エラー37/警告15)が動いていて、エラーが1件でも残っている資料はリリースできません。この記事では、このうち自社の固有名詞に依存する5ルール(社内ツール名の混入・作業メモの残置)を除いた47ルールを公開します。そのままコピーして使える形にしてあります。
AI臭は「額縁」から生まれる
NG例を集めていくうちに、共通する構造が見えてきました。事実を述べた上に、「これは重要だ」というメタな一枚を重ねてしまうことです。
OK ここを重点的に検討します。
どちらも同じことを言っていますが、前者は「重要である」と宣言しています。重要さは内容と配置で示すもので、本文で宣言した時点で安っぽくなります。人間の書き手はこれを自然に避けますが、生成AIは論点を持ち上げる語を好んで使います。
この観点で分類すると、AI臭は次の系統に分かれました。
(b) 比喩的な抽象名詞 — 「検討の重心があります」→ 何をするのか述語から消える
(c) 状態の言い換え — 「担当範囲の中間に落ちています」→「どちらが担当するか決まっていません」
(d) 業界風の動詞比喩 — 「工数に乗ります」→「追加工数が必要です」
(b)〜(d) に共通するのは、比喩を使った瞬間に「具体的に何をするのか」が文から消えることです。「重心があります」と書けば、何を詰めるのかを書かずに済んでしまいます。読み手にとっては情報が減っているのに、書き手は書いた気になれる。これが提案資料でとりわけ困る点です。
体言止めが最大の発生源
語彙のNGリストとは別に、もっと影響が大きい要素があります。体言止めです。
OK 対等な補完パートナーとして組みます。
NG 金融ドメインの上流統御。
OK 金融案件は上流を詳細化できるかどうかで成否が決まります。
体言止めは字面が締まって見えるので、キャッチコピーの型として使われがちです。ただし提案資料の本文で使うと、主語と述語の関係が消え、何を約束しているのかが読み取れなくなります。「パートナーシップ。」では、それをどうするのかが書かれていません。
そのため本文・キャッチ・転換点では体言止めを使わず、必ず述語で言い切るというルールにしました。見出しやラベルなど、一覧性が必要な短い要素だけは例外です。
47ルールの一覧
実際に動いている辞書の全件です。error は1件でもあればリリースを止めます。warn は文脈判断が要るもの(見出しなら許容など)で、目視で判断します。
残る5ルールは社内ツール名や製品名を並べただけの辞書で、他社にそのまま持っていっても意味がないので省きました。実際に使うときは、この一覧をそのまま持っていくより、自社の資料でNG例が出るたびに1行ずつ足していく方が機能します。この辞書も最初は10行程度から始まり、レビューで指摘が出るたびに追加して現在の件数になりました。
実装: 正規表現と、正規表現で書けないもの
語彙のルールは正規表現の配列です。カテゴリ・深刻度・直し方をセットで持たせて、検出時にそのまま提示します。
const RULES = [
// (a) 大げさなメタ評価語 — 論点を持ち上げる額縁
{ re: /核心/g, cat: 'メタ評価語', sev: 'error',
hint: '「ここを重点的に検討します」等、述語でフラットに' },
{ re: /本質/g, cat: 'メタ評価語', sev: 'warn',
hint: '額縁になっていないか確認' },
// (b) 比喩的な抽象名詞
{ re: /温度感/g, cat: '比喩抽象名詞', sev: 'error',
hint: '「先方の意向」等の業務語に' },
{ re: /重点(?!的)/g, cat: '比喩抽象名詞', sev: 'warn',
hint: '「重点的に」はOK。名詞単体の「重点」は要確認' },
// ...
];一方、体言止めは単語ではないので正規表現の辞書では書けません。ここは日本語の性質を使って判定しています。日本語の述語はほぼひらがなで終わるため、句点の直前が漢字・カタカナ・英数字なら体言止めの可能性が高い、という判定です。
// 日本語の述語はほぼひらがなで終わる。
// 「。」の直前が漢字・カタカナ・英数字なら体言止めの可能性が高い。
const HIRAGANA_END = /[ぁ-ゖー]$/;
const LABEL_MAX_LEN = 13; // これ以下は見出し・ラベルとみなして対象外
function checkTaigendome(text) {
const findings = [];
const sentences = text.split('。').map(s => s.trim()).filter(Boolean);
sentences.forEach((s, idx) => {
const isLast = idx === sentences.length - 1;
const hadPeriod = !isLast || text.trimEnd().endsWith('。');
// 「。」で閉じた文は長さに関係なく判定する。
// 「。」が無い断片は見出しの可能性があるため、長さと読点で絞る。
const sentenceLike = hadPeriod
? s.length > 5
: s.length > LABEL_MAX_LEN && s.includes('、');
if (!sentenceLike) return;
if (!HIRAGANA_END.test(s.slice(-1))) findings.push(s);
});
return findings;
}この判定は完全ではありません。「〜を実施。」のような文は拾えますが、「〜が必要だ。」のように「だ」で終わる文も検出対象になります。そのため体言止めは error ではなく warn にして、機械が候補を出し、人が判断する形にしています。誤検出が許される深刻度に置くことで、精度の低い検査でも実用になります。
どこに置くか
このリンターは、資料を生成するパイプラインの最終段に置いています。社内ツールでは4つのAIエージェントが分業していて、構成を作る担当、章立てに落とす担当、実装する担当、品質を検査する担当に分かれます。リンターは4番目の担当が使う道具です。
重要なのは、生成する側と検査する側を分けていることです。同じエージェントに「良い文章を書いて、かつ自分で検査して」と頼むと、自分が書いたものを妥当だと判断しがちです。辞書という外部の基準を、別の工程から当てる形にしています。
# 資料をビルドする前に必ず通す node scripts/lint-tone.mjs output/deck.js || exit 1 # 出力例 # ✗ error L42 [メタ評価語] 「核心」 → 述語でフラットに # これが今回の検討の核心です # ⚠ warn L58 [体言止め?] 「対等な補完パートナーシップ」 → 述語で言い切る # # 結果: error 1件 / warn 1件 # error はリリースブロッカー。修正してから再実行する。
人がレビューで「なんとなくAIっぽい」と指摘するのは、指摘する側も疲れますし、基準が人によってぶれます。辞書に載っていれば機械が止める、載っていなければ通すという形にすると、レビューの争点が「この語を辞書に入れるかどうか」に移ります。個別の文章ではなく基準を議論できるようになるのが、機械化のいちばんの効果でした。
効果と限界
自社紹介資料82枚に対して適用したときは、約120文を書き換えることになりました。生成直後の資料には、それだけの量のAI臭が残っていたということです。
限界もはっきりしています。このリンターは「悪い文章」を検出できません。検出できるのは、辞書に登録した語と、体言止めの候補だけです。内容が薄い、論理が飛んでいる、顧客の状況を踏まえていない、といった本質的な問題は素通りします。文章の質を担保しているのではなく、特定の失敗パターンだけを機械的に止めていると理解した方が正確です。
それでも導入する価値があるのは、この失敗パターンが頻出する上に、書き手が自分では気づきにくい種類のものだからです。人間のレビュアーの時間は、内容の妥当性という機械が扱えない部分に使うべきで、語彙のパターンマッチは機械に任せられます。
なお、この記事自体もこのリンターを通してあります。書きながら数回エラーが出て、そのつど直しました。ただし表1と、NG例として引用している文はもちろん引っかかるので、検査対象から外しています。
AIを業務プロセスに組み込むとき、品質をどう担保するかは必ず論点になります。当社の取り組みは AI・自動化の導入、資料生成ツールそのものは Atelier に掲載しています。