Claude CodeでECN前後のBOM差分を出し、影響部品を洗う手順
設計変更(ECN)で改訂したBOMのCSVを前後で突き合わせ、追加・削除・数量変更を出して在庫・発注残への影響まで洗う手順。claude -pとjqで毎回同じ形に揃えます。
設計変更通知(ECN)が出たら、製造・購買・品質が最初に知りたいのは「どの部品が何個変わったか」です。改訂前後のBOM(部品表)をCSVで書き出せるなら、差分の抽出は決まった手順で済みます。Claude Codeには、その差分を在庫と発注残に当てて「手を打つ部品」を絞り込む役を任せます。
この記事では、差分の計算を小さなスクリプトに固定し、claude -p で影響の読み取りを毎回同じ形式に揃える流れを組みます。出荷記録を結合して回収範囲を洗う話はロットトレーサビリティの記事が扱っています。ここで結合するのは部品表と在庫表で、見る対象は出荷ではなく調達と製造の手配です。
ECNのBOM差分で見る4種類の変化
BOMの差分は、親子と数量の組を前後で比べると4種類に分かれます。
| 種類 | 前の改訂 | 後の改訂 | 最初に確認すること |
|---|---|---|---|
| 追加 | 前の改訂なし | 後の改訂あり | 最初に確認すること在庫・発注残が足りるか |
| 削除 | 前の改訂あり | 後の改訂なし | 最初に確認すること旧部品の在庫と発注残をどう処置するか |
| 数量変更 | 前の改訂数量A | 後の改訂数量B | 最初に確認すること増減分が手配量に効くか |
| 置換 | 前の改訂削除と追加の組 | 後の改訂 | 最初に確認すること同じ位置の入れ替えか |
置換はCSVの上では「削除1行と追加1行」にしか見えません。差分の計算では追加と削除に分け、置換かどうかは位置を示す列(リファレンス番号や品目の位置)があれば照合する、という順にすると取りこぼしが出ません。位置の列がないBOMでは、置換は候補として挙げるだけにして、人が確認する前提で扱います。
入力CSVの整え方 — キー列と文字コード
差分の精度は入力で決まります。計算の前に、次の3点を揃えます。
- キーは「親の品番 + 子の品番」の組にします。同じ親に同じ子が複数行ある場合は、先に数量を合算します
- 数量の単位が混ざる列(個、m、g)は、1列にまとめず単位列を別に持たせます
- 改訂の識別(Rev A、Rev B)はファイル名ではなく列かフォルダで残します
文字コードも見落としやすい点です。ExcelからCSV UTF-8で書き出したファイルは先頭にバイトオーダーマーク(BOM)が付き、Pythonで素直に読むと最初の列名の頭に目に見えない文字(U+FEFF)が付いて、parent で引けなくなります。部品表の「BOM」と同じ略称で紛らわしいのですが、別物です。読み込みには encoding="utf-8-sig" を指定すれば除去できます。PowerShellのスクリプトで同じ種類の文字化けが起きる場合はps1とBOMの記事にまとめています。
差分を出すスクリプトを固定する
差分の計算をClaudeに毎回書かせると、実行ごとに列名の扱いや並び順が変わります。計算は小さなスクリプトに固定し、Claudeには結果を読む側を任せるほうが再現性が上がります。標準ライブラリの csv だけで書いた例です。
# bom_diff.py
import csv, sys
def load(p):
return {(r["parent"], r["child"]): int(r["qty"])
for r in csv.DictReader(open(p, encoding="utf-8-sig"))}
a, b = load(sys.argv[1]), load(sys.argv[2])
stock = {r["part"]: r for r in
csv.DictReader(open(sys.argv[3], encoding="utf-8-sig"))}
print("kind,parent,child,qty_a,qty_b,on_hand,open_po")
for k in sorted(set(a) | set(b)):
qa, qb = a.get(k), b.get(k)
if qa == qb:
continue
kind = ("added" if qa is None else
"removed" if qb is None else "qty_changed")
s = stock.get(k[1], {})
print(",".join(map(str, [kind, *k,
"" if qa is None else qa, "" if qb is None else qb,
s.get("on_hand", "?"), s.get("open_po", "?")])))在庫表(stock.csv)は部品ごとの手持ち在庫と発注残を持つ2列だけの表です。引けなかった部品は ? で出し、在庫表に載っていないこと自体を気づける形にしています。
サンプルとして、PX-100という製品のRev A・Rev Bと在庫表を用意して実行します。ECNの内容は、ネジを8本から6本に減らし、基板をPCB-001からPCB-002に替え、ガスケットを1枚足す想定です。
python3 bom_diff.py bom_rev_a.csv bom_rev_b.csv stock.csvkind,parent,child,qty_a,qty_b,on_hand,open_po
added,PX-100,GASKET-1,,1,0,500
removed,PX-100,PCB-001,1,,300,100
added,PX-100,PCB-002,,1,0,0
qty_changed,PX-100,SCR-M3-8,8,6,1200,0変更のない行(CAP-10UとCASE-A)は出力に現れません。差分が4行に絞られ、各行に在庫と発注残が付いた状態です。ここまでは決定的な処理なので、Claudeを通す必要はありません。
claude -pで影響の読み取りを同じ形に揃える
差分CSVから「誰が何をするか」を読むところにClaudeを使います。claude -p は標準入力を受け取れるので、差分を cat でつなげます。
python3 bom_diff.py bom_rev_a.csv bom_rev_b.csv stock.csv \
| claude -p "ECNの影響を部品ごとに判定する" \
--output-format json \
--json-schema '{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"child":{"type":"string"},"action":{"type":"string"},"reason":{"type":"string"}},"required":["child","action","reason"]}}},"required":["items"]}' \
| jq '.structured_output.items[]'--output-format json と --json-schema を併せて渡すと、結果は応答の structured_output フィールドに入ります。jq で取り出せば、毎回同じ形のリストを後続の表計算や通知に流せます。スキーマが不正だと claude はエラーを出して止まります。また format キーワードは注釈として扱われ、形式の検証には使われません。
プロンプトの本体は、判定の観点を言葉で与える部分です。次のような指示を、CLAUDE.mdか --append-system-prompt で渡すと判定が安定します。
- added: 在庫+発注残が不足なら「起票が必要」、足りるなら「手配不要」
- removed: 在庫または発注残が残るなら「処置が必要」と書き、
発注残のキャンセル可否は購買に確認する旨を理由に入れる
- qty_changed: 減るなら余剰、増えるなら不足分を理由に数字で書く
- 置換の可能性がある removed と added の組は、候補として明記するこの指示のポイントは、判定の根拠に必ず数字(在庫、発注残、前後の数量)を書かせることです。
読み取りの結果をどう受け止めるか
Claudeの判定は、そのまま手配に使わず、元のCSVの行と突き合わせて確認します。公式のベストプラクティスにも、Claudeが完了と判断する根拠は「実行できるチェック」を渡して初めて作れる、という考え方が書かれています。ここでは、差分スクリプトの出力行数と、判定リストの件数が一致するかをチェックにできます。
python3 bom_diff.py bom_rev_a.csv bom_rev_b.csv stock.csv | wc -l行数(見出し行を除く)が判定の items の件数と合わなければ、判定の取りこぼしです。件数の照合は数秒で済み、判定の言い回しが多少ぶれても影響しません。
置換の候補をリファレンス番号で組にする
削除と追加を「同じ位置の入れ替え」として組にできると、読み手の負担は大きく減ります。BOMにリファレンス番号(基板上の部品位置や組付け位置を示す列)があれば、位置をキーにして前後を突き合わせられます。差分スクリプトに、次のような追記を足します。
# ref 列(例: U1, C3)が両方の改訂にあるときの置換候補
def by_ref(p):
return {(r["parent"], r["ref"]): r["child"]
for r in csv.DictReader(open(p, encoding="utf-8-sig"))
if r.get("ref")}
ra, rb = by_ref(sys.argv[1]), by_ref(sys.argv[2])
for k in sorted(set(ra) & set(rb)):
if ra[k] != rb[k]:
print(f"replaced,{k[0]},{k[1]},{ra[k]},{rb[k]}")同じ位置の品番が変わった行だけが replaced として出ます。この行があれば、追加・削除の2行を別々に読ませず、1件の置換として扱わせられます。ref列がない、または1つの位置に複数の部品が載るBOMでは、この照合は成立しません。その場合は品番の近さだけで置換と決めず、候補の提示にとどめます。
置換の組が見つかったときは、プロンプトに「旧品と新品の在庫・発注残を並べて、切替の順序を2通り示す」ように指示すると、読み手が判断しやすい形になります。2通りとは、旧品を使い切ってから新品へ移る場合と、切替日で旧品を打ち切る場合です。前者では旧品の在庫が尽きる日を、後者では余る旧品の数量を、それぞれ数字で出させます。
切替日までに足りるかを日数で見る
在庫と発注残の数字は、ECNの切替日と組み合わせて初めて意味を持ちます。発注残500の納期が切替日の後なら、数字の上では足りていても現場では欠品します。納期列(due_date)を在庫表に足し、スクリプトに切替日の引数(ここでは --cutover)を加えると、「切替日までに入る分」だけを数えられます。引数の処理は argparse などで数行足す形で、次のように呼び出します。
python3 bom_diff.py bom_rev_a.csv bom_rev_b.csv stock.csv \
--cutover 2026-11-01サンプルのGASKET-1は発注残500ですが、納期が11月1日より後ならば、切替日に使える数は0のままです。納期が切替日の前なら、そのまま足りる判定になります。この判定は日付の比較だけなので、スクリプトの側で行い、Claudeには「足りない部品のうち、納期を早められるか購買に聞くべきもの」を読ませる使い方が向いています。納期の交渉余地は、CSVの数字からは分からない情報です。判定に使えない項目を、Claudeが推測で埋めないよう、プロンプトに明記します。
在庫と発注残から手配の優先度を決める
サンプルの4行を、手配の観点で読むと次のようになります。判定は読み手(購買・製造)が行うもので、表は考え方の例です。
| 部品 | 変化 | 在庫 / 発注残 | 見方 |
|---|---|---|---|
| SCR-M3-8 | 変化8本から6本 | 在庫 / 発注残1200 / 0 | 見方在庫は残る。1台あたりの消費が減るので、在庫の消化が遅くなる |
| PCB-001 | 変化削除 | 在庫 / 発注残300 / 100 | 見方旧基板の在庫300と発注残100が宙に浮く。切替日と廃棄・返品の扱いを決める |
| PCB-002 | 変化追加 | 在庫 / 発注残0 / 0 | 見方在庫も発注もない。切替日までに起票が必要 |
| GASKET-1 | 変化追加 | 在庫 / 発注残0 / 500 | 見方在庫はないが発注残が500ある。納期が切替日に間に合うかが焦点 |
PCB-001とPCB-002は、同じ位置の入れ替えの候補です。削除側に在庫と発注残があり、追加側が空なので、「旧品を使い切ってから新品に切り替えるのか、即時切替なのか」が決まらないと手配できません。差分の数字だけでは決まらないこの切替方針は、ECN本体に記載があるか、設計に確認するしかありません。Claudeの出力に切替方針を勝手に埋めさせないよう、プロンプトに「方針が不明なら不明と書く」と入れておきます。
多段BOMの場合は、子部品がさらに子を持つので、展開した後に差分を取る必要があります。展開に使う品番の親子表が別にあるなら、先に最下層まで展開し、上のスクリプトに渡す形にします。
無人実行の権限を絞る
この処理を毎朝のジョブや共有サーバーで回すなら、権限の設定が必要です。読み取り専用コマンドとして挙げられている ls、cat、grep などに python3 は入っていないため、許可ルールがなければ確認待ちになります。許可の範囲を狭くするには --allowedTools に実行する1コマンドだけを書きます。
claude -p "ECNの影響を判定して結果をimpact.mdに書く" \
--permission-mode dontAsk \
--allowedTools "Bash(python3 bom_diff.py *)" "Read" "Write"dontAsk は、許可の対象外で確認が必要になる操作をすべて拒否するモードです。許可ルールで覆われた操作と、diff、cat、wc、grep のような読み取り専用のコマンドは通常どおり動きます。確認の窓口がないジョブでは、拒否で止まるほうが想定外の操作を許すより安全です。
CIや共有環境でホスト側のフック、プラグイン、CLAUDE.mdを読み込ませたくない場合は --bare を付けます。起動が速くなり、どのマシンでも同じ結果になります。ただし --bare ではサブスクリプションのログインを使わないので、ANTHROPIC_API_KEY を環境変数に設定してから実行します。
実行の成否は終了コードで分岐できます。成功は0、失敗は0以外です。認証切れのようなセッション内の失敗は標準出力に結果として出る点に注意してください。標準入力のパイプは10MBが上限なので、数十万行のBOMを丸ごと流すと止まります。その場合は差分を取った後の行だけを渡す構成にします。
つまずきやすい点
差分が「全行変更」になるときは、品番の表記ゆれを疑います。大文字と小文字、ハイフンの全角と半角、先頭のゼロ落ちは、見た目が同じでも別のキーになります。読み込み時に strip() と正規化を入れる位置は、差分の計算の直前が安全です。
数量が小数のBOM(長さ、重さ)では、int() が例外で止まります。単位列を分けたうえで、Decimal で読み込みます。
発効日(切替日)が部品ごとに違うECNでは、改訂前後の2枚だけでは影響が測れません。部品ごとの有効期間を列に持たせ、切替日を境に在庫が足りるかを日付で見る形に拡張します。
影響部品を洗う前の段階として、変更が製品の故障モードにどう効くかを整理する手順はFMEAの故障モード洗い出しにあります。洗い出した部品の発注残を仕入側の書類と照合する工程は、発注書・納品書・請求書の三点照合の手順がそのまま使えます。
まとめ
差分の計算はスクリプトに固定し、Claudeには在庫と発注残を足した差分行の読み取りだけを任せる分担が、再現性と速さの両立点です。判定の出力形式は --json-schema で揃え、件数の照合を検証に使うと、取りこぼしに自分で気づけます。
最初の1回は、実際のECNで改訂前後のCSVを出し、サンプルと同じ4種類に分類できるかを確かめるところから始められます。置換の判定と切替日の扱いは、設計と購買の確認が要る部分として残ります。