Eight nodes, one shared state, no globals.
Every node reads a piece of the same dictionary and writes its piece back. Here's exactly what each one does, how the graph routes between them, and the thresholds that decide what happens next.
How It Thinks
The eight agents
Click a card for what it actually reads and writes.
Under the Hood
The full graph and the retrieval pipeline
The exact LangGraph topology, live from src/agent/graph.py.
flowchart TD
Start(["User query"])
ML["Memory Loader"]
QA{{"Query Analyzer"}}
CA["Clarification Agent"]
RA["Retrieval Agent"]
REA["Reasoning Agent"]
VA{{"Validation Agent"}}
RG["Response Generator"]
MM["Memory Manager"]
End(["Response returned"])
Start --> ML --> QA
QA -->|"ambiguous"| CA
QA -->|"simple / complex"| RA
CA --> MM
RA -->|"simple"| RG
RA -->|"complex"| REA
REA -->|"confidence ≥ 0.7"| VA
REA -->|"0.5–0.7, first pass, out-of-distribution"| CA
REA -->|"0.5–0.7, retries left"| RA
REA -->|"below 0.5, or retries exhausted"| CA
VA -->|"passed"| RG
VA -->|"failed, retries left"| RA
VA -->|"failed, retries exhausted"| RG
RG --> MM --> End
classDef llm fill:#DCE9E1,stroke:#0E6B5A,color:#221C13,stroke-width:1.3px;
classDef tool fill:#EEE4CE,stroke:#6E6350,color:#221C13,stroke-width:1.3px;
classDef gate fill:#EFE1BE,stroke:#9A6B18,color:#221C13,stroke-width:1.3px;
classDef term fill:#0E6B5A,stroke:#093B31,color:#ffffff,stroke-width:1.3px;
class ML,RA,MM tool;
class CA,REA,RG llm;
class QA,VA gate;
class Start,End term;
flowchart LR Q["Query"] --> BM["BM25\nsparse keyword search"] Q --> V["FAISS\ndense search, MiniLM-L6-v2"] BM --> RRF["Reciprocal Rank Fusion\nk = 60"] V --> RRF RRF --> CE["Cross-Encoder Rerank\nms-marco-MiniLM-L-6-v2"] CE --> Top["Top-K chunks → Reasoning Agent"] classDef step fill:#EEE4CE,stroke:#6E6350,color:#221C13,stroke-width:1.2px; classDef out fill:#0E6B5A,stroke:#093B31,color:#ffffff,stroke-width:1.2px; class Q,BM,V,RRF,CE step; class Top out;
| From | Condition | Next |
|---|---|---|
| Query Analyzer | ambiguous | Clarification Agent |
| Query Analyzer | simple / complex | Retrieval Agent |
| Retrieval Agent | simple | Response Generator |
| Retrieval Agent | complex | Reasoning Agent |
| Reasoning Agent | confidence ≥ 0.7 | Validation Agent |
| Reasoning Agent | 0.5–0.7, first pass, out-of-distribution | Clarification Agent |
| Reasoning Agent | 0.5–0.7, retries left | Retrieval Agent (retry) |
| Reasoning Agent | below 0.5, or retries exhausted | Clarification Agent |
| Validation Agent | passed | Response Generator |
| Validation Agent | failed, retries left | Retrieval Agent (retry) |
| Validation Agent | failed, retries exhausted | Response Generator, with uncertainty note |
Guardrails
Why it doesn't just make things up
The thresholds and caps that decide when the system trusts itself, and when it stops to ask instead.
0.70Pass threshold
Confidence needed to skip straight to validation.
0.50Clarify floor
Below this, it asks rather than answers.
2Max retries
Retrieval gets reformulated and re-run, at most twice.
3Validation checks
Grounding, consistency, completeness: all three must pass.
2Fused retrievers
BM25 and FAISS, merged by reciprocal rank fusion, then reranked.
0.50OOD gate
Corpus similarity floor below which a query is treated as out-of-distribution.