AGENTTEMPLATE

Inventory and Reorder Agent

Written for a restaurant, but the structure fits any business where inventory depletes against sales. The agent takes your sales, walks them through recipes to ingredient usage, checks the math against stock on hand, and drafts the reorder; the sales-to-recipe-to-inventory math is already in the file.

It drafts, you send. Keep it that way until the numbers have been right for a month straight.

python
"""
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