# Open Notebook LangGraph Workflows: Source Chat vs Ask Explained

> Understand the difference between Open Notebooks Source Chat and Ask LangGraph workflows. Explore single source conversation vs multi step vector search research.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: deep-dive
- Published: 2026-06-29

---

**Open Notebook implements two distinct LangGraph state-graph workflows—Source Chat for single-source conversation and Ask for multi-step vector search research—each using different state schemas, node architectures, and context-building strategies.**

Open Notebook leverages LangGraph to orchestrate complex LLM interactions through specialized workflows defined in [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py) and [`open_notebook/graphs/ask.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/ask.py). While both workflows generate AI responses, they serve fundamentally different user intents: conversing with a specific document versus conducting open-ended research across your entire knowledge base.

## Core Purpose and Interaction Models

### Source Chat: Conversational Source Exploration

The **Source Chat** workflow enables users to explore a single source—such as a PDF, webpage, or video—through natural conversation. It builds a comprehensive context from the source content and its derived insights, then injects this into a chat prompt to generate responses that reference specific source material. This workflow is optimized for deep dives into individual documents where maintaining the full context of the source is critical.

### Ask: Multi-Step Research and Synthesis

The **Ask** workflow answers open-ended questions by searching across the entire vector store. Rather than focusing on one document, it executes a **strategy** that may involve multiple vector searches, processes each result set through sub-graphs, and synthesizes findings into a final comprehensive answer. This approach supports complex research queries that require aggregating information from multiple sources.

## State Schemas and Graph Architecture

### SourceChatState vs ThreadState

Each workflow maintains distinct state definitions reflecting their operational requirements:

**SourceChatState** (defined in [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py)) tracks:
- `messages`: Conversation history
- `source_id`: Target document identifier
- `source` and `insights`: The loaded source and its extracted insights
- `context`: Generated full-text context (up to 50,000 tokens)
- `model_override`: Optional alternative model selection
- `context_indicators`: Dictionary tracking which sources and insights were referenced

**ThreadState** (defined in [`open_notebook/graphs/ask.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/ask.py)) tracks:
- `question`: The original user query
- `strategy`: A list of `Search` objects generated by the LLM
- `answers`: Accumulated responses from each search execution
- `final_answer`: The synthesized result

### Node Structure and Topology

The **Source Chat** graph compiles to a linear pipeline: `START → source_chat_agent → END`. The single node `source_chat_agent` calls `call_model_with_source_context` to build context and generate a response.

The **Ask** graph compiles to a three-node structure with conditional branching:
1. **`agent`**: Creates a search strategy via the `strategy_model`
2. **`provide_answer`**: Executes vector searches and generates intermediate answers (may run multiple times via `trigger_queries`)
3. **`write_final_answer`**: Synthesizes all sub-answers using the `final_answer_model`

## Context Building and Retrieval Strategies

### Full-Text Context Assembly

Source Chat relies on **ContextBuilder** (from [`open_notebook/utils/context_builder.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/context_builder.py)) to pull the source record, associated insights, and metadata. The `_format_source_context` method formats this into a rich prompt context that can consume up to 50k tokens, providing the LLM with comprehensive source material via the `source_chat/system` prompt template.

### Vector Search Results

Ask bypasses explicit context building in favor of **dynamic retrieval**. The `provide_answer` node executes `vector_search` queries against SurrealDB for each term in the strategy. The raw search results serve as the context, fed directly to the `ask/query_process` prompt alongside user instructions. This approach prioritizes relevance over completeness.

## Model Provisioning and Execution

### Synchronous vs Asynchronous Provisioning

Source Chat invokes `provision_langchain_model` **synchronously** within a new event loop using `run_in_new_loop`, because the graph operates in a synchronous context. It supports an optional `model_override` parameter for flexible model selection.

Ask uses **asynchronous** provisioning with `await provision_langchain_model` throughout its async graph. It employs three distinct configurable model IDs: `strategy_model` for planning, `answer_model` for processing search results, and `final_answer_model` for synthesis.

### Error Handling Patterns

Both workflows utilize `classify_error` from [`open_notebook/utils/error_classifier.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/error_classifier.py) to normalize exceptions into `OpenNotebookError` subclasses. Source Chat wraps the entire call in a single try/except block, while Ask implements granular error handling within each node (`call_model_with_messages`, `provide_answer`, `write_final_answer`).

## Implementation Examples

### Invoking Source Chat

```python
from open_notebook.graphs.source_chat import source_chat_graph

state = {
    "messages": [],
    "source_id": "src_12345",
    "model_override": None,
}

result = source_chat_graph.invoke(state)
print(result["messages"][-1].content)

```

### Invoking Ask

```python
from open_notebook.graphs.ask import graph

state = {
    "question": "What are the performance trade-offs of LangGraph?",
}

answer = graph.invoke(state)
print(answer["final_answer"])

```

## Summary

- **Source Chat** provides a linear, single-source conversation workflow using `SourceChatState` and full-text context building via `ContextBuilder`.
- **Ask** implements a multi-step research workflow using `ThreadState`, conditional branching via `trigger_queries`, and vector search aggregation.
- **Model provisioning** differs significantly: Source Chat uses synchronous calls with optional overrides, while Ask uses async provisioning with role-specific models (strategy, answer, final).
- **Context strategies** contrast sharply: Source Chat builds comprehensive source context (up to 50k tokens), while Ask relies on dynamic vector search results.
- Both workflows share the `classify_error` utility for consistent error handling but implement it at different granularities.

## Frequently Asked Questions

### What is the primary difference between Source Chat and Ask workflows?

Source Chat is designed for exploring a single document through conversation, building rich context from that specific source and its insights. Ask is designed for research questions that require searching across multiple sources, implementing a strategy-planning phase and aggregating results from multiple vector searches before synthesizing a final answer.

### How does the state management differ between these LangGraph workflows?

Source Chat uses `SourceChatState` which tracks a specific `source_id`, conversation `messages`, and pre-built `context` strings. Ask uses `ThreadState` which tracks a `question`, a generated `strategy` of search terms, accumulated `answers` from each search execution, and a final answer string.

### Can I use different AI models for different stages of the Ask workflow?

Yes. The Ask workflow in [`open_notebook/graphs/ask.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/ask.py) supports three distinct model configurations: `strategy_model` for generating the search plan, `answer_model` for processing individual search results, and `final_answer_model` for synthesizing the comprehensive response.

### Why does Source Chat use synchronous model provisioning while Ask uses async?

Source Chat runs in a synchronous context and therefore calls `provision_langchain_model` within a new event loop using `run_in_new_loop`. The Ask workflow is implemented as an async graph, allowing it to use `await provision_langchain_model` directly, which is more efficient for I/O-bound operations like multiple parallel vector searches.