旧PCのころから続いた投資アプリ開発、画像読み取りの壁を越えるまで

作業した日付

2026年7月2日〜9月30日(現存する計画・履歴で確認できる範囲。取り組み自体は旧PC時代から)

要約

証券会社のスクリーンショットを読み取って、手で直せる資産一覧にする投資アプリを、集計の誤りと画像解析エラーに何度もぶつかりながら育ててきた。新PCへの移行、SMBC・SBIの実画像檢証、明細と総額の監査、複数画像・複数日対応、履歴と経時分析、人工智能タブ、利用料金を抑える仕組みまで進めた。最後に共通クラウドAIのテスト経路も、ログイン・残高・Stripeサンドボックス・模擬利用まで実画面で通せた。長いこと止まっていた「まず画像を読ませる」段階に、ようやく区切りを付けられた。

内容

まずは金額を間違えずに読むところから

このアプリでやりたかったのは、SMBC日興証券やSBI証券の保有画面を画像から読み取り、資産の明細を一覧にして、あとから自分で直したり保存したりできるようにすることだ。見た目だけ整っても、評価額や数量を間違えたら意味がない。特にSMBCの金額・株数指定取引(キンカブ)は、画面の列が少し変わるだけで読み取る列がずれたり、評価額が7万円に書き換わったりする問題があった。



現存する開発記録をたどれる最初は7月2日。ヘッダーの文字を基準に列位置を決める方式にし、画像の小計や総資産と読み取った明細の合計を照らし合わせる監査を入れた。前日データをAIが混ぜてくるような誤りを抑える指示や、OCRの小さな揺れを許すテストも整え、6日分のSMBC画像を使った回帰テストを通した。グラフの日本語表示でマイナス記号が文字化けする細かい問題も直した。

ただ、記録に残る日付より前、旧PCのころからこのアプリには取り組んでいた。7月17日に新しいPCへ移った時、Pythonの集計版とNode.jsのスクリーンショット解析版を移行フォルダーから探して戻した。Python版はCSVを読み込んでExcel・CSV・グラフを出せるところまで確認し、Node版も依存関係を更新してlocalhost:3005で起動した。移行直後はGeminiの実キーや元画像がそろわず、実画像を使った回帰テストはまだできなかった。動くように戻ったことと、画像を正しく読めることは別だと、ここから何度も思い知らされた。

実画像を入れると、テストでは見えなかった失敗が出た

8月初め、Geminiの新しい認可キー形式に対応し、ブラウザーの保存領域からAPIキーを勝手に探す危険な処理は外した。公式SDKも更新して、実画像をAPIへ送れるようにした。最初のSMBC画像では、総資産9,281,171円自体は読めたのに、明細が17件しかなく、現金のMRFが抜け、ファンドラップが二重になり、金額株の行も欠けていた。

そこで、同じコードでも口座や数量が違えば別の保有として残すよう明細の識別方法を直した。セクション合計からMRFやファンドラップを取りこぼさないようにし、20明細の合計が総資産と一致するところまで修正した。ところが、俺がブラウザーで確認すると「差額調整」や「セクション不一致」が出た。調べると、サーバーで残した別ロットを画面側が再統合して消していたり、MRFの168,499円が画面集計から抜けたりしていた。つまりAPIの数字だけ見て「できた」と言っても、利用者が実際に見る画面で壊れていた。

ここは檢査のやり方も改めた。保存濟みJSONを差し替えるだけのE2E試験と、実際にGeminiへ画像を送り直すライブ試験を分け、Edge上の画面で明細数、区分合計、総額、監査結果、ブラウザーエラーまで確認するようにした。現金行の欠落、完全重複、銘柄の欠落や余分な行、投資信託の評価額が10倍になるケース、API失敗、タイムアウト、手編集後の再計算やCSV出力も、異常系として試した。実画像ではAIが平均取得単価と現在値の列を入れ替えたり、評価額の桁を10倍にしたりすることがあり、画像のセクション合計と一致する場合に限って補正する処理を足した。SMBCの代表画像では20明細、総額9,281,171円、監査差額0円まで確認できた。

SBIの画像でも別の問題が出た。全画面と切り抜きを一緒に渡すと、同じ資産を二重に足したり、総資産を何十億円と誤読したりする。重なった画像を見つけて同じポートフォリオを二重計上しないようにし、SBIの5銘柄・総額421,665円・差額0円を確認した。JSONの書き出しと再読込、履歴保存、グラフ、10回連続の解析も試した。低画質・回転画像は檢証対象から外すという条件も記録に残した。

8月17日のSMBCとSBIを一緒に扱う時には、証券会社ごとに画像を独立して補正・監査してから合算するようにした。片方の総額をもう片方へ当てはめたり、別口座の同じコードをまとめたりしてはいけない。投資信託の千口単位表示や、SBIの現金2円のような小さな値も含め、手元の画像を人間が照合して基準データを作った。SMBC 9,637,386円とSBI 808,430円、合計10,445,816円、29明細、差額0円を回帰檢査で確認した。

アイコンも画面も、実際のデスクトップで確認

アプリのアイコンには、俺が思いついた「((ぬ))」を採用した。括弧を相場の波や保護リングに見立てた案を作り、favicon、画面のロゴ、Windows用ICOへ反映した。でもデスクトップには古いアイコンが残った。ショートカットの作り直しやキャッシュ対策をしても表示が変わらず、最終的には、操作していたDesktopフォルダーとWindowsが実際に表示しているOneDrive配下のデスクトップが別だと分かった。正しい場所に作り直して、ショートカットから取り出したアイコンまで確認した。

複数画像・複数日を、混ぜずに扱う

フォルダー内の画像をまとめて選ぶ機能も追加した。サブフォルダーを含めて最大50枚を調べ、画像形式や解像度などの条件に合うものだけを選ぶ。1枚あたり約1分かかる目安も画面に出すようにした。18枚を選んだつもりが16枚になった時は、完全に同じ画像が2組あり、コピーを除外していたのが理由だった。これは画面に除外数を表示し、理由を分かるようにした。

最初はフォルダーから最新日の画像だけを表示する方式だったが、せっかく過去の画像もあるのに経時変化が見られない。そこで撮影日ごとに分け、同じ日のSMBCとSBIはまとめる一方、違う日の資産は絶対に足し合わせず、表示日を切り替えられるようにした。同日の完全重複や、同じ証券会社の部分画像と全画面画像の重複も除く。読み取りに失敗した画像は成功扱いせず、何枚除外したか表示する。

保存履歴にも落とし穴があった。読み込み時に、すでに補正濟みの金額へOCR直後の補正をもう一度かけて、総資産が膨らんでいた。履歴の値を確定値として読み込むように直し、古い保存形式もできるだけ壊さず扱えるようにした。さらに、フォルダーに含まれる過去の日付と保存履歴を使って、総資産の推移、月次推移、資産分類別の変化、銘柄の増減ランキングを出すようにした。

その時系列にも、3,000万円というあり得ない山が出たり、ある日だけ大きく下がったりする問題があった。原因は投資信託の数量・単価の桁ずれ、明細の欠落、画像の読み取り失敗など。OCRが返した総額をそのまま信じず、補正濟み明細から時系列を作るようにし、周囲の日付から孤立した不完全データは警告を出して分析対象から外すようにした。先頭の日だけ異常な時も檢出するようにした。差額があるだけで画面全体を「AI読み取り失敗」にするのもやめ、読めた明細と監査結果は表示し、数字の問題を隠さないようにした。セクションカードと資産配分カードが重なるレイアウト、増減ランキングの文字や金額が重なる表示も、実データを使って直した。

読み直すたびの料金と、人工智能タブの育成

大量の画像を選ぶと、19枚なら基本的に19回Geminiを呼ぶ。失敗時の自動再試行まで重なると、呼び出しが何十回にも増えかねず、時間も料金もきつい。そこで、画像ごとの自動再試行はやめ、実行前に最大呼び出し数を表示し、APIの残高切れや混雑では連打せず止まるようにした。成功した画像は内容ハッシュで識別してPC内にキャッシュし、同じ画像を再表示するだけならGeminiを呼ばない。何回APIを呼び、何枚キャッシュを使ったかも画面に出す。キャッシュには失敗結果を残さず、いつでも明示的に消せるようにした。

それでも、初めての解析では画像枚数分の呼び出しが必要なことがある。テストした日にGeminiの前払い残高が切れて、実際に19枚すべてで読み取りに失敗したこともあった。画像が悪いのか、プログラムが止まっているのか、APIの料金枠が尽きたのかを区別して表示し、ローカルサーバー停止も診断できるようにした。費用がかかるからこそ、一枚で確かめてから二枚へ進み、同じ画像を無駄に何度も送らない檢査手順が必要だった。

明細の縦スクロール、固定見出し、「明細」と「人工智能」のタブも作った。最初は質問用モデルが使えず404になったが、利用可能なモデルへ変更して、画像を再解析せず、読み込み濟みの明細や過去データを使って質問できるようにした。片方の証券会社の画像しかない場合は、表示日以前にあるもう片方の最新の監査濟みデータを補完候補にする。ただし違う日の合計は同日総資産や運用損益ではなく、参考値と明示する。

AIが明細を直接勝手に変えるのではなく、まず修正案を見せ、俺が確認して適用・取り消しできるようにした。読み取りを使わないローカル数値点檢、直近の会話の引継ぎ、市場の公開情報檢索、檢索の出典や利用量表示も加えた。保有情報と市場檢索の会話は混ぜない。銘柄相談では俺の保有情報を踏まえつつ、予算や期間、リスク許容度を確認し、売買を断定しない。これは何でも自動で操作する代理人ではなく、データを一緒に確認する機能だ。

三つの使い方と、クラウド側のテスト

利用者にそれぞれAPIキーを用意させるのは面倒だし、画像をたくさん試す開発中は料金も気になる。そこで人工智能の使い方を、①運営側のクラウドAIを利用する、②利用者の手持ちの代理人AIとソフトを組み合わせる、③代理人AIからソフトを操作する、の三段に分ける案にした。②と③では、元画像やAPIキーや全履歴を不用意に共有せず、修正案を画面で確認してから適用する。ローカル代理人との接続も、期限や解除、許可範囲を設けて実サーバーとEdgeで試した。これはCodexと同じ自由なPC操作権限を与えたという意味ではない。

9月には、共通クラウドAIを試すための専用基盤も作った。藤森製作所の名前でSupabaseの東京リージョンにテスト用プロジェクトを用意し、利用者ごとに分かれる台帳を準備した。StripeはCheckoutだけを許すテスト用制限キーとWebhook、Vercelは秘密値がそろうまで503を返す安全な状態から始めた。キー形式の檢査漏れ、Windowsの日本語PC名がVercel CLIで文字化けする問題、DB接続先の名前解決やTLS、パスワード認証エラーにも順番にぶつかった。秘密値を日記やコードに書かず、GitHub連携でデプロイし、Transaction poolerと公式CAで接続を直した。

9月30日、DBパスワードを再設定した後に認証濟みの残高照会が通り、残高ゼロではAI利用を拒否することを確認した。そのあとStripeサンドボックスでテスト決濟をし、署名付きWebhookで1,000テスト単位が台帳に入った。模擬AIを一回実行すると10単位が引かれて990になった。ここは大事なので書いておくが、実際のGeminiなどのAI処理をクラウドで呼んだわけではないし、本番のカード決濟でもない。通ったのは共通クラウドAIのテスト専用経路だ。

いったん区切り、次へ

俺は何度も「直った」と聞いて、実際に画面を見るとまたエラー、を繰り返した。だから今回は、実画像と画面で確認したこと、テストデータだけで確認したこと、まだ実AIでは試していないことを分けて残した。記録上は9月15日に実画像解析・履歴・質問機能などの回帰確認が通り、9月30日に共通AIのテスト専用利用経路も通った。低画質や回転画像など、最初から対象外にしたものまで保証したわけではない。

パソコンを替える前から続いていた「まず一枚を読み取る」段階のエラーに、ようやく区切りをつけられたと思う。長かったし、正直かなり疲れた。でも、画像一枚の読み取りから二枚・複数日・保存履歴へ広げ、画面で檢査して、失敗をまた直すところまで来た。このチャットの目的はいったん終了。次は、この土台と記録を使って、実AIとの接続や精度、使いやすさを一つずつ確かめながら完成度を上げていく。