Valencia Smart City Agent is a multi-agent assistant for answering operational questions about Valencia, Spain. It combines Google ADK agents, an MCP tool server, city open-data integrations, user memory, RAG, and web/API frontends.
The assistant is designed for queries such as:
- "What is the traffic like near the city center?"
- "Can I go by bike today, considering weather and air quality?"
- "Find public parking and EV charging points near my destination."
- "How do I get from UPV to the city center?"
- "Where are nearby police districts, kiosks, hydrants, or public city services?"
The user-facing agent answers in Spanish, while this repository documentation is in English.
- Google ADK multi-agent orchestration with specialist agents for mobility, environment, and city services.
- FastMCP server exposing real-time tools over SSE.
- Dynamic orchestration layer with rule-based and optional LLM planning.
- Real-time and near-real-time integrations for Valencia traffic, EMT/GTFS, Valenbisi, FGV/metro-tram data, weather, air quality, routing, geocoding, and city services.
- Persistent user profile memory for recurring needs such as accessibility or travel preferences.
- REST API backend for a React interface.
- Chainlit and terminal chat interfaces for local demos.
- Evaluation harness and regression tests for tools, orchestration, response synthesis, and data quality.
User
|
| CLI / Chainlit / React
v
Google ADK root orchestrator
|
| routes tasks
v
Specialist agents
|-- mobility_specialist
|-- environment_specialist
|-- services_specialist
|
| filtered MCP toolsets
v
FastMCP SSE server
|
| domain modules
v
Valencia Open Data, NAP GTFS, Open-Meteo, OpenRouteService,
DuckDuckGo, Neo4j/RAG, local SQLite memory
The MCP server owns the tool registry and exposes category-filtered toolsets. The ADK agents connect to it through SSE, normally at http://127.0.0.1:8081/sse.
The MCP server registers tools from src/domains/ and assigns them to agent categories:
- Traffic: congestion summaries, monitored road segments, and traffic-state distributions from Valencia Open Data.
- EMT and public transport: bus lines, stops, arrival-oriented data, and NAP GTFS-backed transport helpers.
- Bikes: Valenbisi station availability and nearby bike searches.
- Mobility: FGV metro/tram stations, public parking, EV charging points, ZBE status, loading zones, bike parking, and related city mobility datasets.
- Routing and geocoding: place search plus pedestrian/car route calculation through OpenRouteService when configured.
- Weather: current weather, daily summaries, and forecasts through Open-Meteo.
- Air quality: pollutant summaries and health-oriented interpretations.
- City services: police districts, kiosks, hydrants, public facilities, tourism, municipal services, and other documentary datasets.
- Web search: recent events, news, and public information lookups.
- Knowledge/RAG: municipal and tourism knowledge backed by the RAG pipeline.
.
|-- agent_runner.py # Terminal chat/demo runner
|-- agent_smartcity/ # ADK root and specialist agents
|-- evals/ # ADK evaluation cases and custom metrics
|-- scripts/ # Debug and paper evaluation helpers
|-- smartcity-react/ # React + Vite web client
|-- src/
| |-- api.py # FastAPI backend for the React client
| |-- frontend.py # Chainlit frontend
| |-- mcp_server.py # FastMCP tool server and registry
| |-- domains/ # City data/tool implementations
| |-- memory/ # User profile persistence
| |-- orchestration/ # Planning, execution, evidence, synthesis
| |-- rag/ # Knowledge/RAG ingestion and query code
| `-- utils/ # Shared API, GTFS, logging, model helpers
|-- tests/ # Unit and integration-style regression tests
|-- requirements.txt # Python dependencies
|-- start_server.sh # Starts the MCP SSE server
`-- start_frontend.sh # Starts the Chainlit frontend
- Python 3.11 or newer.
- Node.js 20 or newer for the React/Vite client.
- A valid
.envfile for model and external API configuration. - Optional: Neo4j with APOC if you want to run the graph RAG ingestion pipeline.
The current agent code uses PoliGPT through LiteLLM for the root and specialist agents. The MCP server configuration also validates GOOGLE_API_KEY, so local runs should provide both keys unless you adjust that validation in code.
git clone <repository-url>
cd SmartCity
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .envThen edit .env. A practical local configuration looks like this:
# LLM backend used by the ADK agents through LiteLLM.
POLIGPT_API_KEY=your_poligpt_api_key
POLIGPT_API_BASE=https://api.poligpt.upv.es/
# Still validated by src/config.py when the MCP server starts.
GOOGLE_API_KEY=your_google_api_key
AGENT_MODEL=gemini-2.0-flash-exp
AGENT_TEMPERATURE=0.7
# MCP server.
MCP_SERVER_HOST=0.0.0.0
MCP_SERVER_PORT=8081
# Optional but recommended for richer mobility answers.
ORS_API_KEY=your_openrouteservice_key
NAP_API_KEY=your_transportes_nap_key
# Defaults are usually fine.
VALENCIA_API_BASE_URL=https://valencia.opendatasoft.com
VALENCIA_LATITUDE=39.47
VALENCIA_LONGITUDE=-0.38
ENABLE_CACHE=true
CACHE_TTL=600
NAP_GTFS_TTL=43200
# Optional graph/RAG backend.
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=password
NEO4J_DATABASE=neo4jStart the MCP server first. The ADK specialist agents connect to this SSE service to access tools.
./start_server.shThe script is idempotent. If an MCP server is already running on the configured port, it reuses it. To deliberately replace the process on that port:
./start_server.sh --restartpython agent_runner.pyThis launches a small terminal UI with an interactive mode and a demo mode.
./start_frontend.shEquivalent manual command:
chainlit run src/frontend.py -wRun the API backend:
uvicorn src.api:app --host 0.0.0.0 --port 8000In another terminal, start the React app:
cd smartcity-react
npm install
npm run devThe Vite dev server proxies /api to http://localhost:8000. If you deploy the backend elsewhere, set VITE_API_BASE_URL for the React client.
adk web .or:
adk run agent_smartcityThe services specialist can use a local services agent by default, or a remote A2A RAG service when available on port 8001.
Start the RAG A2A service:
python src/rag/rag_a2a_server.pyForce local services-agent mode instead of the remote A2A service:
SMARTCITY_FORCE_LOCAL_SERVICES_AGENT=1 python agent_runner.pyRun the web/graph ingestion pipeline:
python src/rag/ingestion/pipeline.pyThe ingestion pipeline crawls configured Valencia-related seed URLs, cleans and chunks documents, creates vector data, and writes graph information to Neo4j. It expects the Neo4j settings in .env and APOC support for entity-resolution post-processing.
The FastAPI backend exposes:
POST /api/chatfor chat requests.POST /api/sessionsto create a chat session.GET /api/sessions/{user_id}to list sessions.GET /api/sessions/{session_id}/messagesto fetch session history.PUT /api/sessions/{session_id}to update title or pin state.DELETE /api/sessions/{session_id}to delete a session.GET /api/profile/{user_id}to inspect persistent user memory.DELETE /api/profile/{user_id}/{category}to delete a saved preference category.GET /api/data-sources/summaryto inspect verified and inferred data-source coverage.
The backend stores chat sessions in smartcity_chat.db. User profile memory uses the database configured by DATABASE_URL, defaulting to local SQLite.
Run the full configured test suite:
python -m pytestUseful focused checks:
python -m pytest tests/test_open_data.py tests/test_adk_patches.py tests/test_response_synthesis.py -q
python -m pytest tests/test_dynamic_orchestrator.py tests/test_orchestration_modules.py -q
python -m pytest evals/evaluate.py::test_mixed_scenarios -vv -sPaper/evaluation helper:
scripts/run_paper_eval_battery.shCommon evaluation environment variables:
ADK_EVAL_CONFIG=test_config_paper.json
ADK_EVAL_NUM_RUNS=1
EVAL_TIMEOUT_SECONDS=240
SMARTCITY_EVAL_DIAGNOSTICS_DIR=/tmp/smartcity_eval_diagnosticsPOLIGPT_API_KEYis required byagent_smartcity/agent.pythroughvalidate_poligpt_environment().POLIGPT_API_BASEdefaults tohttps://api.poligpt.upv.es/.GOOGLE_API_KEYis currently required bysrc/config.pyvalidation when starting the MCP server.ORS_API_KEYenables route calculation through OpenRouteService. Without it, routing answers will be limited.NAP_API_KEYenables GTFS downloads from the Spanish National Access Point for richer EMT, FGV, and interurban data.MCP_SERVER_PORTdefaults to8081. The API backend commonly runs on8000; the optional RAG A2A service runs on8001.SMARTCITY_PLANNER_MODEcontrols root orchestration mode. Supported values includellm,rule_based, andenv.SMARTCITY_LLM_PLANNER_ENABLED=1enables the optional planner whenSMARTCITY_PLANNER_MODE=env.SMARTCITY_FORCE_LOCAL_SERVICES_AGENT=1bypasses the remote A2A services agent.
- Keep the MCP server running while using CLI, Chainlit, ADK, or API flows.
- If tool schemas look stale after code changes, restart the MCP server with
./start_server.sh --restart. - Use
GET /api/data-sources/summarybefore demos to understand which datasets are verified, inferred, or pending. - Prefer focused tests when changing one domain module, then run the orchestration and synthesis tests before demos.
See LICENSE.