Skip to content
Learn Agentic AI
Learn Agentic AI14 min read1 views

Build a Recipe Finder Agent: Ingredient Matching, Dietary Filters, and Cooking Instructions

Build an AI-powered recipe finder agent that matches recipes to available ingredients, respects dietary restrictions, provides step-by-step cooking instructions, and suggests ingredient substitutions.

The Problem With Finding Recipes

You open the fridge, see half a dozen ingredients, and then spend twenty minutes scrolling through recipe websites filled with ads trying to find something that uses what you already have. A recipe finder agent solves this by taking your available ingredients, applying dietary filters, and returning matching recipes with full cooking instructions — all through a single conversational prompt.

This tutorial builds a complete recipe finder agent with an in-memory recipe database, fuzzy ingredient matching, dietary filtering, substitution suggestions, and step-by-step guidance.

Project Structure

mkdir recipe-agent && cd recipe-agent
python -m venv venv && source venv/bin/activate
pip install openai-agents pydantic
mkdir -p src
touch src/__init__.py src/recipes_db.py src/matcher.py src/agent.py

Step 1: Build the Recipe Database

We store recipes as structured Pydantic models with ingredients, tags for dietary info, and ordered cooking steps.

flowchart TD
    START["Build a Recipe Finder Agent: Ingredient Matching,…"] --> A
    A["The Problem With Finding Recipes"]
    A --> B
    B["Project Structure"]
    B --> C
    C["Step 1: Build the Recipe Database"]
    C --> D
    D["Step 2: Build the Ingredient Matcher"]
    D --> E
    E["Step 3: Build the Agent"]
    E --> F
    F["Running the Agent"]
    F --> G
    G["Extending the Project"]
    G --> H
    H["FAQ"]
    H --> DONE["Key Takeaways"]
    style START fill:#4f46e5,stroke:#4338ca,color:#fff
    style DONE fill:#059669,stroke:#047857,color:#fff
# src/recipes_db.py
from pydantic import BaseModel

class Ingredient(BaseModel):
    name: str
    amount: str
    unit: str
    optional: bool = False

class Recipe(BaseModel):
    id: str
    title: str
    tags: list[str]  # e.g., ["vegetarian", "gluten-free"]
    prep_time: int    # minutes
    cook_time: int
    servings: int
    ingredients: list[Ingredient]
    steps: list[str]
    substitutions: dict[str, str]  # ingredient -> substitute

RECIPE_DB: list[Recipe] = [
    Recipe(
        id="r001",
        title="Garlic Butter Pasta",
        tags=["vegetarian"],
        prep_time=5, cook_time=15, servings=2,
        ingredients=[
            Ingredient(name="spaghetti", amount="200", unit="g"),
            Ingredient(name="garlic", amount="4", unit="cloves"),
            Ingredient(name="butter", amount="3", unit="tbsp"),
            Ingredient(name="parmesan", amount="50", unit="g"),
            Ingredient(
                name="red pepper flakes",
                amount="1", unit="tsp", optional=True,
            ),
        ],
        steps=[
            "Boil salted water and cook spaghetti until al dente.",
            "Mince garlic and saute in butter over medium heat.",
            "Toss drained pasta with garlic butter.",
            "Top with grated parmesan and pepper flakes.",
        ],
        substitutions={
            "butter": "olive oil for dairy-free",
            "parmesan": "nutritional yeast for vegan",
            "spaghetti": "gluten-free pasta",
        },
    ),
    Recipe(
        id="r002",
        title="Chicken Stir Fry",
        tags=["gluten-free", "high-protein"],
        prep_time=10, cook_time=12, servings=3,
        ingredients=[
            Ingredient(name="chicken breast", amount="400", unit="g"),
            Ingredient(name="broccoli", amount="2", unit="cups"),
            Ingredient(name="soy sauce", amount="3", unit="tbsp"),
            Ingredient(name="garlic", amount="3", unit="cloves"),
            Ingredient(name="ginger", amount="1", unit="tbsp"),
            Ingredient(name="sesame oil", amount="1", unit="tbsp"),
        ],
        steps=[
            "Slice chicken into thin strips and season with salt.",
            "Heat sesame oil in a wok over high heat.",
            "Stir-fry chicken until golden, about 5 minutes.",
            "Add broccoli, garlic, and ginger; cook 4 minutes.",
            "Pour soy sauce over everything and toss to coat.",
        ],
        substitutions={
            "chicken breast": "tofu for vegetarian",
            "soy sauce": "coconut aminos for soy-free",
        },
    ),
    Recipe(
        id="r003",
        title="Black Bean Tacos",
        tags=["vegan", "gluten-free"],
        prep_time=10, cook_time=10, servings=4,
        ingredients=[
            Ingredient(name="black beans", amount="400", unit="g"),
            Ingredient(name="corn tortillas", amount="8", unit="pieces"),
            Ingredient(name="avocado", amount="2", unit="whole"),
            Ingredient(name="lime", amount="2", unit="whole"),
            Ingredient(name="cumin", amount="1", unit="tsp"),
            Ingredient(name="salsa", amount="1", unit="cup"),
        ],
        steps=[
            "Drain and rinse black beans, heat in a pan with cumin.",
            "Warm corn tortillas in a dry skillet.",
            "Mash avocado with lime juice and salt.",
            "Assemble tacos with beans, guacamole, and salsa.",
        ],
        substitutions={
            "corn tortillas": "flour tortillas (not gluten-free)",
            "black beans": "pinto beans or lentils",
        },
    ),
]

Step 2: Build the Ingredient Matcher

The matcher scores recipes by how many of the user's available ingredients overlap with what each recipe needs. It supports partial matching and dietary filtering.

flowchart LR
    S0["Step 1: Build the Recipe Database"]
    S0 --> S1
    S1["Step 2: Build the Ingredient Matcher"]
    S1 --> S2
    S2["Step 3: Build the Agent"]
    style S0 fill:#4f46e5,stroke:#4338ca,color:#fff
    style S2 fill:#059669,stroke:#047857,color:#fff
# src/matcher.py
from src.recipes_db import Recipe, RECIPE_DB

def normalize(name: str) -> str:
    return name.lower().strip()

def match_recipes(
    available: list[str],
    dietary: list[str] | None = None,
    max_missing: int = 2,
) -> list[dict]:
    available_set = {normalize(i) for i in available}
    results = []

    for recipe in RECIPE_DB:
        # Dietary filter
        if dietary:
            if not all(
                d.lower() in [t.lower() for t in recipe.tags]
                for d in dietary
            ):
                continue

        required = [
            ing for ing in recipe.ingredients if not ing.optional
        ]
        required_names = {normalize(i.name) for i in required}
        matched = required_names & available_set
        missing = required_names - available_set

        if len(missing) <= max_missing:
            subs = {
                m: recipe.substitutions.get(m, "no substitute known")
                for m in missing
            }
            results.append({
                "recipe": recipe,
                "match_pct": round(
                    len(matched) / len(required_names) * 100, 1
                ),
                "missing": list(missing),
                "substitutions": subs,
            })

    results.sort(key=lambda r: r["match_pct"], reverse=True)
    return results

def format_recipe(recipe: Recipe) -> str:
    lines = [f"# {recipe.title}"]
    lines.append(
        f"Prep: {recipe.prep_time}min | Cook: {recipe.cook_time}min "
        f"| Servings: {recipe.servings}"
    )
    lines.append(f"Tags: {', '.join(recipe.tags)}")
    lines.append("\nIngredients:")
    for ing in recipe.ingredients:
        opt = " (optional)" if ing.optional else ""
        lines.append(f"  - {ing.amount} {ing.unit} {ing.name}{opt}")
    lines.append("\nSteps:")
    for i, step in enumerate(recipe.steps, 1):
        lines.append(f"  {i}. {step}")
    return "\n".join(lines)

Step 3: Build the Agent

# src/agent.py
import asyncio
import json
from agents import Agent, Runner, function_tool
from src.matcher import match_recipes, format_recipe

@function_tool
def find_recipes(
    ingredients: str,
    dietary_filters: str = "",
    max_missing: int = 2,
) -> str:
    """Find recipes matching available ingredients.
    ingredients: comma-separated list of what you have.
    dietary_filters: comma-separated dietary tags.
    """
    avail = [i.strip() for i in ingredients.split(",")]
    dietary = (
        [d.strip() for d in dietary_filters.split(",")]
        if dietary_filters else None
    )
    matches = match_recipes(avail, dietary, max_missing)
    if not matches:
        return "No matching recipes found."
    output = []
    for m in matches:
        output.append(format_recipe(m["recipe"]))
        output.append(f"Match: {m['match_pct']}%")
        if m["missing"]:
            output.append(f"Missing: {', '.join(m['missing'])}")
            sub_lines = [
                f"  {k} -> {v}"
                for k, v in m["substitutions"].items()
            ]
            output.append("Substitutions:\n" + "\n".join(sub_lines))
        output.append("---")
    return "\n".join(output)

recipe_agent = Agent(
    name="Recipe Finder",
    instructions="""You are a helpful cooking assistant.
Use the find_recipes tool to search for recipes based on
the user's available ingredients and dietary needs.
Present results clearly with cooking instructions.
Suggest substitutions for missing ingredients.
Ask clarifying questions about allergies or preferences
if the user hasn't specified them.""",
    tools=[find_recipes],
)

async def main():
    result = await Runner.run(
        recipe_agent,
        "I have spaghetti, garlic, butter, and parmesan. "
        "What can I make? I'm vegetarian.",
    )
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

Running the Agent

python -m src.agent

The agent identifies the garlic butter pasta as a perfect match, shows the full recipe with steps, and notes that no ingredients are missing.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

Extending the Project

Scaling the database. Replace the in-memory list with SQLite or PostgreSQL. Add a search_by_tag tool that queries recipes by cuisine type or cooking method.

Fuzzy matching. Use difflib.SequenceMatcher or the rapidfuzz library to handle misspellings — matching "parmesean" to "parmesan" automatically.

Nutritional info. Add a calories, protein, carbs, and fat field to each recipe and create a get_nutrition tool so the agent can factor macros into recommendations.

FAQ

How would I add hundreds of recipes without defining them all in code?

Store recipes in a JSON file or database and load them at startup. You can also build a scraper tool that pulls recipes from public APIs like Spoonacular or Edamam and converts them into your Recipe model format. The matcher works the same regardless of how many recipes are in the database.

Can the agent handle ingredient amounts and adjust servings?

Yes. Add a scale_recipe tool that takes a recipe ID and target servings, then multiplies each ingredient amount by the ratio of target to original servings. The agent can call this tool after finding a match to present adjusted quantities.

How do I make substitution suggestions smarter?

Replace the static substitutions dictionary with an LLM-based tool. When an ingredient is missing, the agent can call a suggest_substitution tool that sends the recipe context and missing ingredient to the model, getting back contextually appropriate alternatives based on flavor profiles and cooking chemistry.


#RecipeFinder #AIAgent #Python #IngredientMatching #OpenAIAgentsSDK #AgenticAI #LearnAI #AIEngineering

Share
C

Written by

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like

Technical Guides

Building Multi-Agent Voice Systems with the OpenAI Agents SDK

A developer guide to building multi-agent voice systems with the OpenAI Agents SDK — triage, handoffs, shared state, and tool calling.

AI Interview Prep

7 AI Coding Interview Questions From Anthropic, Meta & OpenAI (2026 Edition)

Real AI coding interview questions from Anthropic, Meta, and OpenAI in 2026. Includes implementing attention from scratch, Anthropic's progressive coding screens, Meta's AI-assisted round, and vector search — with solution approaches.

Learn Agentic AI

Creating an AI Email Assistant Agent: Triage, Draft, and Schedule with Gmail API

Build an AI email assistant that reads your inbox, classifies urgency, drafts context-aware responses, and schedules sends using OpenAI Agents SDK and Gmail API.

Learn Agentic AI

AI Agent Framework Comparison 2026: LangGraph vs CrewAI vs AutoGen vs OpenAI Agents SDK

Side-by-side comparison of the top 4 AI agent frameworks: LangGraph, CrewAI, AutoGen, and OpenAI Agents SDK — architecture, features, production readiness, and when to choose each.

Learn Agentic AI

OpenAI Agents SDK Deep Dive: Agents, Tools, Handoffs, and Guardrails Explained

Comprehensive guide to the OpenAI Agents SDK covering the Agent class, function tools, agent-as-tool pattern, handoff mechanism, input and output guardrails, and tracing.

Learn Agentic AI

Building a Multi-Agent Data Pipeline: Ingestion, Transformation, and Analysis Agents

Build a three-agent data pipeline with ingestion, transformation, and analysis agents that process data from APIs, CSVs, and databases using Python.