ActiQ is a production-oriented workplace assistant that helps employees plan their workday around commutes, desk bookings, meetings, team presence and organizational knowledge. It combines deterministic services and databases for all factual state with an LLM that only phrases answers from verified tool results — the LLM can never fabricate availability, bookings, meeting times or traffic data.
Web / Client ──► FastAPI (REST, auth-ready) ──► ActiQ Agent orchestrator
│
┌──────────────────┬────────────────────┬──┴───────────┐
▼ ▼ ▼ ▼
CommutePlannerTool DeskAvailabilityTool MeetingLookupTool TeamStatusTool / KnowledgeSearchTool
│ │ │ │
▼ ▼ ▼ ▼
Maps provider PostgreSQL/SQLite Calendar provider RAG retriever
(mock or Google) (SQLAlchemy) (DB-seeded mock) (local vector store)
- Provider independence — Gemini and Groq are both supported behind a
common
LLMProviderinterface (actiq/providers/). Selection is purely configuration-driven; business logic contains no vendor branches. - Deterministic core — intent classification, tool routing, booking validation and answer composition have deterministic implementations. The LLM receives tool facts as JSON data and only writes prose; if no LLM is configured or it fails, a template composer renders the same facts.
- Grounding & verification — every response carries structural source
metadata (
VERIFIED/PARTIALLY_VERIFIED/INFERRED/UNKNOWN), confidence, assumptions and the tools used. No chain-of-thought is exposed.
actiq/
├── main.py # FastAPI app factory + lifespan wiring
├── api_service.py # backwards-compatible app re-export
├── config.py # pydantic-settings configuration (fail-fast)
├── app_context.py # dependency wiring for services/agent/RAG
├── observability.py # structured JSON logging
├── api/ # routes, schemas, middleware, auth dependency
├── agent/ # intents, tools, state, prompts, compose, graph, sessions
├── services/ # employees, desks, meetings, commute (deterministic)
├── rag/ # ingest, embeddings, vector store, retriever, injection guard
├── db/ # SQLAlchemy models, repository, seeder
└── providers/ # llm interface, gemini, groq, maps, calendar
data/ # mock datasets + knowledge documents
tests/ # unit, integration and evaluation suites
scripts/ # ingest.py, seed_db.py, run_evaluation.py
docs/EVALUATION.md # recorded evaluation metrics
Requirements: Python 3.10+ (developed on 3.14).
python -m venv .venv
.venv\Scripts\Activate.ps1 # Windows (bash: source .venv/bin/activate)
pip install -r requirements.txt
Copy-Item .env.example .env # bash: cp .env.example .env| Variable | Default | Purpose |
|---|---|---|
LLM_PROVIDER |
gemini |
Active LLM provider: gemini | groq |
GEMINI_API_KEY |
— | Required iff provider is gemini |
GEMINI_MODEL |
gemini-2.5-flash |
Gemini model id |
GROQ_API_KEY |
— | Required iff provider is groq |
GROQ_MODEL |
llama-3.3-70b-versatile |
Groq model id |
LLM_FALLBACK_ENABLED |
false |
Opt-in cross-provider fallback |
LLM_FALLBACK_PROVIDER |
— | Provider used when fallback enabled |
LLM_TEMPERATURE / LLM_MAX_OUTPUT_TOKENS / LLM_TIMEOUT_SECONDS / LLM_MAX_RETRIES |
see .env.example |
Generation controls |
GOOGLE_MAPS_API_KEY |
— | Optional; enables live routing (otherwise deterministic mock dataset) |
DATABASE_URL |
sqlite:///./actiq.db |
SQLAlchemy URL (PostgreSQL works unchanged) |
VECTOR_STORE_PATH |
./data/vectorstore |
Persisted RAG index location |
RAG_TOP_K |
4 |
Chunks fetched per knowledge search |
LOG_LEVEL |
INFO |
Root log level |
API_KEY |
— | Optional; when set, requires X-API-Key header on API routes |
MAX_REQUEST_BYTES |
65536 |
Rejects larger request bodies (413) |
SESSION_TTL_MINUTES |
120 |
In-memory session retention |
BOOKINGS_ENABLED |
true |
Master switch for desk write operations |
Only the selected provider's key is required. Startup fails fast with a clear error if the active provider has no key.
LLM_PROVIDER=gemini
GEMINI_API_KEY=your-key
GEMINI_MODEL=gemini-2.5-flashLLM_PROVIDER=groq
GROQ_API_KEY=your-key
GROQ_MODEL=llama-3.3-70b-versatileSwitching providers needs no code changes — flip LLM_PROVIDER and supply
that provider's key/model. Pick a currently supported model id from your
provider's console (model names evolve; the defaults above were current at
implementation time). Without any usable key the service still runs in
deterministic mode (template-composed answers), which is how tests operate.
All datasets are clearly fictional and live in data/:
employee_directory.json— employees, teams, offices, scheduled in-office daysdesks.json— desks, floors, zones, features, near-team hintsdesk_bookings.csv— static bookings (a demo booking is materialized daily)commute_routes.json— home→office route durations (typical vs peak)meetings.json— weekday-keyed meeting templates materialized per dayknowledge/*.md— office policies indexed by RAG
The database seeds automatically at startup and refreshes date-sensitive rows
(meetings/bookings) once per day. Attendance follows one explicit rule:
an employee is "in office" iff today's weekday appears in their
attendance_days. Calendars are never used to infer presence.
Ingestion chunks data/knowledge/*.md, indexes structured dataset summaries,
embeds them with a deterministic local embedding model and persists the index:
python scripts/ingest.pyThe API also auto-ingests on startup if the index is missing. Re-running is safe (idempotent rebuild).
python -m pytest95+ tests cover services, tools, intent routing, RAG (including prompt-injection guard), provider adapters (mocked SDKs), the API, and the evaluation suite. No live credentials are required — external APIs are mocked.
Evaluation suite only (writes docs/EVALUATION.md):
python scripts/run_evaluation.pyuvicorn actiq.main:app --reload
# legacy alias also works: uvicorn actiq.api_service:app --reloadInteractive OpenAPI docs: http://localhost:8000/docs
curl http://localhost:8000/health
curl -X POST http://localhost:8000/ask \
-H "Content-Type: application/json" \
-d '{"user_id": "emp_001", "query": "When should I leave to reach the office by 9 AM?"}'Example /ask response shape:
{
"answer": "Leave around 08:10 to reach Prestige Tech Park by 09:00 ...",
"intent": "COMMUTE",
"sources": [
{"name": "Commute Routes Dataset", "type": "mock_database",
"verification": "VERIFIED"}
],
"assumptions": ["Departure time is derived from the route duration estimate..."],
"confidence": 0.85,
"verification": "PARTIALLY_VERIFIED",
"tool_calls": ["CommutePlannerTool"],
"request_id": "..."
}Booking endpoints:
curl -X POST http://localhost:8000/bookings -H "Content-Type: application/json" \
-d '{"employee_id": "emp_002", "desk_id": "D-102", "date": "2026-08-25"}'
curl http://localhost:8000/bookings/emp_002
curl -X DELETE "http://localhost:8000/bookings/<booking_id>?user_id=emp_002"
curl http://localhost:8000/meetings/emp_001Conflicts return HTTP 409; unauthorized cancellations return 403; invalid input returns 422.
docker build -t actiq .
docker run --rm -p 8000:8000 \
-e LLM_PROVIDER=groq -e GROQ_API_KEY=$GROQ_API_KEY \
actiqThe image runs as a non-root user, carries no secrets, and includes a health
check hitting /health. A docker-compose.yml is intentionally omitted:
the default deployment uses SQLite and the local vector store, so there are
no extra services to orchestrate yet.
- Live Gemini/Groq and Google Maps integrations are implemented against their SDKs but verified only through mocks in this environment (no real credentials available here); run a quick smoke test after adding keys.
- The vector store is an in-process cosine-similarity index — fine for policy corpora, swap in pgvector/FAISS for large scale.
- Sessions are in-memory (single process); use Redis for multi-worker deploys.
- Attendance reflects scheduled directory days only; no check-in feeds.
- The evaluation suite measures the offline pipeline (MockLLMProvider); phrasing quality with a live LLM is not automatically scored.
Real Google Calendar OAuth · live traffic alerts · PostgreSQL + pgvector · Redis-backed sessions · Prometheus/OpenTelemetry · web dashboard · calendar-aware desk recommendations.
See docs/API_Documentation.md for endpoint details and
docs/EVALUATION.md for recorded metrics.
Released under the MIT License.