A fully local, runnable MVP of a real-time travel-support voice AI agent. VoxRoute demonstrates the core engineering challenges of a production voice support system โ all running on your laptop without paid APIs or cloud inference.
โ ๏ธ DEMO SYSTEM โ All travel information is sample data for demonstration purposes.
Not official government or agency information.
flowchart TB
subgraph Frontend["๐ฅ๏ธ Streamlit Frontend"]
UI_Mic["๐ค Audio Input / Mic Recorder"]
UI_Transcript["๐ฌ Live Transcript View"]
UI_State["๐ง State & Metrics Dashboard"]
UI_Player["๐ Audio Playback"]
end
subgraph AgentOrchestrator["โก Agent Orchestrator (Async)"]
direction TB
VAD["๐ก๏ธ Silero VAD<br/>(Voice Activity Detection)"]
STT["๐๏ธ faster-whisper<br/>(Local STT Engine)"]
Intent["๐ฏ Intent Detector<br/>(Ollama / Regex Fallback)"]
Memory["๐ง Conversation Memory<br/>(Entity Resolution & History)"]
StateMachine["๐ Agent State Machine<br/>(Transitions & Error Recovery)"]
Retriever["๐ RAG Retriever<br/>(Query Optimizer)"]
ToolRouter["โ๏ธ Tool Execution Router<br/>(Mock Visa DB & API)"]
LLM["๐ค Ollama Engine<br/>(qwen2.5:3b)"]
Guard["๐ก๏ธ Response Guard<br/>(Evidence & Grounding Check)"]
TTS["๐ฃ๏ธ Piper / pyttsx3<br/>(Local TTS Synthesis)"]
Interrupt["โน๏ธ Interruption Handler<br/>(Half-Duplex Playback Control)"]
end
subgraph Knowledge["๐ Local Knowledge & Data"]
Chroma[("๐๏ธ ChromaDB Vector Store<br/>(all-MiniLM-L6-v2)")]
DocFiles["๐ Demo Travel FAQs<br/>& Visa Policy Docs"]
end
subgraph Persistence["๐พ Persistence Layer"]
SQLite[("๐๏ธ SQLite Database<br/>(Conversations, Metrics, Failures)")]
end
UI_Mic -->|WAV Audio Bytes| VAD
VAD --> STT
STT -->|Transcript| Intent
Intent <--> Memory
Intent --> StateMachine
Intent --> Retriever
Retriever <--> Chroma
DocFiles -->|Ingestion| Chroma
Intent --> ToolRouter
Retriever -->|Grounded Context| LLM
ToolRouter -->|Tool Output JSON| LLM
Memory -->|Context Window| LLM
LLM -->|Draft Response| Guard
Guard -->|Validated Text| TTS
TTS -->|WAV Buffer| UI_Player
UI_Player -.->|Stop / Interrupt| Interrupt
Interrupt -.->|Cancel Playback| TTS
AgentOrchestrator -->|Log Turns & Performance| SQLite
SQLite -->|Aggregate Stats & Metrics| UI_State
AgentOrchestrator -->|Live Updates| UI_Transcript
flowchart LR
Mic(["๐๏ธ Microphone"]) --> VAD["Silero VAD\nFilter Silence"]
VAD --> STT["faster-whisper\nSpeech to Text"]
STT --> Intent{"Intent & Entities\n(Ollama LLM)"}
Intent -->|Query Context| RAG["ChromaDB\nKnowledge Retrieval"]
Intent -->|Requires Tool| Tools["Local Travel Tools\n(Status, Docs, Time)"]
RAG --> Gen["Ollama LLM\nResponse Generation"]
Tools --> Gen
Gen --> Guard{"Response Guard\nGrounded & Safe?"}
Guard -->|Pass / Adjusted| TTS["Piper TTS\nSpeech Synthesis"]
Guard -->|Low Confidence / Risk| Escalation["Escalation Router\n(Human Agent Handoff)"]
TTS --> Speaker(["๐ Audio Speaker"])
Escalation --> TTS
stateDiagram-v2
[*] --> IDLE
IDLE --> LISTENING : Start Conversation
LISTENING --> TRANSCRIBING : User Speaks (Audio Input)
TRANSCRIBING --> UNDERSTANDING : Transcript Available
TRANSCRIBING --> ERROR_RECOVERY : STT Failed
UNDERSTANDING --> RETRIEVING : Need Knowledge Base
UNDERSTANDING --> TOOL_CALL : Tool Triggered
UNDERSTANDING --> GENERATING : Direct Query
UNDERSTANDING --> ESCALATION : Explicit Human Request
RETRIEVING --> TOOL_CALL : Tool Also Needed
RETRIEVING --> GENERATING : Context Ready
TOOL_CALL --> GENERATING : Tool Output Ready
TOOL_CALL --> ESCALATION : Tool Failed
GENERATING --> VALIDATING : Draft Answer Ready
VALIDATING --> SPEAKING : Validation Passed
VALIDATING --> GENERATING : Unsafe Certainty Adjusted
VALIDATING --> ESCALATION : Low Evidence / Hallucination
SPEAKING --> LISTENING : Playback Completed / Interrupted
SPEAKING --> IDLE : End Conversation
ESCALATION --> SPEAKING : Synthesize Handoff Notice
ERROR_RECOVERY --> LISTENING : Fallback Recovery
sequenceDiagram
autonumber
actor Customer as ๐ง Customer
participant UI as ๐ฅ๏ธ Streamlit UI
participant Handler as โน๏ธ Interruption Handler
participant TTS as ๐ฃ๏ธ TTS / Audio Engine
participant Agent as โก Orchestrator
Agent->>TTS: Synthesize and begin playback
TTS-->>UI: Streaming audio / playback active
Note over Customer,TTS: Agent is speaking...
Customer->>UI: Clicks "Stop" or Records new utterance
UI->>Handler: interrupt(reason="user_button")
Handler->>TTS: stop_playback() (Set Stop Event)
TTS-->>UI: Playback terminated immediately
Handler->>Agent: Transition state: SPEAKING โ LISTENING
UI->>Agent: Submit new customer utterance
Agent->>Agent: Process new turn
| Component | Technology | Purpose |
|---|---|---|
| Frontend | Streamlit | Web UI with audio I/O |
| STT | faster-whisper | Local speech-to-text |
| LLM | Ollama (qwen2.5:3b) | Local language model |
| Embeddings | sentence-transformers | Document embeddings |
| Vector DB | ChromaDB | RAG retrieval |
| TTS | Piper TTS / pyttsx3 | Local text-to-speech |
| VAD | Silero VAD | Voice activity detection |
| Database | SQLite | Conversation & metrics logging |
- Python 3.10+
- Ollama โ https://ollama.ai
- Microphone (for voice mode)
git clone <repository>
cd voxroute
# Create virtual environment
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt- Download and install Ollama from https://ollama.ai
- Start the Ollama service:
ollama serve- Pull the default model:
ollama pull qwen2.5:3b# Copy the example config
copy .env.example .env # Windows
# cp .env.example .env # Linux/macOS
# Edit .env to customize models, thresholds, etc.Piper provides better quality local TTS. If it fails to install via pip, the app will fall back to system TTS (pyttsx3).
pip install piper-ttsIf pip installation fails on Windows, you can:
- Download the Piper binary from Piper releases
- Download a voice model from Piper voices
- Place the
.onnxand.onnx.jsonfiles inmodels/piper/
The app will automatically fall back to pyttsx3 (system TTS) if Piper is not available.
python run.pyThis single command:
- Checks Python version
- Verifies Ollama is running
- Creates
.envif missing - Launches the Streamlit app
The app opens at http://localhost:8501.
- Click Start Conversation
- Click the microphone widget to record your message
- The system processes through the full pipeline:
- STT โ Intent โ RAG โ Tool โ LLM โ Guard โ TTS
- You'll hear the response and see real-time metrics
- Switch to the Text Mode tab
- Use the quick demo buttons or type your own query
- The system processes through the pipeline (skipping STT)
- Switch to the Evaluation tab
- See aggregate metrics, latency breakdowns, and failure patterns
You: "How long does a France visa normally take?"
Expected: Intent โ VISA_PROCESSING_TIME, RAG retrieval, grounded response about 7-15 working days
You: "Can you check application AT12345?"
Expected: Intent โ VISA_STATUS, Tool โ check_application_status, status from mock database
You: "Will my visa definitely arrive tomorrow?"
Expected: Response Guard catches absolute certainty, modifies response to use hedged language
You: "I want to speak to a human."
Expected: Intent โ HUMAN_REQUEST, handoff_to_human() tool called, escalation message
During agent playback, click End Conversation or record a new message
Expected: TTS stops, new input is processed
voxroute/
โโโ app.py # Streamlit main application
โโโ run.py # Single entry point
โโโ config.py # Central configuration
โโโ requirements.txt
โโโ .env.example
โโโ README.md
โ
โโโ agent/
โ โโโ orchestrator.py # Main pipeline coordinator
โ โโโ intent.py # LLM-based intent detection
โ โโโ state.py # Agent state machine
โ โโโ memory.py # Conversation memory
โ โโโ prompts.py # All LLM prompts
โ
โโโ voice/
โ โโโ stt.py # faster-whisper STT
โ โโโ tts.py # Piper/pyttsx3 TTS
โ โโโ vad.py # Silero VAD
โ โโโ interruption.py # Interruption handler
โ
โโโ rag/
โ โโโ vectorstore.py # ChromaDB management
โ โโโ ingest.py # Document chunking & indexing
โ โโโ retriever.py # RAG retrieval
โ
โโโ tools/
โ โโโ visa.py # Processing times & status
โ โโโ documents.py # Required documents
โ โโโ eligibility.py # Visa eligibility
โ โโโ support.py # Tickets & human handoff
โ
โโโ safety/
โ โโโ response_guard.py # Response validation
โ โโโ confidence.py # Escalation logic
โ
โโโ evaluation/
โ โโโ metrics.py # Quality metrics
โ โโโ failure_analyzer.py # Failure classification
โ
โโโ database/
โ โโโ db.py # SQLite manager
โ
โโโ dashboard/
โ โโโ components.py # Streamlit UI components
โ
โโโ backend/
โ โโโ server.py # Startup & health checks
โ โโโ websocket.py # WebSocket (future)
โ
โโโ data/
โโโ travel_faq.txt # Demo FAQ data
โโโ visa_processing.txt # Demo processing times
โโโ required_documents.txt # Demo document requirements
โโโ travel_support.txt # Demo support info
All settings are configurable via .env:
| Variable | Default | Description |
|---|---|---|
WHISPER_MODEL |
small |
STT model (tiny/base/small/medium/large-v3) |
OLLAMA_MODEL |
qwen2.5:3b |
Ollama LLM model |
OLLAMA_BASE_URL |
http://localhost:11434 |
Ollama API URL |
EMBEDDING_MODEL |
all-MiniLM-L6-v2 |
Embedding model |
PIPER_VOICE |
en_US-lessac-medium |
Piper voice model |
TTS_FALLBACK |
true |
Fall back to pyttsx3 if Piper unavailable |
RAG_TOP_K |
3 |
Number of RAG chunks to retrieve |
MAX_CONVERSATION_TURNS |
10 |
Memory window size |
CONFIDENCE_HIGH |
0.85 |
Threshold for direct answer |
CONFIDENCE_MEDIUM |
0.60 |
Threshold for clarification |
FORCE_CPU |
false |
Force CPU even with CUDA available |
The app automatically detects CUDA availability:
- CUDA GPU: Uses
float16compute for STT, GPU embeddings - CPU only: Uses
int8compute for STT, CPU embeddings
# Start Ollama
ollama serve
# Verify it's running
curl http://localhost:11434/api/tags# Pull the configured model
ollama pull qwen2.5:3bpip install faster-whisper- Install
sounddevice:pip install sounddevice - Check system audio output settings
- In text mode, audio plays via Streamlit's
st.audiowidget
- The
piper-ttspip package may not have Windows wheels - The app automatically falls back to
pyttsx3(system TTS) - For better quality, download the Piper binary from GitHub releases
- Use a smaller Whisper model:
WHISPER_MODEL=tiny - Use a smaller LLM:
OLLAMA_MODEL=qwen2.5:1.5b - If CUDA is available, it will be used automatically
# Delete the vector store and re-ingest
rm -rf data/chroma_db/
python run.py- Intent Accuracy: Based on LLM confidence score
- Response Groundedness: Keyword overlap between response and evidence
- Tool Success: Whether tool calls return successful results
- Latency: Pipeline stage timing (STT + Intent + RAG + Tool + LLM + Guard + TTS)
- Total conversations, average latencies
- Tool success rate, escalation rate
- Grounded response rate, resolution rate
Failures are automatically categorized:
STT_FAILURE: Low confidence or empty transcriptionINTENT_FAILURE: Unknown intent or low confidenceRAG_FAILURE: No relevant chunks foundTOOL_FAILURE: Tool returned errorHALLUCINATION: Response failed grounding checkHIGH_LATENCY: > 5 second response timeHUMAN_ESCALATION: Conversation escalated
- Half-duplex interruption: User must click stop/re-record rather than auto-detecting voice during playback (true full-duplex requires WebRTC)
- Piper TTS Windows: May need manual binary installation;
pyttsx3fallback is available - LLM tool calling: Uses deterministic intentโtool mapping rather than LLM native function calling (more reliable with small models)
- CPU latency: On CPU-only machines, expect 3-10 second total response time
- Single-turn audio: Each recording is processed as a complete utterance (no streaming)
- Demo data only: All travel information is fictional sample data
VoxRoute runs entirely locally:
- No data sent to cloud services
- No API keys required
- All models run on your machine
- Conversations stored only in local SQLite
- Internet only needed for initial model downloads
MIT License โ see LICENSE file for details.