Lumen — Multi-Node Research Agent
A multi-node LangGraph pipeline that researches, cross-checks sources, and writes reports

The Problem
A single-pass LLM call asked to research a topic will happily produce a confident-sounding answer even when its sources disagree with each other or don't actually support the claim being made. Real research requires deliberately checking sources against each other and surfacing contradictions instead of smoothing over them, which a one-shot prompt structurally can't do.
The Solution
Lumen is a six-node LangGraph pipeline, Query Classifier, Researcher, Source Critic, Evidence Extractor, Conflict Detector, and Report Writer, with each stage responsible for one part of the research process rather than one model call trying to do everything at once. The Evidence Extractor processes sources in chunks of five to stay within Groq's rate limits. The pipeline supports human-in-the-loop interrupt and resume using LangGraph's native interrupt() and Command(resume=...) primitives, and a FastAPI layer exposes /research and /research/resume endpoints so a paused pipeline can be picked back up cleanly rather than restarted from scratch. State persistence moved from SQLite to a real Postgres-backed checkpointer to survive restarts without losing an in-progress research run.
Tech Stack
A Real Bug, In Detail
The Conflict Detector node had a quiet failure mode: it was designed to compute a parse_failed flag when it received malformed output from an earlier LLM call, but nothing downstream actually checked that flag before forwarding the result to the Report Writer. The final report would still come out looking polished and confident, with no visible sign that the underlying conflict analysis had actually failed. This is a genuinely dangerous class of bug, not a crash, but a silent quality failure that produces a plausible-looking wrong answer. The fix was to log it explicitly as a conflicts_analysis_failure event instead of letting it pass through unflagged, turning an invisible failure into a visible, queryable one.
Decisions and Tradeoffs
Two decisions shaped the pipeline's reliability more than anything else. Moving persistence from in-memory to a real checkpointer was necessary specifically because Lumen is a long-running, multi-step process, not a single request-response call, an in-memory approach meant any restart lost all in-progress research state. Separately, the entire class of extraction failures the pipeline was hitting turned out to trace back to one unset default, max_tokens had no explicit ceiling, which caused systematic output truncation. The initial assumption was that input size was the problem, since that's the more intuitive failure mode for a research pipeline processing many sources, but the real constraint was on the output side. Setting an explicit token ceiling (8192) resolved the majority of extraction failures in one change.
Lessons Learned
The max_tokens root cause is the clearest lesson from this project: when a system is failing on complex, information-heavy tasks, the instinct is to assume the input is overwhelming the model. Here, that assumption was wrong, and chasing it first would have meant restructuring how sources were chunked and fed in, solving the wrong problem entirely. The actual fix was a single default value. Since then, checking output-side constraints (token ceilings, truncation, format limits) is a standard early step before assuming an input-side redesign is needed, because it's a cheaper hypothesis to rule out first and it was the actual cause here.