100本ノック / 確率統計 / 確率・統計マーケティング応用100本ノック

製造業DIをPythonで実践|KPI・知識グラフ・最適化・AIエージェント10本ノック

データを「判断」に変える製造業DI:受注・品質・設備をつなぐ実践10本ノック

本記事では、架空の精密部品工場を題材に、製造業の DI(Decision Intelligence:意思決定インテリジェンス) を小さく構築します。受注、製造実績、品質、設備停止を共通のデータモデルで結び、KPIの異常を発見するだけでなく、原因候補、将来シナリオ、推奨アクション、実行後の記録までを一つの流れとして扱います。

対象は No.091〜No.100 です。個々の技術を並べるのではなく、「どの意思決定を、どのデータとモデルで、誰が安全に実行するか」という製造業の実務に沿って解説します。

[!NOTE] 本資料は、数理工房 (もしくは代表である和山個人) が過去に企業研修において使用した notebook を企業様の許可を得て再構成・編集のうえ公開しています。
掲載データはすべて架空のものであり、実在する企業・工場・数値とは一切関係ありません。

はじめに:この記事で扱う製造業の実務課題

工場には、生産管理、品質管理、設備保全、原価管理などのシステムがあります。しかし、会議で本当に知りたいのは個別システムの数値ではなく、たとえば「今月の納期未達は何が原因か」「残業と外注のどちらで回復すべきか」「対策によって粗利と品質はどう変わるか」です。

ここでは、精密バルブ部品を生産する3ラインについて、納期遵守・不良・停止・限界利益を同時に見ながら、翌週の生産方針を決める課題を扱います。

現場でよくある状況

  • KPI資料の作成に時間がかかり、会議時点ではデータが古い
  • 部門ごとに「稼働率」「良品数」「納期」の定義や集計粒度が異なる
  • 異常を見つけても、受注・設備・品質の関係を人手でたどっている
  • シミュレーションや最適化が分析担当者のPCで止まり、実行履歴が残らない
  • AIの提案理由、参照データ、承認者が追跡できず、現場投入できない

DIの狙いはダッシュボードを増やすことではありません。観測から判断、実行、学習までのリードタイムを短くし、意思決定の再現性を高めることです。

なぜこの問題は判断が難しいのか

製造業の意思決定には、複数の時間軸と目的が混在します。設備稼働を上げれば短期の生産量は増えますが、品質悪化や保全先送りにつながることがあります。納期を守るための外注は売上を確保する一方、限界利益を下げます。

さらに、実績値には需要変動、停止時間、歩留まりという不確実性があります。したがって、単一KPIの最大化ではなく、KPI間の因果関係、制約、予測分布、承認ルールを含めた判断設計が必要です。

今回扱うノックの全体像

No.テーマこの notebook での問い
091製造業DIとはデータを判断と実行にどう接続するか
092KPI設計経営成果と現場の先行指標をどう結ぶか
093データモデル設計異なる粒度のデータをどう安全に結合するか
094オントロジー設計用語と関係をどう共通化するか
095知識グラフKPI異常から原因候補をどうたどるか
096シミュレーション基盤不確実性の下で案をどう比較するか
097最適化基盤制約を守る実行案をどう選ぶか
098AIエージェント活用提案を安全な業務フローにどう載せるか
099製造業版Palantirの設計データ・モデル・業務をどう一体化するか
100数理工房の目指す世界継続的に学習する意思決定基盤をどう育てるか

後半へ進むほど、記述(何が起きたか)→診断(なぜか)→予測(どうなるか)→処方(何をするか)→実行と学習へ発展します。

Python 環境の準備

外部データには依存せず、numpypandas で架空データを生成し、matplotlib で可視化します。乱数生成器は seed を固定します。networkx は、後半の知識グラフに使用します。

import platform
import sys

import japanize_matplotlib
import matplotlib
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import pandas as pd
from IPython.display import display

SEED = 42
rng = np.random.default_rng(SEED)
pd.set_option("display.max_columns", 20)
pd.set_option("display.float_format", lambda x: f"{x:,.2f}")

print(f"Python      : {sys.version.split()[0]}")
print(f"OS          : {platform.system()} {platform.release()}")
print(f"numpy       : {np.__version__}")
print(f"pandas      : {pd.__version__}")
print(f"matplotlib  : {matplotlib.__version__}")
print(f"networkx    : {nx.__version__}")

架空データの作成

90日分のライン別日次実績を作成します。主キーは date × line_id です。需要、計画、生産、良品、不良、停止、残業、電力、納期内数量を持たせます。L3ラインは後半45日に停止時間と不良率が悪化する設定とし、DIが検知すべき変化を意図的に埋め込みます。

限界利益は簡略化して、良品売上から材料費、残業費、電力費を引いた値とします。実務では会計上の定義と一致させ、配賦前後を混同しないことが重要です。

dates = pd.date_range("2025-01-01", periods=90, freq="D")
lines = pd.DataFrame({
    "line_id": ["L1", "L2", "L3"],
    "line_name": ["切削1号", "切削2号", "複合加工"],
    "rated_units": [128, 116, 104],
    "product_id": ["P-A", "P-B", "P-C"],
})
products = pd.DataFrame({
    "product_id": ["P-A", "P-B", "P-C"],
    "product_name": ["標準バルブ", "耐熱バルブ", "高圧バルブ"],
    "price_yen": [8200, 10600, 14800],
    "material_yen": [3900, 5200, 7600],
})

rows = []
for day_no, date in enumerate(dates):
    weekday_factor = 0.90 if date.dayofweek >= 5 else 1.00
    for rec in lines.itertuples(index=False):
        demand = max(55, int(rng.normal(rec.rated_units * 0.91 * weekday_factor, 12)))
        planned = min(int(rec.rated_units * weekday_factor), demand + int(rng.integers(2, 11)))
        stop_mean = 42 if (rec.line_id == "L3" and day_no >= 45) else 24
        downtime = max(0, rng.normal(stop_mean, 10))
        overtime = max(0, demand - planned) * 0.12 + rng.uniform(0, 1.8)
        availability = np.clip(1 - downtime / 480, 0.70, 1.00)
        produced = max(0, int(planned * availability + overtime * 5 + rng.normal(0, 3)))
        defect_base = {"L1": 0.018, "L2": 0.025, "L3": 0.028}[rec.line_id]
        if rec.line_id == "L3" and day_no >= 45:
            defect_base += 0.025
        defective = rng.binomial(produced, min(0.15, defect_base + downtime / 12000))
        good = produced - defective
        on_time = min(good, max(0, int(demand - max(0, rng.normal(downtime / 18 - 1, 3)))))
        rows.append([date, rec.line_id, rec.product_id, demand, planned, produced,
                     good, defective, downtime, overtime, produced * rng.uniform(2.7, 3.3), on_time])

daily = pd.DataFrame(rows, columns=[
    "date", "line_id", "product_id", "demand_units", "planned_units",
    "produced_units", "good_units", "defect_units", "downtime_min",
    "overtime_h", "energy_kwh", "on_time_units"
]).merge(products, on="product_id", validate="many_to_one")
daily["sales_yen"] = daily["good_units"] * daily["price_yen"]
daily["contribution_yen"] = (
    daily["good_units"] * (daily["price_yen"] - daily["material_yen"])
    - daily["overtime_h"] * 3_200 - daily["energy_kwh"] * 24
)
daily["defect_rate"] = daily["defect_units"] / daily["produced_units"].clip(lower=1)
daily["otd_rate"] = daily["on_time_units"] / daily["demand_units"].clip(lower=1)

print(f"日次ライン実績: {len(daily):,}行 / 期間: {daily['date'].min().date()}{daily['date'].max().date()}")
display(daily.head())

No.091:製造業DIとは — 観測から実行までを一つの判断ループにする

実務での意味

BIが「何が起きたか」を可視化するのに対し、DIは、その情報を原因分析、選択肢評価、推奨、承認、実行、効果検証へつなげます。製造業では、納期未達を赤く表示するだけでなく、停止と不良の寄与を確認し、残業・保全・外注のどれを採るかまで設計することが重要です。

分析・モデル化の考え方

最小の判断ループを Observe → Diagnose → Predict → Decide → Act → Learn と置きます。まずライン別に納期遵守率(OTD)、不良率、停止時間、限界利益を同じ粒度で集計します。

OTD=納期内数量要求数量,Defect Rate=不良数量生産数量\mathrm{OTD}=\frac{\text{納期内数量}}{\text{要求数量}},\qquad \mathrm{Defect\ Rate}=\frac{\text{不良数量}}{\text{生産数量}}

Pythonで確認する

直近30日を意思決定対象期間とし、複数KPIを同じスコアカードで比較します。

recent = daily[daily["date"] >= daily["date"].max() - pd.Timedelta(days=29)]
di_scorecard = recent.groupby("line_id").agg(
    需要数=("demand_units", "sum"),
    良品数=("good_units", "sum"),
    納期内数=("on_time_units", "sum"),
    生産数=("produced_units", "sum"),
    不良数=("defect_units", "sum"),
    停止時間分=("downtime_min", "sum"),
    限界利益円=("contribution_yen", "sum"),
)
di_scorecard["納期遵守率"] = di_scorecard["納期内数"] / di_scorecard["需要数"]
di_scorecard["不良率"] = di_scorecard["不良数"] / di_scorecard["生産数"]
display(di_scorecard[["需要数", "良品数", "納期遵守率", "不良率", "停止時間分", "限界利益円"]])

ax = di_scorecard[["納期遵守率", "不良率"]].plot.bar(figsize=(8, 4), color=["#2a6fbb", "#d95f02"])
ax.set_title("直近30日のライン別DIスコアカード")
ax.set_xlabel("ライン")
ax.set_ylabel("比率")
ax.grid(axis="y", alpha=0.3)
ax.legend(loc="best")
plt.tight_layout()
plt.show()

結果の読み取り

L3は他ラインより納期遵守率が低く、不良率が高いうえ、停止時間も大きいことが確認できます。単一の赤信号ではなく、複数KPIが同時に悪化しているため、単純な増産より設備・品質起点の診断を優先すべきです。DIでは、このスコアカードを次の原因探索と対策比較の入口にします。

No.092:KPI設計 — 経営成果を現場で動かせる指標へ分解する

実務での意味

売上や利益だけでは、現場が今日何を変えるべきか分かりません。一方、測りやすい稼働率だけを追うと、作り過ぎや保全先送りを招きます。遅行指標と先行指標を因果仮説で結び、責任者と更新頻度を定める必要があります。

分析・モデル化の考え方

ここでは成果KPIを限界利益と納期遵守率、先行KPIを不良率・停止率・残業時間とします。目標からの達成度を方向付きで標準化し、重み付きの健康度スコアを作ります。

S=100kwkak,kwk=1S=100\sum_k w_k a_k,\qquad \sum_k w_k=1

大きいほど良いKPIは ak=min(xk/tk,1)a_k=\min(x_k/t_k,1)、小さいほど良いKPIは ak=min(tk/xk,1)a_k=\min(t_k/x_k,1) とします。ただし、総合点はドリルダウンの入口であり、個別指標を隠してはいけません。

Pythonで確認する

kpi = di_scorecard.copy()
kpi["停止率"] = kpi["停止時間分"] / (30 * 480)
kpi["残業時間"] = recent.groupby("line_id")["overtime_h"].sum()
kpi["利益達成"] = (kpi["限界利益円"] / 12_000_000).clip(upper=1)
kpi["OTD達成"] = (kpi["納期遵守率"] / 0.97).clip(upper=1)
kpi["品質達成"] = (0.025 / kpi["不良率"]).clip(upper=1)
kpi["停止達成"] = (0.07 / kpi["停止率"]).clip(upper=1)
kpi["KPI健康度"] = 100 * (
    0.35 * kpi["利益達成"] + 0.30 * kpi["OTD達成"]
    + 0.20 * kpi["品質達成"] + 0.15 * kpi["停止達成"]
)
display(kpi[["納期遵守率", "不良率", "停止率", "残業時間", "限界利益円", "KPI健康度"]].round(3))

fig, ax = plt.subplots(figsize=(8, 4))
kpi["KPI健康度"].sort_values().plot.barh(ax=ax, color=["#d95f02", "#e6ab02", "#1b9e77"])
ax.axvline(90, color="black", linestyle="--", label="要注意ライン 90点")
ax.set_title("経営・現場KPIを統合した健康度")
ax.set_xlabel("KPI健康度(0〜100)")
ax.set_ylabel("ライン")
ax.grid(axis="x", alpha=0.3)
ax.legend()
plt.tight_layout()
plt.show()

結果の読み取り

L3の健康度が低い理由は、OTDだけでなく品質と停止の目標未達が重なっているためです。実務では重みを分析者だけで決めず、工場長・製造・品質・保全で合意し、定義、単位、集計粒度、更新頻度、責任者、目標値をKPI台帳に残します。

No.093:データモデル設計 — 集計粒度と主キーを先に決める

実務での意味

受注は明細単位、製造はロット単位、設備は秒単位、品質は検査単位です。これらをキーの確認なしに結合すると、行が増殖して売上や不良数が二重計上されます。良いデータモデルは、分析速度以上に「同じ問いに同じ答えが返る」ことを支えます。

分析・モデル化の考え方

ファクト(実績)とディメンション(製品・設備などの属性)を分離します。日次ライン実績の粒度は date × line_id × product_id、ラインマスタと製品マスタはそれぞれ一意であることを契約にします。結合前後で行数と合計値が保存されることも検査します。

Pythonで確認する

fact = daily[["date", "line_id", "product_id", "good_units", "defect_units", "contribution_yen"]].copy()
dim_line = lines[["line_id", "line_name", "rated_units"]].copy()
dim_product = products.copy()

assert not fact.duplicated(["date", "line_id", "product_id"]).any()
assert dim_line["line_id"].is_unique
assert dim_product["product_id"].is_unique

model = (fact
         .merge(dim_line, on="line_id", validate="many_to_one")
         .merge(dim_product, on="product_id", validate="many_to_one"))
quality_contract = pd.DataFrame({
    "検査項目": ["ファクト主キー重複", "結合前後の行数差", "良品数の差", "限界利益の差"],
    "結果": [
        fact.duplicated(["date", "line_id", "product_id"]).sum(),
        len(model) - len(fact),
        model["good_units"].sum() - fact["good_units"].sum(),
        model["contribution_yen"].sum() - fact["contribution_yen"].sum(),
    ],
    "合格条件": ["0", "0", "0", "0"],
})
display(quality_contract)
display(model.head(3))

結果の読み取り

4つの検査結果がすべて0であり、ディメンション結合による二重計上がないことを確認できました。実務では、この検査をパイプライン実行時の自動テストにし、違反時はダッシュボード更新や最適化実行を止めます。「とりあえず結合」ではなく、粒度と主キーをデータ契約として管理することが出発点です。

No.094:オントロジー設計 — 業務用語と関係を機械可読にする

実務での意味

同じ「停止」でも、計画停止を含む部門と含まない部門があれば、会議の比較は成立しません。オントロジーは、設備、製品、KPI、イベント、対策などの概念と関係を共通語彙として定義し、データと業務をつなぐ設計図になります。

分析・モデル化の考え方

概念を 主語―関係―目的語 の三つ組で表します。重要なのは用語集にとどめず、ラインが製品を生産する停止イベントが設備に発生するKPIが実績から計算される のように関係と制約を持たせることです。KPIの式とデータ来歴も同じ意味層に置きます。

Pythonで確認する

triples = pd.DataFrame([
    ("L3", "type", "ProductionLine"),
    ("L3", "produces", "P-C"),
    ("P-C", "type", "Product"),
    ("DT-L3", "type", "DowntimeEvent"),
    ("DT-L3", "occursOn", "L3"),
    ("DT-L3", "affects", "Availability"),
    ("Availability", "contributesTo", "OTD"),
    ("DefectRate", "contributesTo", "OTD"),
    ("OTD", "calculatedFrom", "DailyLineFact"),
    ("MaintenanceAction", "mitigates", "DT-L3"),
], columns=["subject", "predicate", "object"])

ontology_summary = triples.groupby("predicate").size().rename("関係数").to_frame()
display(triples)
display(ontology_summary)

結果の読み取り

L3の停止イベントから可用性、OTD、保全対策までが共通の関係として表現されました。これにより、画面名やテーブル名が変わっても「停止がどのKPIへ影響するか」を意味で検索できます。実務導入では、全社語彙を一度に作らず、納期回復など一つの意思決定に必要な概念から始め、現場の言葉を残しながら版管理します。

No.095:知識グラフ — KPIから原因候補と対策をたどる

実務での意味

KPI異常のたびに熟練者へ聞く運用は、属人化し、調査時間も長くなります。知識グラフは、設備、イベント、KPI、原因、対策の関係をたどれる形にし、診断の初動を支援します。ただし、経路は因果の証明ではなく、調査仮説です。

分析・モデル化の考え方

オントロジーが意味の設計図なら、知識グラフは具体的な対象と関係を格納したものです。異常KPIから逆向きに影響源を探索し、さらに対策候補へ接続します。グラフ距離が短い候補を優先できますが、時系列整合性や統計検証を別途行います。

Pythonで確認する

edges = [
    ("主軸摩耗", "不良率", "increases"),
    ("主軸摩耗", "突発停止", "causes"),
    ("突発停止", "可用性", "decreases"),
    ("可用性", "納期遵守率", "decreases"),
    ("不良率", "納期遵守率", "decreases"),
    ("予防保全", "主軸摩耗", "mitigates"),
    ("条件補正", "不良率", "mitigates"),
    ("残業", "納期遵守率", "improves"),
]
G = nx.DiGraph()
for source, target, relation in edges:
    G.add_edge(source, target, relation=relation)

paths = []
for source in ["主軸摩耗", "突発停止", "不良率", "可用性"]:
    if nx.has_path(G, source, "納期遵守率"):
        path = nx.shortest_path(G, source, "納期遵守率")
        paths.append({"原因候補": source, "距離": len(path) - 1, "説明経路": " → ".join(path)})
display(pd.DataFrame(paths).sort_values("距離"))

pos = nx.spring_layout(G, seed=SEED)
plt.figure(figsize=(9, 6))
nx.draw_networkx(G, pos, node_color="#d9edf7", edge_color="#777777", node_size=1900,
                 font_size=9, arrows=True, arrowsize=16)
edge_labels = nx.get_edge_attributes(G, "relation")
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=7)
plt.title("納期遵守率を中心とした原因・対策の知識グラフ")
plt.xlabel("概念間の関係(配置自体に数量的意味はない)")
plt.ylabel("概念間の関係(配置自体に数量的意味はない)")
plt.grid(alpha=0.15)
plt.tight_layout()
plt.show()

結果の読み取り

納期遵守率へ直接つながる不良率・可用性と、その上流の主軸摩耗・突発停止を候補として列挙できました。主軸摩耗には予防保全、不良率には条件補正という対策も接続されています。次の調査では、L3の保全記録、振動値、加工条件を確認します。グラフに経路があるだけで原因と断定せず、現物確認とデータ検証を承認条件にします。

No.096:シミュレーション基盤 — 平均値ではなく結果の分布で案を比較する

実務での意味

「保全すれば停止が減る」「残業すれば生産量が増える」と分かっても、需要・停止・歩留まりが毎回同じではないため、期待値だけでは計画の安全性を判断できません。シミュレーション基盤は、前提と乱数seed、モデル版、実行結果を記録し、案を再現可能に比較します。

分析・モデル化の考え方

L3の翌週について、現状維持、残業、予防保全の3案をモンテカルロ法で2,000回ずつ評価します。評価指標は需要充足率と限界利益です。

P(service95%)1Ni=1NI(servicei0.95)P(\mathrm{service}\geq 95\%)\approx\frac{1}{N}\sum_{i=1}^{N} I(\mathrm{service}_i\geq0.95)

Pythonで確認する

sim_rng = np.random.default_rng(SEED)
scenarios = {
    "現状維持": {"capacity": 104, "stop_mean": 42, "defect": 0.055, "fixed_cost": 0},
    "残業2時間": {"capacity": 114, "stop_mean": 42, "defect": 0.058, "fixed_cost": 44_800},
    "予防保全": {"capacity": 104, "stop_mean": 18, "defect": 0.030, "fixed_cost": 180_000},
}
sim_rows = []
for scenario, p in scenarios.items():
    for run in range(2_000):
        demand = sim_rng.normal(99, 11, size=7).clip(60)
        stop = sim_rng.normal(p["stop_mean"], 9, size=7).clip(0, 120)
        gross = p["capacity"] * (1 - stop / 480)
        good = sim_rng.binomial(np.floor(gross).astype(int), 1 - p["defect"])
        shipped = np.minimum(good, demand)
        service = shipped.sum() / demand.sum()
        contribution = shipped.sum() * (14_800 - 7_600) - p["fixed_cost"]
        sim_rows.append([scenario, run, service, contribution])
sim = pd.DataFrame(sim_rows, columns=["scenario", "run", "service_rate", "contribution_yen"])
sim_summary = sim.groupby("scenario").agg(
    平均充足率=("service_rate", "mean"),
    充足率5パーセンタイル=("service_rate", lambda x: x.quantile(0.05)),
    95パーセント達成確率=("service_rate", lambda x: (x >= 0.95).mean()),
    平均限界利益円=("contribution_yen", "mean"),
)
display(sim_summary)

fig, ax = plt.subplots(figsize=(8, 4))
for name, group in sim.groupby("scenario"):
    ax.hist(group["service_rate"], bins=28, alpha=0.45, label=name)
ax.axvline(0.95, color="black", linestyle="--", label="目標95%")
ax.set_title("翌週の需要充足率シミュレーション")
ax.set_xlabel("需要充足率")
ax.set_ylabel("試行回数")
ax.grid(alpha=0.3)
ax.legend()
plt.tight_layout()
plt.show()

結果の読み取り

予防保全案は充足率の下方リスクを改善し、95%目標の達成確率も現状維持より高くなります。残業案は能力を増やしますが、不良率悪化と変動が残ります。意思決定では平均利益だけでなく、5パーセンタイルや目標達成確率を並べ、どの程度のリスクを許容するかを責任者が選べるようにします。

No.097:最適化基盤 — 制約を守りながら利益と納期を両立する

実務での意味

シミュレーションが候補案の結果を評価するのに対し、最適化は多数の組合せから良い案を探索します。現場では「数学上の最適解」より、能力、需要、最低供給、契約、段取り、要員などの実制約を守り、説明・修正できる計画が重要です。

分析・モデル化の考え方

翌日の3製品について、10個単位の生産数量 xpx_p を全探索します。目的は限界利益最大化、制約は合計加工時間720分以内、製品別需要以下、重要製品P-Cを60個以上とします。

maxxpmpxps.t.ptpxp720,0xpdp\max_x \sum_p m_p x_p\quad \mathrm{s.t.}\quad \sum_p t_p x_p\leq720,\quad 0\leq x_p\leq d_p

Pythonで確認する

planning = pd.DataFrame({
    "product_id": ["P-A", "P-B", "P-C"],
    "demand": [100, 90, 80],
    "minutes_per_unit": [2.1, 2.8, 3.6],
    "margin_per_unit": [4300, 5400, 7200],
})

candidates = []
for a in range(0, 101, 10):
    for b in range(0, 91, 10):
        for c in range(60, 81, 10):
            qty = np.array([a, b, c])
            used = float(qty @ planning["minutes_per_unit"].to_numpy())
            if used <= 720:
                margin = int(qty @ planning["margin_per_unit"].to_numpy())
                candidates.append([a, b, c, used, 720 - used, margin])
plans = pd.DataFrame(candidates, columns=["P-A", "P-B", "P-C", "使用分", "余力分", "限界利益円"])
best_plans = plans.sort_values(["限界利益円", "余力分"], ascending=[False, True]).head(8)
display(best_plans)

fig, ax = plt.subplots(figsize=(8, 4))
ax.scatter(plans["使用分"], plans["限界利益円"] / 10_000, alpha=0.25, label="実行可能案")
best = best_plans.iloc[0]
ax.scatter(best["使用分"], best["限界利益円"] / 10_000, color="red", s=90, label="最適案")
ax.set_title("実行可能な生産計画と限界利益")
ax.set_xlabel("使用可能時間の消費(分)")
ax.set_ylabel("限界利益(万円)")
ax.grid(alpha=0.3)
ax.legend()
plt.tight_layout()
plt.show()

結果の読み取り

最適案だけでなく上位案を残すことで、段取りや担当者スキルなどモデル外の事情を現場が反映できます。実務基盤では、入力スナップショット、制約、目的関数、ソルバー版、採用案、手修正理由を保存します。最適化が実行不能を返した場合は、制約を黙って外さず、どの制約緩和が必要かを提示します。

No.098:AIエージェント活用 — 分析結果を安全な提案と承認へつなぐ

実務での意味

AIエージェントは、KPI監視、原因候補の収集、シミュレーション実行、推奨案の要約を横断できます。しかし、設備停止や発注を自動実行すると影響が大きいため、権限境界、根拠、承認、監査ログが不可欠です。

分析・モデル化の考え方

ここでは外部LLMを使わず、判断ルールを明示した最小エージェントを作ります。入力はKPIとシミュレーション結果、出力は提案、根拠、確信度、必要承認者です。読み取り、提案、実行を権限として分離し、今回は提案までに限定します。

Pythonで確認する

l3 = kpi.loc["L3"]
recommended_scenario = sim_summary["95パーセント達成確率"].idxmax()
confidence = float(sim_summary.loc[recommended_scenario, "95パーセント達成確率"])

agent_log = pd.DataFrame([{
    "agent_run_id": "AGENT-20250331-001",
    "検知": "L3 KPI健康度が90点未満",
    "根拠": f"OTD={l3['納期遵守率']:.1%}, 不良率={l3['不良率']:.1%}, 停止率={l3['停止率']:.1%}",
    "提案": f"{recommended_scenario}を候補として保全計画を作成",
    "確信度": confidence,
    "自動実行": False,
    "必要承認": "製造課長・保全課長",
    "参照モデル": "weekly-simulation:v1.0",
}])
display(agent_log.T.rename(columns={0: "値"}))

guardrails = pd.DataFrame({
    "操作": ["KPI読取", "原因候補検索", "シミュレーション実行", "保全指図の発行", "設備の停止"],
    "エージェント権限": ["許可", "許可", "許可", "承認後", "禁止"],
})
display(guardrails)

結果の読み取り

エージェントはL3の異常、数値根拠、参照モデル、推奨案を一つの監査可能なレコードにまとめました。一方、保全指図は承認後、設備停止は権限外です。実務では、プロンプト対策だけでなく、アクセス制御、許可されたツール、金額・停止時間の上限、二者承認、ロールバック手順をシステム側で強制します。

No.099:製造業版Palantirの設計 — データ・モデル・業務オブジェクトを統合する

実務での意味

分析基盤、シミュレーション、ワークフローが別々では、提案がメールや表計算へ転記され、実行結果がモデルへ戻りません。ここでいう「製造業版Palantir」は特定製品の模倣ではなく、データ、意味層、モデル、意思決定、業務アクションを一貫してつなぐ設計思想を指します。

分析・モデル化の考え方

中心に DecisionCase(意思決定案件)を置き、対象ライン、KPI異常、根拠データ、モデル実行、選択肢、承認、アクション、効果を関連付けます。ユースケースは、価値、実現性、再利用性、リスクで優先順位付けします。

Priority=0.40V+0.25F+0.20R0.15K\mathrm{Priority}=0.40V+0.25F+0.20R-0.15K

Pythonで確認する

use_cases = pd.DataFrame({
    "ユースケース": ["納期回復", "予知保全", "品質条件推薦", "在庫補充", "エネルギー最適化"],
    "価値": [5, 5, 4, 4, 3],
    "実現性": [5, 3, 4, 4, 3],
    "再利用性": [5, 4, 4, 3, 3],
    "リスク": [2, 3, 4, 2, 3],
})
use_cases["優先度"] = (
    0.40 * use_cases["価値"] + 0.25 * use_cases["実現性"]
    + 0.20 * use_cases["再利用性"] - 0.15 * use_cases["リスク"]
)
display(use_cases.sort_values("優先度", ascending=False))

fig, ax = plt.subplots(figsize=(8, 5))
scatter = ax.scatter(use_cases["実現性"], use_cases["価値"],
                     s=use_cases["再利用性"] * 180,
                     c=use_cases["リスク"], cmap="YlOrRd", alpha=0.75)
for row in use_cases.itertuples(index=False):
    ax.annotate(row.ユースケース, (row.実現性, row.価値), xytext=(5, 5), textcoords="offset points")
ax.set_title("DIユースケースの価値・実現性・再利用性")
ax.set_xlabel("実現性(5段階)")
ax.set_ylabel("価値(5段階)")
ax.set_xlim(2.5, 5.5)
ax.set_ylim(2.5, 5.5)
ax.grid(alpha=0.3)
plt.colorbar(scatter, ax=ax, label="リスク(5段階)")
plt.tight_layout()
plt.show()

結果の読み取り

納期回復は価値・実現性・再利用性が高く、最初のDecisionCaseとして適しています。ここで整備したライン、製品、KPI、シミュレーション、承認という部品は予知保全や在庫補充にも再利用できます。全社データを先に集め切るのではなく、一つの高頻度な意思決定を閉ループ化し、共通部品を増やす順序が現実的です。

No.100:数理工房の目指す世界 — 意思決定が継続的に学習する工場へ

実務での意味

最終目標は、AIが現場を置き換えることではありません。現場知とデータ、統計、シミュレーション、最適化を組み合わせ、担当者が速く、説明可能で、変動に強い判断を行える状態です。実行結果が次の判断へ戻ることで、モデルと業務の両方が学習します。

分析・モデル化の考え方

成熟度を、可視化、共通KPI、診断、シナリオ比較、最適化、半自動実行の段階で考えます。価値は売上増だけでなく、判断時間短縮、欠品・不良・停止削減、属人性低減で測ります。ここではDI成熟度が上がるにつれ、納期未達損失と判断工数が減る架空のロードマップを示します。

Pythonで確認する

roadmap = pd.DataFrame({
    "段階": ["現状", "共通KPI", "原因探索", "シナリオ比較", "最適化連携", "承認付き実行"],
    "DI成熟度": [0, 1, 2, 3, 4, 5],
    "月間判断時間_h": [180, 145, 110, 78, 58, 42],
    "月間機会損失_万円": [920, 780, 610, 460, 350, 280],
    "累積投資_万円": [0, 180, 420, 760, 1180, 1650],
})
roadmap["月間改善額_万円"] = roadmap.loc[0, "月間機会損失_万円"] - roadmap["月間機会損失_万円"]
roadmap["単純回収月数"] = np.where(
    roadmap["月間改善額_万円"] > 0,
    roadmap["累積投資_万円"] / roadmap["月間改善額_万円"],
    np.nan,
)
display(roadmap)

fig, ax1 = plt.subplots(figsize=(9, 4.5))
ax1.plot(roadmap["段階"], roadmap["月間判断時間_h"], marker="o", color="#2a6fbb", label="判断時間")
ax1.set_title("DI成熟度ロードマップと期待効果(架空試算)")
ax1.set_xlabel("導入段階")
ax1.set_ylabel("月間判断時間(時間)", color="#2a6fbb")
ax1.tick_params(axis="x", rotation=20)
ax1.grid(alpha=0.3)
ax2 = ax1.twinx()
ax2.plot(roadmap["段階"], roadmap["月間機会損失_万円"], marker="s", color="#d95f02", label="機会損失")
ax2.set_ylabel("月間機会損失(万円)", color="#d95f02")
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc="best")
plt.tight_layout()
plt.show()

結果の読み取り

架空試算では、共通KPIだけでも判断時間と機会損失が改善し、その後の診断・シミュレーション・最適化で効果が積み上がります。重要なのは成熟度5を最初から狙わず、各段階で採用率、判断時間、KPI改善、モデル逸脱を測って次の投資を判断することです。数理工房が目指すのは、現場の経験がデータで増幅され、判断と結果が組織知として循環する世界です。

対象ノックを通して見える実務上の示唆

  1. 起点はデータではなく意思決定である:誰が、いつ、何を選び、その結果をどのKPIで評価するかを先に決めます。
  2. KPIの定義と粒度がモデル精度より先である:二重計上や部門間の定義差が残れば、高度なAIほど誤りを速く拡散します。
  3. 意味層が再利用性を生む:オントロジーと知識グラフにより、設備・品質・納期の関係を部門横断で再利用できます。
  4. シミュレーションと最適化は役割が異なる:最適化で候補を作り、シミュレーションで変動耐性を確かめる組合せが有効です。
  5. AIは権限設計まで含めて導入する:提案根拠、承認、実行上限、監査ログ、停止手段が揃って初めて業務に載せられます。
  6. 閉ループで効果を測る:推奨の採否と実行結果を戻し、モデル、制約、業務ルールを更新します。

実務導入する場合に必要なこと

1. 意思決定案件を一つ選ぶ

「工場DX」のような広いテーマではなく、「毎朝の納期回復案を30分以内に決める」のように、責任者、頻度、選択肢、期限が明確な案件を選びます。

2. データ契約とKPI台帳を整備する

主キー、粒度、単位、欠損、更新時刻、責任者、計算式、目標値を管理します。入力品質が基準未満なら、モデル実行を止められる仕組みも必要です。

3. モデル運用と業務運用を同時に設計する

モデルの精度・版・再現性だけでなく、誰が提案を確認し、例外時にどう手修正し、誰が承認するかを定めます。採用率、上書き理由、実現効果を記録します。

4. セキュリティと安全性を組み込む

最小権限、職務分離、操作ログ、承認上限、個人情報・営業秘密の保護、障害時の手動運用、ロールバックを準備します。OTへの直接操作は、検証環境と段階的な権限付与が不可欠です。

5. 効果を比較可能に測る

導入前の判断時間、OTD、不良率、停止時間、利益をベースラインとして保存し、導入後と比較します。季節性や需要構成の違いも考慮し、単なる前後比較で過大評価しないようにします。

まとめ

No.091〜No.100では、製造業DIを、単なる可視化やAI導入ではなく、意思決定を中心にした業務基盤として捉えました。架空データを用いて、KPI設計、データモデル、オントロジー、知識グラフ、シミュレーション、最適化、AIエージェント、統合アーキテクチャ、成熟度ロードマップを一つの流れで確認しました。

小さく始めるなら、頻度と価値が高い意思決定を一つ選び、共通KPIとデータ契約を整え、提案から承認、実行結果までを記録するところからです。その閉ループが、次のユースケースへ再利用できる製造業の知能基盤になります。

法人向けのご相談

数理工房では、製造業におけるKPI・データモデル設計、需要・品質・設備の分析、シミュレーション/数理最適化、知識グラフ、AIエージェントを含む意思決定基盤の構想策定からPoC、業務実装、内製化支援までご相談いただけます。

「データはあるが判断につながらない」「個別PoCを業務へ定着させたい」「シミュレーションや最適化を現場で継続運用したい」といった段階でも、対象となる意思決定の整理から伴走します。

📩 お問い合わせ: surikobo.co.jp/contact
まずはお気軽にご相談ください。