DEV Community

shashank ms
shashank ms

Posted on

Engineering LLM Solutions for Manufacturing and Supply Chain

We are building a supply chain risk agent that ingests raw inventory and demand data, flags shortages, and outputs structured purchase order recommendations. It drops into existing ERP pipelines and runs on Oxlo.ai's flat per-request pricing, so analyzing a dense Bill of Materials does not inflate your inference bill. I shipped a version of this last quarter for a hardware team and this is the exact scaffold I started with.

What you'll need

Step 1: Bootstrap the Oxlo.ai client

I keep the client setup in a single module so I can swap models later without touching business logic. Oxlo.ai is fully OpenAI SDK compatible, so the only change is the base_url.

import os
import json
from datetime import datetime, timedelta
from openai import OpenAI

client = OpenAI(
    base_url="https://api.oxlo.ai/v1",
    api_key=os.environ.get("OXLO_API_KEY")
)

Step 2: Lock down the system prompt

The system prompt is the contract with the model. I force a strict JSON schema so downstream ERP scripts can parse results without regex, and I embed the inventory math rules so the model reasons consistently.

SYSTEM_PROMPT = """You are a manufacturing supply chain analyst. Analyze the provided SKU record and respond with exactly one valid JSON object. Do not wrap the JSON in markdown.

Schema:
{
  "sku": "string",
  "stock_status": "critical|low|adequate",
  "projected_stockout_date": "YYYY-MM-DD or null",
  "recommended_order_quantity": integer,
  "recommended_supplier": "string",
  "risk_factors": ["string"],
  "confidence": "high|medium|low"
}

Rules:
- Compute projected_stockout_date as reference_date plus (current_inventory / average_daily_demand) days. If average_daily_demand is zero, return null.
- If projected_stockout_date is on or before (reference_date + lead_time_days + 7 days), set stock_status to "critical".
- If projected_stockout_date is on or before (reference_date + lead_time_days + 14 days) but after the critical threshold, set stock_status to "low".
- recommended_order_quantity must cover 30 days of demand beyond lead_time, minus current_inventory. If negative, return 0.
- Populate risk_factors with concrete strings such as "single_source", "lead_time_exceeds_14_days", or "demand_volatility".
- Base confidence on data completeness. If any required field is missing, downgrade to "medium" or "low".
"""

Step 3: Model the supply chain payload

Manufacturing data is messy. I normalize each SKU into a flat dictionary before sending it to the model. This keeps the prompt structure predictable and avoids nested parsing errors on the return trip.

def build_user_message(sku_record: dict, reference_date: str) -> str:
    payload = {
        "task": "analyze_inventory",
        "reference_date": reference_date,
        "data": sku_record
    }
    return json.dumps(payload, indent=2)

Step 4: Call the model with JSON mode

I use Oxlo.ai's JSON mode to guarantee valid output. Temperature stays low because inventory math is not creative writing. I also mirror the input SKU into the result for traceability.

def analyze_sku(sku_record: dict) -> dict:
    reference = datetime.now().strftime("%Y-%m-%d")
    user_msg = build_user_message(sku_record, reference)

    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_msg},
        ],
        response_format={"type": "json_object"},
        temperature=0.2,
    )

    parsed = json.loads(response.choices[0].message.content)
    parsed["input_sku"] = sku_record["sku"]
    return parsed

Step 5: Batch process a full Bill of Materials

A real BOM has dozens of line items. I loop over the list and collect results. Oxlo.ai serves popular models with no cold starts, so the loop moves at network speed.

BOM = [
    {
        "sku": "PCB-CTRL-001",
        "current_inventory": 80,
        "average_daily_demand": 20,
        "lead_time_days": 14,
        "supplier": "Shenzen PCB Ltd",
        "unit_cost_usd": 12.50
    },
    {
        "sku": "CAP-10UF-0603",
        "current_inventory": 2000,
        "average_daily_demand": 50,
        "lead_time_days": 30,
        "supplier": "Yageo",
        "unit_cost_usd": 0.02
    },
    {
        "sku": "CONN-USBC-01",
        "current_inventory": 500,
        "average_daily_demand": 5,
        "lead_time_days": 21,
        "supplier": "Amphenol",
        "unit_cost_usd": 0.80
    }
]

def analyze_bom(bom: list[dict]) -> list[dict]:
    results = []
    for item in bom:
        results.append(analyze_sku(item))
    return results

Step 6: Generate a planner summary

Raw JSON feeds the ERP, but humans need a paragraph. I ship the batch results back to the model for a short executive summary. Keeping this as a separate call lets me tune the voice without touching the structured schema.

def planner_summary(bom: list[dict], analyses: list[dict]) -> str:
    enriched = []
    for item, analysis in zip(bom, analyses):
        line = {
            "sku": item["sku"],
            "unit_cost": item["unit_cost_usd"],
            **analysis
        }
        enriched.append(line)

    response = client.chat.completions.create(
        model="llama-3.3-70b",
        messages=[
            {
                "role": "system",
                "content": (
                    "You are a supply chain planner. Given JSON inventory analyses, "
                    "write a brief summary for the operations team. Include total estimated spend "
                    "for critical items and the earliest stockout date. Under 120 words. No markdown."
                )
            },
            {
                "role": "user",
                "content": json.dumps(enriched, indent=2)
            },
        ],
        temperature=0.3,
    )
    return response.choices[0].message.content

Run it

I run this every morning before standup. It prints both the structured records and the human summary.

if __name__ == "__main__":
    analyses = analyze_bom(BOM)

    print("=== STRUCTURED RESULTS ===")
    for a in analyses:
        print(json.dumps(a, indent=2))

    print("\n=== PLANNER SUMMARY ===")
    print(planner_summary(BOM, analyses))

Example output:

=== STRUCTURED RESULTS ===
{
  "sku": "PCB-CTRL-001",
  "stock_status": "critical",
  "projected_stockout_date": "2025-01-25",
  "recommended_order_quantity": 800,
  "recommended_supplier": "Shenzen PCB Ltd",
  "risk_factors": ["single_source", "lead_time_exceeds_14_days"],
  "confidence": "high",
  "input_sku": "PCB-CTRL-001"
}
{
  "sku": "CAP-10UF-0603",
  "stock_status": "low",
  "projected_stockout_date": "2025-03-02",
  "recommended_order_quantity": 1000,
  "recommended_supplier": "Yageo",
  "risk_factors": ["lead_time_exceeds_14_days"],
  "confidence": "high",
  "input_sku": "CAP-10UF-0603"
}
{
  "sku": "CONN-USBC-01",
  "stock_status": "adequate",
  "projected_stockout_date": "2025-05-01",
  "recommended_order_quantity": 0,
  "recommended_supplier": "Amphenol",
  "risk_factors": [],
  "confidence": "high",
  "input_sku": "CONN-USBC-01"
}

=== PLANNER SUMMARY ===
Two items need attention. PCB-CTRL-001 is critical and will stock out by January 25. Order 800 units immediately at an estimated spend of $10,000. CAP-10UF-0603 is low risk with stockout by March 2; order 1,000 units at roughly $20. CONN-USBC-01 is adequate through Q2. Total critical exposure is $10,000.

Next steps

Wire this into a FastAPI endpoint so your ERP can POST BOM snapshots and receive the JSON array in real time. If you need to reason across multi-tier supplier networks with correlated failure modes, swap llama-3.3-70b for kimi-k2.6 or deepseek-v3.2 on Oxlo.ai for deeper chain-of-thought analysis.

Top comments (0)