CARDIO4Cities / architecture overview

Evidence before confidence.

A team of agents turns live public sources into a cited city brief, and refuses to publish anything it cannot find in a source and have checked by a second agent.

FastAPILangGraph · 8 agentsGroq · open-weights modelsSQLiteQdrantGraphiti + Neo4jDocker

System context

One container serves the API and the UI. Every model call passes through one boundary that handles rate limits and fallbacks. Knowledge lives in hosted stores because free hosting has an ephemeral disk.

PRESENTATION API ORCHESTRATION MODEL BOUNDARY STATE EXTERNAL Intelligence Studio UI — static, no build step Depth choice · live progress · findings & gaps · evidence · graph · Q&A · history · runtime. Served by FastAPI or GitHub Pages. FastAPI application — research runs as a background job the UI polls POST /api/research GET /api/progress POST /api/ask GET /api/workflow GET /api/runtime LangGraph — 8 agents, 3 gates, one bounded loop intake→plan discover crawl_gate extract claims fact_check sufficiency index→report llm.py — every model call passes through here Per-model pacing and shared cooldown · extraction model pool with failover · throttle vs. quota classification Half-open circuit breakers (chat and embeddings separately) · deterministic fallback, counted and reported RELATIONAL SQLite run ledger Runs, sources, crawl decisions, facts, gaps, guardrails, traces. VECTOR Qdrant Cloud Verified evidence, city-filtered. Collection named by embedder. GRAPH Graphiti → Neo4j Typed entities per city, built in the background after the brief. Search APIs Tavily web search (keyed); OpenAlex, Europe PMC, Wikipedia (keyless). Source websites HTML, XML and PDF. Fetched only after robots.txt allows; crawl-delay honoured per host. Generation provider Groq, OpenAI-compatible API. Swappable for Ollama, vLLM or OpenAI by one URL. Embeddings In-process bge-small (local), or hosted API with its own key on the 512 MB tier. Solid border = inside the deployed service · dashed = third party reached at request time
Figure 1 — Request path and state ownership

The agents

Each agent has one job and hands its output to the next. LangGraph orchestrates them and also runs three plain steps: intake, saving and brief assembly. Three agents are gates: anything they remove is recorded as a gap the user can see.

01 · Planner

Decides what to search for

Writes searches for each of the 5 areas. In a second round, only for the areas still missing.

Uses fast model; seed terms if no model

02 · Discovery

Finds sources

Searches the live web at request time and ranks results by publisher authority. Tavily reaches government, municipal and programme pages; the scholarly APIs reach city studies.

Uses Tavily web search, OpenAlex, Europe PMC, Wikipedia

03 · Crawl gate

Asks permission

Reads robots.txt before any page is fetched. A refused site is never read.

Removes disallowed sources → gap

04 · Reader

Reads the pages

Fetches permitted HTML, XML and PDF, best sources first, within the chosen depth.

Removes unreadable pages → gap

05 · Claim agent

Pulls out findings

Proposes specific claims, each with a quote. A quote that cannot be found in the page is dropped.

Uses a pool of 3 models

06 · Fact checker

Double-checks each claim

A model that did not write the claim rules it supported, scope-flagged or withheld. Numbers must appear in the quote.

Removes unsupported claims → gap

07 · Sufficiency

Decides if it is enough

Needs verified city evidence in 3 of 5 areas. Otherwise sends the Planner back, up to the round limit.

Records empty areas as open questions

08 · Answer agent

Answers questions

Answers from stored evidence only, citing it, or says the evidence is not enough.

Uses vectors, graph, ledger

The trust rule: no agent both writes a claim and approves it. No located quote means no claim; no independent verdict means no publication.

Models

JobModelWhy
Claim extractionqwen3.8-27b, gpt-oss-120b, gpt-oss-20bThe bottleneck; rotating 3 models triples the free rate limit.
Fact checkAny model except the extractorIndependence is enforced by routing, not by prompt.
Planning, answersgpt-oss-20bShort structured tasks; fast, with a fallback.
Knowledge graphgpt-oss-120bIts own budget, so it never slows the brief.
Embeddingsbge-small locally, hosted API on RenderFree locally; moved off the 512 MB host.

Data and retrieval

StoreHoldsAnswers
SQLiteRuns, sources, crawl decisions, facts, gaps, metrics“What did the system decide, and why?”
QdrantVerified evidence, filtered by city“What evidence is near my question?”
Graphiti + Neo4jOrganisations, programmes, policies, places“Who is connected to what?”

A question is tied to one city, searched in vectors and graph at the same time, reranked, and answered with numbered citations that resolve to stored records. If a store is down, the ledger still answers.

Resilience

Trade-offs

Chosen, and what it costs