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– FetchesSourceobjects viaSource.get, then callssource.get_context()and optionallysource.get_insights()to extract textual content._add_notebook_context– RetrievesNotebookobjects viaNotebook.get, then gathers all linked sources and notes throughnotebook.get_sources()andnotebook.get_notes()._add_note_context– Loads individualNoteobjects viaNote.getand invokesnote.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:
- Query Embedding – User queries are converted into embeddings by the service defined in
api/embedding_service.py. - Vector Search – The embedding is compared against stored vectors in SurrealDB, returning the most similar source IDs and optional note IDs.
- Context Construction – Retrieved IDs are passed to convenience helpers like
build_source_contextorbuild_notebook_context, which instantiateContextBuilder, pull full text, include insights, and enforce token limits. - LLM Integration – The constructed context dictionary is concatenated with the user prompt and sent to the selected LLM through the Esperanto-based AI layer.
- 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 genericContextBuilderclass,ContextConfig,ContextItemdataclass, 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/contextHTTP endpoint that returns ready-made RAG payloads to clients.open_notebook/domain/– HousesSource,Notebook, andNotemodels with methods likeget_context()andget_insights()consumed by the builder.
Summary
- The Context Builder utility standardizes the assembly of knowledge objects into LLM-ready payloads through the
ContextBuilderclass inopen_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_contextandbuild_mixed_contextprovide 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →