Design Rationale
Short explanations for the project choices that matter most when learning how to build a production-style LangGraph RAG system.
Why use a retrieval subgraph?
Retrieval has its own loop: rewrite, fan-out, retrieve, merge, grade, synthesize, validate, and sometimes retry. Keeping that inside a subgraph lets the supervisor treat retrieval as one tool while the evidence pipeline stays independently understandable.
Why grade documents before synthesis?
Raw retrieval can return chunks with the right company or year but the wrong content. grade_docs narrows the context to chunks with actual financial facts, which reduces noise before the LLM writes a draft.
Why grade the answer after synthesis?
A draft can still be weak, empty, or partially unsupported. grade_answer separates "did we retrieve something?" from "did the answer actually use grounded financial data?"
Why keep company_status?
Multi-company questions can partially succeed. company_status lets the retry path target only missing companies instead of repeating successful branches.
Why not let the LLM do arithmetic directly?
Financial answers often need exact differences, ratios, growth rates, or averages. A calculator tool gives deterministic arithmetic and keeps the LLM focused on explanation.
Why use semantic cache tiers?
A very high match can skip RAG and return a saved answer. A weaker but still useful match can seed the pipeline as context without bypassing retrieval.
Why keep Tavily separate from report retrieval?
Annual reports are historical and fixed. News is current and externally sourced. Keeping them separate makes it clear which facts come from report evidence and which come from recent web context.
Why have guardrails before answer?
Guardrails catch empty or too-short aggregated context before final synthesis. That gives the app a clear fallback path instead of returning a brittle answer.