3 min read · 537 words
Backend Overview
The backend is a layered FastAPI service in backend/app/: routers validate and orchestrate, services own all AI logic, SQLAlchemy models persist, and every automated step is logged.
Module map
Rendering diagram…
Layering rules
- Routers may import schemas, models, and services. They own HTTP concerns, tool gating, transactions, and observability logging.
- Services may import other services but never routers or models — they are pure logic over inputs (the one exception:
document_serviceowns its Chroma client as module state). - Models import only
database.Base. - Imports are top-level (
from database import get_db), which is why the server must run frombackend/app/— see Installation.
Page directory
| Page | Module | One-line summary |
|---|---|---|
| Application Entry | main.py | Table creation, migrations, router mounting, health checks |
| Database Layer | database.py | Engine, SessionLocal, get_db(), startup migrations |
| ORM Models | models.py | The 9 tables and their relationships |
| Schemas | schemas.py | Every Pydantic request/response shape |
| Routers: Agents | routers/agents.py | 15 endpoints — the platform's core API |
| Routers: Documents | routers/documents.py | Document upload, listing, search, RAG ask |
| Service: OpenAI | services/openai_service.py | All direct LLM calls and their prompts |
| Service: Multi-Agent Email | services/multi_agent_email_service.py | The three email-pipeline agents |
| Service: RAG Graph | services/rag_graph_service.py | The LangGraph corrective RAG graph |
| Service: Documents | services/document_service.py | Chunking, embeddings, vector store |
| Service: Monitoring | services/monitoring_service.py | Token and cost estimation |
| Observability | cross-cutting | The ToolCall/AgentRun/WorkflowRun contract |
| Error Handling | cross-cutting | Failure modes, status codes, degradation strategy |
Conventions you must keep
Every automated/LLM step is logged as a ToolCall (with model_name, estimated_tokens, estimated_cost, latency_ms when it involved an LLM) and, when part of a chat, linked to an AgentRun via agent_run_id. The dashboard and UI depend on this. Full spec: Observability.
Endpoints that execute a tool must call agent_has_tool(agent, "<tool>") first and return 403 when disabled. Canonical tool names: Tools.
New columns on existing models need a matching entry in run_startup_migrations() — there is no Alembic. How-to.
Related pages
- Architecture — the system this backend implements
- API Reference — the endpoints by HTTP contract rather than by module
- Guides — extending the backend safely