An AI operations agent for an LED manufacturing company. It reads the production line, counts good vs. scrap units, posts finished-goods stock and component consumption into Zoho Inventory, auto-raises a supplier PO the moment a part drops below its reorder point, and WhatsApps the shift summary — all without a person re-typing a single number.
An LED factory has machines that place tiny components on circuit boards (the "SMT line"), bake them, inspect them, and pack finished bulbs and tubes. Every shift, someone counts how many good units came out, how many were scrap, what raw materials were used, and types it all into the inventory and accounting system. This agent is the worker who does that counting and typing — instantly, accurately, every shift, and it shouts the moment a part is about to run out.
/post Line-A 2026-06-07 Shift-1It is the exact same safe pattern as the Excel + WhatsApp → ERP Smart Executive — just wired to the production line instead of a spreadsheet. Nothing is posted to inventory or sent to a supplier without a human-approved dry-run. Every post is logged. Errors are surfaced loudly, never hidden.
Design targets for this build — your real numbers depend on line instrumentation and BOM accuracy. The demo runs the same logic in your browser so you can judge it yourself.
Read good/scrap counters and downtime straight from the PLC or MES — via OPC-UA, Modbus/TCP, an MQTT topic, or the MES REST API. No clipboard counting.
OPC-UAModbusMQTTRoll the shift into a production batch: SKU, good qty, scrap qty, yield %, and the component consumption exploded from the Bill of Materials.
BOM explosionyield / OEEFlag anomalies before they hit the books: yield below the floor, AOI reject spikes, counts that don't reconcile, negative stock. Bad batches are held, not posted.
QC gatesMatch every finished SKU and component in Zoho Inventory, then plan: stock-in finished goods, consume components, and reorder anything below its point.
idempotentShow the planned stock adjustments and draft POs, wait for "yes", then post one Zoho call per line and capture each record ID. Errors are loud; the batch aborts above a 20% failure rate.
human in the loopWrite the audit log and WhatsApp the production WhatsApp group the shift summary: units, yield, stock posted, POs raised.
audit logWhatsAppPick a line, press Start line and watch good/scrap units tick up with live yield. Then Post the shift to run the pipeline: build batch → QC → look up SKUs → dry-run the stock adjustments and auto-POs → approve → post to Zoho Inventory → WhatsApp the shift report. Everything runs in your browser; Live API mode calls your own Zoho + WhatsApp endpoints (your keys, never stored).
The simulator injects a little random scrap each run, so occasionally the yield dips below the 95% floor and QC holds the batch — exactly what should happen on a real line.
The full spec for the factory variant: the agent's system prompt, the connector configuration for line telemetry and Zoho, the WhatsApp Cloud API number, and the stand-up checklist.
The same skeleton as the ERP Smart Executive, retargeted at the production line.
# LED Factory Ops Agent You are the LED Factory Ops Agent for [CLIENT_COMPANY_NAME]. Your job: read the production line, build the shift's production batch, and post finished- goods stock + component consumption to Zoho Inventory — raising supplier POs when components fall below their reorder point. ## Tools 1. line — MES / PLC telemetry (OPC-UA, Modbus, MQTT, or MES REST). Read-only. 2. zoho — Zoho Inventory + Books MCP. ALL stock writes + PO creation. 3. whatsapp — WhatsApp Cloud API. Read /post commands, send shift summaries. 4. analysis tool — compute yield, OEE, BOM explosion. Never eyeball counts. ## Hard rules 1. Never invent a Zoho item/PO ID. Look up by SKU/vendor first. 2. Never post stock or raise a PO without a dry-run + explicit "yes" (unless auto_approve:true for the run). 3. One adjustment / PO = one tool call. 4. Counts are integers; yield = good/(good+scrap). Empty telemetry = null, not 0. 5. Idempotency: one batch_id per (line, date, shift). Re-running updates, never doubles. 6. QC gate: if yield < floor or counts don't reconcile, HOLD the batch and report. 7. Errors are loud: surface Zoho 4xx/5xx verbatim + WhatsApp. Retry once max. 8. Audit log: batch_log_{line}_{date}_{shift}.md (item, action, qty, Zoho ID, notes). ## Workflow Sense -> Build batch -> QC/validate -> Lookup -> Dry-run -> Execute -> Report. ## WhatsApp inbound Commands: /post [line] [date] [shift], /status [line], /hold [line], /help. Free text EN/UR/AR/HI -> reply same language. Never post from chat alone — the batch must come from line telemetry or an attached production sheet. ## Refuse - Posting a held/failed-QC batch. - Writing to items/vendors not in item_schema.md / vendor_allowlist.md. - Sending WhatsApp to numbers not in whatsapp_allowlist.md. - Bulk stock deletions (done manually in Zoho). ## Tone Operational, terse, factual. Tables for >3 rows. No emojis, no preamble.
Pick whatever your line already speaks — most modern SMT lines speak at least one:
Wrap the chosen reader behind a tiny MCP server (or a FastAPI service) so the agent calls one tool: line.read_shift(line, date, shift).
Same setup as the ERP build — native Zoho MCP at zoho.com/mcp (or Composio's Zoho Inventory toolkit). Enable: inventory.items.list/get, inventory.inventoryadjustments.create, inventory.purchaseorders.create, inventory.vendors.search. Add it as a custom Web connector named zoho; connect via OAuth.
Identical to the ERP build — get a number on Meta (or use a Twilio sender). The full provisioning steps and the send call are documented on the ERP page → section C. The agent posts shift summaries to the production group.
vendor_allowlist.mdline reader (OPC-UA/Modbus/MQTT/MES) behind one toolitem_schema.md (SKU → BOM, reorder point)Because a factory has more moving parts than a spreadsheet, here is the full kit: read the line over four different industrial protocols, explode the BOM, compute yield, post to Zoho Inventory, auto-raise reorder POs, and receive the line's events over a webhook. Pick the tab that matches your shop floor.
# line_read.py — read good/scrap counters from a PLC over OPC-UA from asyncua import Client # pip install asyncua import asyncio OPC_URL = "opc.tcp://10.20.0.5:4840" TAGS = { "good": "ns=2;s=Line_A.Packing.GoodCount", "scrap": "ns=2;s=Line_A.AOI.RejectCount", "downtime": "ns=2;s=Line_A.Status.DowntimeMin", } async def read_shift(line="A"): async with Client(url=OPC_URL) as client: out = {} for name, node_id in TAGS.items(): node = client.get_node(node_id) out[name] = await node.read_value() out["good"], out["scrap"] = int(out["good"]), int(out["scrap"]) out["yield"] = round(out["good"] / max(1, out["good"] + out["scrap"]), 4) return out if __name__ == "__main__": print(asyncio.run(read_shift()))
# modbus_read.py — older PLC over Modbus/TCP holding registers from pymodbus.client import ModbusTcpClient # pip install pymodbus def read_modbus(host="10.20.0.7", unit=1): c = ModbusTcpClient(host); c.connect() # 40001-40002 = good (32-bit), 40003 = scrap rr = c.read_holding_registers(0, 3, slave=unit) good = (rr.registers[0] << 16) + rr.registers[1] scrap = rr.registers[2]; c.close() return {"good": good, "scrap": scrap, "yield": round(good / max(1, good + scrap), 4)} # mqtt_read.py — Industry-4.0 gateway publishing to a broker import json, paho.mqtt.client as mqtt # pip install paho-mqtt latest = {} def on_message(client, _u, msg): latest.update(json.loads(msg.payload)) # {"good":..,"scrap":..} def subscribe(host="10.20.0.9", topic="factory/lineA/shift"): cli = mqtt.Client(); cli.on_message = on_message cli.connect(host, 1883); cli.subscribe(topic); cli.loop_start() return latest # read latest["good"] / latest["scrap"] any time
# batch.py — turn raw counts into a production batch + BOM consumption import datetime as dt YIELD_FLOOR = 0.95 # Bill of Materials: finished SKU -> {component: qty per unit} BOM = { "LED-A19-9W": {"LED-CHIP-2835": 12, "PCB-A19": 1, "DRIVER-IC": 1, "HOUSING-A19": 1}, "LED-T8-18W": {"LED-CHIP-2835": 96, "PCB-T8": 1, "DRIVER-IC": 1, "TUBE-T8": 1}, "LED-PNL-36W": {"LED-CHIP-2835": 144, "PCB-PNL": 1, "DRIVER-36W": 1, "FRAME-PNL": 1}, } def build_batch(sku, line, shift, telem): good, scrap = telem["good"], telem["scrap"] y = good / max(1, good + scrap) batch = { "batch_id": f"{line}-{dt.date.today()}-{shift}", "sku": sku, "good": good, "scrap": scrap, "yield": round(y, 4), "consume": {c: q * good for c, q in BOM[sku].items()}, "hold": y < YIELD_FLOOR, # QC gate } return batch def validate(batch): errs = [] if batch["hold"]: errs.append(f"yield {batch['yield']:.1%} below floor") if batch["good"] < 0 or batch["scrap"] < 0: errs.append("negative count") return errs
# zoho_post.py — stock-in finished goods, consume components, auto-PO import os, requests BASE = "https://www.zohoapis.com/inventory/v1" ORG = os.environ["ZOHO_ORG_ID"] H = {"Authorization": f"Zoho-oauthtoken {os.environ['ZOHO_OAUTH_TOKEN']}"} def item(sku): r = requests.get(f"{BASE}/items", headers=H, params={"organization_id": ORG, "sku": sku}) hits = r.json().get("items", []); return hits[0] if hits else None def adjust(sku, qty, reason): """One stock adjustment = one call. +qty stock-in, -qty consume.""" it = item(sku) body = {"reason": reason, "adjustment_type": "quantity", "date": __import__("datetime").date.today().isoformat(), "line_items": [{"item_id": it["item_id"], "quantity_adjusted": qty}]} r = requests.post(f"{BASE}/inventoryadjustments?organization_id={ORG}", headers=H, json=body) r.raise_for_status(); return r.json()["inventory_adjustment"]["inventory_adjustment_id"] def reorder_check(sku): """Below reorder point? Draft a PO to the preferred vendor.""" it = item(sku) if float(it["stock_on_hand"]) >= float(it.get("reorder_level", 0)): return None qty = int(it.get("reorder_level", 0)) * 2 # simple reorder qty po = {"vendor_id": it["vendor_id"], "line_items": [ {"item_id": it["item_id"], "quantity": qty, "rate": float(it["purchase_rate"])}]} r = requests.post(f"{BASE}/purchaseorders?organization_id={ORG}", headers=H, json=po) r.raise_for_status(); return r.json()["purchaseorder"]["purchaseorder_number"] def post_batch(batch): log = [("stockin", batch["sku"], batch["good"], adjust(batch["sku"], batch["good"], "Production"))] for comp, qty in batch["consume"].items(): log.append(("consume", comp, -qty, adjust(comp, -qty, f"BOM {batch['batch_id']}"))) po = reorder_check(comp) if po: log.append(("reorder", comp, qty, po)) return log
# webhook.py — receive line events + WhatsApp /post commands (FastAPI) from fastapi import FastAPI, Request import os, requests, line_read, batch, zoho_post app = FastAPI() def whatsapp(to, body): requests.post(f"https://graph.facebook.com/v21.0/{os.environ['WA_PHONE_ID']}/messages", headers={"Authorization": f"Bearer {os.environ['WA_TOKEN']}"}, json={"messaging_product": "whatsapp", "to": to, "type": "text", "text": {"body": body}}) @app.get("/webhook") # Meta verification handshake def verify(req: Request): p = req.query_params return int(p["hub.challenge"]) if p.get("hub.verify_token") == os.environ["VERIFY"] else "no" @app.post("/webhook") # inbound WhatsApp messages async def inbound(req: Request): data = await req.json() msg = data["entry"][0]["changes"][0]["value"]["messages"][0] text, sender = msg["text"]["body"], msg["from"] if text.startswith("/post"): _, line, date, shift = text.split() telem = await line_read.read_shift(line) b = batch.build_batch(SKU_FOR[line], line, shift, telem) errs = batch.validate(b) if errs: whatsapp(sender, f"HELD {b['batch_id']}: {', '.join(errs)}"); return {"ok": True} # dry-run summary first — wait for "yes" before zoho_post.post_batch(b) whatsapp(sender, f"{b['good']} good, {b['scrap']} scrap, " f"{b['yield']:.1%} yield. Reply YES to post.") return {"ok": True}
line reader pointed at your real PLC/MES tagsThe line demo runs simulated so anyone can try it. Flip to Live API, paste your Zoho + WhatsApp endpoints, and it posts for real from your browser — nothing stored. For the full plant build (real PLC tags, scheduled posting, supplier PO automation) it's the cutover in the checklist — message me and we'll wire it to your floor.
Line in, ledger out — finished goods stocked, components consumed, POs raised, every post logged and approved. Same safe pattern as the ERP Smart Executive, wired to your shop floor.