はじめに
業務効率化のため、「手書きの申込書をスキャンして、AIでデジタルデータ化したい」という要望があり、Python (FastAPI) × Google Gemini 2.5 Flash を使ったOCR Webアプリを作成しました。
今回は要件定義からコーディングまで、Claude 4.5 Sonnet と Cursor をフル活用することで、実質 1時間 でプロトタイプを完成させることができました。
特に「字が汚くても認識するのか?」という検証結果も含めて共有します。
作成したアプリの概要
PDFのスキャンデータをアップロードし、AIが読み取った内容を画面上で確認・修正してデータ化できるアプリです。
実際の画面フロー
1. 書類選択・アップロード
書類タイプ(会員登録、サービス申込など)を選んでPDFをドラッグ&ドロップします。

2. AI解析 (Gemini)
「解析開始」を押すと、Gemini 2.5 Flash APIへリクエストが飛びます。
待機中はローディング表示を出し、UXを損なわないようにしています。

3. 結果確認・編集
左に画像、右に解析結果を表示します。間違っている箇所をその場で修正可能です。

なぜ Gemini 2.5 Flash なのか?
今回の要件において、Gemini 2.5 Flashを選定した理由は以下の3点です。
- 圧倒的な安さと速度: 大量の帳票を処理する場合、Proモデルよりも Flashの方がレスポンスが高速でコストも抑えられます。
- 高いマルチモーダル性能: OCR専用エンジンを使わなくても、画像ごとの文脈を理解して文字起こしが可能です。
-
JSONモード:
response_mime_type='application/json'を指定するだけで、揺らぎのないJSONが返ってくるため、バックエンドの実装が楽になります。
【検証】字が汚くても読めるのか?
今回、あえてOCR専用ライブラリではなくLLMを採用した最大の理由は「悪筆への対応力」です。
Gemini 2.5 Flash がどれほど「クセのある字」や「枠からはみ出した字」を読めるかテストしました。
テスト結果
かなり崩した字(走り書き)で書かれた「サービスご利用申込書」を読み込ませてみました。
特に「鈴木」の字や、住所の書き方がかなり雑ですが……。

驚くべきことに、完璧に認識しています。
- 氏名: 「鈴木 花子」と正しく認識("フ"のような崩し字も"花"と理解)
- 住所: 枠からはみ出していても、問題なく抽出
- 電話番号: ハイフン入りの数字も正確
従来のOCRでは座標指定(どこに何が書いてあるか)が必須だったり、枠からはみ出すと認識不能になることが多いですが、Geminiは 「画像全体を見て、そこにある意味(文脈)を理解する」 ため、人間と同じような感覚で文字を読み取れていることが分かります。
開発フロー:AIリレー開発
今回は 「Claudeに設計させ、Cursorに実装させる」 というフローを取りました。
Step 1: Claudeに設計を依頼
まずは「やりたいこと」と「技術スタック」をClaudeに伝え、Cursorに渡すための詳細なプロンプト(設計図)を作成してもらいました。
この時点で、Geminiへのプロンプト例やディレクトリ構成まで出力させるのがコツです。
Step 2: Cursor (Composer) に実装を依頼
Claudeが作成した設計プロンプトを、そのままCursorのComposer(Ctrl + I / Cmd + I)に貼り付けました。
実際に使用したプロンプトがこちらです。
Cursorに投げたプロンプト(クリックで展開)
PDF OCR Webアプリケーション開発
概要
手書き申込書PDFをAI-OCRで解析し、抽出結果を確認・編集できるFastAPI Webアプリを作成してください。
技術スタック
- Backend: Python, FastAPI
- Frontend: HTML, Jinja2テンプレート, TailwindCSS (CDN)
- OCR: Google Gemini API (モデル: gemini-2.5-flash)
- 画像処理: pdf2image, Pillow
機能要件
1. トップページ(アップロード画面)
- UI: TailwindCSSを使用したモダンでクリーンなデザイン
- 書類タイプ選択(ラジオボタン)
- 会員登録申込書
- サービスご利用申込書
- お客様情報登録申込書
- PDFファイル選択(ドラッグ&ドロップ対応エリア)
- 「解析開始」ボタン
- クリック時に「解析中...」のローディング表示(スピナー等)を出すこと
2. 解析処理 (Gemini 2.5 Flash)
- PDFを画像に変換(pdf2image)
- Gemini 2.5 Flash APIを使用してOCR実行
- プロンプトでJSON形式での出力を強制する
-
generation_config={"response_mime_type": "application/json"}を使用すること
- 書類タイプに応じたフィールドマッピング
3. 結果確認ページ
- 2カラムレイアウト
- 左側: アップロードPDFの画像プレビュー
- 右側: 抽出データの表示・編集フォーム
- 各フィールドの機能:
- チェックボックス(取り込み対象選択)
- 編集可能なinputテキストボックス
- アクション:
- 「選択した項目を確定」ボタン
- 「やり直し」ボタン
4. 確定結果ページ
- 取り込み確定したデータの一覧表示
- JSON/CSVダウンロード機能
Gemini API プロンプト指示詳細
Geminiには画像データと共に以下の指示を送る実装にしてください:
「この手書き申込書画像を解析し、以下のフィールドを抽出してJSON形式で返してください。読み取れない場合はnullまたは空文字にしてください。」
期待するJSON構造例:
{
"fields": {
"name_sei": {"value": "佐藤", "confidence": 0.95},
...
}
}
Step 3: 修正と調整
ベースとなるコードは一発でほぼ動作しました。その後、以下の微調整を行いました。
- APIキーの設定(
.env) - 日本語フォント等の調整
- 信頼度(confidence)表示の追加
これらを含めても所要時間は約1時間でした。
実装の技術的ポイント
バックエンド(FastAPI)からGeminiを呼び出す際、generation_config でJSONを指定するのが最大のポイントです。
model = genai.GenerativeModel("gemini-2.5-flash")
response = model.generate_content(
[image, prompt],
generation_config={"response_mime_type": "application/json"}
)
これにより、テキスト解析(正規表現での抜き出し等)が不要になり、開発工数が劇的に下がりました。
まとめ
CursorとGemini 2.5 Flashを組み合わせることで、従来なら数日かかっていたようなOCRアプリのプロトタイプ作成が、驚くほどの短時間で実現できました。
特に 「汚い字でもJSONで返ってくる」 という点は、実務への導入において非常に強力な武器になります。
今後は、抽出データのデータベース保存機能などを追加していく予定です。