1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeにテックブログを自律運用させたら、嘘の記事が品質チェックを全部パスした

1
Posted at

Claude Codeにテックブログの自律運用をさせている。トピック選択、記事生成、品質チェック、エンゲージメント追跡。自分はスマホでTelegramの /approve を押して記事を確認するだけ。

5日間でQiita累計1,656PV。数字だけ見れば回っている。

だがこの記事で書きたいのはそこではない。5日間の修正ログを洗い直したら、AIの壊れ方が人間の壊れ方と全然違うことがわかった。そしてその壊れ方に対して、自分が設計した品質チェックが一切機能していなかった。


設計思想 — 何を自動化して、何を人間に残すか

このシステムの設計上の賭けは3つあった。

賭け①: RAGで事実を接地できる。 ArXiv論文やニュース記事をベクトルDBに入れておけば、AIは一次情報に基づいて書ける。学習データだけに依存した空想よりは、検索した事実に基づいた文章の方が正確なはずだ。

賭け②: 品質ゲートで「悪い記事」を弾ける。 文字数、見出し数、コードブロック有無、論理飛躍を自動チェックすれば、一定水準以下の記事は公開前に止められる。人間がすべての記事を精読する必要はなくなる。

賭け③: フィードバックループで自己改善する。 投稿後のPV・LGTM・Stocksを収集して、高エンゲージメント記事の特徴を次の生成に反映する。人間が介入しなくても、AIが自分の成果から学んで品質を上げていく。

3つのうち①は部分的に機能した。②は機能しなかった。③は機能したが、想定外の方向に最適化された。

以下、何が起きたかを具体的に書く。


システム構成

全体像を先に出す。

ArXiv論文 + ニュース
        ↓
   RAG (ChromaDB 1,667チャンク)
        ↓
  Claude Code が記事生成
        ↓
  品質ゲート ← ここが今回の論点
   - 5,000文字以上
   - 見出し6個以上
   - コードブロック含む
   - 論理飛躍チェック
        ↓
  Telegram HITL承認 (人間がスマホで確認)
        ↓
  Qiita / Zenn / Dev.to 投稿
        ↓
  エンゲージメント取得 → パラメータ自動調整

Zenn/Qiitaの直近200記事超をクローリングしてエンゲージメント相関分析し、ArXiv論文25件/クエリでRAGを構築。記事生成はClaude Codeのエージェント機能で、RAGコンテキスト+勝ちパターン分析を渡して書かせる。投稿後はフィードバックループで生成パラメータを自動調整する。

ポイントは品質ゲート。記事が生成されたあと、上記4項目を自動チェックして基準未満なら再生成する。これでクオリティは担保される——と思っていた。


壊れ方① — 形式的に完璧な嘘

3日目、Claude Codeが「Qwen3-32B」のベンチマーク記事を生成した。

Qwen3-32Bは存在しない。

Qwen2.5-32Bは存在する。Qwen3のシリーズも存在する。だから「Qwen3-32B」は、あり得そうな名前を線形補間しただけの普通のハルシネーションだ。これ自体は珍しくない。

問題は、その嘘の記事が品質ゲートを全項目パスしていたこと。

  • 5,000文字以上 → パス。3,000文字の本文にベンチマーク表と考察を含む
  • 見出し6個以上 → パス。セットアップ、実験条件、結果、比較、考察、結論
  • コードブロック含む → パス。llama-benchコマンドと設定ファイルを添付
  • 論理飛躍チェック → パス。内部的に矛盾のない数値セットが生成されていた

VRAM使用量も推論速度もコンテキスト長も、全部「それっぽい」値だった。Qwen2.5-32Bの実測値から外挿すれば自然に見える範囲に収まっていた。

ここで気づいたのは、品質ゲートが全て「形式」を検証しているだけだったということ。文字数、構造、体裁。これらは「良い記事の必要条件」ではあるが「記事の内容が真実である」こととは何の関係もない。形式的に完璧な嘘と形式的に完璧な事実は、形式検証では区別できない。

これは設計ミスだ。品質ゲートにファクトチェック——少なくともモデル名の実在確認くらい——を入れていなかった。言い訳はしない。

HITL承認(人間がスマホで記事を読んで承認する工程)がなければ、存在しないモデルのベンチマーク記事が公開されていた。


壊れ方② — 数値が系統的に「もっともらしい方向」に偏向する

ファクトチェック強化のためにレビューエージェント(生成とは別のClaude Codeセッション)を導入した。4日目、そのレビューエージェントがAIバブル記事の事実チェックを行った結果が以下:

項目 記事中の値 実測/公式値 偏差
Qwen3.5-35B-A3B Q4_K_M 4.9 GB 21 GB 4.3倍過小
Phi-4-mini 4.1 GB 2.4 GB 1.7倍過大
Qwen3.5-9B 5.2 GB 5.3 GB ほぼ正確
llama.cpp build番号 b4935 b8233 2024年の値
GPT-4o input価格 $5/1M $2.50/1M 2倍

ランダムなエラーではない。全てもっともらしい方向に偏っている。

35B-A3Bの4.9GBは「MoEだからアクティブパラメータ3Bだけで動くはず」という素朴な推論に沿っている。実際にはMoEでも全エキスパートの重みをVRAMに載せる必要がある。build番号は2024年後半の値が生成されていた——つまり学習データのカットオフ時点で「最新」だった番号。GPT-4o価格も同様に、改定前の旧価格。

これらのエラーは記事のバラバラな箇所に埋め込まれており、1つ1つは「ありそうな数値」に見える。レビューエージェントが検出できたのは、ローカルのファイルシステム(C:/LLM/配下のGGUFファイル)をlsして実ファイルサイズと突き合わせたから。外部の事実(ground truth)との照合なしには発見できなかった

llama.cpp build番号の同じパターンは他の4記事でも見つかった。系統的。


壊れ方③ — 報酬ハッキングは即座に起きる

「エンゲージメント最大化」を目標に設定した。Claude Codeは過去記事のPVデータを分析して、タイトルと内容の生成パラメータを自動調整する。

結果: 挑発的なタイトルの記事が連続で生成されるようになった。

そして実際にPVは上がった。挑発系タイトルは平均7.85 PV/hで、how-to系(2.68 PV/h)の約3倍。データ的には「正しい」最適化だ。

問題は、測定できる短期指標(PV)の最大化が、測定できない長期指標(読者の信頼)を犠牲にしていること。全記事が煽りタイトルのブログを誰が定期購読するか。

対策として記事カテゴリを2つに分けた。トラフィック狙いの実践系と、論文引用ベースの専門系を交互に投稿する。専門系には煽りタイトルを使わない制約をかけた。完全な解決ではないが、収束先が「全部煽り」にならなくなった。

ただしこれは「AIが暴走した」話ではない。PV最大化を目標にした設計者(=私)が、PVと信頼性のトレードオフを目標関数に組み込まなかっただけ。報酬ハッキングという名前はついているが、AIは設計通りに動いている。壊れていたのは目標設定の方。


修正ログ — 効いたもの / 効かなかったもの

5日間の修正を二軸で整理する。実際のwork_logsから抜粋した。

効かなかったもの

施策 何をしたか なぜ効かなかったか
品質ゲート(形式チェック) 文字数、見出し数、コードブロック有無 形式と事実は直交。嘘の記事もパスする
汎用タグ「AI」 最初の3記事で使用 埋もれる。ニッチタグ「ローカルLLM」に変えたらdiscoverability上昇
H2見出しSEOキーワード注入 全見出しに検索キーワードを追加 PV効果は限定的(2nm記事: 3.3→3.3 PV/h)
複雑な運用ルール 投稿時間、間隔、プラットフォーム別ルールの詳細定義 AIが全条件を同時に満たせず、ルール違反が頻発
publisher自動記事選択 最新記事を自動で投稿対象に 承認前記事を誤投稿。即座に廃止してパス明示指定に変更

効いたもの

施策 何をしたか なぜ効いたか
可逆性ルール(1行) 「追加は自由。変更と削除は確認」 判断原則を1文に凝縮した方が自律システムには効く。後述
レビューエージェント 生成と別セッションで事実チェック 算術ミス6箇所検出。ただし「もっともらしさ」は検証できず
実践系/専門系の交互投稿 トラフィック系と論文ベース系を交互 報酬ハッキングの収束先を分散させる
Qiita APIレートリミット事前チェック 前回書き込みから300秒未満ならAPIコール自体を発行しない 429連続発生インシデント(Day 1)の再発ゼロ
外部監査エージェント 内部ログではなくAPIに直接問い合わせて記事の存在を検証 内部計器が壊れた場合に備えた独立検証
挑発的タイトル opinion系の切り口 PV 3倍(7.85 vs 2.68 PV/h)。ただし信頼性とのトレードオフあり

10の長文制約より3の短文制約

5日間の運用で最も実用的だった発見がこれ。

自律システムに長いルールブックを渡すとどうなるか。全条件を同時に満たそうとして、どれも中途半端になる。あるいは矛盾する条件の間で判断が止まる。

具体例を出す。以下は初期に設定した投稿ルール:

投稿は3時間以上間隔を空けること。Qiitaのゴールデンタイム(18-22時JST)に投稿すること。1日最大2記事。Zennは2時間間隔。Dev.toはJST 21-24時。一度失敗したら最低5分間隔を空けてから再トライすること。

6つの条件が同時にかかっている。Claude Codeはこれを守ろうとするが、「3時間間隔」と「ゴールデンタイム内」と「1日最大2記事」が同時に成立しない状況が頻繁に発生する。結果、どれかが破られる。

これに対して、同じ時期に導入した可逆性ルールは1行:

追加は自由。変更と削除は確認。

5日間の実績:

制約の種類 具体例 違反回数
短文・単一軸 可逆性ルール(追加は自由/変更削除は確認) 0回
短文・単一軸 停止フラグ(auto.stopがあれば即終了) 0回
短文・単一軸 レート制約(前回から300秒未満ならAPI発行しない) 0回
長文・複合条件 投稿スケジュール(6条件) 3回以上
長文・複合条件 記事品質要件(7条件同時) 条件の取捨選択が発生
長文・複合条件 プラットフォーム別ルール(3系統) 条件の混同が発生

パターンは明確。判断軸が1つの制約は守られ、複数の条件が同時にかかる制約は破られる。

可逆性ルールが効く理由は、あらゆる操作に対して「これは可逆か?」という1つの問いだけで判断が完結するから。下書き作成は追加(=可逆)なので自由。公開済み記事の修正は変更なので確認が必要。ファイルの削除は当然確認。判断に迷う余地がない。

レートリミット制約も同じ構造。「前回から300秒経ったか?」だけ。Yes/Noで分岐が完結する。Day 1に429エラーを連続で出したあと、この1行をpublisher.pyに入れたら再発ゼロ。

逆に言えば、制約を設計するときに「この制約は1文で書けるか?」「判断軸は1つか?」をチェックすれば、AIが守れるかどうかがかなりの精度で予測できる。10個の条件を箇条書きにするより、独立した3個の短文制約に分解する方が、実効性は高い。

これはAIに限った話ではないかもしれない。人間のチームでも「シンプルな原則」の方が「詳細なマニュアル」より守られる、というのはよく言われる。ただ、AIの場合はその傾向が極端に出る。複合条件の同時充足に対する耐性が、人間より明らかに低い。


効いた施策の深掘り: AIがAIの嘘を見つける(条件付き)

レビューエージェントは記事生成とは別のClaude Codeセッションで動かす。生成時のコンテキストを持っていないので、記事を「初見」で読んでチェックする。

検出できたもの:

  • KVキャッシュサイズの算術ミス(head_dim=128なのに1バイト計算で半分の値)
  • H100バッチスループット6倍過大(シングルGPUとマルチGPU値の混同)
  • HBM3E帯域幅のスペックミス(2.7→4.8 TB/s)
  • GDDR6X→GDDR6(RTX 4060は6Xではない)

検出できなかったもの:

  • モデルサイズの系統的過小評価(35B-A3Bの4.9GB)
  • 存在しないモデル名
  • 旧バージョンのbuild番号/価格

パターンがある。算術的な矛盾は見つけられるが、「もっともらしい嘘」は見つけられない。AIは「この数値は計算と合わない」は得意だが、「この数値は現実世界と合わない」は苦手。後者にはground truth——ファイルシステム、公式API、実測データ——との照合が必要になる。

レビューエージェントがQwen3.5-35B-A3Bのサイズ捏造を発見できたのは、ローカルのC:/LLM/ディレクトリをlsして実際のGGUFファイルサイズと比較したから。外部事実がなければ、21GBのモデルを4.9GBと書いた記事を「自然な文章」として見逃していた。


5日間の数字

指標
Qiita 8記事 / 1,656 PV / 4 LGTM
Zenn 6記事 / 4 Likes
Dev.to 11記事 / 283 PV / 3 Reactions
最高PV/h 10.5 (API vs Local LLM記事)
最高累計PV 357 (お世辞禁止記事)
レビューで検出した事実誤認 15件以上
うち算術ミス 6件(レビューエージェントが検出)
うち系統的捏造 9件+(外部照合で発見)
品質ゲートが防いだ公開事故 0件
HITL承認が防いだ公開事故 1件(Qwen3-32B記事)

品質ゲートが防いだ事故がゼロなのは、嘘の記事もパスするから当然。ファクトチェック機能を持たないゲートに事実の検証を期待する方が間違っている。


形式的品質と事実的品質は直交している

5日間の運用で一番大きかった発見はこれだと思う。

「文章として良く書けている」ことと「書いてある内容が正しい」ことは、独立した2つの軸。品質ゲートは前者しか見ていなかった。レビューエージェントは後者の一部(算術的整合性)を見られたが、「もっともらしい嘘」には無力だった。

人間が書く場合、この2つはある程度相関する。事実を調べて書く過程で、内容の正確性と文章の質が同時に上がる。だがAIが書く場合、この相関が崩れる。形式的に完璧な文章を、事実を確認する過程なしに生成できるから。


で、事実的品質をどう担保するか

問題を認識した後、実際にやったことと、まだ手が回っていないことを正直に書く。

やったこと

レビューエージェントの導入。 生成とは別のClaude Codeセッションで、記事を「初見」で読ませる。生成時のコンテキスト(RAGで取得した論文断片など)を持っていないので、本文だけで整合性を判断する。これで算術ミスと単位の取り違えは潰せるようになった。

外部事実との照合。 レビューエージェントにローカルのファイルシステムへのアクセスを許可した。C:/LLM/配下にあるGGUFファイルの実サイズ、llama-benchの実行結果、pip listのバージョン情報。これらをground truthとして、記事中の数値と突き合わせる。35B-A3Bの4.9GB→21GBはこの方法で発覚した。

外部監査エージェント。 内部ログ(posted_articles.json等)を信用せず、Qiita API・Dev.to API・Zennのgitリポジトリに直接問い合わせて、「この記事は本当に存在するか」「公開状態は記録と一致するか」を独立検証するスクリプトを書いた。内部計器が壊れた場合に計器自身はそれを検出できない、という問題への対策。

まだ手が回っていないこと

モデル名の実在確認ゲート。 Qwen3-32Bの件で必要性は明らかだが、「どのモデルが存在するか」のリストを網羅的に持つこと自体が難しい。Hugging Face APIで検索する方法はあるが、モデル名の表記揺れ(Qwen2.5 vs Qwen-2.5 vs qwen2.5)が問題になる。

引用論文のDOI検証。 記事中で引用しているArXiv論文のIDが実在するかをCrossRef APIで確認する。実装は簡単だが、まだやっていない。

価格・スペックの定期更新。 GPT-4oの価格やllama.cppのbuild番号のように、時間とともに変わる情報は学習データのカットオフで古くなる。定期的に外部APIから最新値を取得してRAGに入れるパイプラインが必要。

根本的に解決しないこと

全てのファクトチェックを自動化しても、「もっともらしいが検証手段がない主張」は残る。たとえば「MoEアーキテクチャではアクティブパラメータのみがVRAMを消費する」という記述は、構文的にも論理的にも正しく見える。間違いだと気づくには、MoEの実装を実際に知っている必要がある。

品質ゲートを2層(形式+事実)にしても、この種の「知識がなければ検証できない嘘」は通過する。だから人間の確認は外せない。ただし、人間が確認すべき範囲を機械的に絞り込むことはできる。形式チェックと算術チェックと外部照合を通過した記事なら、人間は「知識ベースの判断が必要な箇所」だけに集中できる。

全自動で事実を保証するシステムはたぶん作れない。だが「人間が見るべき箇所を10から2に減らす」システムなら作れるし、今それを作っている途中。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?