The product page covers what she does for a pilot. This one covers how — the graph, the server, the guardrails against acting on bad information, the stack, and what is not built yet.
A topic gate sends each utterance to respond ⇄ tools, a short social reply, or an in-character decline — see app/graph.py.
A thread (thread_id, MemorySaver) is the conversation a pilot is having with ALICE, held in memory for the session. A run is one graph.ainvoke() cycle inside it — one LangSmith trace when tracing is on. Trade-run state lives in the ledger, not in the thread.
Tool-selection accuracy is reported to degrade past 10–15 tools in rotation; RAG-MCP's retrieval approach took it from 13.6% to 43.1% in that paper. ALICE runs 18, chosen by the chat model. Behind a flag, a small decision model picks which tool groups to bind for each request. In a measurement over 79 phrasings it cut tool tokens by about 55% and missed a needed group once.
The desktop client holds no API keys and no database connection. A small FastAPI server is the only thing that talks to Postgres, the model box, the TTS engine, or the prompt store — every request authenticated by a JWT from a one-time Discord login. It also holds the prompts: pilots never receive the persona text, and a Hub tag is the release gate.
"ALICE surfaces computed facts. The pilot makes the judgment calls."
Design principle, applied twice — the same rule that keeps Finalize Leg / Finalize Run manual-only.What each safeguard does, as built in the current release.
Every utterance is classified as a request, social talk, or off-topic before the main model runs. Off-topic input gets an in-character decline; greetings get a short separate reply.
Only a tool call changes anything. The persona forbids claiming an action no tool performed, and a failing tool tells the model the action did not happen.
Every tool argument is a validated Pydantic field. Spoken prices, quantities and names are converted before they reach a tool.
Finalizing a leg or a run is never done by voice or by the model. The pilot confirms it in the overlay.
Ship, terminal and commodity names are resolved through aliases and known mishearings. A weak match produces a question back to the pilot instead of a guess.
The server counts upstream market-data calls per day and serves cached data when the budget is spent. Hosted-model fallback is capped by a daily spend limit and a per-pilot request limit.
| Layer | Choice | Why |
|---|---|---|
| LLM orchestration | LangChain | Structured output, provider-agnostic model binding |
| Conversational graph | LangGraph | Explicit guardrail/tool-calling loop as a graph, not prompt logic |
| Observability | LangSmith | Full pipeline tracing; also hosts the persona & classifier prompts |
| Schemas | Pydantic | Structured LLM output and tool args |
| Trade-run storage | Postgres + SQLAlchemy + Alembic | Durable, queryable ledger — not a semantic-retrieval problem |
| Desktop overlay | PySide6 | LGPL licensing vs. PyQt6; real layout power vs. tkinter |
| Speech-to-text | faster-whisper | Runs locally on the pilot's machine; model size set by one env var |
| Text-to-speech | Kokoro (self-hosted), ElevenLabs fallback | ~0.1 s per reply on a home GPU and no per-character bill; the hosted voice only steps in when the box is down |
| Model serving | Ollama (gemma4) + Claude Haiku 5.5 fallback | Chosen on eval results; about 2–3 s per turn through the graph on the home box (October 2026 measurements); hosted fallback capped by daily spend |
| Topic decision | Tev1, with chat-model fallback | A small local model decides request, social or off-topic in about 30–40 ms; the chat model only decides if it is unavailable |
| Server | FastAPI + Discord OAuth/JWT | Sole Postgres client and gateway; no secrets ever ship to a pilot's machine |
| Evals | Custom harness + Claude Haiku 5.5 judge | Real graph, deterministic fake world — results reproducible regardless of today's market |
| Packaging | PyInstaller + Inno Setup | A one-folder Windows build: a one-file executable unpacks itself on every launch and is the shape antivirus tools flag most |
| Deploy | Docker Compose on Hetzner | One box, one command; the pilot's client is never containerized |
Nothing here is claimed as built unless it ships in the current release.
Every phrasing of every case runs through the real graph against a deterministic fake world with real Stanton geography. Scored per dimension — topic gate, tool, arguments, location, price, unit, unsupported claims — with Claude Haiku 5.5 judging free text. Used to choose the serving model across ten-odd local candidates. A separate voice evaluation scores character, naturalness and restraint on a fixed judge model.
A small decision model picks which tool groups ALICE binds for each request. Off by default. Measured on 79 phrasings: about 55% fewer tool tokens, one missed group. Turning it on is the next step.
Prompt-injection and jailbreak defense, beyond topic filtering. Every tool argument is already Pydantic-validated — a baseline, not a solution.
Within a session ALICE sees the last six pilot turns, with older tool results replaced by a placeholder. A bounded session recall was evaluated — recall of older facts went from 0 of 6 to 5 of 6 in a 36-turn test — and is off by default. Durable memory across sessions is not built.
A presence layer — mini tracker, timer and route cards — that shows what ALICE logged and lets options be compared by eye instead of by ear. Designed, not built.