ResearchState TypedDict

Every node we build reads and writes from the same shared state. Getting that shape right is most of the design work. If the state is messy, every node becomes messy with it. If it is clean, the pipeline almost writes itself.

mini-perplexity.ipynb
python
from typing import TypedDict, List, Dict, Any

class ResearchState(TypedDict):
    """State for the research assistant."""
    question: str
    search_results: List[Dict[str, Any]]
    extracted_content: List[Dict[str, Any]]
    synthesized_answer: str
    final_answer: str
    sources: List[Dict[str, str]]
    current_step: str

TypedDict is the sweet spot between a plain dict (too loose) and a full dataclass (too strict for LangGraph). You get type hints and IDE support, while the runtime value stays a dict the graph can serialize.

Notice what the state does not hold. There is no LLM object, no HTTP session, no configuration. Those are dependencies each node owns internally. The state only carries data that flows between nodes, which keeps it serializable and easy to reason about.

Keeping both gives us an observability win. When the refined answer looks wrong, you compare it to the synthesized draft and see which pass broke. If we overwrote the draft, that debugging clue is gone. Cheap to store, valuable when something goes sideways.

Quiz: Quiz

Loading practice…