メインコンテンツへスキップ
見出し画像

「父さん、過去問の類題作って!」に本気で答えたら、家庭専用のAI受験ポータルができた話──中学受験過去問AI活用記【第3部:インフラ総集編】

    1. はじめに:中学受験の過去問演習が抱える「構造的な課題」

    関西の小学6年生の子を持つ父親です。
    私の本業は生粋のWeb系ソフトウェアエンジニアではありません。普段から業務でPythonやデータ分析には日常的に触れているものの、あまり本格的なものをプライベートでは作ってきませんでした。

    そんな私が、週末の限られた時間を使って「家族専用の中学受験過去問AIポータル」を開発し、Google Cloud Platform(GCP)に本番デプロイするに至った経緯と、その過程で直面した技術的な試行錯誤を記録します。

    直面した3つの課題

    受験に際して過去問演習を進める中で、家庭学習として以下の3つの壁に直面しました。

    1. 過去問のストック枯渇と類題不足:
      市販の過去問集(赤本)は数年分しか掲載されておらず、ひと通り解いてしまうと答えの数値を覚えてしまい、本番レベルの演習が続けられなくなる。

    2. 「小学生算数ルール(方程式禁止)」の厳格さ:
      大人が算数を解く場合、連立方程式(x や y)を使えば容易に解けますが、中学入試では方程式が使えません。「つるかめ算」「旅人算」「線分図」「面積図」「比」といった小学生特有の解法で作問・解説しなければならず、数値設定を少しでも誤ると「答えが割り切れない」といった破綻が起きます。

    3. 印刷・製本と解説の手間:
      本番を想定した演習にはA4/B4の見開き紙面が欠かせません。毎週末、赤本を拡大コピーして切り貼りする作業に膨大な時間を取られていました。また、子どもがつまずいた手書きノートを見ても、どこで思考の論理が狂ったのかを特定して解説するのは親にとっても大きな負荷でした。

    普段からPythonを触っている身として、「この課題はLLMとクラウドを使えば自動化・仕組み化できる」という仮説はありました。しかし、フロントエンドUI、DB設計、バックエンドの作問パイプライン、PDF組版エンジン、認証基盤、コンテナ化までを一人で実装するとなると、通常であれば数週間の工数がかかります。

    これを解決してくれたのが、Google AI Pro(Gemini 3.8 Flash)と、Google DeepMindが開発した自律型AIコーディングエージェント「Antigravity」でした。


    2. なぜ「オールGoogle」スタックを選んだのか?

    画像
    オールGoogle

    短期間で実用レベルのプロダクトを組み上げるにあたり、アーキテクチャはすべてGoogleのエコシステムで統一しました。

    【採用したアーキテクチャ・スタック】
    ・LLM / VLM: Google AI Pro / Vertex AI(Gemini 3.8 Flash)
    ・開発パートナー: Google Antigravity(自律型コーディングエージェント)
    ・アプリケーション実行基盤: Google Cloud Run(Python 3.11 / Streamlit)
    ・データベース / ストレージ: Google BigQuery & Google Cloud Storage(GCS)
    ・認証 / 認可: Google OAuth 2.0 + YAMLホワイトリスト
    ・PDF組版エンジン: Playwright (Headless Chromium) + KaTeX

    このスタックを採用した理由は次の3点です。

    1. Gemini 3.8 Flashの数理推論・マルチモーダル性能:
      中学入試問題には、図形(立体の切断、天体、進行グラフ)や手書き答案画像が頻出します。長文脈の入試問題と画像を高精度かつ低レイテンシで処理できるモデルとして選定しました(レポート化の一部でAzure OpenAI GPT-6-Astraは使いましたが)。

    2. Antigravityによる開発の高速化と品質担保:
      仕様の策定から単体テストの自動生成、エラーの自己修復、リファクタリングまでを自律的に支援してくれるため、非専門領域(Streamlitの非同期制御、Docker、Cloud Run設定など)の実装コストを劇的に圧縮できます。Claude CodeやCodexも選択肢としてはありますが、低コストかつ高速と言う点では、家庭用はAntigravityが強いと私は考えています。

    3. Cloud Runのサーバーレス特性(アイドル時ほぼ0円):
      家族専用アプリのため、利用は平日夜や週末に限られます。常時起動の仮想マシン(GCE)を置くことなく、リクエストがあった時のみ起動するCloud Runを採用することで、月額数百円レベルの極小コストで運用できます。


    3. アプリ開発で直面した技術的課題と泥臭い解決策

    ここからは、実際にシステムを組み上げる過程で直面した技術的なボトルネックと、それをどのように克服したのかを技術目線で詳述します。

    課題1:一発生成の破綻と「3段階多段パイプライン」への刷新

    画像
    3段階多段パイプライン

    初期の実装では、1つのプロンプトで「大問のリード文・図表・全小問・検算解説」を一括生成させていました。
    しかし、これには明確な限界がありました。

    • 制約の希釈: プロンプトが巨大化するにつれてアテンションが分散し、小問(3)あたりで中学校の連立方程式を使ってしまったり、問題文と模範解答の数値が矛盾する現象が発生。

    • 再試行効率の極端な悪化: 小問1問の計算ミスや表記揺れを検知しただけで、大問全体(本文や図表)を破棄してゼロから再生成するため、生成時間が数分単位に膨らみタイムアウトが多発。

    そこで、大問作成パイプラインを「大問設定の先行確定」と「小問群の展開」に分離する多段アーキテクチャへ刷新しました。

    【3段階多段パイプラインの構成】
    Stage 1: [大問設定・出題図の先行設計]
             スキーマ: SectionLeadDraft
             リード文、背景設定、出題意図、図表パラメータ(figure_spec)を確定。
             ↓
    Stage 2: [確定設定に基づく小問群の段階的展開]
             スキーマ: SubQuestionsDraft
             小問(1)基本 → (2)標準 → (3)思考力へと、確定したリード文に基づいて出題。
             ↓
    Stage 3: [科目別バリデーション & ピンポイント再試行]
             小学生算数解法(方程式排除・比の正当性)や日本語文体を二重検問。

    不備が発生した場合も、「リード文側の不備(天体の比率破綻や下線部の欠落など)」と「小問側の不備(計算不整合など)」を判定し、小問側だけの問題であれば確定済みのリード文と図表を保持したまま小問群のみをピンポイントで再生成する設計にしました。これにより、作問の安定性と生成速度が大幅に改善しました。


    課題2:幾何図形の縮尺破綻・ネタバレ問題と「パラメータ化SVG + VLM視覚検問」

    画像
    縮尺破綻・ネタバレ

    理科の天体問題や算数の立体切断問題において、画像生成モデル(Diffusion系)を使うと線の比率や文字ラベルが崩れて入試問題として機能しません。

    当初はLLMにSVGコードを直接出力させていましたが、これにも「目隠し描画」による問題が生じました:

    • 天体問題で、太陽より地球の半径が大きく描かれてしまう。

    • 小問で生徒に求めさせる未知数(進行グラフの出会い時刻など)を、問題図の中に最初から印字してしまう(出題ネタバレ)。

    これを解消するため、「構造化パラメータによる数理描画」と「VLMによる視覚的検問」を組み合わせました。

    1. パラメータ化ビルダー(`src/generator/svg_renderer.py`):
      LLMには生のSVGではなく、`figure_spec`(描画パラメータのJSON)を出力させます。コード側で太陽(半径45px) ≫ 地球(18px) > 月(7px)といった物理縮尺を数理的に強制固定し、ダイヤグラムの未知数ラベルは伏せ字(`【ア】` や `?`)としてレンダリングします。

    2. Gemini 3.8 Flash Visionによる自己視覚検問:
      Playwrightを用いてコンテナ内でSVGをPNG画像へラスタライズし、GeminiのマルチモーダルAPIに渡して「文字の重なりがないか」「比率に歪みがないか」を視覚的に検査させます。欠陥があれば修正プロンプトを自動生成してリトライへ回すフィードバックループを構築しました。


    課題3:KaTeX数式DOM分断とPlaywrightによるA4製本PDF出力

    家庭学習では「印刷して紙で解く」ことが必須です。
    当初はWeb画面のHTMLをそのままブラウザ印刷していましたが、改ページ位置がずれ、計算スペースが不足するという課題がありました。そこで、サーバーサイドでA4試験冊子PDFを直接バイナリ生成する仕組みを実装しました。

    ここでぶつかったのがLaTeX数式のレンダリング崩れです。
    算数の逆算問題(\Box)や分数の式をきれいに印字するためKaTeXを導入したのですが、数式が生の文字列(`$$\frac{5}{8}...$$`)のまま出力されるトラブルが起きました。

    ログを追跡した結果、以下の2点が判明しました:

    • バックエンドのテキスト整形関数が改行文字を一律に `replace("\n", "<br>")` していたため、複数行の数式ブロック内部に `<br>` タグが挟まれ、DOMテキストノードが分断されてKaTeXの構文解析がタイムアウトしていた。

    • PlaywrightのPDF化が、KaTeXのレンダリングスクリプトの実行完了前に走っていた。

    これを解決するため、`<br>` 置換を廃止してCSSの `white-space: pre-wrap;` による自然改行へ統一。さらにPlaywright側で `window.__katex_rendered === true` および `document.fonts.ready` の完了を確実に待機するガードを追加しました。これにより、本番の入試問題と見分けがつかない美しいA4冊子PDFの直接出力が実現しました。


    課題4:Streamlitのスレッドコンテキスト競合と非同期キュー化

    複数の大問を並行生成(フル模試モード)させる際、Pythonの `ThreadPoolExecutor` を用いてGemini APIを並行呼び出ししました。
    しかし、実行開始直後にStreamlitのセッションがクラッシュする現象に遭遇しました。

    原因は、ワーカースレッド内から `progress_bar.progress()` などのStreamlit UI操作関数を直接呼んでいたことでした。Streamlitは各ブラウザセッションが単一のメインスレッド(`ScriptRunContext`)に紐付いており、別スレッドからの直接操作はコンテキスト例外(`NoSessionContext`)を引き起こします。しかもこの例外は文字列化すると空文字を返すため、画面には「エラーが発生しました: 」としか表示されず、原因特定に時間を要しました。

    Antigravityとペアプログラミングを行い、プロデューサー・コンシューマーパターン(`queue.Queue`)へ移行しました:

    • ワーカースレッド: 作問処理と進捗データのキュー投入(`progress_queue.put(...)`)のみを担当し、UIライブラリには一切依存しない。

    • メインスレッド: `while worker_thread.is_alive():` ループでキューを定期ポーリング(0.3秒間隔)し、UIウィジェットの更新を100%メインスレッド側で実行。

    この分離により、スレッドセーフな進捗バー表示と堅牢なエラーハンドリングが確立しました。


    課題5:Google OAuth 2.0 と Cloud Run によるセキュア運用

    最後に、システムをクラウドへデプロイするフェーズです。
    家庭専用アプリであるため、外部からの不正アクセスは厳格に遮断(HTTP 403)する必要がありますが、個人開発でユーザーDBやパスワード管理基盤を一から構築するのは運用リスクを伴います。

    そこで、Google OAuth 2.0 / OIDCと、あらかじめ許可した家族のGmailアドレスのみを記載した設定ファイル(`allowed_users.yaml`)を組み合わせる方式を採用しました。

    Cloud Runにコンテナをデプロイし、リクエストヘッダーに含まれるGoogle IDトークンを検証。ホワイトリストに含まれないアカウントはアプリケーション手前で確実に弾く設計としました。これにより、DBを一切持たないステートレスな構成でありながら、強固なセキュリティを担保できました。


    4. 運用成果と家庭での変化

    完成したシステムは、現在Cloud Run上で安定稼働しています。

    • 蓄積されたデータ: 志望校の過去9年分(2018〜2026年)、4教科・全36回分、大問162題、小問1,075問、図表314点をBigQueryに完全構造化。

    • 演習の効率化: 「立体切断の難問」「天体の運行」といった苦手単元を指定すれば、1分程度で志望校の出題傾向に沿った予想問題が作成され、A4の試験冊子PDFとして印刷可能。

    • 手書き答案のマルチモーダル診断: ノートや計算用紙の写真をスマホでアップロードすると、Gemini 3.8 Flashが手書きの途中式から「逆比の取り違え」などの思考のつまずきを特定し、的確なアドバイスと類題を提示。

    • テストの網羅性: 開発全体を通じてAntigravityと共に作成した単体テストは全146件に達し、すべてパスした状態でCI/CDを維持。

    何より大きかったのは、週末のコピー取りや作問に頭を抱えていた時間がなくなり、子どもが間違えた問題の思考プロセスを一緒に確認する「本質的な伴走の時間」が増えたことです。


    5. おわりに:#AIとできたこと

    生粋のソフトウェアエンジニアではない技術者が、週末の短期間でプロダクト品質のWebアプリを設計・実装し、クラウド本番運用まで漕ぎ着ける──。従来の開発環境であれば、到底考えられないことでした。

    今回のプロジェクトを通じて実感した「#AIとできたこと」の本質は、単にコードを自動生成してくれたことではありません。

    • 複雑なプロンプト設計やアーキテクチャのトレードオフを客観的に検証してくれたこと。

    • スレッド競合やDOM分断といった泥臭いバグに直面した際、的確な仮説とリファクタリング方針を提示してくれたこと。

    • 140件を超えるテストコードを整備し、品質への確信を与えてくれたこと。

    「作りたいという明確な目的」と「一定の技術的素養」を持つ人間が、Google AI Pro(Gemini)とAntigravityという自律エージェントを相棒に得ることで、個人のエンジニアリング能力は何倍にもスケールするという事実を、身をもって体験しました。

    AIは人間の仕事を奪うものではなく、家族のために課題を解決したいと願う人の背中を強力に押し、実現へと導いてくれるパートナーです。これからもこのシステムとともに、子の受験本番まで伴走を続けていきたいと思います。


    【技術スタックまとめ】

    • LLM / Vision: Google AI Pro / Vertex AI(Gemini 3.8 Flash, Gemini Vision)

    • Agent: Google Antigravity

    • Backend / Web: Python 3.11, Streamlit, Pydantic, Tenacity, Pytest (146 tests)

    • Infra / GCP: Google Cloud Run (asia-northeast1), BigQuery, Cloud Storage

    • Auth: Google OAuth 2.0 / YAML Whitelist

    • Rendering: Playwright (Headless Chromium), KaTeX, Matplotlib / SVG

    • Running Cost: アイドル時0円(月額約500円〜1,000円)

     
     

    yoichi

     
     
    職業:研究開発(非IT系) Noteの活用:趣味(IT系)のメモ置き場

    あなたへのおすすめ