# LangGraph State Machine for Chat and Search in Open Notebook

> Open Notebook uses LangGraph state machines to power its chat and search AI. Discover three graph architectures for enhanced conversational RAG capabilities. Learn more.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-07-05

---

**Open Notebook orchestrates its conversational AI and retrieval-augmented generation (RAG) capabilities through LangGraph state machines, implementing three distinct graph architectures for simple chat, multi-step search synthesis, and source-specific conversations.**

Open Notebook leverages LangGraph to manage complex LLM interactions via deterministic state graphs. The repository located at `lfnovo/open-notebook` defines specialized workflow patterns in the `open_notebook/graphs/` directory, each utilizing typed state dictionaries and modular node functions to ensure type-safe data flow between processing steps.

## Architecture Overview: Three Core Graph Patterns

The implementation separates concerns across three specialized graphs that handle different interaction modes:

- **Chat Graph** ([`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py)): Handles pure conversational sessions with optional notebook context
- **Ask Graph** ([`open_notebook/graphs/ask.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/ask.py)): Executes search-plus-synthesis flows with strategic planning and intermediate answer generation
- **Source Chat Graph** ([`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py)): Manages context-rich conversations tied to specific data sources

Each graph follows the LangGraph pattern of **typed state definitions** → **node functions** → **graph wiring**, creating deterministic pipelines for LLM orchestration.

## The Chat Graph: Simple Conversational State Flow

The chat implementation provides a streamlined two-node graph for direct LLM interactions, defined in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) (lines 22-29).

### Defining ThreadState in chat.py

The chat workflow begins with a `ThreadState` definition that maintains a list of messages and optional notebook context fields:

```python

# Structure based on open_notebook/graphs/chat.py

from typing import TypedDict, Optional, List, Any
from langchain_core.messages import BaseMessage

class ThreadState(TypedDict):
    messages: List[BaseMessage]
    notebook_context: Optional[Any]
    # Additional context fields for notebook integration

```

### Node Implementation and Model Invocation

The graph wires a minimal two-node flow that processes user input. The core logic resides in `call_model_with_messages`, which accepts the state and a `RunnableConfig`. According to the source code, this function:

1. Builds system prompts via **ai-prompter**
2. Provisions the correct LLM using `provision_langchain_model`
3. Invokes the model and cleans the response

```python

# Conceptual implementation based on chat.py patterns

def call_model_with_messages(state: ThreadState, config: RunnableConfig):
    # Build system prompt via ai-prompter

    system_prompt = build_system_prompt(state)
    # Provision model via provision_langchain_model

    model = provision_langchain_model(config)
    # Invoke with message history

    response = model.invoke(system_prompt + state["messages"])
    return {"messages": [response]}

```

This pattern keeps the chat graph lightweight while supporting optional context injection from notebooks.

## The Ask Graph: Multi-Step Search and Synthesis

For retrieval-augmented generation, the Ask graph implements a sophisticated three-node state machine defined in [`open_notebook/graphs/ask.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/ask.py) (lines 44-55).

### Rich State Management with Strategy

The Ask graph extends the basic state pattern with a `Strategy` field containing a JSON-structured search plan:

```python

# Based on open_notebook/graphs/ask.py implementation

class ThreadState(TypedDict):
    question: str
    strategy: dict  # JSON-structured search plan

    intermediate_answers: List[str]
    final_answer: str

```

### Three-Node Workflow Architecture

The graph wires three distinct nodes to transform a question into a synthesized answer:

- **`agent`**: Generates the search strategy (JSON plan) based on the input question
- **`provide_answer`**: Executes vector searches and generates intermediate answers
- **`write_final_answer`**: Synthesizes final output from intermediate results

This architecture separates **planning** from **execution** and **synthesis**, enabling complex multi-source retrieval patterns while maintaining state across the `strategy` and `intermediate_answers` fields.

## Source-Specific Conversations with SourceChatState

The [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py) module (lines 23-31) implements specialized handling for source-attached conversations using a distinct `SourceChatState`.

### Context Building Pattern

Unlike the generic chat graph, the source chat workflow constructs context via **ContextBuilder** before LLM invocation:

```python

# Representative structure from source_chat.py

class SourceChatState(TypedDict):
    messages: List[BaseMessage]
    source_insights: dict
    metadata: dict
    formatted_context: str

def build_context(state: SourceChatState):
    # ContextBuilder constructs formatted context

    context = ContextBuilder(state).format()
    return {"formatted_context": context}

```

This pattern ensures the LLM receives pre-processed source metadata and insights rather than raw source documents, following the same two-node execution pattern as the generic chat but with enriched state preparation.

## Common Patterns Across All Graphs

All three implementations share core LangGraph design principles implemented in the Open Notebook codebase:

- **TypedDict States**: Each graph uses strictly typed state definitions (e.g., `ThreadState`, `SourceChatState`) that enumerate exactly what data passes between nodes
- **RunnableConfig**: Node functions accept `RunnableConfig` for runtime configuration, enabling dynamic model selection and parameter passing
- **Single Responsibility**: Each node performs one discrete function—whether calling `provision_langchain_model`, building context via **ContextBuilder**, or synthesizing answers
- **State Immutability**: Nodes return state updates rather than mutating inputs directly, ensuring predictable state transitions

## Summary

Open Notebook's LangGraph implementation provides a modular framework for conversational AI:

- **Chat Graph** ([`chat.py`](https://github.com/lfnovo/open-notebook/blob/main/chat.py)): Lightweight two-node flow for direct conversations using `ThreadState` with optional notebook context
- **Ask Graph** ([`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py)): Three-node search-and-synthesis workflow with explicit strategy generation, intermediate answer storage, and final synthesis via `agent`, `provide_answer`, and `write_final_answer` nodes
- **Source Chat** ([`source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/source_chat.py)): Context-enhanced conversations using `SourceChatState` and `ContextBuilder` to pre-process source insights before LLM invocation
- All graphs utilize `TypedDict` state definitions and the `RunnableConfig` pattern for type-safe, configurable execution across the repository

## Frequently Asked Questions

### What is the difference between the Chat and Ask graphs in Open Notebook?

The **Chat graph** ([`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py)) handles simple conversational exchanges with optional notebook context, using a basic two-node flow that sends messages directly to an LLM provisioned via `provision_langchain_model`. The **Ask graph** ([`open_notebook/graphs/ask.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/ask.py)) implements a complex retrieval workflow that first generates a JSON search strategy, executes vector searches, and synthesizes answers across three distinct nodes: `agent`, `provide_answer`, and `write_final_answer`.

### How does Open Notebook handle LLM provisioning in LangGraph nodes?

According to the source code in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py), nodes call `provision_langchain_model` with the `RunnableConfig` to instantiate the correct model, then build system prompts via **ai-prompter** before invocation. This pattern ensures consistent model configuration and prompt engineering across all graph implementations.

### What data structure does the Ask graph use to track search progress?

The Ask graph uses a `ThreadState` TypedDict containing `question`, `strategy` (JSON plan), `intermediate_answers`, and `final_answer` fields. This structure allows the graph to pass search strategies and partial results between the `agent`, `provide_answer`, and `write_final_answer` nodes while maintaining type safety throughout the execution.

### Can the Source Chat graph access notebook context like the standard Chat graph?

Yes, the Source Chat graph ([`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py)) extends beyond basic context by using **ContextBuilder** to pre-process source-specific insights and metadata into a `formatted_context` field. While the standard Chat graph accepts optional notebook context directly in `ThreadState`, Source Chat constructs a formatted context layer before LLM invocation via the `SourceChatState` definition.