# This page will use the following imports:
from lasagna import Model, EventCallback, AgentRun
from lasagna import (
recursive_extract_messages,
override_system_prompt,
flat_messages,
)from lasagna import known_models
from lasagna.tui import tui_input_loop
import os
import sympy as sp # type: ignore
from dotenv import load_dotenv
Tool Use
Give your agents superpowers! 💪
Tools let your agents interact with the outside world.
In Lasagna AI, tools are simply Python callables (i.e. functions or callable objects) that you pass to the AI model. The framework handles the complex orchestration of:
- communicating tools’ details to the AI model,
- invoking tools safely (when the AI model asks for it), and
- sending the tools’ results back to the AI model.
We need to set up our “binder” (see the quickstart guide for what this is).
load_dotenv()
if os.environ.get('OPENAI_API_KEY'):
print('Using OpenAI')
= known_models.openai_gpt_5_mini_binder
binder
elif os.environ.get('ANTHROPIC_API_KEY'):
print('Using Anthropic')
= known_models.anthropic_claude_sonnet_4_binder
binder
else:
assert False, "Neither OPENAI_API_KEY nor ANTHROPIC_API_KEY is set! We need at least one to do this demo."
Using OpenAI
Create Your First Tool
Let’s make a tool! Remember, tools are just Python callables.
AI models (at the time of writing) are bad at math. Here’s a simple math tool to give our AI the ability to evaluate complex math expressions accurately.
def evaluate_math_expression(expression: str) -> float:
"""
This tool evaluates a math expression and returns the result.
Pass math expression as a string, for example:
- "3 * 6 + 1"
- "cos(2 * pi / 3) + log(8)"
- "(4.5/2) + (6.3/1.2)"
- ... etc
:param: expression: str: the math expression to evaluate
"""
= sp.sympify(expression)
expr = float(expr.evalf())
result return result
The format of the docstring is important! We’ll cover this in a later section!
Pass the Tool to the AI Model
It is your agent’s job to decide which tools the AI has access to. When your agent does model.run(...)
, it passes zero or more tools to the AI model. Here’s a quick demo!
async def math_agent(
model: Model,
event_callback: EventCallback,list[AgentRun],
prev_runs: -> AgentRun:
) = recursive_extract_messages(prev_runs, from_tools=False, from_extraction=False)
messages = override_system_prompt(messages, 'You are a math assistant. Your name is Bob. Answer all prompts briefly.')
messages
= await model.run(
new_messages
event_callback,
messages,=[
tools# <-- 🔨 the tool is passed here!
evaluate_math_expression,
],
)
return flat_messages('math_agent', new_messages)
await tui_input_loop(binder(math_agent)) # type: ignore[top-level-await]
> Hi!
Hi — I'm Bob. How can I help with math today?
> Who are you?
I'm Bob, an AI math assistant. I can help solve problems, explain concepts, check work, and show steps. What math do you need help with?
> What is pi to the pi?
evaluate_math_expression({"expression":"pi**pi"}) -> 36.46215960720791 π^π = π^π ≈ 36.46215960720791.
> exit
In the tui_input_loop(...)
output above, notice:
- Green is your input.
- Black is the AI output.
- Red is a tool invocation.
- Blue is a tool result.
Tool Features
Definition Flexibilities
Tools can be either functions or callable objects (classes with __call__
method).
Also, tools can either be sync or async. Lasagna is natively async, so there’s a preference for async tools; but, if you pass a synchronous tool then Lasagna will run it in a thread pool (no worries).
Docstrings
Your tool’s docstring is critically important. It is used to:
- Describe to the AI what the tool does and when to use it.
- Define the tool’s input parameters. Each parameter has a name, type, and description!
It is formatted like this (for a tool with n
input parameters):
"""
{tool_description}
:param: {param_1_name}: {param_1_type}: {param_1_description}
:param: {param_2_name}: {param_2_type}: {param_2_description}
...
:param: {param_n_name}: {param_n_type}: {param_n_description}
"""
You should spend LOTS of time and energy writing your tool’s docstring. The AI depends on you describing exactly how your tool works, when to use it, and what the parameters represent.
You’ll likely want to iterate and test different tool docstrings to see which perform best.
The following types are supported as parameters of tools:
str
float
int
bool
enum {A} {B} ... {Z}
(i.e. enum types list the enum string values as a space-separated list following the word “enum”)
Parameters can be optional by putting the string “(optional)” at the start of the parameter’s description in the docstring.
Lasagna will check that your tool’s docstring’s parameter definitions match the callable’s actual parameters (names and types). It will also ensure that all “(optional)” parameters have default values in the callable’s signature.
If any docstring-to-signature mismatch is found, you’ll get a runtime exception when you first attempt to pass the tool to a model.
Parallel Execution
If the AI model asks for more than one tool call, then Lasagna will call those tools in parallel! This provides a speed boost, but keep this in mind so that you manage state correctly (i.e. no race conditions). If you are a well-behaved functional-style programmer who never modifies state, you’ll be fine.
Tool Recipes
Many of the recipes show examples of tool use. See:
Layered Agents
Layered agents are a founding idea behind the Lasagna AI library. With Lasagna, we can call agents like we call functions in procedural programming. Consider good-ol’ functions:
- You define a function.
- You can invoke it.
- Other functions can also invoke it.
- It can invoke other functions.
- Each function has its own well-defined input/output and behavior.
In Lasagna, agents are the same!
- You define an agent (as a function or callable object).
- You can invoke it.
- Other agents can also invoke it.
- It can invoke other agents.
- Each agent has its own well-defined input/output and behavior.
Just like you compose a program by layering functions from low-level to high-level, you do the same with Lasagna and AI Agents!
🎉🎉🎉 This is why it’s called Lasagna! Because it has layers! 🤓🤓🤓
Agents as Tools
A similar founding idea was that you should be able to layer agents by passing agents as tools to other agents. So, you can!
See the Agents as Tools recipe for a working example.
Agent Routing
Creating an agent whose purpose is to route to one (or many) downstream agents is also a key concept for any “layered” system.
See the Agent Routing recipe for a working example.
Next Steps
Now that you understand tools, you can explore:
- Structured Output: How your agents can extract structured data
- Layered (multi-agent) Systems: Methods for layering agents in Lasagna AI