LangGraph State Machine for Chat and Search in Open Notebook

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:

Each graph follows the LangGraph pattern of typed state definitionsnode functionsgraph 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 (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:


# 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

# 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 (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:


# 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 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:


# 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): Lightweight two-node flow for direct conversations using ThreadState with optional notebook context
  • Ask Graph (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): 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) 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) 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, 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) 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →