8.9TEMPLATE
Inventory And Reorder Agent (restaurant)
For a restaurant, but the structure works for any business that has inventory depleting against sales.
"""
Inventory and Reorder Agent (Restaurant)
- Reads yesterday's sales by item.
- Multiplies by recipe ingredients to compute consumption.
- Subtracts from current inventory.
- Flags items below reorder threshold.
- Drafts a reorder for manager approval.
Prerequisites:
- Sales data accessible (POS API, exported CSV, database).
- A recipes table mapping menu items to ingredient quantities.
- An inventory table with current stock and reorder thresholds.
- A vendor table mapping ingredients to suppliers.
"""
import json
from crewai import Agent, Task, Crew, Process
from crewai_tools import tool
# ---------- Stub tools (replace with real DB / API calls) ----------
@tool("get_yesterday_sales")
def get_yesterday_sales() -> str:
"""Return yesterday's sales by menu item.
[{menu_item_id, item_name, quantity_sold}, ...]"""
# Real version: SELECT FROM your POS database or call its API.
return json.dumps([
{"menu_item_id": 1, "item_name": "Cheeseburger",
"quantity_sold": 47},
{"menu_item_id": 2, "item_name": "Caesar Salad",
"quantity_sold": 22},
{"menu_item_id": 3, "item_name": "Fries (large)",
"quantity_sold": 89},
])
@tool("get_recipes")
def get_recipes() -> str:
"""Return the recipe (ingredient breakdown) for each menu item.
[{menu_item_id, ingredients: [{ingredient_id, name, qty,
unit}]}, ...]"""
return json.dumps([
{"menu_item_id": 1, "ingredients": [
{"ingredient_id": 101, "name": "Beef patty", "qty": 1,
"unit": "patty"},
{"ingredient_id": 102, "name": "Bun", "qty": 1,
"unit": "bun"},
{"ingredient_id": 103, "name": "Cheese slice", "qty": 1,
"unit": "slice"},
]},
{"menu_item_id": 2, "ingredients": [
{"ingredient_id": 201, "name": "Romaine", "qty": 4,
"unit": "oz"},
{"ingredient_id": 202, "name": "Caesar dressing",
"qty": 2, "unit": "oz"},
]},
{"menu_item_id": 3, "ingredients": [
{"ingredient_id": 301, "name": "Potatoes", "qty": 8,
"unit": "oz"},
]},
])
@tool("get_current_inventory")
def get_current_inventory() -> str:
"""Return current inventory and reorder thresholds.
[{ingredient_id, name, on_hand, unit, reorder_threshold,
reorder_quantity, vendor_id}, ...]"""
return json.dumps([
{"ingredient_id": 101, "name": "Beef patty", "on_hand": 60,
"unit": "patty", "reorder_threshold": 100,
"reorder_quantity": 200, "vendor_id": 1},
{"ingredient_id": 102, "name": "Bun", "on_hand": 75,
"unit": "bun", "reorder_threshold": 100,
"reorder_quantity": 144, "vendor_id": 2},
{"ingredient_id": 103, "name": "Cheese slice", "on_hand": 200,
"unit": "slice", "reorder_threshold": 150,
"reorder_quantity": 480, "vendor_id": 1},
{"ingredient_id": 301, "name": "Potatoes", "on_hand": 50,
"unit": "oz", "reorder_threshold": 500,
"reorder_quantity": 2000, "vendor_id": 3},
])
@tool("draft_purchase_order")
def draft_purchase_order(items_json: str) -> str:
"""Draft a purchase order. items_json is a JSON list of
{ingredient_id, quantity, vendor_id}."""
items = json.loads(items_json)
print("\n=== DRAFTED PURCHASE ORDER (for manager review) ===")
for it in items:
print(f" Ingredient {it['ingredient_id']}: "
f"{it['quantity']} units, vendor {it['vendor_id']}")
print("=== END ===\n")
return "drafted"
# ---------- Agents ----------
analyst = Agent(
role="Inventory Analyst",
goal=(
"Compute yesterday's ingredient consumption and update the "
"running inventory."
),
backstory=(
"You are precise with numbers. You always reconcile units "
"and flag any anomalies."
),
tools=[get_yesterday_sales, get_recipes, get_current_inventory],
verbose=True,
)
reorder_clerk = Agent(
role="Reorder Clerk",
goal=(
"Identify ingredients below reorder threshold and draft a "
"purchase order grouped by vendor."
),
backstory=(
"You group orders by vendor to minimize delivery overhead. "
"You round up to typical case sizes. You flag anything "
"unusual to the manager."
),
tools=[draft_purchase_order],
verbose=True,
)
# ---------- Tasks ----------
analysis_task = Task(
description=(
"1. Read yesterday's sales.\n"
"2. Read the recipes.\n"
"3. For each ingredient, compute consumption (sum across "
"all menu items: quantity_sold * recipe_qty).\n"
"4. Read current inventory.\n"
"5. Compute new on-hand for each ingredient (current "
"minus consumption).\n"
"Output: JSON list of {ingredient_id, name, "
"consumption, projected_on_hand, reorder_threshold, "
"needs_reorder (bool)}."
),
agent=analyst,
expected_output="JSON inventory analysis.",
)
reorder_task = Task(
description=(
"From the analysis, identify ingredients where "
"needs_reorder is true. For each, draft a purchase order "
"line with the reorder_quantity from inventory. Group lines "
"by vendor_id. Call draft_purchase_order with the result. "
"Output: confirmation message for the manager."
),
agent=reorder_clerk,
expected_output="Manager-ready summary of drafted orders.",
)
crew = Crew(
agents=[analyst, reorder_clerk],
tasks=[analysis_task, reorder_task],
process=Process.sequential,
verbose=True,
)
if __name__ == "__main__":
result = crew.kickoff()
print("\n=== DAILY REORDER REVIEW ===")
print(result)
Notes for adapting:
- Replace the four stubbed tools with real database queries against your POS, recipe management, and inventory systems.
- Add a "send_to_manager_for_approval" step so the manager confirms before any PO actually goes out to a vendor.
- Schedule daily at 6 AM via launchd/systemd.
- Hook the approval step to a Slack message, an email, or a simple web page where the manager clicks "Approve all" or modifies quantities.
================================================================================ PART 9 — KEEP GOING
Something wrong on this page? →
Curriculum last updated 2026-04-30