Chapter 7 · Tools: Built-in Toolkit Principles and Usage
Learning objectives: Understand the complete tool-call execution loop in Agno, learn to use built-in toolkits, and clarify "how a Python function becomes a model-callable tool."
7.1 Why Agents Need Tools
Large models can only generate text — they don't know today's stock price, can't query your database, and can't send an email. Tools bridge this gap: register Python functions with the Agent, and the model "requests calls" to these functions when needed. Agno handles the actual execution and feeds results back to the model.
Key insight
Tools are not magical plugins — fundamentally, it's a loop: the model outputs "I want to call get_weather(city='Beijing')" → the framework executes the function → the result is appended to context → the model continues reasoning. This loop continues until the model stops requesting tools and gives a final answer.
The official docs describe the execution flow in five steps:
- The Agent sends the current context and tool definitions (schema) to the model;
- The model returns a normal answer, or requests one/more tool calls;
- Agno validates arguments and executes each requested tool;
- Tool results join the model's context;
- The loop continues until the model gives a final answer.
7.2 How Functions Become Tool Schemas
This is the most important principle in this chapter: Agno auto-builds the tool schema from the function name, docstring, and type annotations. Here's an official-style example:
import os
from agno.agent import Agent
from agno.models.openai import OpenAIChat
def lookup_order(order_id: str) -> dict:
"""Return the status and delivery date for an order.
Args:
order_id: The order ID to look up.
"""
return {
"order_id": order_id,
"status": "shipped",
"delivery_date": "2026-07-22",
}
agent = Agent(
# Unified third-party OpenAI-compatible model
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
tools=[lookup_order], # Pass the function directly
instructions="Use the order tool when a customer asks about a delivery.",
)
agent.print_response("When will order ORD-123 arrive?")The model actually receives a JSON Schema roughly equivalent to:
{
"name": "lookup_order",
"description": "Return the status and delivery date for an order.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The order ID to look up."
}
},
"required": ["order_id"]
}
}From this we can derive three iron rules for writing tools (called "Design Tools for Reliable Calls" in the official docs):
- Function names must be specific:
lookup_orderis far better thando_something; the model picks tools by name; - Write docstrings that explain "when to use": it's the model's instruction manual;
- Type annotations + parameter descriptions must be complete: they directly determine the accuracy of the input schema.
7.3 Built-in Toolkit Practice
Writing custom tools is flexible, but common needs (search, quotes, web reading) already have community wrappers. Agno bundles related functions into a Toolkit — functions in one package share internal state and work collaboratively. Using DuckDuckGoTools as the most common example:
pip install -U agno ddgs # DuckDuckGoTools requires the ddgs library# search_agent.py —— Let the Agent search the web
import os
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools.duckduckgo import DuckDuckGoTools
agent = Agent(
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
tools=[DuckDuckGoTools()],
instructions=["Cite source links when answering"],
markdown=True,
debug_mode=True, # v2 way to inspect tool calls: prints detailed run logs
)
agent.print_response("What are the big events in AI Agent space this week?")After running, you'll see a process like this in the logs:
● Tool call: web_search(query="AI Agent big events this week") ← Model initiates
● Tool result: [{"title": "...", "url": "..."}, ...] ← Function execution result
● Final answer (citing links from search results)Note: Agno removed the old
show_tool_calls=Trueparameter starting in 2.x (also in 3.x). To see tool call details, usedebug_mode=True; in streaming mode, listen for tool events inRunContentEvent.
Other commonly used toolkits include YFinanceTools (financial quotes), HackerNewsTools, Newspaper4kTools (web reading), FileTools (local files), and more — see the official Toolkit Index for the full list. Here's a combined example:
# stock_agent.py —— Quote tool + search tool working together
import os
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.tools.yfinance import YFinanceTools
agent = Agent(
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
tools=[
YFinanceTools(enable_stock_price=True, enable_analyst_recommendations=True),
DuckDuckGoTools(),
],
instructions=[
"Use the quote tool for real-time financial data",
"Search news with the search engine and cite sources",
"Present data comparisons in tables",
],
markdown=True,
debug_mode=True,
)
agent.print_response("Compare NVIDIA and AMD latest stock prices and analyst ratings")7.4 Toolkit Parameters and Surface Reduction
Toolkits are not black boxes — most expose constructor parameters to control behavior, and support unified reduction of exposed functions. Using DuckDuckGoTools as an example:
# toolkit_params.py —— Precisely control toolkit behavior
from agno.tools.duckduckgo import DuckDuckGoTools
tools = DuckDuckGoTools(
enable_news=False, # Disable news search, keep only web_search
fixed_max_results=3, # Cap results at 3
region="cn-zh", # Region setting
timeout=15, # Timeout in seconds
)Additionally, the Agent level has two universal parameters to further narrow the exposure surface:
agent = Agent(
# ...model config as above...
tools=[YFinanceTools(all=True)],
include_tools=["get_current_stock_price"], # Whitelist only these functions
# exclude_tools=["get_company_profile"], # Or exclude specific functions
)Why reduce the tool surface
The more tools exposed, the harder it is for the model to choose, and the higher the mis-call rate (the official docs explicitly state "Expose only the tools needed for the task"). In production, an Agent with 2 well-chosen tools is almost always more reliable than one with 20 scattered tools.
7.5 Tool Execution Controls
Agno provides a set of fine-grained controls. Here we cover the two most common; the rest (human confirmation, external execution) are covered in Chapter 21 on Guardrails:
| Config | Purpose |
|---|---|
tool_call_limit=N | Cap the max number of tool calls per run to prevent infinite loops burning tokens |
read_chat_history=True | Give the model an extra get_chat_history() tool for on-demand history lookback |
Inject run_context into tools | Framework auto-injects runtime context (user, session state, etc.); never enters the model schema |
run_context is the key to understanding subsequent chapters (session state): as long as the tool function declares this parameter, Agno auto-passes it at execution time, and does not count it in the model-visible parameters:
# run_context_demo.py —— Framework-injected hidden parameter
import os
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.run import RunContext
def who_is_calling(run_context: RunContext) -> str:
"""Get the current user id of this session."""
# run_context is injected by the framework; the model cannot see it
return f"Current user: {run_context.user_id or 'anonymous'}"
agent = Agent(
model=OpenAIChat(
id="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
),
tools=[who_is_calling],
)
agent.print_response("Who am I?", user_id="gao", session_id="s1")Other built-in injected parameters include agent, team, and images / videos / audios / files (access to multimodal inputs carried in this run).
Chapter Summary
- Tools = Python functions registered with an Agent; execution is a "request → execute →回填 → reason again" loop;
- Function name + docstring + type annotations together form the model-visible tool schema; quality of all three directly determines call reliability;
- Built-in Toolkits are ready out of the box (
DuckDuckGoTools,YFinanceTools, etc.); constructor params allow precise control; - Reduce exposure with
include_tools/exclude_tools; fewer tools are better; - v2+ inspect tool calls with
debug_mode=True(show_tool_callsremoved);run_context: RunContextis a framework-injected hidden parameter.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What does Agno use to generate a tool schema from a plain Python function?
2. To inspect an Agent's tool call process in Agno, what is the correct approach?
3. Regarding declaring run_context: RunContext in a tool, which statement is correct?
4. An Agent has 20 tools but the model frequently picks the wrong one. What is the most effective fix?
🛠️ Hands-On Exercises
- Write a
word_count(text: str) -> dicttool (count characters/words); have the Agent answer "how many words in this passage" via it; capture a full tool-call loop log withdebug_mode=True. - Install
ddgs; build a "daily tech news" Agent withDuckDuckGoTools(fixed_max_results=3)that requires citing source links in answers. - Add
tool_call_limit=3to the Agent from section 7.5; then deliberately ask a question that tempts continuous searches (e.g., "look up stock prices for 5 companies in sequence"); observe the Agent's behavior after hitting the cap.
After completing the hands-on exercises, proceed to Chapter 8: Custom Tools and Toolkit Development.