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 workflow
  • prompts/chat/system.jinja – System prompts for general chat interactions
  • prompts/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:

  1. Loads the specified Jinja2 template from prompts/ask/entry.jinja
  2. Merges the supplied state dictionary into the template context
  3. Adds format_instructions to the context when a parser is present
  4. 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:

  1. The graph node prepares a state dictionary containing conversation history, document context, and user queries
  2. Prompter.render(data=state) generates the system prompt
  3. The rendered prompt combines with user messages into a LangChain message payload
  4. The provisioned model receives the complete context and returns a response
  5. 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.Prompter class handles template loading and rendering in open_notebook/graphs/prompt.py
  • Structured Output: Passing a LangChain OutputParser automatically 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:

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 →