All tutorials
Track 16·Blueprints

Read the planning prompt

Call build_plan_prompt(task) on any agent to see the exact string the model receives. Three sections appear: DEFINITIONS (primitives and examples), CONTEXT (the task), and INSTRUCTIONS (fixed output rules).

intermediate8 min
Video coming soon
Browse this tutorial's folder in tutorials-pygithub.com/OpenSymbolicAI/tutorials-py/tree/main/16-read-the-prompt

Before you start

Track 15 added expanded_intent to a decomposition to make the planner's reasoning explicit. This tutorial shows where that reasoning actually lands: inside the full prompt string the model receives. Call build_plan_prompt(task) and the string comes back without sending anything to the model.

1. The agent#

The same Calculator from Track 1, plus one decomposition:

python
# calculator.py
from opensymbolicai.blueprints import PlanExecute
from opensymbolicai.core import decomposition, primitive


class Calculator(PlanExecute):
    """A tiny calculator agent."""

    @primitive(read_only=True)
    def add(self, a: float, b: float) -> float:
        """Add two numbers."""
        return a + b

    @primitive(read_only=True)
    def multiply(self, a: float, b: float) -> float:
        """Multiply two numbers."""
        return a * b

    @primitive(read_only=True)
    def subtract(self, a: float, b: float) -> float:
        """Subtract b from a."""
        return a - b

    @decomposition(intent="what is 2 plus 3?")
    def _example_add(self) -> float:
        result = self.add(2, 3)
        return result

2. Print the prompt#

The section markers in the raw string are machine-readable tokens. Import them and replace them with readable labels before printing:

python
# main.py
from calculator import Calculator
from opensymbolicai.llm import LLMConfig
from opensymbolicai.models import (
    PROMPT_CONTEXT_BEGIN, PROMPT_CONTEXT_END,
    PROMPT_DEFINITIONS_BEGIN, PROMPT_DEFINITIONS_END,
    PROMPT_INSTRUCTIONS_BEGIN, PROMPT_INSTRUCTIONS_END,
)

LABELS = {
    PROMPT_DEFINITIONS_BEGIN: "# --- DEFINITIONS START ---",
    PROMPT_DEFINITIONS_END:   "# --- DEFINITIONS END ---",
    PROMPT_CONTEXT_BEGIN:     "# --- CONTEXT START ---",
    PROMPT_CONTEXT_END:       "# --- CONTEXT END ---",
    PROMPT_INSTRUCTIONS_BEGIN:"# --- INSTRUCTIONS START ---",
    PROMPT_INSTRUCTIONS_END:  "# --- INSTRUCTIONS END ---",
}

def annotate(prompt: str) -> str:
    for marker, label in LABELS.items():
        prompt = prompt.replace(marker, label)
    return prompt

llm = LLMConfig(provider="ollama", model="qwen2.5-coder:7b")
agent = Calculator(llm=llm)

prompt = agent.build_plan_prompt("what is 7 times 8 minus 3?")
print(annotate(prompt))

build_plan_prompt never calls the model, so Ollama does not need to be running.

bash
uv run main.py

3. What you see#

text
# --- DEFINITIONS START ---

## Available Primitive Methods

add(a: float, b: float) -> float
    """Add two numbers."""
multiply(a: float, b: float) -> float
    """Multiply two numbers."""
subtract(a: float, b: float) -> float
    """Subtract b from a."""

## Example Decompositions

### Example 1
Intent: what is 2 plus 3?
Python:
result = add(2, 3)
return result

# --- DEFINITIONS END ---

# --- CONTEXT START ---

Generate Python code to accomplish this task: what is 7 times 8 minus 3?

# --- CONTEXT END ---

# --- INSTRUCTIONS START ---

1. Output Python assignment statements, then a final `return` statement
2. You can ONLY call the primitive methods listed above
3. Do NOT use imports, loops, conditionals, or function definitions
...

# --- INSTRUCTIONS END ---

4. What lands where#

  • DEFINITIONS: everything the model is allowed to call. @primitive methods appear under "Available Primitive Methods" with their signatures and docstrings. @decomposition methods appear under "Example Decompositions" with self. stripped from the body. This section changes when you add or remove primitives or decompositions.
  • CONTEXT: the task string you passed to build_plan_prompt. This is the only part that varies per call.
  • INSTRUCTIONS: fixed output rules. The model must produce assignment statements ending with return. This section never changes.

5. Try it: add a primitive#

Add a divide primitive to Calculator, run again, and watch DEFINITIONS grow by one entry. CONTEXT and INSTRUCTIONS stay identical.