開発完了
勘定科目AI判定機能の実装
概要
/validate-receipts のサブエージェントに勘定科目の判定も行わせ、Gemini OCRの結果と照合する機能を追加する。
現状(Before)

- サブエージェントは日付・金額・支出先・摘要のみ読み取り
- 勘定科目はGemini OCRの結果のみ表示
- AIによるダブルチェックがない
目標(After)
- サブエージェントが勘定科目も判定
- Gemini OCRとサブエージェントの結果を照合
- 不一致の場合、UIで視覚的に表示
実装内容
1. DB変更
subagent_reads テーブルに勘定科目フィールドを追加:
ALTER TABLE subagent_reads ADD COLUMN read_account_category TEXT;
ALTER TABLE subagent_reads ADD COLUMN account_category_confidence INTEGER DEFAULT 100;
2. スキル修正(/validate-receipts)
サブエージェントに渡すプロンプトに勘定科目の判定を追加:
1. 各画像を読み取り、以下の情報を抽出:
- 日付 (YYYY/MM/DD形式)
- 金額 (円単位、整数)
- 支出先 (支払先の店舗名・会社名)
- 摘要 (取引内容の簡潔な要約)
- 勘定科目 (以下から選択) ← 追加
- 信頼度 (0-100、読み取りにくい場合は下げる)
- 手書きフラグ
勘定科目の選択肢:
- 会議費
- 接待交際費
- 消耗品費
- 旅費交通費
- 新聞図書費
- 通信費
- 支払手数料
- 車両費
- 仮払金
3. 比較スクリプト修正(validation.py)
compare_batch_reads 関数で勘定科目の比較を追加:
# 勘定科目は完全一致で判定
account_cmp = compare_values(
ocr["account_category"], row["read_account_category"],
"account_category", row["account_category_confidence"] or 100
)
4. UI修正(ReceiptForm.vue)
勘定科目ボタンの表示ロジックを変更:
| 条件 | 表示 |
|---|---|
| OCR結果 = サブエージェント結果 | 青ボタン(現状通り) |
| OCR結果 ≠ サブエージェント結果 | サブエージェント結果のボタンに紫枠線 + エージェントアイコン |
UIイメージ
勘定科目
┌─────────┐ ┌─────────────┐ ┌─────────┐
│ 会議費 │ │ 接待交際費 │ │ 消耗品費 │
│ (青) │ │ (グレー) │ │ (グレー) │
└─────────┘ └─────────────┘ └─────────┘
┌─────────────┐ ┌─────────────┐
│ 🤖 │ │ │
│ 旅費交通費 │ │ 新聞図書費 │
│ (紫枠線) │ │ (グレー) │
└─────────────┘ └─────────────┘
- エージェントアイコン(🤖)はボタンの左上に小さく表示
- 紫枠線で「AIが別の勘定科目を提案している」ことを示す
- 判定詳細は不要(ボタンを見れば違いがわかる)
実装順序
- DB:
subagent_readsテーブルにカラム追加 - Python:
db.pyのsave_read_result関数を修正 - スキル:
/validate-receiptsのプロンプトを修正 - Python:
validation.pyの比較ロジックを修正 - API: 勘定科目の比較結果を返すように修正
- Vue:
ReceiptForm.vueのボタン表示ロジックを修正
注意事項
- 勘定科目の選択肢はクライアントごとに異なる可能性あり(将来的にはDBで管理)
- 現状は固定の選択肢で実装
実装結果(After)

- サブエージェントが勘定科目も判定するようになった
- OCR結果(会議費)とAI判定(接待交際費)が異なる場合、紫枠線+🤖アイコンで表示
- 不一致は
overall_scoreにも反映(勘定科目30%の重みで減点)
実装で修正したファイル
| ファイル | 修正内容 |
|---|---|
src/db.py | subagent_readsテーブルへのカラム追加、save_read_result関数の修正 |
src/validation.py | 勘定科目の比較ロジック追加、overall_scoreへの反映 |
src/ocr_server.py | APIレスポンスにread_account_categoryを追加 |
.claude/commands/validate-receipts.md | サブエージェントプロンプトに勘定科目判定を追加 |
frontend/app/components/receipt/AccountCategory.vue | AIの提案を紫枠線+🤖アイコンで表示 |
frontend/app/components/receipt/ReceiptForm.vue | 不一致時のみ紫枠線を表示するスタイル修正 |
frontend/app/composables/useReceipts.ts | クリック時のバリデーションデータ消失バグを修正 |
バグ修正
クリック時にバリデーションデータが消える問題
原因: selectItem関数でfile_nameのみで検索していたため、複数バッチがある場合に別のバッチのアイテムを見つけてしまっていた。
修正: batch_idとfile_nameの両方で比較するように変更。
// Before
const globalIdx = allItems.value.findIndex(i => i.file_name === item.file_name)
// After
const globalIdx = allItems.value.findIndex(
i => i.batch_id === item.batch_id && i.file_name === item.file_name
)