This folder contains examples demonstrating how to use local tools with the Agent Framework. Local tools allow agents to interact with external systems, perform computations, and execute custom logic.
Note: Several examples set approval_mode="never_require" to keep the samples concise. For production scenarios,
keep approval_mode="always_require" unless you are confident in the tool behavior and approval flow. See
function_tool_with_approval.py and function_tool_with_approval_and_threads.py for end-to-end approval handling.
| File | Description |
|---|---|
function_tool_declaration_only.py |
Demonstrates how to create function declarations without implementations. Useful for testing agent reasoning about tool usage or when tools are defined elsewhere. Shows how agents request tool calls even when the tool won't be executed. |
function_tool_from_dict_with_dependency_injection.py |
Shows how to create local tools from dictionary definitions using dependency injection. The function implementation is injected at runtime during deserialization, enabling dynamic tool creation and configuration. Note: This serialization/deserialization feature is in active development. |
function_tool_recover_from_failures.py |
Demonstrates graceful error handling when tools raise exceptions. Shows how agents receive error information and can recover from failures, deciding whether to retry or respond differently based on the exception. |
function_tool_with_approval.py |
Shows how to implement user approval workflows for function calls without using threads. Demonstrates both streaming and non-streaming approval patterns where users can approve or reject function executions before they run. |
function_tool_with_approval_and_threads.py |
Demonstrates tool approval workflows using threads for automatic conversation history management. Shows how threads simplify approval workflows by automatically storing and retrieving conversation context. Includes both approval and rejection examples. |
function_tool_with_kwargs.py |
Demonstrates how to inject custom arguments (context) into a local tool from the agent's run method. Useful for passing runtime information like access tokens or user IDs that the tool needs but the model shouldn't see. |
function_tool_with_thread_injection.py |
Shows how to access the current thread object inside a local tool via **kwargs. |
function_tool_with_max_exceptions.py |
Shows how to limit the number of times a tool can fail with exceptions using max_invocation_exceptions. Useful for preventing expensive tools from being called repeatedly when they keep failing. |
function_tool_with_max_invocations.py |
Demonstrates limiting the total number of times a tool can be invoked using max_invocations. Useful for rate-limiting expensive operations or ensuring tools are only called a specific number of times per conversation. |
function_tool_with_explicit_schema.py |
Demonstrates how to provide an explicit Pydantic model or JSON schema dictionary to the @tool decorator via the schema parameter, bypassing automatic inference from the function signature. |
tool_in_class.py |
Shows how to use the tool decorator with class methods to create stateful tools. Demonstrates how class state can control tool behavior dynamically, allowing you to adjust tool functionality at runtime by modifying class properties. |
- Function Declarations: Define tool schemas without implementations for testing or external tools
- Explicit Schema: Provide a Pydantic model or JSON schema dict to control the tool's parameter schema directly
- Dependency Injection: Create tools from configurations with runtime-injected implementations
- Error Handling: Gracefully handle and recover from tool execution failures
- Approval Workflows: Require user approval before executing sensitive or important operations
- Invocation Limits: Control how many times tools can be called or fail
- Stateful Tools: Use class methods as tools to maintain state and dynamically control behavior
from agent_framework import tool
from typing import Annotated
@tool(approval_mode="never_require")
def my_tool(param: Annotated[str, "Description"]) -> str:
"""Tool description for the AI."""
return f"Result: {param}"@tool(approval_mode="always_require")
def sensitive_operation(data: Annotated[str, "Data to process"]) -> str:
"""This requires user approval before execution."""
return f"Processed: {data}"from pydantic import BaseModel, Field
from agent_framework import tool
from typing import Annotated
class WeatherInput(BaseModel):
location: Annotated[str, Field(description="City name")]
unit: str = "celsius"
@tool(schema=WeatherInput)
def get_weather(location: str, unit: str = "celsius") -> str:
"""Get the weather for a location."""
return f"Weather in {location}: 22 {unit}"@tool(max_invocations=3)
def limited_tool() -> str:
"""Can only be called 3 times total."""
return "Result"
@tool(max_invocation_exceptions=2)
def fragile_tool() -> str:
"""Can only fail 2 times before being disabled."""
return "Result"class MyTools:
def __init__(self, mode: str = "normal"):
self.mode = mode
def process(self, data: Annotated[str, "Data to process"]) -> str:
"""Process data based on current mode."""
if self.mode == "safe":
return f"Safely processed: {data}"
return f"Processed: {data}"
# Create instance and use methods as tools
tools = MyTools(mode="safe")
agent = client.as_agent(tools=tools.process)
# Change behavior dynamically
tools.mode = "normal"When tools raise exceptions:
- The exception is captured and sent to the agent as a function result
- The agent receives the error message and can reason about what went wrong
- The agent can retry with different parameters, use alternative tools, or explain the issue to the user
- With invocation limits, tools can be disabled after repeated failures
Two approaches for handling approvals:
- Without Threads: Manually manage conversation context, including the query, approval request, and response in each iteration
- With Threads: Thread automatically manages conversation history, simplifying the approval workflow
- Use declaration-only functions when you want to test agent reasoning without execution
- Use dependency injection for dynamic tool configuration and plugin architectures
- Implement approval workflows for operations that modify data, spend money, or require human oversight
- Set invocation limits to prevent runaway costs or infinite loops with expensive tools
- Handle exceptions gracefully to create robust agents that can recover from failures
- Use class-based tools when you need to maintain state or dynamically adjust tool behavior at runtime
Each example is a standalone Python script that can be run directly:
uv run python function_tool_with_approval.pyMake sure you have the necessary environment variables configured (like OPENAI_API_KEY or Azure credentials) before running the examples.