Difference Between Chat, Ask, and Source_Chat LangGraph Graphs in Open Notebook
The chat, ask, and source_chat graphs in Open Notebook represent three distinct conversational architectures: simple one-turn chat, multi-step research workflows with vector search, and source-aware contextualized responses.
Open Notebook utilizes LangGraph to power its conversational AI features, implementing three specialized graphs that handle different interaction patterns. Each graph serves a unique purpose within the lfnovo/open-notebook repository, from simple message exchanges to complex research workflows that retrieve and synthesize information. Understanding the difference between these LangGraph implementations helps developers select the appropriate backend for specific user interactions.
The Chat Graph: Simple Conversational Flow
The chat graph handles basic one-turn conversational interactions with or without a notebook context. Located in open_notebook/graphs/chat.py, this graph provides the most straightforward entry point for generic chat functionality.
State Structure and Implementation
The graph operates on ThreadState, a TypedDict that stores the message list, an optional Notebook object, raw context, context_config, and a potential model_override. Unlike the other graphs, chat does not fetch additional data sources or insights.
The primary node call_model_with_messages builds a system prompt using the chat/system Jinja template, appends user messages, and provisions a LangChain chat model via provision_langchain_model. The implementation returns a cleaned AI reply without additional context retrieval.
# open_notebook/graphs/chat.py (simplified)
from open_notebook.graphs.chat import graph as chat_graph
async def chat_endpoint(state: dict, model_id: str):
# State conforms to ThreadState
result = await chat_graph.ainvoke(
state,
config={"configurable": {"model_id": model_id}}
)
return result["messages"] # AIMessage ready for client
The Ask Graph: Multi-Step Research Workflows
The ask graph implements a sophisticated "question-answer" workflow designed for research tasks. Defined in open_notebook/graphs/ask.py, this graph orchestrates strategy generation, vector searches, and answer synthesis through multiple coordinated steps.
Strategy Generation and State Management
The graph uses ThreadState to store the original question, a generated Strategy object, intermediate answers, and the final response. It also utilizes SubGraphState within the provide_answer sub-graph to manage search terms, instructions, raw results, and retrieved IDs.
The workflow begins with call_model_with_messages prompting the model using the ask/entry template to produce a JSON-encoded Strategy. The trigger_queries node then creates Send edges to the provide_answer sub-graph for each search defined in the strategy.
Vector Search and Answer Synthesis
Within the sub-graph, provide_answer executes vector searches using vector_search from open_notebook/domain/notebook.py, then processes results using a second LLM call with the ask/query_process template. Finally, write_final_answer stitches all intermediate responses into a cohesive final answer using the ask/final_answer template.
Notably, the ask graph provisions three distinct models (strategy_model, answer_model, final_answer_model) allowing each step to utilize specialized LLM configurations.
# open_notebook/graphs/ask.py (simplified)
from open_notebook.graphs.ask import graph as ask_graph
async def ask_endpoint(question: str):
initial_state = {"question": question}
# Orchestrates strategy, searches, and final answer
result = await ask_graph.ainvoke(initial_state)
return result["final_answer"]
The Source_Chat Graph: Source-Aware Contextual Responses
The source_chat graph enables conversations grounded in specific sources such as PDFs, web pages, or notebook entries. Implemented in open_notebook/graphs/source_chat.py, this graph enriches responses with source-specific insights and metadata.
Context Building and State Structure
Operating on SourceChatState, the graph maintains the message list, target source_id, optional Source object, SourceInsight list, built context string, and context_indicators map. The state structure explicitly tracks which pieces of context were utilized during generation.
The call_model_with_source_context node leverages ContextBuilder from open_notebook/utils/context_builder.py to construct rich source-specific context. This context includes insights, optional notes, and metadata, which is then rendered into a system prompt via the source_chat/system template.
Execution Flow
After provisioning a chat-type model (max 8192 tokens) through provision_langchain_model, the graph invokes the LLM with the enriched context and returns the cleaned reply. The state is subsequently enriched with source, insights, formatted context, and usage indicators.
# open_notebook/graphs/source_chat.py (simplified)
from open_notebook.graphs.source_chat import source_chat_graph
async def source_chat_endpoint(source_id: str, messages: list):
state = {
"source_id": source_id,
"messages": messages,
}
result = await source_chat_graph.ainvoke(state)
return result["messages"] # Reply with source context
Key Architectural Differences
Understanding the distinction between these three LangGraph implementations requires examining their execution models, state complexity, and context handling:
State Complexity
- Chat: Uses minimal
ThreadStatewith basic message and notebook references - Ask: Employs
ThreadStateplusSubGraphStatefor complex multi-step research workflows - Source_Chat: Utilizes
SourceChatStatewith rich domain-specific fields for source tracking
Context Retrieval
- Chat: No additional context retrieval beyond optional notebook objects
- Ask: Dynamically builds search context through vector searches during execution
- Source_Chat: Pre-builds comprehensive context via
ContextBuilderbefore model invocation
Execution Model
- Chat and Source_Chat: Execute from synchronous entry points, spinning up temporary event loops for async provisioning
- Ask: Fully async to accommodate
awaitoperations for vector searches and multiple LLM calls
Model Provisioning
All three graphs use provision_langchain_model from open_notebook/ai/provision.py, but ask uniquely provisions three separate model instances for different workflow stages, while chat and source_chat use single chat-type models.
Summary
- Chat Graph (
open_notebook/graphs/chat.py): Provides simple one-turn conversational responses using minimal state and no external data retrieval - Ask Graph (
open_notebook/graphs/ask.py): Implements multi-step research workflows with strategy generation, vector searches, and synthesized answers usingThreadStateandSubGraphState - Source_Chat Graph (
open_notebook/graphs/source_chat.py): Delivers source-aware responses by building rich context throughContextBuilderand tracking insight utilization viaSourceChatState
Frequently Asked Questions
When should I use the ask graph versus the chat graph?
Use the ask graph when you need to perform research across multiple sources and synthesize information into a comprehensive answer. The ask graph generates search strategies, executes vector searches, and processes results through multiple LLM calls. Use the chat graph for simple conversational exchanges where you only need a direct response to user messages without retrieving external documents or sources.
What makes source_chat different from standard chat implementations?
The source_chat graph differs from standard chat by accepting a specific source_id and building enriched context using ContextBuilder before model invocation. While the chat graph operates on generic ThreadState with minimal context, source_chat uses SourceChatState to track SourceInsight objects, context indicators, and metadata from a specific source. This ensures responses are grounded in the particular document or entry referenced by the user.
How does the ask graph handle multiple search queries?
The ask graph processes multiple queries through LangGraph's Send edges. After the trigger_queries node generates a strategy, it creates parallel execution paths to the provide_answer sub-graph for each search term. Each sub-graph execution performs its own vector search and LLM processing independently, with results aggregated in the parent state's intermediate answers list before write_final_answer synthesizes the final response.
Can I customize the model used for each graph?
Yes, all three graphs support model customization through the provision_langchain_model function in open_notebook/ai/provision.py. The chat and source_chat graphs accept a model_override parameter in their state, while the ask graph explicitly provisions three different models (strategy_model, answer_model, final_answer_model) allowing you to assign different LLM configurations to strategy generation, individual answer synthesis, and final answer composition respectively.
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 →