The Restaurant Voice Ordering Agent is a phone-capable, MCP-enabled, RAG-grounded restaurant voice ordering agent.
The official grading and reproduction path is Docker Compose. Docker is required for the supported run flow, and graders should not create a local Python virtual environment manually. The project image uses Python 3.11 through the Dockerfile.
It shall:
- greet restaurant callers
- answer menu questions using retrieved menu evidence
- handle dietary/allergen questions cautiously
- add, remove, and update order items
- support modifications and special instructions
- track running totals
- collect customer name
- read back the order
- confirm or cancel the order
The primary production interface is Twilio Programmable Voice and the local browser voice interface is the reproducible walkthrough and fallback interface.
- Python 3.11
- FastAPI
- Uvicorn
- Pydantic
- Anthropic Claude Haiku, default model
claude-haiku-4-5 - MCP tool layer
- sentence-transformers
sentence-transformers/all-MiniLM-L6-v2- rapidfuzz
- NumPy
- Twilio Programmable Voice
- Browser Web Speech API
- pytest
- pytest-cov
- ruff
- black
- mypy
- pip-audit
- Locust
- Docker and Docker Compose
Docker Compose is the only official grading/reproduction run path. Do not use a local Python virtual environment or direct Uvicorn run for grading. .env must never be committed, and .env.example contains placeholders only.
git clone https://github.com/FernCanCode/Restaurant-Voice-Agent
cd Restaurant-Voice-Agent
cp .env.example .envUse this mode if you only want to verify that the container starts and the local app is reachable.
Required .env edits:
- None. Leave placeholder values as-is.
Run:
docker compose up --buildVerify:
curl -s http://localhost:8000/health
curl -s http://localhost:8000/ready
curl -s http://localhost:8000/voice/config-checkExpected result:
/healthreturns status ok./readyreturns ready components or degraded status as documented./voice/config-checkis expected to show Twilio disabled or unconfigured.- Missing Twilio fields are expected in Mode A and are not a failure for this basic verification mode.
- Browser fallback opens at
http://localhost:8000.
Browser fallback UI:
http://localhost:8000
The browser UI is a fallback/walkthrough aid. The Twilio phone path is the primary voice path.
Use this mode if you want to reproduce the real phone smoke test.
Before running Docker:
- Start ngrok or cloudflared for local port
8000and copy the public HTTPS base URL. - Edit
.envand set these required fields:
ANTHROPIC_API_KEY=<your Anthropic API key>ANTHROPIC_MODEL=claude-haiku-4-5ENABLE_TWILIO=trueTWILIO_ACCOUNT_SID=<your Twilio Account SID>TWILIO_AUTH_TOKEN=<your Twilio Auth Token>TWILIO_PHONE_NUMBER=<your Twilio phone number>TWILIO_WEBHOOK_BASE_URL=<your public HTTPS tunnel URL>
Then run:
docker compose up --buildThen configure Twilio using that same public base URL:
- Voice webhook:
POST <PUBLIC_BASE_URL>/voice/incoming - Status callback:
POST <PUBLIC_BASE_URL>/voice/status
If .env is changed after Docker is already running, restart the app:
docker compose down
docker compose up --buildThen verify:
curl -s http://localhost:8000/health
curl -s http://localhost:8000/ready
curl -s http://localhost:8000/voice/config-checkExpected result:
/voice/config-checkshows enabled/configured true with no missing fields.- A real Twilio call can follow the script documented in
reports/phone_smoke_test.md.
- Twilio phone calls and browser voice turns both route into the same FastAPI backend.
- Both paths use the same shared agent orchestrator.
- The agent uses Anthropic Claude Haiku for language understanding and tool-routing proposals.
- MCP tools perform deterministic menu lookup, dietary lookup, order mutation, total calculation, confirmation, and cancellation.
- RAG retrieves grounded menu evidence from canonical menu JSON and local index files.
- The LLM does not compute totals or mutate order state.
Architecture diagrams and specification:
Routes:
POST /voice/incomingPOST /voice/turnPOST /voice/statusGET /voice/config-check
Requires Twilio credentials and a public webhook URL.
Routes:
GET /GET /uiPOST /api/browser/start-callPOST /api/browser/voice-turn
Used for local reproducible walkthroughs and does not require Twilio credentials.
Routes:
GET /healthGET /readyGET /api/statusPOST /api/sessionsPOST /api/turnGET /api/sessions/{session_id}GET /api/sessions/{session_id}/orderPOST /api/sessions/{session_id}/readbackPOST /api/sessions/{session_id}/confirmPOST /api/sessions/{session_id}/cancelPOST /api/menu/ingest-textPOST /api/menu/ingest-urlPOST /api/menu/ingest-filePOST /api/menu/rebuild-indexGET /api/menu/itemsGET /api/menu/items/{item_id}POST /api/menu/searchGET /api/logging/exampleGET /api/debug/sessions/recentGET /api/debug/session/{session_id}
For a complete list of dependencies, see:
api_dependencies.yaml
Dependencies:
-
Anthropic Claude API
-
Twilio Programmable Voice
-
Browser Web Speech API
-
Hugging Face / sentence-transformers model download
-
No personal API keys are committed.
-
All keys are read from
.env. -
The TA/professor supplies their own keys when needed.
Configuration template:
.env.example
Important variables:
-
ANTHROPIC_API_KEY -
ANTHROPIC_MODEL -
ENABLE_TWILIO -
TWILIO_ACCOUNT_SID -
TWILIO_AUTH_TOKEN -
TWILIO_PHONE_NUMBER -
TWILIO_WEBHOOK_BASE_URL -
ENABLE_BROWSER_VOICE -
MENU_RAW_FIXTURE_PATH -
MENU_DATA_PATH -
MENU_INDEX_PATH -
HF_HOME -
TRANSFORMERS_CACHE -
ENABLE_DEBUG_ROUTES -
Twilio credentials are required when
ENABLE_TWILIO=true. -
ANTHROPIC_API_KEYis required for full Anthropic LLM behavior. -
Browser walkthrough does not require Twilio.
-
Missing Anthropic should trigger degraded LLM mode for simple supported flows.
-
.env.examplecontains placeholders only. -
Placeholder values are acceptable for no-secret local health checks.
-
.envmust not be committed. -
/voice/config-checkreports missing Twilio fields without exposing secrets.
Official grading/reproduction path:
docker compose up --buildExpected:
- app listens on
http://localhost:8000 /healthworks/readyworks/voice/config-checkshows Twilio enabled/configured status without secrets- browser voice UI loads
- Twilio routes are registered
- the container uses Python 3.11 from the Dockerfile
curl -s http://localhost:8000/health
curl -s http://localhost:8000/ready
curl -s http://localhost:8000/voice/config-check/health: Checks if the service is alive and responding./ready: Reports menu, RAG, MCP, Anthropic, Twilio, browser voice, and degraded-mode status./voice/config-check: Confirms whether Twilio is enabled/configured and lists missing fields without exposing secrets.
For a real phone test:
- Start ngrok or cloudflared for local port
8000and copy the public HTTPS base URL. - Edit
.envand set:ANTHROPIC_API_KEYANTHROPIC_MODEL=claude-haiku-4-5ENABLE_TWILIO=trueTWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKENTWILIO_PHONE_NUMBERTWILIO_WEBHOOK_BASE_URL=<PUBLIC_BASE_URL>
- Do not commit
.env. - Start the app with
docker compose up --build. - If
.envwas changed after Docker was already running, restart with:docker compose downdocker compose up --build
- Verify:
curl -s http://localhost:8000/healthcurl -s http://localhost:8000/readycurl -s http://localhost:8000/voice/config-checkcurl -s <PUBLIC_BASE_URL>/healthcurl -s <PUBLIC_BASE_URL>/voice/config-check
- In the Twilio Console, configure the phone number voice webhook as:
POST <PUBLIC_BASE_URL>/voice/incoming
- Optional status callback:
POST <PUBLIC_BASE_URL>/voice/status
- Call the Twilio number from a real phone.
- After the call, inspect:
GET /api/debug/sessions/recentGET /api/debug/session/{session_id}- logs correlated by
twilio_call_sid,session_id, orrequest_id
Open:
http://localhost:8000
Use the browser UI for walkthrough and fallback verification only. It is not the main phone interface.
- Final preflight status: passed
- Latest automated validation count:
264 passed - Twilio phone smoke test: passed
- Reference:
reports/phone_smoke_test.md
- Reference:
- Browser walkthrough screenshots: captured for
US-01throughUS-10- Reference:
reports/walkthrough.md
- Reference:
pip-auditnote: it could not complete in the local sandbox because DNS resolution topypi.orgwas unavailable- Reference:
reports/security.txt
- Reference:
- Start the app with Docker Compose:
docker compose up --build- Verify the app locally:
curl -s http://localhost:8000/health
curl -s http://localhost:8000/ready
curl -s http://localhost:8000/voice/config-check- From the repository root on the host, run the automated grading commands. These commands execute through Docker Compose and do not require host Python packages or a local virtual environment:
make reproduce
make test
make lint
make loadtest
scripts/regenerate.sh- Open
docs/STORIES.mdand manually walk US-01 through US-10. - Compare screenshots in
docs/assets/stories/. - Review
reports/phone_smoke_test.mdandreports/walkthrough.md.
- If Docker reports that port
8000is already in use, stop the existing process or change the Docker port mapping before rerunningdocker compose up --build. - Example diagnostic:
sudo lsof -i :8000 - Example fix: stop the process using that port, then rerun
docker compose up --build. - Docker is required for the official grading/reproduction path.
- Local Python 3.13 is not supported by the pinned dependency stack used by this project. Docker handles Python 3.11 automatically through the Dockerfile.
- Developer-only note: if someone is debugging locally outside the grading flow, any direct Uvicorn/local-Python run is unsupported for grading and reproduction and should not be used as the TA/professor path.
docs/SPEC.mddocs/STORIES.mddocs/REPRODUCE.mddocs/usage.mddocs/MODEL_CARD.mddocs/LOGGING.mdgrading/manifest.yamlgrading/traceability.yamlreports/phone_smoke_test.mdreports/walkthrough.mddocs/assets/stories/us_01_expected.pngthroughdocs/assets/stories/us_10_expected.png
Official one-command replay:
Run from the repository root on the host. This repository automation command executes through Docker Compose, does not replace docker compose up --build, and does not require host Python packages.
make reproduceHelper commands for debugging the reproduction pipeline:
Run from the repository root on the host:
make download-data
make download-modelsExpected generated files:
data/processed/menu.jsondata/index/menu_chunks.jsondata/index/menu_metadata.jsondata/index/embeddings.npy
Run from the repository root on the host. These commands execute through Docker Compose and are not an alternative app startup path.
make testExpected reports:
reports/unit.xmlreports/integration.xmlreports/user_stories.xmlreports/coverage.xmlreports/coverage_html/
Targets:
- business logic coverage target: at least 70 percent
- user-story pass target: at least 90 percent
Run from the repository root on the host. These commands execute through Docker Compose and are not an alternative app startup path.
make lintTools:
- ruff
- black --check
- mypy
- pip-audit
Expected report:
reports/security.txt
Run from the repository root on the host. These commands execute through Docker Compose and are not an alternative app startup path.
make loadtestExpected report:
reports/benchmarks.json
Targets:
- 10 requests per second when resources allow
- under 5 percent error rate
Run from the repository root on the host. These commands execute through Docker Compose and are not an alternative app startup path.
make demoor:
scripts/demo.shThe demo should exercise:
- greeting
- menu search
- dietary question
- add item
- modification
- total
- customer name
- readback
- confirmation
When Twilio credentials and public webhook are configured, the TA can verify phone behavior through:
- spoken Twilio phone call
- structured Docker logs
twilio_call_sidsession_idGET /api/debug/sessions/recentGET /api/debug/session/{session_id}
docker compose logs app | grep "<twilio_call_sid>"
curl http://localhost:8000/api/debug/sessions/recent
curl http://localhost:8000/api/debug/session/<session_id>The phone call itself is the user interface; logs and debug routes provide grading observability.
- docs/SPEC.md
- docs/STORIES.md
- docs/usage.md
- docs/DATA.md
- docs/MODELS.md
- docs/REPRODUCE.md
- docs/MODEL_CARD.md
- docs/LOGGING.md
- docs/benchmarks.md
- docs/diagrams/architecture.svg
- grading/traceability.yaml
- grading/manifest.yaml
| Report | Path | Generated By |
|---|---|---|
| Unit tests | reports/unit.xml |
make test |
| Integration tests | reports/integration.xml |
make test |
| User story tests | reports/user_stories.xml |
make test |
| Coverage XML | reports/coverage.xml |
make test |
| Coverage HTML | reports/coverage_html/ |
make test |
| Security audit | reports/security.txt |
make lint |
| Benchmarks | reports/benchmarks.json |
make loadtest |
| Walkthrough notes | reports/walkthrough.md |
Manual walkthrough |
| Git contributions | reports/git_contributions.txt |
contribution report command |
Run every make ... command in this table from the repository root on the host. These commands execute through Docker Compose and are not an alternative app startup path.
- no payment processing
- no payment card collection
- no real POS submission
- no guaranteed allergy safety
- no universal website scraping
- no real-time inventory unless represented in ingested menu
- Twilio live phone mode requires credentials and public webhook URL
- browser voice quality depends on browser and microphone
- menu answers are limited to ingested menu data
- LLM cannot compute totals
- LLM cannot mutate order state directly
- LLM cannot invent menu items or prices
- MCP tools are authoritative for actions
- dietary/allergen claims must be grounded
- customer name and readback are required before confirmation
- payment collection is refused
- logs must not expose secrets or payment data
Read the full model card:
src/restaurant_agent/tests/docs/data/grading/scripts/reports/
- CONTRIBUTIONS.md
reports/git_contributions.txt