Learn by building

Tutorials

Hands-on tutorials for building reliable AI agents with OpenSymbolicAI. From your first 5-minute agent to production-ready systems. Each track pairs a written walkthrough with a project repo and a video.

0

Set Up Python and uv

beginner

Install Python 3.12+ and the uv package manager. One-time setup before your first OpenSymbolicAI tutorial.

5 min
0.5

Set Up Ollama

beginner

Install Ollama, pull a coding model, and confirm it responds. One-time setup before your first OpenSymbolicAI tutorial.

5 min
1

Hello, OpenSymbolicAI

beginner

The five-minute first win: install the framework, point it at a local model, write a three-primitive agent, and watch it plan and execute.

5 min
1.1

Your code is the prompt

beginner

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.

5 min
1.2

Prompts inside primitives

beginner

The planning call writes orchestration code. Small, focused model calls inside individual primitives handle judgment: classification, extraction, relevance scoring, rephrasing. Each gets only the data it needs.

8 min
2

Swap the local model

beginner

Run Track 1's agent on a different Ollama model by changing one string. The agent, primitives, and task stay identical.

3 min
3

Swap to a cloud provider

beginner

Run the same agent on a hosted provider. Provider, model, and API key live in .env. Moving between providers is a config change, not a code change.

5 min
4

What @primitive actually does

beginner

The gate that makes a method callable by the planner. A plain method is invisible; @primitive registers it.

5 min
5

read_only

beginner

The flag that signals whether a primitive modifies state. Put a read-only primitive next to a mutating one to see the difference.

5 min
6

deterministic

beginner

The flag that signals whether a primitive is a pure function. Put a pure primitive next to one that reads the clock to see the difference.

5 min
7

Type annotations are the contract

beginner

How parameter and return types reach the model. The planner never sees your method bodies, only the signatures your annotations produce.

5 min
8

Read the generated plan

beginner

The Python the model wrote is in result.plan. Print it to see exactly what the model produced before your primitives ran it.

5 min
9

Read the execution trace

beginner

The plan after it ran, step by step, in result.trace. Each step records the statement, the value it produced, and the namespace before and after.

10 min
10

Read the metrics

beginner

What a run cost in time and tokens, in result.metrics. Planning is slow and uses tokens. Executing is fast and uses none.

5 min
11

Plan without executing

intermediate

Generate a plan with agent.plan, review it, then run it with agent.execute. The model's output is just text until you choose to run it.

5 min
12

Execute a plan you already have

intermediate

Pass plan text to agent.execute, validation and all. The plan can come from the model, a file, or your own hand. Execution validates before anything runs.

5 min
13

Analyze a plan's structure

intermediate

Read the primitive calls and read_only flags with agent.analyze_plan. Find out which mutating primitives a plan would touch before you run it.

10 min
14

Your first decomposition

intermediate

Teach the planner with a worked example via @decomposition. The decorator tags a method body with the natural-language intent it answers.

10 min
15

expanded_intent

intermediate

Describe a decomposition's approach, not just its intent. The expanded_intent renders as an Approach line in the prompt so the planner reads the reasoning before the steps.

10 min
16

Read the planning prompt

intermediate

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).

8 min
17

When you need DesignExecute

intermediate

PlanExecute only allows assignment statements. When a task needs a loop, switch the base class to DesignExecute. Everything else stays the same.

8 min
18

The loop guard and max_loop_iterations

intermediate

Every loop in a DesignExecute plan has a built-in iteration counter. DesignExecuteConfig(max_loop_iterations=N) sets the cap. Trip it to see the error; raise it to let the task finish.

8 min
19

max_total_primitive_calls and allow_break_continue

intermediate

The two remaining DesignExecuteConfig knobs: a whole-plan call cap and a toggle for break/continue in loops.

8 min
20

Conditional logic end to end

intermediate

No new API. A shopping cart with a tiered discount shows DesignExecute, an if/elif/else in a real plan, and reading the trace to see which branch fired.

10 min
21

Your first @evaluator and seek()

intermediate

GoalSeeking runs a plan-execute-evaluate loop. Mark one method @evaluator, return GoalEvaluation, and call seek() instead of run(). The loop continues until the evaluator says the goal is met.

10 min
22

Intermediate data lives in Python, not the prompt

intermediate

When one primitive returns a list and another consumes it, the data travels as a Python variable. The model never sees it. This holds whether the list has 100 entries or 100,000.

8 min
23

Fetched data stays in Python, not the prompt

intermediate

The agent downloads full Wikipedia articles and analyses them as Python variables. The text never re-enters the model context, whether the article is 30,000 characters or 80,000.

10 min
24

A primitive that takes and returns a Pydantic model

intermediate

Define a BaseModel, use it as a primitive param or return type, and it appears automatically under Type Definitions in the plan prompt. The plan reads fields and passes the whole object to the next primitive.

8 min
25

Nested models and list[Model] as primitive types

intermediate

A model that holds another model. Both appear under Type Definitions automatically. Plans read nested fields with dot notation and work with list[Recipe] from a search primitive.

8 min
26

The on_mutation policy hook

intermediate

A function that runs before every non-read-only primitive. Return None to allow the call, return a string to block it. Read-only primitives never trigger it.

8 min
27

Human-in-the-loop mutation approval

intermediate

execute_stepwise() pauses before every read_only=False primitive and yields a checkpoint. Inspect the pending call, ask the user, then resume or abandon. The primitive only runs if approved.

10 min
28

Token accounting with result.metrics

intermediate

Every agent.run() returns result.metrics.plan_tokens with input_tokens, output_tokens, and total_tokens. Input is nearly fixed; output grows with plan complexity.

6 min
29

Stopping a batch when the token budget runs out

intermediate

BudgetedRunner wraps any agent and tracks cumulative token usage. Before each task it checks whether enough tokens remain. If not, it raises BudgetExceeded and the batch stops.

6 min
30

max_iterations and the no-progress circuit breaker

intermediate

GoalSeekingConfig(max_iterations=N) stops an agent that hasn't converged. result.status is ACHIEVED or MAX_ITERATIONS. result.iteration_count tells you how many iterations ran.

7 min
31

Decomposition coverage: routing by question shape

intermediate

A decomposition is a few-shot example. The planner matches on question shape, not on the specific values in the intent string. Two examples cover two shapes; queries outside both fall back to docstrings.

10 min
32

Multi-turn conversations with multi_turn=True

intermediate

Set multi_turn=True and call agent.run() multiple times on the same agent. State held in instance variables persists across turns. The model receives the conversation history on each turn.

8 min
33

Constraint satisfaction with Z3

intermediate

Wrap Z3 as three primitives and let the LLM translate word problems into integer constraints. Z3 finds the assignment; the LLM never solves an equation.

8 min
34

Symbolic calculus with SymPy

intermediate

Wrap SymPy as three primitives and let the LLM pick the right operation. SymPy returns exact symbolic answers, not floating-point approximations.

7 min
35

Two agents, one problem

intermediate

Split a multi-domain problem across two specialist agents. A master agent holds both specialists as primitives and routes each part of the task to the right one.

9 min
36

Mortgage eligibility with Z3

intermediate

Encode four lending rules as Z3 constraints. SAT means approved. UNSAT names every rule the applicant failed. Z3 Optimize finds the minimum income needed to qualify.

9 min
37

Prompt injection defence

intermediate

Run the same task through a tool-calling agent and a PlanExecute agent against an injected document. The tool-calling agent follows the injection. PlanExecute does not, because the plan is fixed before any document is opened.

10 min
38

Structured memory across sessions

intermediate

Store and recall typed facts in a JSON file. Each new agent instance reads the same file, so what the user says in session 1 is available in session 2.

9 min
39

Unstructured memory across sessions

intermediate

Append free-text session notes to a plain text file. Each new instance reads the full diary as context. No schema, no keys, just text.

8 min
40

Voice date agent

intermediate

Speak a calendar question, hear the answer spoken back. Whisper transcribes your voice, a GoalSeeking agent computes the date, and macOS say reads the result aloud.

10 min
41

Custom LLM with a response cache

intermediate

Subclass LLM to connect to any provider. Add a cache so repeated prompts are served from memory instead of making another network call.

8 min
42

Two models, one agent

intermediate

Use a vision model and a text model together in a single agent. The VLM describes the image; the text model reasons about what it saw.

8 min
43

Parallel document research

intermediate

A ResearchAgent decomposes a multi-part question into per-document sub-tasks and runs a DocumentAgent for each in parallel threads. Results are synthesized into a single answer.

10 min
44

Per-user data isolation

intermediate

Bind each agent instance to one user's directory at construction time. Path traversal attacks in the LLM's plan are silently neutralized at the primitive level.

9 min
45

Coding agent

intermediate

An agent reads a Python file, rewrites it per an instruction, saves it in place, and runs it to confirm the output is unchanged.

9 min
46

Natural language to SQL

intermediate

An agent translates a plain-English question into a SQL query and runs it against a real database. The full schema is injected into the task so the agent writes correct SQL in one shot.

8 min
47

CSV analyst

intermediate

An agent answers plain-English questions about a CSV file using real pandas operations. Column names and sample rows are injected into the task so the agent writes correct code in one shot.

8 min
48

Chat frontend over a SQL database

intermediate

A FastAPI backend serves a browser chat UI. Questions become SQL via an NL-to-SQL agent, follow-ups are rephrased using conversation history, and the generated SQL is shown alongside the answer.

10 min