TL;DR
手持ちの雑な CSV / Excel / 写真 / PDF を投げると、AI が「読み取って提案」し、決定的なコードが「矯正・分類・確定・適用」して nanco に取り込む機能。
合言葉は「AI は提案だけ、確定はコード」と「データのある列を黙って消さない」。
全体は 6層(API / ハンドラ / 共有ロジック / クライアント / UI / フック)+ DB・cron で構成され、型の背骨は contract.ts 1枚。このページはその地図です。
「データを nanco の形に整える手作業」を、AI と決定的コードの分担で肩代わりする取り込みアシスタント。
これまでの取り込みは「列の対応表をユーザーが自分で埋める」形でした。列が多いと分かりにくく、特に新規ワークスペースでは AI が属性提案を出し切れず、データのある列が無言で捨てられて消える事故(実例: 40列中20列消失)が起きていました。
本ブランチはそれを作り直し、次の3つの入口を1つの会話型UIに統合しました。
| 入口 | 対象 | 主なファイル |
|---|---|---|
| 在庫CSV/Excel | 初回の一括登録・移行(オンボーディングの肝) | CSVImportModal → AiImportThread |
| 入荷 / 出荷の伝票 | 仕入先・得意先ごとの受払データ | Receiving/ShippingCsvImportMultipleModal |
| 写真 / PDF | 納品書・出荷表をスマホ撮影 / スキャン | extract API(vision) |
どの入口でも、最終的にユーザーが見るのは「AI が読み取った内容を1問ずつ確認していく会話」です。確認が終わると、できあがった整形済みデータが既存の取り込みウィザードにそのまま流れ込む(=検証ロジックは作り直さず再利用)のがポイントです。
このシステムを理解する鍵は、たった2つの原則です。コードの隅々がこの2つから演繹されています。
AI(大規模言語モデル)は「この列はたぶんアイテム名」「この日付はたぶん賞味期限」と当たりをつける役だけ。実際にデータを変換し、全行に適用し、最終的に何を取り込むかを決めるのは必ず普通のコードです。理由は2つ。
sanitizeAnalyzeResult)applyAnalysisToRows)buildColumnPlans)checkCsvHeader)旧フローの最大の事故が「列が無言で消える」ことでした。新フローの不変条件は「中身のある列は、捨てるとしても必ず一度ユーザーに見せる」。AI が取りこぼした列・捨てようとした列は、コード側が拾い直して「これは使いますか?」と確認に回します(sanitizeAnalyzeResult の救済フォールバック)。
sanitize が付く関数群がその関門です。コードは責務でくっきり層分けされています。上から下へ依存(上が下を呼ぶ)、下は上を知りません。ブラウザ側(上3層)とサーバー側(下4層)は HTTP で繋がります。
共有ロジック層が「両側から使われる」のがこの設計の特徴です。たとえば applyAnalysisToRows はブラウザ側(確定時の整形)でも使われ、sanitizeAnalyzeResult はサーバー側(AI出力の矯正)で使われます。型(contract.ts)を1枚に集約することで、ブラウザとサーバーが「同じ言葉」で会話できます。
本ブランチで追加・変更した本番コード(テスト除く)の主役を、層ごとに並べます。背骨 が型の中心 contract.ts。詳細な責務は ②レイヤーと責務 へ。
src/services/aiImport/)sanitizeAnalyzeResult。全ての中心。buildColumnPlans)。store:false 強制・tier切替。サーバーが公開する入口は5つだけ。LLM(お金がかかる呼び出し)を使うのは3つ(analyze / extract / interpret-column)、残り2つ(commit / lookup)は DB 操作だけです。
| API | 役割 | LLM | モデル | 上限 |
|---|---|---|---|---|
analyze | 列統計+サンプルを解析して取り込み方針を出す | 使う | gpt-4o(strong) | 200/日/社 |
extract | 画像/PDFから明細表+伝票ヘッダーを抽出(vision) | 使う | gpt-4o(vision) | 200/日/社 |
interpret-column | 列カードの自由入力(「数値にして」等)を解釈 | 使う | gpt-4o-mini(cheap) | 1000/日/社 |
commit | 取り込み確定。再利用ルールを保存 | 使わない | — | — |
lookup | 「前回と同じ形式」を過去セッションから探す | 使わない | — | — |
全APIは withAuth(Firebase認証)でラップされ、ハンドラ内で必ず会社メンバーシップ検証(越境=IDOR防止)を通します。LLMを呼ぶ3つと書き込みのcommitは assertCanWrite で権限・サブスクも確認。詳細は ⑤コスト制御。
在庫CSVの場合の最短ルート。各段の詳細は ③在庫CSVの全行程 で1ステップずつ解説します。
AnalyzeResultcontract.ts。sanitize〜(矯正・検算)sanitizeAnalyzeResult / sanitizeExtractedDocument / sanitizeInterpretResult。ColumnPlan / buildColumnPlansnfkc 全角半角統一 / extractNumber 数字抜き出し / normalizeDate 日付整形 / cleanText 整える)。AIは「どれを使うか」選ぶだけで、実装はコード。documentFields(伝票ヘッダー)ReuseDecisions(形式別再利用)AiUsageCounterDB の原子的カウントで管理。全体像(このページ)の次は、知りたい切り口で読み進めてください。それぞれ独立して読めます。
contract.ts を背骨にした依存関係を1枚で。「それぞれのクラスの関係性」を知りたい人はここから。姉妹ページ AIインポート 列確認フロー(UX/進捗) は、同じ機能をユーザー体験とスライス進捗の視点でまとめたものです(このアーキ集とは視点違いの補完)。