🤖

Gemini CLIとペアプロし、PDF発注書の自動転記システムを開発した全記録

に公開

はじめに

LLMがコーディングを支援する時代、私たちはAIとどう向き合うべきでしょうか?
今回は、GoogleのGemini CLIを「壁打ち相手」兼「デバッグパートナー」として、面倒な事務作業を自動化するWebアプリケーション開発に挑戦してみました。

開発したのは、「PDFで届く発注書を、Googleスプレッドシートに自動転記するシステム」 です。
この記事では、AIとの対話を通じてエラーを解決し、アプリケーションを完成させるまでの、生々しいペアプログラミングの全記録を共有します。

Step 1: 要件定義と技術選定

まず、実現したいことをGeminiに伝えます。

私:
PDFがアップロードされたらOCRで文字起こしし、AI(Gemini)が内容を解析して、Googleスプレッドシートに追記するPythonプログラムを開発したい。

これに対し、Geminiは必要なライブラリ(Google Cloud Vision, Google Sheets, Gemini)と、CLIツールとしての基本的な開発計画を提案してくれました。ここまでは順調です。

Step 2: Web UIの実装とsystemdでの永続化

開発途中で「CLIツールよりWeb UIの方が使いやすい」と考え、Geminiに相談。すぐにFlaskを使ったWebアプリケーションへの方針転換が決まりました。ファイルアップロード画面の実装もスムーズに進み、WebサーバーGunicornsystemdを使って永続化する段階まで進みました。しかし、ここからが本番でした。

最初の壁:503 Service Unavailable

systemdでサービスを起動した途端、ブラウザに「503 Service Unavailable」が表示されました。

私:
systemdで起動すると503エラーになります。sudo systemctl status myapp.serviceのログを見ると、Flaskアプリ自体は正常に起動しています。

Gemini:
ログの提供ありがとうございます。アプリが起動しているのにApacheから接続できない場合、最も一般的な原因はファイルパーミッションです。Apacheユーザー(www-dataなど)が、あなたのアプリケーションディレクトリやPythonの実行ファイルにアクセスできていない可能性があります。ディレクトリの権限を確認・修正してください。

Geminiの指摘は的確でした。Flaskアプリのディレクトリに実行権限を付与すると、503エラーは解消されました。

次の壁:500 Internal Server Error (メモリ不足)

接続できたと思ったら、今度はPDFの処理中に「500 Internal Server Error」が発生。Gunicornのログには不穏なメッセージが。
Worker was sent SIGKILL! Perhaps out of memory?

私:
500エラーが出ました。ログにPerhaps out of memory?とあります。Gunicornのワーカー数を3から1に減らしましたが、改善しません。

Gemini:
メモリ不足が根本原因のようですね。ワーカー数を減らしても解決しない場合、処理方法の見直しが有効です。PDFをメモリに読み込む代わりに、Google Cloud Storageに一旦アップロードし、Vision APIにGCS上のファイルを直接処理させる非同期アーキテクチャを推奨します。

この提案は正攻法ですが、私はある点が気になっていました。

私:
重要な情報です。python main.py手動実行した場合は、メモリ不足エラーは起きません。 systemd経由でGunicornで動かした場合のみ発生します。この違いを踏まえて、他の解決策はありますか?

Gemini:
なるほど!それは非常に重要な切り分けです。手動実行時と挙動を近づけるアプローチを試しましょう。Gunicornのワーカータイプを、デフォルトのsyncから、メモリ効率が良いとされる**geventに変更してみてください。また、PDFのOCR処理には時間がかかる可能性があるため、Gunicornのタイムアウト時間**をデフォルトの30秒から120秒などに延長するのも有効です。

この提案が大当たりでした。ワーカータイプをgeventに変更し、タイムアウトを延長することで、メモリ不足エラーは完全に解決しました。AIが後からの追加情報で、より深いコンテキストを理解し、提案の精度を上げていく過程は、まさに人間とのペアプロそのものでした。

最後の壁:AIの応答がJSONとして不正

最後に、データ抽出に使うGeminiモデルを、高速なgemini-flashから高精度なgemini-proに変更したところ、json.loads()でエラーが発生。

私:
gemini-proの応答がJSONとしてパースできません。ログを見ると、応答テキストの末尾に```のような余計な記号が付いています。

Gemini:
gemini-proは、より丁寧に応答を生成するため、説明文やマークダウンのコードブロック記号を含めてしまうことがあります。堅牢性を高めるため、応答テキスト全体をJSONとしてパースしようとせず、正規表現などを使って{で始まり}で終わる最初の部分文字列だけを抽出してから、JSONとしてパースする方法に切り替えましょう。

この修正により、AIの応答形式の揺らぎに強い、安定したデータ抽出処理が完成しました。

まとめ:AIは粘り強いデバッグパートナーである

今回の開発を通じて、AI、特にGemini CLIは、単なるコード生成器ではないことを実感しました。

  • 仮説提案: エラーログから、可能性の高い原因を即座にリストアップしてくれる。
  • 軌道修正: 「手動実行ならOK」といった追加情報(コンテキスト)を与えることで、より精度の高い解決策を再提案してくれる。
  • 堅牢性の向上: エラーの根本原因(応答形式の揺らぎなど)を指摘し、より安定した実装方法を提案してくれる。

もちろん、最終的な判断を下すのは人間です。しかし、思考の壁打ちとデバッグの選択肢を高速で提供してくれるAIは、間違いなく開発者の生産性を劇的に向上させる、最高のパートナーになり得ると感じました。


この記事で紹介した内容以外にも、技術情報をブログで発信しています。
MEANTECH
https://meantech.fontfontfont.com/

Discussion