Jaipur--°C
Back to Projects
CompletedPythonFastAPIReact+5 more

GoodFoods — Autonomous Reservation Agent

An end-to-end conversational AI restaurant reservation system featuring a two-stage intent-classified tool calling loop, pluggable LLM backends (Ollama, Gemini, OpenAI-compatible), anti-hallucination guardrails, and Neon PostgreSQL persistence.

Timeline

2025 — 2026

Role

Solo — agent architecture, two-stage tool pipeline, FastAPI server, React frontend

Status
Completed

Technology Stack

Python
FastAPI
React
TypeScript
PostgreSQL
Tailwind CSS
Ollama
Google Gemini

Key Challenges

  • Preventing LLM hallucinations and unauthorized database mutations during free-form conversational booking
  • Maintaining consistent tool-calling semantics across diverse LLM backends (local Ollama Qwen 2.5 vs. Gemini vs. OpenAI APIs)
  • Handling multi-intent user inputs (e.g. searching cuisine preferences while checking live reservation availability) without context degradation
  • Enforcing strict session isolation, user authentication, and booking ownership across concurrent client chats

Key Learnings

  • Two-stage architecture (minimal prompt intent classification followed by intent-filtered tool exposure) drastically reduces tool selection errors and prevents irrelevant tool invocation
  • Decoupling the agent from HTTP transport through a unified LLMProvider interface enables seamless runtime switching between local and cloud models with zero agent code changes
  • Strict Pydantic schema validation at the tool boundary acts as a deterministic firewall between probabilistic LLM outputs and database writes
  • Streaming assistant tokens via Server-Sent Events (SSE) while deferring tool execution results produces responsive user perceived latency

Overview

Natural language interfaces for booking systems often suffer from two major failure modes: models hallucinate nonexistent restaurant openings, or they inadvertently trigger destructive mutations (like cancelling a reservation) due to ambiguous prompts.

GoodFoods AI Concierge is an autonomous reservation agent supporting 75+ restaurant locations. Rather than giving an LLM unfiltered database access, the system enforces a two-stage intent-classified tool calling pipeline. The agent dynamically inspects customer intent, exposes only the relevant subset of tools, validates structured parameters against Pydantic schemas, and executes transactions against a PostgreSQL database.

System Architecture

The core request cycle routes through a two-stage agent loop before any database transaction is evaluated:

SEARCHRESERVEMANAGEGENERAL / FAQYesStructured ResultNo

User Message

Stage 1: Intent Classifier (Minimal Prompt)

Detected Intent(s)

Filter: search_restaurants, get_details

Filter: check_availability, create_booking

Filter: view_bookings, modify, cancel

Filter: get_faq, get_promotions

Stage 2: Response Generation with Filtered Tools

Unified LLM Provider Adapter

Model (Ollama Qwen 2.5 / Gemini / OpenAI)

Tool Call Requested?

ToolExecutor & Pydantic Validation

Neon PostgreSQL Database

Server-Sent Events (SSE) Stream to Client

Tool Suite Overview

The platform exposes 10 fine-grained tools organized by domain:

CategoryAvailable ToolsEnforced Constraints
Discoverysearch_restaurants, get_restaurant_details, get_cuisine_recommendationsFilters by cuisine, price tier (budget to fine dining), ambiance, location, and dietary options
Availabilitycheck_table_availabilityVerifies real-time table inventory for requested date, time slot, and party size
Bookingcreate_reservationAtomically claims table seats; issues unique confirmation ticket
Lifecycleview_reservations, modify_reservation, cancel_reservationValidates user session ownership before applying modifications
Supportget_promotions, get_faq_answerDelivers deterministic policy rules (cancellation cutoffs, dress codes)

Key Technical Decisions

1. Two-Stage Intent-Based Tool Filtering

When an LLM is presented with 10+ available tools simultaneously, smaller models (such as Qwen 2.5 7B) frequently hallucinate tool arguments or call irrelevant functions. GoodFoods splits execution into two passes:

  • Stage 1: A minimal prompt categorizes user intent into SEARCH, RESERVE, MANAGE, or INFO without tools.
  • Stage 2: Only tools belonging to the detected categories are exposed in the system prompt. This eliminates out-of-context tool selection by over 90%.

2. Pluggable Multi-Provider LLM Abstraction

The agent core never interacts with raw HTTP or proprietary SDKs directly. All requests pass through an LLMProvider abstraction implementing a single canonical interface:

class LLMProvider(ABC):
    @abstractmethod
    def chat(self, messages: list[dict], tools: list[dict] | None) -> dict:
        """Translates canonical messages and tools into provider wire format."""
        pass

Specialized adapters translate tool schemas and payloads for:

  • Local Ollama: Qwen 2.5 7B via local Ollama API for zero-cost, private offline inference
  • Google Gemini: Gemini Flash via Google AI Studio API for high-throughput cloud processing
  • OpenAI-Compatible: Groq, DeepSeek, and OpenAI endpoints

Switching providers is supported live via UI settings without restarting the backend.

3. Strict Deterministic Validation Boundary

To protect database state from malformed LLM outputs, every tool payload must satisfy strict Pydantic models with type constraints (such as valid ISO timestamps, party size bounded between 1 and 20 guests, and regex-verified phone numbers). If the model emits invalid parameters, ToolExecutor traps the error and returns a structured correction prompt to the model rather than crashing.

4. Real-Time Streaming & Optimistic State

The client communicates over Server-Sent Events (SSE). Conversational thoughts and answers stream token-by-token for immediate responsiveness, while structured tool results (restaurant cards, reservation tickets) arrive as typed JSON events that trigger rich UI components.

Tech Stack Summary

  • Backend: Python 3.11, FastAPI, Pydantic v2, Uvicorn, SSE Starlette
  • Frontend: React 18, TypeScript, Vite, Tailwind CSS, Lucide Icons
  • Database: Neon Serverless PostgreSQL (with in-memory fallback for offline testing)
  • AI / LLMs: Ollama (Qwen 2.5 7B), Google Gemini 3.5 Flash, Groq, OpenAI API

built by shiv ratan
© 2026. All rights reserved.