Toilを無くして徒然なるままに日暮し硯に向かひたい

生成AIアプリケーション開発などを行うエンジニアのブログです。

帰ってきた「完結編」のその先 — Gemini 3.5 Flash への対応と、Google Antigravity が暴いた「堅すぎるAIが引き起こした Layer 1 のバグ」

はじめに

「もう二度と完結編とは書かない」と心に誓ったはずでした。しかし、技術の進歩と開発ツールの変遷は、容赦なく私を机の前に引き戻します。

今回、リポジトリに新しい仲間が加わりました。最新モデル Gemini 3.5 Flashgemini-3.5-flash)です。

これまで、この自転車青切符判定システムの開発には Anthropic の Claude Code を相棒として使ってきました。しかし今回は、Google が開発したエージェント型開発環境 Google Antigravity(Antigravity IDE)に乗り換え、Gemini 3.5 Flash の実装と評価を行いました。

そこで見えてきたのは、単なる「モデルの精度向上」という退屈なニュースではありませんでした。「モデルの指示追従性(Negative Constraints)が極限まで高まった結果、Layer 1(18年前の卒論コンパイラ)に眠っていた潜在的なバグが白日の下に晒される」という、ハイブリッド構造ならではの知的でスリリングな事件でした。

今回は、ツールとしての Google Antigravity の使用感と、Gemini 3.5 Flash の驚くべき挙動について報告します。


開発環境の変更:Claude Code から Google Antigravity へ

今回の実装は、開発エージェントを Claude Code から Google Antigravity に切り替えて進めました。

2026年5月のGoogle I/Oで Google Antigravity のアップデートがあり、試してみたかったので。

Antigravity がもたらす「IDE協調型」の開発体験

Claude Code がターミナル完結型の非常に強力なCUIエージェントだったのに対し、Google Antigravity はワークスペースの構造化された把握力と、実行タスクのバックグラウンド管理に強みを持っています。

  • コードベースの迅速な理解と拡張: Antigravity は、リポジトリ構成や過去のブログ草稿(drafts/ 以下のファイル群)を一瞬でスキャンし、本プロジェクトの文脈やユーモラスな文体を理解した上で対話を開始してくれました。
  • 並行処理とタスク追跡: 30ケースのベンチマーク実行(直列で30回 API を叩くため数分かかる)をバックグラウンドタスクとして非同期に回し、ログの進行状況(TC-014通過、など)を管理しながら、開発者と別の議論を進めることができます。

Antigravity に「Gemini 3.5 Flash に対応して」と指示するだけで、設定ファイル (config.py) から API ディスパッチャ (judge_service.py)、CLI エントリポイント (main.py)、さらにはユニットテスト (test_config.py) まで、関連するすべての箇所が一発で修正されました。

修正後、Antigravity 上で実行したテストスイートは 178 件すべてが 0.09 秒でパス。極めてスマートな開発体験でした。

============================= 178 passed in 0.09s ==============================

Gemini 3.5 Flash 単体ベンチマーク:実力は本物か?

さっそく、新しく実装した Gemini 3.5 Flash を単体ベンチマークにかけてみます。

  • コマンド: python -m src.main --benchmark --model flash35
  • 結果: 27/30 件正解 (正答率 90%)
  • ハルシネーション検出: 2件 (TC-029: 酒酔い運転, TC-030: あおり運転)

観察:単体性能の大幅な底上げ

初代 Gemini 3 Flash の頃(単体 71%)と比べると、ベースとなる道交法の解釈能力や数値の正確性は大幅に向上しています。30問という広範囲なテストセットにおいて、大半の反則金や条文を単体(Layer 1 によるグラウンディングなし)で正確に答えてみせました。

しかし、やはり「酒酔い運転」や「あおり運転」といった、「青切符(反則金)の対象外であり、赤切符による刑事罰が下される」という法規の例外・分岐構造(階層構造)に対しては、単体だと例外規定を無視して答えてしまうハルシネーション(階層無視)が僅かに残りました。

やはり、法務コンプライアンスのような堅牢性が求められる用途では、Layer 1 を前段に置くハイブリッドアプローチが必須であることに変わりはありません。


ハイブリッド判定で起きた「事件」:TC-028 誤判定の謎

続いて、Layer 1(条文パース + VSM/Embedding検索)と Layer 2(Gemini 3.5 Flash)を組み合わせたハイブリッド判定を実行しました。

  • コマンド: python -m src.main --hybrid --model flash35

ここで、予想外の判定が下されました。対象は TC-028(身体障害者の歩道走行) です。

  • クエリ: 「身体障害者手帳を持つ40歳の人が普通自転車で歩道を走行しています。これは違反ですか?」
  • 期待される正解: 合法 (道交法第63条の4第1項第2号および施行令第26条に基づき、身体障害者は歩道通行が認められる)
  • これまでのLLM(Flash / Pro / Claude)のハイブリッド結果: 合法 (正解)
  • Gemini 3.5 Flash (Hybrid) の回答: 違反 (誤判定)

なぜ、性能が上がったはずの Gemini 3.5 Flash が、ハイブリッド構成にした途端に誤判定を出してしまったのでしょうか?

その答えは、Gemini 3.5 Flash が出力した reasoning(推論プロセス)に克明に刻まれていました。

Gemini 3.5 Flash の「言い分」

{
  "judgement": "違反",
  "article": "第六十三条の四第一項",
  "reasoning": "第六十三条の四第一項により、普通自転車は原則として歩道を通行できませんが、同項各号に該当する場合は例外的に通行が認められます。提供された条文データには「身体障害者手帳を持つ者」を「普通自転車により車道を通行することが危険であると認められるものとして政令で定める者」とする規定(第二号)や、その他の例外に該当することを示す記載がありません。したがって、40歳の身体障害者手帳保持者が普通自転車で歩道を走行することは、同項の例外要件を満たさず、原則通り違反となります。"
}

この推論を読んで、私は鳥肌が立ちました。 「提供された条文データには『身体障害者手帳を持つ者』とする規定がありません。だから違反です」 と言っているのです。

そうなのです。プロンプトに注入された条文(道交法第63条の4)の生の文言は、以下のように書かれています。

「児童、幼児その他一般的に道路において普通自転車により車道を通行することが危険であると認められるものとして政令で定める者

具体的な「身体障害者手帳を持つ者」という規定は、法律(道交法)ではなく、委任された政令(道路交通法施行令第26条第1項第3号)に書かれています。

本システムでは、この委任関係を解決するため、Layer 1 が条文内から 政令で定める という表現(delegation フラグ)を検出した際に、以下の【委任規定の解決済み情報】をプロンプトに動的に注入する仕様になっていました。

【委任規定の解決済み情報】
道路交通法施行令第26条により、第63条の4第1項第2号の「政令で定める者」は以下のとおり:
  一 児童(6歳以上13歳未満)及び幼児(6歳未満)
  ...
  三 身体に障害を有する者で政令で定めるもの

しかし、今回の TC-028 の実行ログを見ると、Layer 1 は以下のように出力していました。

[2008-Thesis-Logic]: 論理フラグ検出: ['exception', 'notwithstanding', 'obligation', 'permission', 'prohibition', 'proviso']
(※ 'delegation' フラグが検出されていない!)

delegation フラグが検出されなかったため、プロンプトには【委任規定の解決済み情報】(施行令の定義)が注入されていませんでした。


原因究明:18年前の卒論のロジックを元にしたコンパイラに眠っていたバグ

なぜ delegation フラグが漏れたのでしょうか? Google Antigravity と共にコードを調査したところ、驚くべきバグが発覚しました。

問題は hybrid_judge.py のフラグ収集ロジックにありました。

# backend/src/judgement/hybrid_judge.py のバグ箇所
for m in vsm_matches:
    ...
    for p in m.article.paragraphs:
        for s in p.sentences:
            all_flags.extend(s.logic_flags)

このコードは、条文の「パラグラフ直下の文章(p.sentences)」からのみ論理フラグを収集しており、パラグラフの下にネストされた「号(p.items)」や「細号(subitems)」の中の文章を完全に無視していたのです。

道路交通法第63条の4第1項において、「政令で定める者」という文言は、第一項直下ではなく第二号(Itemの中に記述されています。そのため、18年前に設計されたこの抽出ロジックは、号の中に隠れた delegation(政令で定める)というキーワードを素通りしていたのです。

結果として、政令の解決情報がプロンプトに載ることはありませんでした。


浮き彫りになった「堅すぎる新AI」と「甘かった旧AI」のコントラスト

ここで最も興味深いのは、「なぜこれまでの LLM(Gemini 3 Flash/Pro、Claude)はこのバグがありながら TC-028 を『合法』と正解できていたのか?」 という点です。

理由はシンプルです。これまでのモデルは、システムプロンプトの「与えられた情報のみを根拠にせよ」という指示を破り、自らの学習データから「障害者は歩道OK」という知識を勝手に補完(ハルシネーション)して回答していたのです。

つまり、Layer 1(コンパイラ側)に「政令の解決情報が抜けている」という致命的なバグ(穴)があったにもかかわらず、Layer 2(従来のLLM)が「親切に」その穴を勝手に埋めてくれていたため、テスト上は「正解」に見えてしまっていました。

しかし、Gemini 3.5 Flash は違いました

Gemini 3.5 Flash は、システム指示(Negative Constraints / 外部知識の持ち込み禁止)に対する従順さが極めて高いため、「与えられたコンテキストに書かれていない例外規定は、自分が知識として知っていても絶対に適用しない」 という鉄の意志を貫きました。その愚直なまでの誠実さが、Layer 1 の「フラグ収集漏れ」というバグを正面から突き刺し、結果として「違反(提供データに例外規定の記載なし)」という「誤答」を出したのです。

これは、プログラムの世界における 「サイレントフェイラー(静かな失敗)が、コンパイラの厳格化によってコンパイルエラーとして表面化した」 瞬間そのものです。

LLMの進化がもたらす責任境界のシフト

この事件から学べる教訓は深遠です。

  1. 指示追従性の向上は、前処理のバグを暴く: LLM の性能が向上し、ネガティブプロンプトを厳格に守るようになるほど、前段の決定論的システム(Layer 1)のバグが判定の失敗としてダイレクトに露出するようになります。
  2. 「親切なハルシネーション」に頼る危険性: 旧世代の LLM が出していた「正解」は、指示違反というハルシネーションに支えられた砂上の楼閣でした。バグを隠蔽する「都合の良いハルシネーション」は、システムの信頼性を損なう最大の要因です。
  3. 境界設計の重要性: LLM に「考えるな、整形しろ」と制約を課すハイブリッドシステムにおいて、LLM が指示を完璧に守るようになった今、すべての責任は Layer 1(法令コンパイラおよびリトリーバー)のパース・抽出精度に回帰します。

おわりに

Google Antigravity という新たな開発パートナーを迎え、Gemini 3.5 Flash という「堅牢な推論エンジン」を組み込んだことで、本プロジェクトは新たなフェーズに突入しました。

Gemini 3.5 Flash は、単体で 90% の精度を出す地力を持ちながら、ハイブリッド環境下では「与えられたデータのみに忠実に従う」という、法実務において最も望ましいキャラクターを見せてくれました。

「卒論コードがLLMの尻拭いをする」という構図から始まった本シリーズですが、AIが賢く、そして「生真面目」になった結果、今度は「LLMが卒論のバグをデバッグしてくれる」という、美しくも皮肉な逆転劇が幕を開けました。

次回の宿題は明確です。

  • hybrid_judge.py を修正し、Paragraph の配下にある ItemSubitem からも再帰的に論理フラグ(delegation 等)を回収するようにアップデートする。
  • これにより、Gemini 3.5 Flash の「鉄の指示追従性」を活かしたまま、TC-028 などの委任規定例外を正しく「合法」と判定できるようにする。

技術は螺旋のように進化し、互いを磨き合います。この幸福なデバッグのループは、まだまだ終わりそうにありません。

(今度こそ本当に、タイトルに完結編とは書かないでおきます)

お読みいただきありがとうございました。

ソースコードは↓こちら

github.com