All tutorials
Track 1.1·Foundation

Your code is the prompt

There is no prompt string to write. The primitives you define, their type signatures, and their docstrings are assembled into the prompt automatically. Changing your code changes what the model sees.

beginner5 min
Video coming soon

Before you start

Most AI frameworks ask you to write a prompt. OpenSymbolicAI does not. You write Python methods. The framework assembles the prompt from them.

This is not an implementation detail. It changes how you think about building agents.

What the model actually receives#

When you call agent.run("what is 7 times 8 minus 3?"), the model receives something like this:

text
## 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."""

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

No instructions written by hand. No role description. No few-shot examples (yet). Just the signatures and docstrings of the three methods you defined, followed by the task.

Where each part came from#

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

The method name multiply, the parameter names a and b, the types float, the return type float, and the docstring "Multiply two numbers." all go into the prompt. The body return a * b does not. The model never sees it.

So when you write a primitive, you are writing a prompt entry. The name, the types, and the docstring are your interface to the model. The body is your implementation.

Changing the code changes the prompt#

  • Rename multiply to times and the model sees times(a: float, b: float).
  • Change a: float to a: int and the model plans with integers.
  • Improve the docstring and the model gets a better description.
  • Add a new primitive and the model can call it. Remove one and it cannot.

There is no separate prompt file to keep in sync. The code and the prompt are the same thing.

What you never write#

You do not write:

  • "You are a calculator agent."
  • "You can call the following functions:"
  • "Always return valid Python."

The framework handles all of that. The only thing you control is the primitives you define and the docstrings you write on them.

What this means in practice#

When a plan comes out wrong, the first place to look is the primitive definition, not a prompt string. Is the name clear? Is the docstring accurate? Are the types right? Those are the levers.

Type annotations are the contract goes deeper on how types shape the plan. Read the planning prompt shows the full prompt string if you want to inspect it directly.