Open Notebook Context Builder Utility: How RAG Retrieval Works

The Context Builder utility is a flexible orchestration class that assembles relevant sources, notes, and insights into a token-budgeted context payload, enabling retrieval-augmented generation (RAG) by feeding retrieved knowledge into LLM prompts.

Open Notebook implements a sophisticated RAG retrieval pipeline that transforms vector search results into LLM-ready context windows. At the heart of this system lies the Context Builder utility located in open_notebook/utils/context_builder.py, a generic framework that fetches, deduplicates, and formats knowledge entities while enforcing strict token constraints. This article examines how the utility assembles context and powers the end-to-end retrieval workflow.

What Is the Context Builder Utility?

The Context Builder is a generic utility class designed to standardize how knowledge objects are prepared for LLM consumption. It accepts flexible parameters through its constructor, retrieves entities from the domain layer, and produces a structured payload that respects configurable token budgets.

Parameter Parsing and Initialization

The ContextBuilder.__init__ method receives keyword arguments such as source_id, notebook_id, include_insights, and max_tokens. These parameters are stored in self.params, with common values extracted into instance attributes for quick access. Developers can also inject a custom ContextConfig object to tune behavior, and the _process_custom_params hook allows future extensions without modifying core logic.

Data Gathering from Domain Models

Depending on the supplied IDs, the builder executes specific retrieval methods:

  • _add_source_context – Fetches Source objects via Source.get, then calls source.get_context() and optionally source.get_insights() to extract textual content.
  • _add_notebook_context – Retrieves Notebook objects via Notebook.get, then gathers all linked sources and notes through notebook.get_sources() and notebook.get_notes().
  • _add_note_context – Loads individual Note objects via Note.get and invokes note.get_context() for content extraction.

These methods reside in the domain layer under open_notebook/domain/ and return raw content that the builder will package into structured items.

ContextItem Creation and Metadata Calculation

For each fetched entity, the builder instantiates a ContextItem dataclass containing id, type, content, priority, and token_count. The ContextItem.__post_init__ method automatically calculates token counts using token_utils.token_count, ensuring accurate budget tracking. Items are registered via the add_item method, which populates the internal collection for further processing.

Deduplication and Prioritization

Before assembly, the builder calls remove_duplicates to eliminate redundant IDs from the collection. It then executes prioritize, which sorts items by the priority field. Priority values can be tuned via ContextConfig.priority_weights, allowing certain content types (such as insights or specific sources) to rank higher in the final context.

Token Budget Enforcement

When max_tokens is specified, the truncate_to_fit method iteratively drops the lowest-priority items until the cumulative token_count falls within the budget. This ensures the LLM receives the most relevant information without exceeding model context limits.

Response Formatting

Finally, _format_response groups items by type into sources, notes, and insights, returning a dictionary that includes the formatted content, total token counts, item tallies, and the original configuration. This standardized output integrates seamlessly with downstream LLM calls.

How RAG Retrieval Works in Open Notebook

The RAG retrieval pipeline leverages the Context Builder to bridge vector search and LLM generation. The workflow follows five distinct stages:

  1. Query Embedding – User queries are converted into embeddings by the service defined in api/embedding_service.py.
  2. Vector Search – The embedding is compared against stored vectors in SurrealDB, returning the most similar source IDs and optional note IDs.
  3. Context Construction – Retrieved IDs are passed to convenience helpers like build_source_context or build_notebook_context, which instantiate ContextBuilder, pull full text, include insights, and enforce token limits.
  4. LLM Integration – The constructed context dictionary is concatenated with the user prompt and sent to the selected LLM through the Esperanto-based AI layer.
  5. Grounded Response – The LLM generates a response based on the retrieved knowledge, achieving true RAG behavior.

Integration with LangGraph

A concrete implementation of this flow appears in open_notebook/graphs/source_chat.py, where a graph node creates a ContextBuilder instance for a specific source. The node configures the builder with include_insights=True and a max_tokens limit, then forwards the built context to the LLM node for completion.

Context Builder Implementation Examples

Building Context for a Single Source

Use the build_source_context helper to assemble context from one source, including its insights and respecting a token budget:

from open_notebook.utils.context_builder import build_source_context

async def get_source_context(source_id: str):
    # Limit the final context to 1500 tokens

    ctx = await build_source_context(
        source_id=source_id, 
        include_insights=True, 
        max_tokens=1500
    )
    return ctx

Building Mixed Context from Multiple Entities

For queries spanning multiple sources and notes, use build_mixed_context:

from open_notebook.utils.context_builder import build_mixed_context

async def mixed_context(source_ids, note_ids, notebook_id=None):
    ctx = await build_mixed_context(
        source_ids=source_ids,
        note_ids=note_ids,
        notebook_id=notebook_id,
        max_tokens=2000,
    )
    return ctx

Direct Instantiation in LangGraph Nodes

For custom graph implementations, instantiate ContextBuilder directly:

from open_notebook.utils.context_builder import ContextBuilder

async def build_context_for_source(source_id: str):
    builder = ContextBuilder(
        source_id=source_id,
        include_insights=True,
        max_tokens=1200,
    )
    return await builder.build()

All three patterns utilize the same core class, ensuring consistent token counting, deduplication, and formatting across the application.

Key Source Files

The RAG retrieval pipeline and Context Builder utility are implemented across the following files:

  • open_notebook/utils/context_builder.py – Contains the generic ContextBuilder class, ContextConfig, ContextItem dataclass, and helper functions (build_source_context, build_mixed_context).
  • open_notebook/graphs/source_chat.py – Demonstrates graph-node integration, showing how context is built for a source and passed to the LLM.
  • api/embedding_service.py – Generates query embeddings and performs vector similarity search against SurrealDB.
  • api/routers/context.py – Exposes the /context HTTP endpoint that returns ready-made RAG payloads to clients.
  • open_notebook/domain/ – Houses Source, Notebook, and Note models with methods like get_context() and get_insights() consumed by the builder.

Summary

  • The Context Builder utility standardizes the assembly of knowledge objects into LLM-ready payloads through the ContextBuilder class in open_notebook/utils/context_builder.py.
  • It supports token-budget enforcement via truncate_to_fit, ensuring contexts never exceed specified limits by dropping low-priority items first.
  • The utility performs automatic deduplication and prioritization using configurable weights, maximizing the relevance of included content.
  • RAG retrieval integrates vector search (SurrealDB via api/embedding_service.py) with context construction, feeding retrieved IDs into the builder to ground LLM responses.
  • Helper functions like build_source_context and build_mixed_context provide convenient async interfaces for common use cases.

Frequently Asked Questions

How does the Context Builder enforce token limits?

The Context Builder uses the truncate_to_fit method, which calculates the total token count of all assembled ContextItem objects and iteratively removes the lowest-priority items until the sum fits within the max_tokens parameter specified during initialization.

What types of content can the Context Builder assemble?

The utility can process Source objects (with optional insights), Notebook objects (including all linked sources and notes), and individual Note objects. Each type is handled by dedicated private methods (_add_source_context, _add_notebook_context, _add_note_context) that fetch content from the domain models.

How does RAG retrieval integrate with the vector database?

When a user submits a query, the system generates an embedding via api/embedding_service.py and performs a similarity search against vectors stored in SurrealDB. The returned source and note IDs are passed to the Context Builder, which retrieves the full text and formats it for the LLM, completing the retrieval-augmented generation loop.

Can developers customize the Context Builder behavior?

Yes, developers can inject a custom ContextConfig object to adjust priority_weights for different content types, or use the _process_custom_params extension hook to handle additional keyword arguments without modifying the core ContextBuilder class.

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 →