Chat, source_chat, and ask Graphs in LangGraph: Open Notebook's Three Conversation Patterns
Open Notebook implements three distinct LangGraph graphs—chat, source_chat, and ask—that serve different conversational purposes ranging from simple notebook chat to research-driven Q&A with vector search.
Open Notebook leverages LangGraph to orchestrate LLM interactions through specialized state machines. Each graph targets a specific interaction pattern: generic conversation, source-grounded dialogue, or multi-step research synthesis.
The Three Graphs at a Glance
Open Notebook defines three distinct LangGraph graphs that drive the conversational back-ends:
| Graph | Primary Purpose | State Structure | When It Is Used |
|---|---|---|---|
| chat | Simple one-turn chat with a notebook | ThreadState – holds messages, optional Notebook, raw context |
Generic chat interactions without source-specific context |
| source_chat | Source-aware conversation | SourceChatState – contains source_id, SourceInsights, built context |
When user refers to a specific source (e.g., "Explain Source 123") |
| ask | Multi-step research workflow | ThreadState with Strategy, intermediate answers |
Research questions requiring vector search and synthesis |
The Chat Graph: Simple Conversational State
The chat graph handles basic conversational interactions with minimal overhead.
State Structure and Nodes
In open_notebook/graphs/chat.py, the graph uses ThreadState to persist:
- A message list
- An optional
Notebookobject - Optional raw
contextandcontext_config - An optional
model_override
The primary node is call_model_with_messages, which:
- Builds a system prompt from the
chat/systemJinja template - Appends user messages
- Provisions a LangChain chat model via
provision_langchain_model - Returns the cleaned AI reply
No additional data fetching occurs—sources and insights are not retrieved unless explicitly provided in the initial state.
When to Use Chat
Use this graph for generic chat interactions where the user wants a conversational reply or wishes to continue a notebook-wide thread without needing source-specific context.
The Source Chat Graph: Context-Aware Source Interactions
The source_chat graph enables conversations grounded in specific documents, PDFs, or notebook entries.
State Structure and Context Building
Defined in open_notebook/graphs/source_chat.py, SourceChatState contains:
- The message list
- Target
source_idand optionalSourceobject - A list of
SourceInsights - The built
contextstring context_indicatorsmapping usage
The graph leverages ContextBuilder (from open_notebook/utils/context_builder.py) to construct rich source-specific context before invoking the model.
Execution Flow
The main node call_model_with_source_context:
- Builds source-specific context using
ContextBuilder(including insights, notes, and metadata) - Renders the context into a system prompt via the
source_chat/systemtemplate - Provisions a chat-type model (max 8192 tokens)
- Enriches state with
source,insights, and formatted context indicators
When to Use Source Chat
Use source_chat when the user explicitly refers to a particular source (e.g., "Explain the findings from Source 123") and the system should ground the reply in that source's content and derived insights.
The Ask Graph: Multi-Step Research Workflows
The ask graph implements a complex, multi-step "question-answer" workflow involving strategy generation, parallel vector searches, and answer synthesis.
State Structure
The graph uses two state types:
ThreadState: Stores the original question, aStrategy(JSON-encoded), intermediate answers, and final answerSubGraphState: Used by theprovide_answersub-graph for search terms, instructions, raw results, and retrieved IDs
Workflow Nodes
- Strategy Generation:
call_model_with_messagesprompts the model withask/entryto produce a JSON-encodedStrategycontaining search queries - Parallel Execution:
trigger_queriescreates Send edges to theprovide_answersub-graph for each search in the strategy - Answer Synthesis:
provide_answerruns vector search (vector_search), then feeds results to a second LLM using theask/query_processtemplate - Final Assembly:
write_final_answerstitches intermediate answers using theask/final_answertemplate
Model Specialization
Unlike chat and source_chat, the ask graph provisions three different models:
strategy_modelfor query generationanswer_modelfor processing search resultsfinal_answer_modelfor synthesis
When to Use Ask
Use ask for "research" questions where the system must retrieve relevant documents, synthesize information, and present a concise answer.
Key Architectural Differences
State Definitions
Each graph declares a TypedDict that tells LangGraph which fields persist between steps. The chat graph's state is minimal, while ask and source_chat carry richer, domain-specific data structures.
Prompt Templates
chat/system(inchat.py): Generic conversational promptask/entry,ask/query_process,ask/final_answer: Chain of prompts for research workflowssource_chat/system: Template receiving fully-built source context fromContextBuilder
Execution Model
Chat and Source Chat: Executed from sync entry points; they spin up temporary event loops to run the async provisioner.
Ask: Fully async because it must await vector search results and multiple LLM calls across sub-graphs.
Context Building
Only source_chat builds complex context via ContextBuilder. The ask graph builds context dynamically through vector_search operations, retrieving relevant chunks across the corpus rather than focusing on a single pre-identified source.
Implementation Examples
Simple Chat Invocation
# From api/chat_service.py
from open_notebook.graphs.chat import graph as chat_graph
async def chat_endpoint(state: dict, model_id: str):
# State conforms to ThreadState (messages, notebook, …)
result = await chat_graph.ainvoke(
state,
config={"configurable": {"model_id": model_id}}
)
return result["messages"] # AIMessage for client
Research Ask Workflow
# From api/ask_service.py
from open_notebook.graphs.ask import graph as ask_graph
async def ask_endpoint(question: str):
initial_state = {"question": question}
# Orchestrates strategy, vector searches, and final answer
result = await ask_graph.ainvoke(initial_state)
return result["final_answer"]
Source-Aware Chat
# From api/source_chat_service.py
from open_notebook.graphs.source_chat import source_chat_graph
async def source_chat_endpoint(source_id: str, messages: List[dict]):
state = {
"source_id": source_id,
"messages": messages,
}
result = await source_chat_graph.ainvoke(state)
return result["messages"] # Reply enriched with source context
Summary
- chat: Use for quick conversational replies with minimal state; relies on
ThreadStateand generic system prompts - source_chat: Use when grounding responses in specific sources; leverages
ContextBuilderandSourceChatStateto include insights and metadata - ask: Use for research workflows requiring vector search; implements a multi-step strategy with parallel sub-graph execution and specialized model provisioning
Frequently Asked Questions
What is the difference between ThreadState and SourceChatState?
ThreadState serves as the base state for general conversation in open_notebook/graphs/chat.py, holding messages and optional notebook context. SourceChatState extends this pattern to include source-specific fields like source_id, SourceInsights, and context_indicators, enabling the graph to track which source materials informed the response.
Can I use the ask graph for simple chat queries?
While technically possible, the ask graph introduces unnecessary overhead for simple queries. It provisions three separate models and executes parallel vector searches through sub-graphs. For straightforward conversation, use the chat graph to minimize latency and token consumption.
How does source_chat build context differently from ask?
The source_chat graph uses ContextBuilder (in open_notebook/utils/context_builder.py) to construct a rich textual representation of the source, including extracted insights and notes. The ask graph instead builds context dynamically through vector_search operations, retrieving relevant chunks across the corpus rather than focusing on a single pre-identified source.
Are all three graphs async by default?
Only the ask graph is fully async from the entry point. Both chat and source_chat are designed to run from synchronous contexts, spinning up temporary event loops internally to handle async model provisioning via provision_langchain_model.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →