開発メモ
OCRバリデーションUIの3カラムレイアウト実装
結論
OCR Checkerの検証UIを3カラムテーブルレイアウトで実装した。「OCR結果」「読取確認」「判定」の3列で、OCR結果とサブエージェント読取値を並べて比較できる。
実装後、「読取確認」カラムに値が表示されない問題が発生した。原因はバックエンドサーバーの再起動忘れだった。
背景
税理士業務のレシートOCR処理において、以下の要件があった。
- OCR結果(編集可能)とサブエージェントの読取値を並べて表示したい
- 両者の一致/不一致を視覚的に判定したい
- 信頼度に応じて枠線の色を変えたい
実装内容
3カラムテーブル構造
| OCR結果(編集可能) | 読取確認(参照のみ) | 判定 |
|-------------------|-------------------|-----|
| 2024/03/27 | 2024/03/27 | ✓ |
| 180 | 180 | ✓ |
| 地下鉄運賃(きっぷ) | きっぷ(交通費) | × |
| 東京地下鉄株式会社 | 東京メトロ 半蔵門駅 | × |
APIレスポンスの拡張
ocr_server.pyにサブエージェント読取値と信頼度を追加。
if validation:
result["validation"] = {
# 既存フィールド
"overall_score": validation.get("overall_score"),
"date_score": validation.get("date_score"),
# ...
# サブエージェント読取値(追加)
"read_date": validation.get("read_date") or "",
"read_amount": validation.get("read_amount"),
"read_payee": validation.get("read_payee") or "",
"read_summary": validation.get("read_summary") or "",
# サブエージェント信頼度(追加)
"date_confidence": validation.get("date_confidence"),
"amount_confidence": validation.get("amount_confidence"),
"payee_confidence": validation.get("payee_confidence"),
"summary_confidence": validation.get("summary_confidence"),
}
フロントエンドの型定義
api.tsのReceiptValidationインターフェースを拡張。
export interface ReceiptValidation {
overall_score: number
date_score: number
date_comment: string
// ...
// サブエージェント読取値
read_date: string
read_amount: number | null
read_payee: string
read_summary: string
// サブエージェント信頼度
date_confidence: number | null
amount_confidence: number | null
payee_confidence: number | null
summary_confidence: number | null
}
Vue コンポーネント
ReceiptForm.vueでヘルパー関数を定義。
// サブエージェント読取値を取得
const getReadValue = (field: 'date' | 'amount' | 'payee' | 'summary') => {
if (!validation.value) return null
const key = `read_${field}` as keyof ReceiptValidation
return validation.value[key] as string | number | null
}
// 一致判定(スコア90%以上を一致とみなす)
const isMatch = (field: 'date' | 'amount' | 'payee' | 'summary') => {
const score = getFieldScore(field)
if (score === null) return null
return score >= 90
}
// 信頼度に基づく枠線CSSクラス(紫系)
const getBorderClass = (score: number | null) => {
if (score === null) return ''
if (score >= 90) return 'border-high' // 薄い紫
if (score >= 60) return 'border-medium' // 中程度の紫
return 'border-low' // 濃い紫 + 背景色
}
テンプレートで3カラムレイアウトを構成。
<div class="field-table">
<div class="field-table-header">
<div class="col-header">OCR結果</div>
<div class="col-header">読取確認</div>
<div class="col-header">判定</div>
</div>
<div class="field-row">
<div class="field-label">日付</div>
<div class="field-cols">
<div class="col-ocr"><!-- 編集可能な入力フィールド --></div>
<div class="col-read" :class="getBorderClass(getFieldScore('date'))">
<span v-if="getReadValue('date')">{{ getReadValue('date') }}</span>
<span v-else class="no-value">-</span>
</div>
<div class="col-match">
<span v-if="isMatch('date') === true" class="match-icon">✓</span>
<span v-else-if="isMatch('date') === false" class="no-match">×</span>
</div>
</div>
</div>
<!-- 他のフィールドも同様 -->
</div>
トラブルシューティング
問題: 読取確認カラムに値が表示されない
実装後、ブラウザで確認すると「読取確認」カラムがすべて「-」と表示された。
調査
APIレスポンスを確認したところ、read_dateなどの新しいフィールドが含まれていなかった。
curl -s "http://localhost:8000/api/receipts?batch_id=..." | head -c 2000
返ってきたJSONにread_date, read_amountなどのフィールドがない。
原因
バックエンドサーバー(Python FastAPI)を再起動していなかった。ocr_server.pyを編集しても、サーバーを再起動しなければ変更は反映されない。
解決
# ポート8000のプロセスを特定して終了
$pid = (Get-NetTCPConnection -LocalPort 8000).OwningProcess | Select-Object -Unique
Stop-Process -Id $pid -Force
# サーバーを再起動
python src/ocr_server.py
ブラウザをキャッシュ無効でリロードして、APIから最新データを取得する。
Ctrl + Shift + R(または F5 + Shift)
これで読取確認カラムに値が表示された。
まとめ
- 3カラムテーブルレイアウトで、OCR結果とサブエージェント読取値を並べて比較できるようになった
- 一致/不一致を✓/×で視覚的に判定できる
- 信頼度に応じた紫系の枠線で、確認が必要な項目を強調表示できる
- バックエンドを変更したらサーバー再起動を忘れない