Thursday we set up the OpenAI project and its helpers. Today we gave the agent the two things that make it useful: tools to act with, and memory to remember with.
1. Check the Pipe First
Before adding anything, confirm the key and the connection work with the smallest possible agent and a question with a known answer, such as the capital of France.
One change at a time. If the basic agent fails, it’s your key or your environment. If it works and the next step fails, you know exactly where to look.
2. The Exercise
I have 5 apples and buy 2 more. How many do I have?
The agent must recognise this needs addition, call the add tool, and answer 7.
We’ve built a calculator before, as an MCP server. This time the tools live directly inside the agent.
3. Custom Tools: calci_tools.py
from agents import function_tool
@function_tool
def add(a: float, b: float) -> float:
"""Add two numbers.
Args:
a: The first number.
b: The second number.
"""
print(f"[tool] add({a}, {b})")
return a + b
@function_tool
def divide(a: float, b: float) -> float | str:
"""Divide a by b.
Args:
a: The number to divide.
b: The number to divide by.
"""
print(f"[tool] divide({a}, {b})")
if b == 0:
return "Cannot divide by zero. Ask the user for a different divisor."
return a / b
# subtract, multiply and modulus follow the same pattern
What to notice:
@function_toolturns an ordinary function into a tool the agent can call.- The docstring is the contract. The SDK reads Google-style docstrings, including the
Args:section, to tell the model what each parameter means. A vague docstring gets a confused model. - The
printstatements show you exactly when a tool is called and with what values. This is the fastest way to confirm the model really used the tool rather than guessing. - The zero check in
dividereturns a plain message instead of crashing. The agent reads that message and can recover, which a stack trace wouldn’t allow.
All five tools: add, subtract, multiply, divide and modulus.
4. The Calculator Agent
from agents import Agent, Runner
from calci_tools import add, subtract, multiply, divide, modulus
agent = Agent(
name="Smart Calculator",
instructions=(
"You understand maths questions written in plain language. "
"Always use the calculator tools for every calculation. "
"Never work out a number yourself. "
"Explain the final answer simply."
),
tools=[add, subtract, multiply, divide, modulus],
model=MODEL_NAME,
)
result = Runner.run_sync(agent, "I have 5 apples and buy 2 more. How many do I have?")
print(result.final_output)
Why insist on the tools? An LLM predicts tokens; it doesn’t calculate. It will often get simple sums right and then get a larger one confidently wrong. Forcing every calculation through a tool makes the arithmetic correct and makes the agent’s behaviour visible in your logs.
5. Short-Term Memory: SQLiteSession
from agents import SQLiteSession
session = SQLiteSession("session_101", "calculator_memory.db")
Runner.run_sync(agent, "I have 5 apples.", session=session)
result = Runner.run_sync(agent, "I bought 2 more. How many now?", session=session)
print(result.final_output) # 7: it remembered the 5
SQLiteSessionis the SDK’s built-in session, an implementation of its session base class (SessionABC). It stores the conversation in a local SQLite file.- Same session ID → the agent remembers, even across separate runs of the program, because the history is on disk.
- New session ID → a clean slate.
- The session goes to the
Runner, not theAgent. The agent is the configuration; the session is the conversation.
Compare with Claude: there, the SDK gave you a session ID and you had to map it to the user yourself. Here, you choose the ID, so a customer ID or a student name can be the session ID. Same concept, less plumbing.
6. Same Concepts, Every Framework
The names change; the ideas don’t.
| Concept | OpenAI Agents SDK | Google Gemini ADK |
|---|---|---|
| The agent | Agent | LlmAgent |
| Runs it | Runner | Runner |
| Conversation memory | A session such as SQLiteSession, passed to the runner | A session service such as InMemorySessionService |
| Custom tools | @function_tool | Plain Python functions |
LangChain and CrewAI follow the same patterns again. Once you can name the concept, finding it in a new framework is a quick documentation search. That’s the memory-map approach in practice.
7. What’s Next
- A downloads agent, combining built-in and custom tools.
- The tutor agent in OpenAI, with the same three tones as the Claude version, detailed, revision and beginner, plus memory.
✅ Action Items
- Build
calci_tools.pywith all five tools, including the divide-by-zero guard. - Run the apple question and use your
printoutput to confirmaddwas actually called. - Add a
SQLiteSession, ask a question that depends on the previous one, then change the session ID and watch the memory disappear. - Research OpenAI’s built-in tools. The SDK calls them hosted tools. Pick one and build a small agent around it.
- Try the tutor agent in the OpenAI SDK with the three tones and a session.
⏭️ Next up: the downloads agent, with built-in and custom tools together.
