Multi-agent AI backend for intelligent airport management
AeroMind is a production-ready REST API that powers an intelligent airport assistant. Passengers and operators interact with it through a natural language chat interface. Behind the scenes, an orchestrator powered by Google Gemini classifies every message and routes it to the most suitable specialized AI agent.
Each agent can call real airport tools β searching flights, tracking baggage, filing incidents β via a Model Context Protocol (MCP) server. Agents that deal with policies and procedures also perform Retrieval-Augmented Generation (RAG) over an official document store backed by pgvector.
graph TD
Client["π HTTP Client"] --> API
subgraph API["β‘ FastAPI"]
R["/auth Β· /flights Β· /bookings\n/baggage Β· /chat Β· /incidents Β· /documents"]
end
API --> Orch["π§ Orchestrator\nGemini LLM Β· temperature=0\nIntent classification"]
Orch -->|"βοΈ flight queries"| FA["FlightAgent"]
Orch -->|"π§³ baggage queries"| BA["BaggageAgent"]
Orch -->|"π¬ support & policies"| SA["SupportAgent"]
Orch -->|"π security & procedures"| SeA["SecurityAgent"]
FA --> MCP
BA --> MCP
SA --> RAG
SeA --> RAG
subgraph MCP["π MCP Server Β· FastMCP"]
T["9 airport tools"]
end
subgraph RAG["π RAG System"]
E["Gemini Embeddings\n3072 dimensions"]
V[("pgvector\ncosine similarity")]
E --> V
end
MCP --> AppL
RAG --> AppL
subgraph AppL["π Application Layer"]
UC["Use Cases Β· DTOs Β· Ports"]
end
AppL --> DB[("π PostgreSQL 17\n+ pgvector")]
graph BT
style Domain fill:#1e3a5f,stroke:#4a9eff,color:#e8f4ff
style Application fill:#1a3a2a,stroke:#4aff8c,color:#e8fff0
style Infra fill:#3a1e3a,stroke:#cc4aff,color:#f0e8ff
style API fill:#3a2a1a,stroke:#ff8c4a,color:#fff0e8
Domain["π Domain\nEntities Β· Value Objects Β· Domain Exceptions"]
Application["π Application\nUse Cases Β· Ports Β· DTOs"]
Infra["π§ Infrastructure\nSQLAlchemy Β· Gemini Β· JWT Β· bcrypt"]
API["π API\nRouters Β· Middleware Β· Dependencies"]
Application --> Domain
Infra --> Application
API --> Application
| Technology | Purpose |
|---|---|
| β‘ FastAPI | Async REST framework |
| ποΈ SQLAlchemy 2 (async) | ORM with async session support |
| π PostgreSQL 17 + pgvector | Relational DB + vector similarity search |
| π Alembic | Database migrations |
| π€ Google Gemini | LLM (gemini-2.0-flash) + Embeddings (gemini-embedding-001) |
| π FastMCP | Model Context Protocol server for agent tools |
| β Pydantic v2 | Data validation and settings management |
| π bcrypt | Password hashing |
| πͺ JWT (PyJWT) | Stateless authentication |
| π¦ Poetry | Dependency management |
| π³ Docker / Docker Compose | Containerization |
| π§ͺ pytest + pytest-asyncio | Testing |
| π ruff | Linting |
| π¬ mypy | Static type checking |
Before you start, make sure you have:
- π³ Docker and Docker Compose v2 β verify with
docker compose version - π Python 3.12+ β only needed for local development
- π¦ Poetry β install with
pip install poetry - π Google AI Studio API key with Gemini access β get one here
The fastest way to run the full stack (database + API) is with Docker Compose.
β Clone the repository
git clone https://github.com/blandev/aeromind-api.git
cd aeromind-apiβ‘ Create your environment file
cp .env.example .envOpen .env and fill in your values:
# PostgreSQL (used by docker-compose)
POSTGRES_USER=aeromind
POSTGRES_PASSWORD=aeromind
POSTGRES_DB=aeromind
POSTGRES_PORT=5432
# Database URL β use this format when running inside Docker
DATABASE_URL=postgresql+asyncpg://aeromind:aeromind@db:5432/aeromind
# JWT β generate a secret with: openssl rand -hex 32
JWT_SECRET_KEY=replace-with-a-long-random-string
JWT_ALGORITHM=HS256
JWT_EXPIRATION_MINUTES=60
# Google Gemini
GOOGLE_API_KEY=your-google-api-key-here
GOOGLE_EMBEDDING_MODEL=models/gemini-embedding-001β’ Start all services
docker compose up -dThis starts two containers:
| Container | Description | Port |
|---|---|---|
db |
PostgreSQL 17 with pgvector | 5432 |
api |
AeroMind REST API | 8000 |
β£ Apply database migrations
The API container starts but does not run migrations automatically. Run them once:
make migrateβ€ Open the interactive docs
Visit http://localhost:8000/docs β the full Swagger UI with all endpoints is ready.
β₯ Register your first admin user
curl -X POST http://localhost:8000/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "admin@aeromind.com", "password": "secret123", "role": "ADMIN"}'β¦ Log in and get your JWT token
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "admin@aeromind.com", "password": "secret123"}'Copy the access_token from the response and use it as a Bearer token in the Authorization header for all protected endpoints.
For active development, run only the database in Docker and the API locally for hot-reload.
β Start only the database
docker compose up -d dbβ‘ Install dependencies
poetry installβ’ Switch the database URL in .env
# Comment out the Docker URL and use localhost instead:
DATABASE_URL=postgresql+asyncpg://aeromind:aeromind@localhost:5432/aeromindβ£ Apply migrations
make migrateβ€ Start the development server
make dev
# β http://localhost:8000 with auto-reloadβ₯ Run the test suite
make test # run tests
make coverage # run tests + coverage report| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
β | β | AsyncPG connection string |
POSTGRES_USER |
β | β | PostgreSQL username (Docker only) |
POSTGRES_PASSWORD |
β | β | PostgreSQL password (Docker only) |
POSTGRES_DB |
β | β | PostgreSQL database name (Docker only) |
POSTGRES_PORT |
β | 5432 |
Exposed PostgreSQL port |
JWT_SECRET_KEY |
β | β | Secret for signing JWT tokens |
JWT_ALGORITHM |
β | HS256 |
JWT signing algorithm |
JWT_EXPIRATION_MINUTES |
β | 60 |
Token validity in minutes |
GOOGLE_API_KEY |
β | β | Google AI Studio API key |
GOOGLE_EMBEDDING_MODEL |
β | models/gemini-embedding-001 |
Gemini embedding model |
GOOGLE_LLM_MODEL |
β | models/gemini-2.0-flash |
Gemini LLM model |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/auth/register |
Public | Register a new user |
POST |
/auth/login |
Public | Log in and receive a JWT token |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/flights |
Any | Search flights (origin, destination, date) |
GET |
/flights/{flight_id} |
Any | Get a specific flight by ID |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/bookings/{booking_id} |
Passenger+ | Get a booking by ID |
GET |
/bookings/user/{user_id} |
Passenger+ | Get all bookings for a user |
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/baggage/{tag} |
Passenger+ | Track baggage by tag number |
POST |
/baggage/{tag}/report-lost |
Passenger+ | Report lost baggage (auto-creates an incident) |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/incidents |
Operator+ | Create a new incident |
GET |
/incidents |
Operator+ | List all incidents |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/documents/ingest |
Admin | Ingest a document into the vector store |
POST |
/documents/search |
Any | Semantic search over ingested documents |
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/chat |
Passenger+ | Send a message to the multi-agent system |
Example:
// Request
{ "message": "What is the status of flight AM-201?" }
// Response
{
"response": "Flight AM-201 departs BOG at 08:30 and arrives MDE at 09:20. Status: ON TIME.",
"agent": "flight_agent"
}| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/health |
Public | Service health check |
| Role | Access Level |
|---|---|
π§ PASSENGER |
Chat, baggage tracking, view bookings |
π OPERATOR |
Everything above + incident management |
π ADMIN |
Full access including document ingestion |
sequenceDiagram
actor U as π€ User
participant O as π§ Orchestrator
participant A as π€ Specialized Agent
participant G as β¨ Gemini LLM
participant T as π§ Tools / RAG
U->>O: POST /chat { message }
O->>G: Classify intent (temperature=0)
G-->>O: "flight_agent" | "baggage_agent" | "support_agent" | "security_agent"
O->>A: run(message)
loop Agentic Loop (up to 5 rounds)
A->>G: message + tool declarations
G-->>A: function_call(name, args)
A->>T: execute tool
T-->>A: tool result (JSON)
A->>G: function_response
end
G-->>A: final text response
A-->>U: { response, agent }
| Agent | Handles | Tools |
|---|---|---|
FlightAgent |
Flight searches and status queries | search_flights, get_flight |
π§³ BaggageAgent |
Baggage tracking and lost reports | track_baggage, report_lost_baggage |
π¬ SupportAgent |
Policies, FAQs, general assistance | search_policies (RAG) |
π SecurityAgent |
Security procedures and regulations | search_policies (RAG) |
The SupportAgent and SecurityAgent ground their responses in official airport documents using RAG.
flowchart LR
subgraph Ingestion["π₯ Ingestion (POST /documents/ingest)"]
direction TB
D["π Plain-text\nDocument"]
C["Chunks\n500 chars Β· 50 overlap"]
E["Embeddings\nGemini Β· 3072 dims"]
D --> C --> E
end
DB[("π pgvector")]
E --> DB
subgraph Retrieval["π Retrieval (at chat time)"]
direction TB
Q["β User Query"]
QE["Query Embedding"]
TOP["Top-5 chunks\ncosine similarity"]
Q --> QE --> TOP
end
DB --> TOP
TOP --> R["π¬ Grounded\nAgent Response"]
AeroMind exposes a Model Context Protocol (MCP) server via FastMCP, allowing any MCP-compatible client (such as Claude Desktop) to call airport tools directly.
| Tool | Description |
|---|---|
search_flights |
Search for available flights |
get_flight |
Get details of a specific flight |
get_booking |
Get a booking by ID |
get_user_bookings |
Get all bookings for a user |
track_baggage |
Track baggage by tag |
report_lost_baggage |
Report lost baggage |
create_incident |
Create an airport incident |
get_incidents |
List all incidents |
search_documents |
Semantic search over airport documents |
aeromind-api/
βββ app/
β βββ agents/ # π€ Multi-agent system
β β βββ base_agent.py # Agentic loop base class
β β βββ rag_agent.py # Base for RAG-capable agents
β β βββ orchestrator.py # Intent classification + routing
β β βββ flight_agent.py
β β βββ baggage_agent.py
β β βββ support_agent.py
β β βββ security_agent.py
β βββ api/
β β βββ routers/ # π FastAPI route handlers
β β βββ auth.py # JWT middleware
β β βββ dependencies.py # Dependency injection
β βββ application/
β β βββ use_cases/ # π Business logic (one file per use case)
β β βββ ports/ # Repository & service interfaces
β β βββ dtos/ # Data Transfer Objects
β β βββ exceptions/ # Application-level exceptions
β βββ domain/
β β βββ entities/ # π Core business entities
β β βββ exceptions/ # Domain-level exceptions
β βββ infrastructure/
β β βββ config/ # βοΈ Settings (pydantic-settings)
β β βββ database/ # SQLAlchemy models, session, base
β β βββ repositories/ # Concrete SQLAlchemy implementations
β β βββ services/ # Gemini, JWT, bcrypt implementations
β βββ mcp/
β β βββ server.py # π FastMCP server definition
β β βββ tools/ # One file per tool category
β βββ main.py # π FastAPI app + exception handlers
βββ migrations/ # π Alembic migration files
βββ tests/ # π§ͺ pytest test suite
βββ docker-compose.yml
βββ Dockerfile
βββ Makefile
βββ pyproject.toml
βββ .env.example
| Command | Description |
|---|---|
make dev |
Start dev server with hot-reload |
make test |
Run the test suite |
make coverage |
Run tests with coverage report |
make lint |
Lint with ruff |
make type-check |
Type check with mypy |
make migrate |
Apply all pending migrations |
make migrate-gen m="msg" |
Generate a new migration |
make migrate-down |
Roll back the last migration |
make docker-up |
Start all Docker services |
make docker-down |
Stop all Docker services |
make docker-logs |
Follow Docker logs |
make docker-build |
Rebuild Docker images |
make db-shell |
Open a psql shell in the container |
GitHub Actions workflow (.github/workflows/ci.yml) runs on every push and pull request:
flowchart LR
Push["π€ git push /\nPull Request"] --> Lint["π Lint\nruff check"]
Lint --> Types["π¬ Type Check\nmypy"]
Types --> Tests["π§ͺ Tests\npytest --cov"]
Tests --> Done["β
All checks\npassed"]
Required GitHub Secrets:
| Secret | Description |
|---|---|
DATABASE_URL |
Test database connection string |
JWT_SECRET_KEY |
Any random string for tests |
GOOGLE_API_KEY |
Google AI Studio API key |