How the Open Notebook Context Builder Constructs Prompts for AI Models
The Open Notebook context builder constructs prompts for AI models by aggregating sources, notes, and insights into prioritized, token-aware context items that are deduplicated, truncated to fit within model limits, and formatted into a dictionary ready for prompt template interpolation.
The context builder is the core utility in the lfnovo/open-notebook repository that transforms raw notebook data into LLM-ready prompt content. Located in open_notebook/utils/context_builder.py, this asynchronous pipeline gathers data from multiple domain models, applies intelligent filtering, and ensures the final output respects token budgets before injection into AI prompts.
How the Context Builder Works
The ContextBuilder class follows a modular, asynchronous workflow designed to handle complex data aggregation while maintaining strict token constraints.
Initializing the Builder
When instantiating ContextBuilder(**kwargs), the __init__ method (lines 65‑98) stores all input parameters—including source_id, notebook_id, include_insights, and max_tokens—in self.params. If no ContextConfig is provided, the builder creates a default configuration with predefined priority weights for different content types.
Orchestrating the Build Process
The await builder.build() method (lines 105‑138) serves as the main orchestration entry point. It first clears any previous context items, then sequentially invokes private helper methods to load data for the requested entities based on the initialization parameters.
Adding Source Context
For individual source retrieval, _add_source_context (lines 142‑202) loads the Source record from open_notebook/domain/source.py and fetches either short or long content snippets via source.get_context. When include_insights is enabled, it also retrieves processed insights through source.get_insights. Each retrieved piece is wrapped in a ContextItem object with a priority value derived from self.context_config.priority_weights.
Adding Notebook Context
When processing entire notebooks, _add_notebook_context (lines 210‑250) loads the Notebook record and iterates over its associated sources and notes. This method respects custom configuration maps in ContextConfig that specify whether to include "insights", "full content", or exclude specific items entirely.
Adding Note Context
The _add_note_context method (lines 254‑288) handles individual note IDs by fetching the Note record from open_notebook/domain/note.py, determining the appropriate content length, wrapping it in a ContextItem, and adding it to the builder's internal collection.
Processing Custom Parameters
The _process_custom_params hook (lines 296‑304) allows subclasses to extend the builder's behavior. By default, it only logs any custom_* kwargs passed during initialization, providing a clean extension point for specialized context processing.
Deduplicating and Prioritizing Content
Before finalization, the builder calls remove_duplicates (lines 351‑363) to eliminate any ContextItem instances sharing the same id, ensuring no source or note appears twice in the context. Then prioritize (lines 315‑319) sorts the remaining items by their priority value in descending order (highest first). The default ContextConfig assigns weights of source:100, insight:75, and note:50.
Enforcing Token Limits
If max_tokens is specified, truncate_to_fit (lines 320‑349) walks the sorted item list from the end (lowest priority) and removes items until the total token count—computed via token_count from open_notebook/utils/token_utils.py—falls within the specified limit. This guarantees the final prompt will not exceed the model's context window.
Formatting the Final Response
The _format_response method (lines 367‑416) groups the processed items by type (sources, notes, insights), calculates the total token count, and returns a structured dictionary. This output format allows downstream LangGraph workflows to directly interpolate the content into Jinja2 prompt templates without additional processing.
Using the Context Builder in Practice
The repository provides three convenience functions that wrap the ContextBuilder class for common use cases (lines 421‑495).
Building Context for a Notebook
To construct context for an entire notebook with token ceiling enforcement:
from open_notebook.utils.context_builder import build_notebook_context
async def get_notebook_prompt(notebook_id: str):
ctx = await build_notebook_context(
notebook_id=notebook_id,
max_tokens=2048,
)
# Returns: {"sources": [...], "notes": [...], "insights": [...], ...}
return ctx
Building Context for Individual Sources
For single-source context generation including AI-generated insights:
from open_notebook.utils.context_builder import build_source_context
async def source_prompt(source_id: str):
ctx = await build_source_context(
source_id=source_id,
include_insights=True,
max_tokens=1500,
)
return ctx
Mixing Sources and Notes
To combine arbitrary sources and notes into a single context:
from open_notebook.utils.context_builder import build_mixed_context
async def mixed_prompt(source_ids, note_ids):
ctx = await build_mixed_context(
source_ids=source_ids,
note_ids=note_ids,
max_tokens=1800,
)
return ctx
Key Implementation Details
The context builder leverages asynchronous I/O throughout the pipeline, utilizing FastAPI's async stack and the SurrealDB driver to efficiently handle large notebooks. Key architectural components include:
- Domain models:
Source(open_notebook/domain/source.py),Notebook(open_notebook/domain/notebook.py), andNote(open_notebook/domain/note.py) provide context retrieval methods - Token management:
open_notebook/utils/token_utils.pyhandles LLM token counting for truncation decisions - Consumer integration: The
search_and_synthesizeLangGraph workflow consumes the builder's output and interpolates it into prompt templates without additional trimming logic
Summary
- The
ContextBuilderclass inopen_notebook/utils/context_builder.pyaggregates data from notebooks, sources, and notes into unified context objects. - Content is wrapped in
ContextIteminstances with configurable priority weights (sources:100, insights:75, notes:50 by default). - Deduplication ensures unique content, while token-aware truncation enforces model limits by removing lowest-priority items first.
- Three convenience functions—
build_notebook_context,build_source_context, andbuild_mixed_context—provide simple async interfaces for common use cases. - The final output is a dictionary formatted for direct insertion into Jinja2 prompt templates, used by downstream AI workflows.
Frequently Asked Questions
How does the context builder handle token limits?
The context builder enforces token limits through the truncate_to_fit method, which removes lowest-priority items until the total token count falls within the specified max_tokens parameter. This ensures the final prompt never exceeds the model's context window while preserving the most important content.
Can I customize which content types are included in the context?
Yes, the ContextConfig class allows customization of content inclusion through sources and notes maps that specify whether to include "insights", "full content", or exclude items entirely. You can also override the default priority weights to change how different content types are ranked during truncation.
Is the context builder compatible with async workflows?
Absolutely. The entire ContextBuilder pipeline is implemented asynchronously using async/await patterns, making it compatible with FastAPI endpoints and LangGraph workflows. All database operations through SurrealDB use async drivers to maintain efficiency when processing large notebooks.
What is the difference between build_notebook_context and build_source_context?
build_notebook_context aggregates all sources and notes belonging to a specific notebook ID, while build_source_context focuses on a single source and optionally includes its AI-generated insights. The former is ideal for comprehensive queries across entire notebooks, while the latter is optimized for deep-dives into specific source materials.
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 →