How Open Notebook Implements Prompt Templating with ai_prompter and Jinja2 Templates
Open Notebook uses the ai_prompter library to render Jinja2 templates stored in the prompts/ directory, automatically injecting format instructions from LangChain OutputParsers when structured LLM outputs are required.
The prompt templating system in the lfnovo/open-notebook repository provides a flexible, file-based approach to managing LLM interactions. By combining Jinja2 templates with the ai_prompter library's Prompter class, the system separates prompt content from application logic while supporting dynamic variable injection and structured output parsing.
Template Organization in the prompts/ Directory
All Jinja2 templates reside in the prompts/ directory at the repository root. The system organizes templates by workflow, with each functional area (such as ask, chat, source_chat, and podcast) maintaining its own subfolder containing one or more .jinja files.
prompts/ask/entry.jinja– Entry point for the Ask workflowprompts/chat/system.jinja– System prompts for general chat interactionsprompts/CLAUDE.md– Documentation covering prompt conventions and structure
This hierarchical organization allows developers to modify LLM behavior without touching Python code, making the system maintainable for non-technical contributors.
The ai_prompter Prompter Class
The rendering engine centers on the Prompter class from the ai_prompter library, implemented in open_notebook/graphs/prompt.py. This class wraps Jinja2's template engine and provides two instantiation modes:
Named template loading via prompt_template=:
system_prompt = Prompter(prompt_template="ask/entry", parser=parser).render(data=state)
Raw template strings via template_text=:
custom_prompt = Prompter(template_text="Hello {{ name }}").render(data={"name": "User"})
When using the prompt_template parameter, the class automatically resolves the path relative to the prompts/ directory, loading the corresponding .jinja file.
Rendering Prompts with OutputParsers
For workflows requiring structured responses, the Prompter class integrates with LangChain's OutputParser to enforce JSON schema compliance. When a parser is provided, the system automatically injects {{ format_instructions }} into the template context.
In open_notebook/graphs/ask.py (lines 54–57), the implementation demonstrates this pattern:
from langchain.output_parsers import PydanticOutputParser
parser = PydanticOutputParser(pydantic_object=Strategy)
system_prompt = Prompter(prompt_template="ask/entry", parser=parser).render(data=state)
The render() method executes the following steps:
- Loads the specified Jinja2 template from
prompts/ask/entry.jinja - Merges the supplied
statedictionary into the template context - Adds
format_instructionsto the context when a parser is present - Returns the fully rendered prompt string
Integration with LangGraph Workflows
Within Open Notebook's LangGraph architecture, each node constructs its system prompt using the Prompter class before invoking the LLM. The typical flow follows this pattern:
- The graph node prepares a
statedictionary containing conversation history, document context, and user queries Prompter.render(data=state)generates the system prompt- The rendered prompt combines with user messages into a LangChain message payload
- The provisioned model receives the complete context and returns a response
- The graph strips any markdown code fences from the LLM output before processing
This separation ensures that prompt engineering concerns remain isolated from graph orchestration logic, while the Jinja2 inheritance and inclusion features enable complex template composition.
Summary
- Template Storage: All Jinja2 templates live in
prompts/with subdirectories for each workflow (ask,chat, etc.) - Core Class: The
ai_prompter.Prompterclass handles template loading and rendering inopen_notebook/graphs/prompt.py - Structured Output: Passing a LangChain
OutputParserautomatically injects format instructions via the{{ format_instructions }}placeholder - Usage Pattern: Instantiate with
prompt_template="path/to/template"and call.render(data=state)to generate final prompts - Integration: The system operates within LangGraph nodes, connecting template rendering to LLM execution
Frequently Asked Questions
Where are Jinja2 templates stored in Open Notebook?
Templates are stored in the prompts/ directory at the repository root, with subfolders organizing files by workflow (e.g., prompts/ask/entry.jinja, prompts/chat/system.jinja). This structure is documented in prompts/CLAUDE.md.
How does ai_prompter handle structured output formatting?
When a PydanticOutputParser is passed to the Prompter constructor, the render() method automatically adds the parser's format_instructions to the template context. This inserts JSON schema requirements into the prompt without manual template editing.
What is the difference between template_text and prompt_template parameters?
The prompt_template parameter loads a file from the prompts/ directory (e.g., "ask/entry" resolves to prompts/ask/entry.jinja), while template_text accepts a raw Jinja2 string directly for inline or dynamically generated templates.
Which workflows use the prompt templating system?
The system supports multiple workflows including ask (question answering), chat (conversational interface), source_chat (document-specific chat), and podcast (audio content generation), each with dedicated template subdirectories in prompts/.
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 →