①設計思想と技術構成編 の続きです。今回はコードの話ではなく、「個人開発でAIにどこまでデータ品質のチェックを任せて大丈夫か」という話を書きます。
TL;DR
ユーザー報告をきっかけに、単語1万2千語・フレーズ6千7百件(合計約1.9万件)のうち、例文とその日本語訳が食い違っているという不具合が見つかった
全件をAIエージェントに人手点検の代わりに読ませて直したが、1回のAI修正をそのまま信用せず、必ず別のAIエージェントに独立で再確認させてから本番に反映する、という二段構えのパイプラインにした
実際にこのパイプラインの査読ステップが、最初の修正案の事実誤認・極性の逆転(「間違っていない」と「間違っている」が入れ替わる)を複数件検出した
逆に、査読エージェント自身が根拠として引用したデータが古いローカルDBのもので、本番とは内容が食い違っていたという事故も起きた。AIの「確認しました」を鵜呑みにせず、最終的には自分で本番に直接クエリして裏取りした
結果、単語側で約400件・フレーズ側で12件を修正、加えて分野タグと内容が食い違っていた6件も発見・修正。全件バックアップ→ドライラン→適用→ヘルスチェックの手順を徹底した
発端: ユーザー報告から見つかったバグ
詳細画面で、ある単語の例文とその日本語訳を見比べると、明らかに違う文章の訳になっていました。
例文: We toggled a spare GPIO pin to trigger the oscilloscope.
訳文: このボードのGPIOピンを使ってLEDを制御した。
原因を追うと、例文本体(words.example)とその訳(detail列のJSON内example_ja)が別カラムに保存されており、過去に書いた「英単語名だけで突き合わせて訳をマージするスクリプト」が、後から例文を差し替えたときに古い訳を残したままにしていたことが分かりました。同じ英単語で複数の意味・例文がある場合に、名前だけの突き合わせでは別の例文の訳を拾ってしまう、という典型的なデータ移行バグです。
機械的に検出できる分(detail.examples[]に同じ英文の訳が既にあるケース)は自動修復スクリプトで対応できましたが、照合先が無い語(本番で6,700語超)は、人が英文を読んで訳が合っているか判断するしかありません。1.9万件近い規模で、これを人手だけでやるのは現実的ではありませんでした。
二段構えのAI査読パイプライン
考え方はシンプルです。AIの1回の判断を、そのまま本番に反映しない。
レビュー(発見)フェーズ: 複数のAIエージェントに分担させ、それぞれ「英文とその訳の内容が食い違っていないか」を数百件単位で判定させる。食い違っていれば書き直し案を出す。一致していれば触らない(言い回しの違いだけで直訳から外れているものは対象外、というルールを明示)
査読(検証)フェーズ: 「修正あり」と判定された分だけを抜き出し、レビューを担当したのとは別のAIエージェントに、独立に同じ英文を読ませて「その修正は本当に正しいか」を判定させる。問題があれば査読エージェント自身がさらに書き直す
適用フェーズ: 査読を通った分だけをまとめ、本番DBのバックアップを取ってからドライラン→実際の反映→ヘルスチェック
このパイプラインを、単語(12,085件)・フレーズ(6,699件)それぞれ全件に対して回しました。バッチサイズは1回150〜200件程度、並列でレビューさせてから査読に回す、という単位です。
査読フェーズが実際に拾った問題
査読ステップは「念のため」ではなく、実際に機能しました。例えば:
レビューフェーズの修正案「間違ってはいない…とまでは言い切れませんが。」("That's not wrong, exactly."の訳)に対し、査読エージェントが極性が原文と逆(「間違っていない」方向のはずが「間違っている」方向になっていた)と指摘し、再修正
孫子の格言の英訳に対する日本語訳に、英文には無い「百戦危うからず」という一節が付加されていた(原典の知識で"補完"してしまっていた)のを検出・削除
ベンサムの名言の訳に、同様に英文には無い「立法」という語が紛れ込んでいたのを検出・削除
いずれも、内容としてはもっともらしく読めてしまうため、レビューフェーズの担当が見落としていても不思議はないものでした。二段目のチェックを別エージェントにやらせる意味は、ここにあると感じています。
査読エージェント自身が間違えた話
これが一番書いておきたい話です。
分野タグ(domain)と例文の内容が食い違っている疑いを調べていたとき、査読エージェントに「この修正候補は本当にバグか、それとも許容される設計か」を判定させました。エージェントは「本番に重複エントリがあり、そちらは正しい内容になっている」と、具体的なIDまで挙げて報告してきました。
これを鵜呑みにせず、自分で本番DBに直接クエリして裏取りしたところ、そのエージェントが根拠にしていたのはリポジトリ内のローカル開発用DBで、本番とはIDと内容の対応がまるで違っていたことが分かりました(同じIDでも、本番では"fish sauce"、ローカルでは"submerged rock"、といった具合です)。ローカルと本番でデータ投入の順序が異なる、という以前から分かっていた既知の問題が、ここまで深刻に食い違うとは想定していませんでした。
幸い、エージェントの核心的な判断(内容が食い違っている、という事実自体)は正しかったので実害はありませんでしたが、「AIが自信満々に具体的な証拠を挙げてくる」ことと「その証拠が正しい」ことは別問題だと、身をもって確認した形になりました。以降、本番データの調査は必ず本番に直接クエリする、というルールを徹底しています。
結果
単語: 12,085件全件を点検し、約400件を修正(誤り訳の混入率は分野・ID帯によって大きな偏りがあり、後年に一括投入した分野語彙で誤り率が高く、初期のTOEIC由来データはほぼ無傷でした)
フレーズ: 6,699件全件を点検し、12件を修正
分野タグ(domain)と例文内容の不一致も別途調査し、6件を修正・1件は「専用の分野が存在しないための間借り」で正常と判定(安易に「バグだから直す」と判断しないよう、分野taxonomy側の制約も考慮した)
すべて本番反映前にバックアップを取り、ドライランで対象件数を確認してから適用、反映後は/api/healthとアプリのログを確認する、という手順を徹底しました。
個人開発でAIに任せるときに意識していること
今回の一連の作業を通じて、あらためて意識するようになったのはこのあたりです。
AIの修正案は「候補」であって「正解」ではない。特に本番データを書き換える作業では、人間のレビューの代わりに単一のAI判断を置き換えるのではなく、複数のAI(できれば別の視点・別のプロンプトで動く別エージェント)に互いにチェックさせる構成にする
AIが挙げる「根拠」も検証対象。具体的なIDやデータを挙げられると説得力があるように見えるが、それが実際に参照可能な最新の情報源から来ているかは別途確認する
書き込み系の操作は必ずドライラン→バックアップ→適用の順にし、失敗したときに戻せる状態を常に保つ
個人開発だと、本来なら同僚のレビューを挟むところをAIエージェント同士のチェックで代替することになります。今回のケースは、その代替がそれなりに機能する一方で、完全に人間の確認を省略してよいわけではない、という感触を持てた事例でした。
試してみたい方へ
ログイン不要ですぐ単語・フレーズを閲覧できます。今回査読・修正した約1.9万件のデータも、もちろんそのまま反映されています。