Tools
Tools extend the capabilities of your AI agents by allowing them to perform specific actions, access external data, or execute custom logic. The CAIP Agents SDK provides a unified tool system that works across PydanticAI, LangChain, and LangGraph frameworks.
What is a Tool?
A Tool is a function that an agent can call to perform a specific task. Tools enable agents to:
- Access external data: Query databases, APIs, search engines
- Perform calculations: Math operations, data processing
- Execute actions: Send emails, create tickets, update records
- Retrieve information: Get current time, weather, user data
┌─────────────────────────────────────────────────────────┐
│ Agent │
│ ┌───────────────────────────────────────────────────┐ │
│ │ User: "What's the weather in Munich?" │ │
│ │ ↓ │ │
│ │ Agent decides to use: get_weather("Munich") │ │
│ │ ↓ │ │
│ │ Tool returns: "Sunny, 22°C" │ │
│ │ ↓ │ │
│ │ Agent: "The weather in Munich is sunny, 22°C" │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Registering Tools
The SDK provides two ways to register tools:
1. Decorator Syntax (Recommended)
Use @agent.tool_plain to register a tool with a decorator:
@agent.tool_plain
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
# Your implementation here
return f"The weather in {city} is sunny, 22°C"
2. Programmatic Registration
Use agent.add_tool_plain() to register an existing function:
def calculate_tip(bill: float, tip_percent: float = 15.0) -> str:
"""Calculate tip amount for a bill."""
tip = bill * (tip_percent / 100)
return f"Tip: ${tip:.2f}, Total: ${bill + tip:.2f}"
# Register the function as a tool
agent.add_tool_plain(calculate_tip)
Tool Types
Plain Tools (tool_plain)
Plain tools are simple functions without any framework-specific context. This is the recommended approach as it works across PydanticAI, LangChain, and LangGraph.
@agent.tool_plain
def get_current_time() -> str:
"""Get the current date and time."""
from datetime import datetime
return datetime.now().strftime("%A, %B %d, %Y at %I:%M %p")
PydanticAI Context Tools (tool)
Context tools receive a RunContext parameter that provides access to dependencies and other runtime information. This example works with PydanticAI.
from caip_agents_sdk import RunContext
@dataclass
class EmailAgentDependencies:
"""Dependencies for email agent execution."""
gmail_credentials_path: str
gmail_token_path: str
session_id: str = None
client = CAIPAgentsClient()
thread = await client.create_thread(
agent_id="your-agent-id",
thread_data={"title": "Tool with context test", "status": "open"},
)
# Create agent
email_agent = client.create_agent(
framework="pydantic_ai",
agent_id="your-agent-id",
deps_type=EmailAgentDependencies,
thread_id=thread.threadId,
)
@email_agent.tool
def get_user_data(ctx: RunContext[EmailAgentDependencies], user_id: str) -> str:
"""Get user data with access to context."""
# Access dependencies via ctx.deps
return f"User {user_id} data from context {ctx.deps}"
# Create dependencies
deps = EmailAgentDependencies(
gmail_credentials_path="/path/to/credentials.json",
gmail_token_path="/path/to/token.json",
)
# Run research agent
await email_agent.initialize()
result = await email_agent.run(
"get me user data for user 1234",
deps=deps
)
print("Result:", result)
LangChain Context Tools (tool)
Context tools receive a ToolRuntime parameter that provides access to dependencies and other runtime information. This example works with LangChain.
from caip_agents_sdk import ToolRuntime
client = CAIPAgentsClient()
thread = await client.create_thread(
agent_id="your-agent-id",
thread_data={"title": "Tool with context test", "status": "open"},
)
# Create agent
email_agent = client.create_agent(
framework="langchain",
agent_id="your-agent-id",
context_schema=EmailAgentDependencies,
thread_id=thread.threadId,
)
@email_agent.tool
def get_user_data(ctx: ToolRuntime[EmailAgentDependencies], user_id: str) -> str:
"""Get user data with access to context."""
# Access runtime context via ctx.context
return f"User {user_id} data from context {ctx.context}"
# Create dependencies
deps = EmailAgentDependencies(
gmail_credentials_path="/path/to/credentials.json",
gmail_token_path="/path/to/token.json",
)
# Run research agent
await email_agent.initialize()
result = await email_agent.run(
"get me user data for user 1234",
context=deps,
)
print("Result:", result)
LangGraph Tools
LangGraph tools are registered on the SDK agent before initialization, then passed to your graph_factory. Inside the graph, you decide which nodes can use which tools.
This is useful for multi-agent workflows where each worker should have access only to the tools it needs.
from caip_agents_sdk import CAIPAgentsClient
client = CAIPAgentsClient()
agent = client.create_agent(
framework="langgraph",
agent_id="your-agent-id",
graph_factory=build_support_graph,
)
@agent.tool_plain
def check_invoice(customer_id: str) -> str:
"""Check invoice status for a customer."""
return f"Invoice for {customer_id}: paid"
@agent.tool_plain
def run_diagnostics(customer_id: str, symptom: str) -> str:
"""Run technical diagnostics for a customer issue."""
return f"Diagnostics complete for {customer_id}: probable cause is {symptom}"
await agent.initialize()
Your graph factory receives the registered tools through the tools argument. This example builds one complete worker and tool loop:
from caip_agents_sdk import END, MessagesState, START, StateGraph, SystemMessage, ToolNode, tools_condition
class SupportState(MessagesState):
pass
def build_support_graph(llm, tools, checkpointer=None):
tool_map = {tool.name: tool for tool in tools}
billing_tools = [tool_map["check_invoice"]]
billing_llm = llm.bind_tools(billing_tools)
async def billing_worker(state: SupportState):
messages = [SystemMessage(content="You are a billing specialist.")] + list(state["messages"])
response = await billing_llm.ainvoke(messages)
return {"messages": [response]}
builder = StateGraph(SupportState)
builder.add_node("billing_worker", billing_worker)
builder.add_node("billing_tools", ToolNode(billing_tools))
builder.add_edge(START, "billing_worker")
builder.add_conditional_edges(
"billing_worker",
tools_condition,
{"tools": "billing_tools", "__end__": END},
)
builder.add_edge("billing_tools", "billing_worker")
if checkpointer is not None:
return builder.compile(checkpointer=checkpointer)
return builder.compile()
In LangGraph, the most important difference is placement: registering a tool makes it available to the graph factory, but the graph controls which worker node can call it.
For a router that splits tools across billing, technical, general, and escalation workers, see Build a Multi-Agent Workflow.
Recommendation: Use
tool_plainfor cross-framework compatibility.
Key SDK Patterns
| Pattern | PydanticAI | LangChain | LangGraph |
|---|---|---|---|
| Create agent | framework="pydantic_ai" | framework="langchain" | framework="langgraph" |
| Context schema | deps_type=MyDeps | context_schema=MyContext | StateGraph(MyState, context_schema=MyContext) |
| Plain tool | @agent.tool_plain | @agent.tool_plain | @agent.tool_plain before initialize() |
| Tool with context | @agent.tool + ctx: RunContext[MyDeps] | @agent.tool + ctx: ToolRuntime[MyContext] | Use LangGraph Runtime[MyContext] in graph nodes |
| Access context | ctx.deps.my_field | ctx.context.my_field | runtime.context.my_field |
| Run with context | agent.run("...", deps=deps) | agent.run("...", context=context) | agent.run("...", context=context) |
| Tool execution node | Managed by framework | Managed by framework | Add ToolNode([...]) and tools_condition |
Request Context in Tools
RequestContext is an agent execution concept, not a tool concept.
The full explanation lives in Request Context. On this page, the important part is simpler: if the SDK attaches request context during run() or stream(), your tools can read it from the active framework carrier.
Where tools read it from
- PydanticAI tools read it from
ctx.deps.request_context - LangChain tools read it from
ctx.context.request_context - LangGraph nodes read it from
runtime.context.request_context
PydanticAI example
from dataclasses import dataclass
from typing import Optional
from caip_agents_sdk import RequestContext, RunContext
@dataclass
class AppDeps:
request_context: Optional[RequestContext] = None
@agent.tool
def trace_request(ctx: RunContext[AppDeps]) -> str:
rc = ctx.deps.request_context
request_id = rc.request_id if rc is not None else ""
return f"request_id={request_id}"
LangChain example
from dataclasses import dataclass
from typing import Optional
from caip_agents_sdk import RequestContext, ToolRuntime
@dataclass
class AppContext:
request_context: Optional[RequestContext] = None
@agent.tool
def trace_request(ctx: ToolRuntime[AppContext]) -> str:
rc = ctx.context.request_context
request_id = rc.request_id if rc is not None else ""
return f"request_id={request_id}"
LangGraph example
from dataclasses import dataclass
from typing import Optional
from caip_agents_sdk import MessagesState, RequestContext
from langgraph.runtime import Runtime
@dataclass
class AppContext:
request_context: Optional[RequestContext] = None
class SupportState(MessagesState):
pass
async def trace_node(state: SupportState, runtime: Runtime[AppContext]):
rc = runtime.context.request_context
request_id = rc.request_id if rc is not None else ""
return {"messages": [f"request_id={request_id}"]}
For how to build, pass, and merge request context during agent execution, see Request Context.
Writing Effective Tools
1. Use Clear Docstrings
The docstring becomes the tool description that the agent uses to understand when to call it:
@agent.tool_plain
def search_products(query: str, category: str = "all") -> str:
"""
Search for products in the catalog.
Use this tool when the user asks about products, inventory,
or wants to find items to purchase.
Args:
query: Search terms for finding products
category: Product category to filter by (default: "all")
Returns:
List of matching products with prices
"""
# Implementation
return "Found 5 products matching your query..."
2. Use Type Hints
Type hints help the agent understand what parameters to provide:
@agent.tool_plain
def calculate_shipping(
weight_kg: float,
destination: str,
express: bool = False
) -> str:
"""Calculate shipping cost for a package."""
base_cost = weight_kg * 2.50
if express:
base_cost *= 1.5
return f"Shipping to {destination}: ${base_cost:.2f}"
3. Return Strings
Tools should return strings that the agent can include in its response:
# ✅ Good - Returns a string
@agent.tool_plain
def get_order_status(order_id: str) -> str:
"""Get the status of an order."""
return f"Order {order_id}: Shipped, arriving tomorrow"
# ❌ Bad - Returns complex object
@agent.tool_plain
def get_order_status(order_id: str) -> dict:
"""Get the status of an order."""
return {"id": order_id, "status": "shipped"} # Agent can't use this directly
Complete Example
import asyncio
from datetime import datetime
from caip_agents_sdk import CAIPAgentsClient
async def main():
client = CAIPAgentsClient()
agent = client.create_agent(
framework="pydantic_ai", # Plain tools also work with "langchain" and "langgraph"
agent_id="your-agent-id"
)
# Initialize agent
await agent.initialize()
# Create thread
thread = await client.create_thread(
agent_id="your-agent-id",
thread_data={"title": "Tools Demo"}
)
agent.thread_id = thread.threadId
# Register tools using decorator
@agent.tool_plain
def get_current_time() -> str:
"""Get the current date and time."""
return datetime.now().strftime("Today is %A, %B %d, %Y at %I:%M %p")
@agent.tool_plain
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
# In production, call a real weather API
weather_data = {
"munich": "Sunny, 22°C",
"berlin": "Cloudy, 18°C",
"hamburg": "Rainy, 15°C"
}
return weather_data.get(city.lower(), f"Weather data not available for {city}")
@agent.tool_plain
def calculate_tip(bill_amount: float, tip_percentage: float = 15.0) -> str:
"""Calculate the tip amount for a restaurant bill."""
tip = bill_amount * (tip_percentage / 100)
total = bill_amount + tip
return f"Bill: ${bill_amount:.2f}, Tip ({tip_percentage}%): ${tip:.2f}, Total: ${total:.2f}"
# Test the tools
response = await agent.run(
"What time is it, what's the weather in Munich, and calculate a 20% tip for a $85 dinner?"
)
print(f"Agent: {response}")
if __name__ == "__main__":
asyncio.run(main())
Built-in Tools
The SDK provides pre-built tools for common use cases, saving you development time.
KnowledgeBaseSearchTool
The KnowledgeBaseSearchTool is a production-ready tool for searching vector stores with RAG capabilities. It provides:
- ✅ Multiple search strategies (hybrid, vector, keyword, keyword_filter)
- ✅ Automatic embedding generation
- ✅ Metadata filtering
- ✅ Cross-framework compatibility (PydanticAI, LangChain, and LangGraph)
Quick Example:
from caip_agents_sdk.tools import KnowledgeBaseSearchTool
# Create and register the tool
kb_tool = KnowledgeBaseSearchTool(
client=client,
vector_store_id="your-vector-store-id"
)
@agent.tool_plain
async def search_docs(query: str) -> str:
"""Search documentation using hybrid search."""
return await kb_tool.search(
query=query,
search_type="hybrid",
top_k=5
)
For comprehensive KnowledgeBaseSearchTool documentation including:
- All search strategies and parameters
- Metadata filtering examples
- Troubleshooting guide
- Advanced usage patterns
FAISS Mode (Bring Your Own Index)
The KnowledgeBaseSearchTool also supports local FAISS indexes, allowing you to bring your own pre-built FAISS index for semantic search without uploading data to a remote vector store. The tool auto-detects FAISS mode when faiss_index is provided.
Key Features:
- ✅ Bring your own FAISS index of any type (flat, IVF, HNSW, etc.)
- ✅ Accepts any FAISS index
- ✅ Query embeddings generated via CAIP API
- ✅ Supports PydanticAI, LangChain, and LangGraph agent frameworks
- ✅ No remote vector store required
Required Files:
Your index directory must contain:
index.faiss— the FAISS index fileindex.pkl— a(docstore, index_to_docstore_id)tuple (e.g. from LangChain'sFAISS.save_local())
How It Works:
- You load your FAISS index and extract texts + metadata from the companion pickle
- Pass them to
KnowledgeBaseSearchToolalong with the embedding model and dimension used to build the index - At search time, the SDK generates a query embedding via the CAIP API and searches your local index
- Use
search_type="faiss"when callingkb_tool.search()
Constructor Parameters (FAISS mode):
| Parameter | Type | Description |
|---|---|---|
client | CAIPAgentsClient | SDK client (used for embedding generation) |
faiss_index | faiss.Index | Your loaded FAISS index object |
faiss_texts | list[str] | Document texts aligned with index vectors |
faiss_metadata | list[dict] | Metadata dicts aligned with index vectors |
embedding_model | str | Model name matching the one used to build the index |
embedding_dimension | int | Dimension matching the index vectors |
The embedding_model and embedding_dimension must match the model and dimensions used when building your FAISS index. The SDK uses the CAIP API to generate query embeddings at search time.
For a complete end-to-end example including index loading and interactive Q&A, see FAISS Search Example.
HTTPRequestTool
The HTTPRequestTool is a production-ready built-in tool that lets your agent make HTTP requests to external APIs and services. It supports GET, POST, and other HTTP methods and can be configured with authentication headers, a base URL, and host restrictions.
- ✅ GET, POST, and other HTTP methods
- ✅ JSON request bodies
- ✅ Pre-configured default headers (e.g.,
Authorization,Accept) - ✅
base_urlsupport — agent only needs to provide the path - ✅
allowed_hostsallowlist for security - ✅ Cross-framework compatibility (PydanticAI, LangChain, and LangGraph)
Import:
from caip_agents_sdk.tools import HTTPRequestTool
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
name | str | "http_request" | Tool name shown to the agent |
description | str | auto-generated | Tool description used by the agent to decide when to call it |
base_url | str | None | None | Base URL prepended to all requests; agent provides only the path |
allowed_hosts | list[str] | None | None | Restrict requests to these hostnames only |
default_headers | dict | None | None | Headers added to every request (e.g., auth tokens) |
timeout | float | 30.0 | Request timeout in seconds |
Usage Patterns
1. Basic GET — unrestricted
Register the tool with defaults; the agent decides the full URL, method, and body:
from caip_agents_sdk import CAIPAgentsClient
from caip_agents_sdk.tools import HTTPRequestTool
client = CAIPAgentsClient()
agent = client.create_agent(framework="pydantic_ai", agent_id="your-agent-id")
await agent.initialize()
thread = await client.create_thread(
agent_id="your-agent-id",
thread_data={"title": "HTTP Tool Demo", "status": "open"},
)
agent.thread_id = thread.threadId
http_tool = HTTPRequestTool()
agent.add_tool_plain(http_tool)
response = await agent.run(
"Fetch the list of users from https://jsonplaceholder.typicode.com/users "
"and tell me how many users there are."
)
print(response)
2. POST with decorator wrapper
Wrap the tool to expose only specific parameters to the agent:
http_tool = HTTPRequestTool()
@agent.tool_plain
async def call_api(url: str, method: str = "GET", body: str | None = None) -> str:
"""Make an HTTP request to an API endpoint."""
return await http_tool.execute(url=url, method=method, body=body)
response = await agent.run(
"Create a new post on https://jsonplaceholder.typicode.com/posts "
'with title "Hello from CAIP" and body "Testing the HTTP tool". '
"Use a POST request with JSON body."
)
print(response)
3. Restricted tool with base_url and allowed_hosts
Lock the tool to a single API host. The agent provides only the path (e.g., /posts/1), not the full URL. Combine with default_headers to inject authentication:
http_tool = HTTPRequestTool(
name="jsonplaceholder_api",
description="Query the JSONPlaceholder API. Only provide the path (e.g. /posts/1).",
base_url="https://jsonplaceholder.typicode.com",
allowed_hosts=["jsonplaceholder.typicode.com"],
default_headers={"Accept": "application/json"},
timeout=15.0,
)
agent.add_tool_plain(http_tool)
response = await agent.run("Get the details of post number 1 and tell me who wrote it.")
print(response)
4. Combined with agent streaming
HTTPRequestTool works transparently with agent.stream():
http_tool = HTTPRequestTool()
agent.add_tool_plain(http_tool)
stream_result = await agent.stream(
"Fetch https://jsonplaceholder.typicode.com/todos/1 and summarize it."
)
async for chunk in stream_result.stream_text():
print(chunk, end="", flush=True)
Real-World Tool Examples
Custom RAG Search Tool
Here's how to build your own custom RAG search tool using the SDK's vector search capabilities. This demonstrates the underlying mechanics that power the built-in KnowledgeBaseSearchTool:
Understanding Embedding Models and Dimensions
When building custom RAG tools, you need to choose an embedding model and dimension that matches your vector store configuration:
Available Embedding Models:
| Model | Provider | Best For | Default Dimension |
|---|---|---|---|
text-embedding-3-small | OpenAI | Fast, cost-effective, general purpose | 1536 |
text-embedding-3-large | OpenAI | Higher accuracy, better semantic understanding | 3072 |
titan-text-embeddings-v2 | AWS Bedrock | AWS-native, multilingual support | 1024 |
Supported Dimensions:
The SDK supports three dimension sizes: 1024, 1536, and 3072
- 1024: Balanced performance, lower storage costs, good for most use cases
- 1536: Standard dimension, good balance of accuracy and performance (recommended)
- 3072: Maximum accuracy, best semantic understanding, higher storage and compute costs
Not all models support all dimensions. Choose a dimension that is compatible with your selected embedding model and vector store configuration.
The embedding model and dimensions you use in generate_embeddings() must match the configuration of your vector store. Mismatched dimensions will cause search errors.
Implementation Example
@agent.tool_plain
async def search_custom_docs(
query: str,
vector_weight: float = 0.7,
score_threshold: float = 0.0
) -> str:
"""
Custom RAG search tool that searches documentation.
Supports vector, keyword, and hybrid search strategies with filtering options.
"""
try:
# Step 1: Generate embedding for vector/hybrid search
# (Skip this step for keyword search)
embedding = await client.text_to_embedding(
query,
vector_store_id="your-vector-store-id"
)
# Step 2: Build search request with filters
search_request = {
"limit": 5, # Number of results to return
"scoreThreshold": score_threshold,
"includeEmbeddings": False
}
# Optional: filter by specific document ID
# search_request["documentId"] = "your-document-id"
# Optional: filter by metadata (filename, fileType, page, etc.)
# search_request["metadataFilter"] = {"filename": "report.pdf", "page": 5}
# Step 3: Execute search based on strategy
# VECTOR SEARCH (current implementation - semantic search using embeddings)
search_request["queryEmbedding"] = embedding
results = await client.vector_search(
vector_store_id="your-vector-store-id",
search_request=search_request
)
# For HYBRID SEARCH (combines semantic + keyword matching):
# search_request["queryText"] = query
# search_request["queryEmbedding"] = embedding
# search_request["vectorWeight"] = vector_weight # 0.0-1.0 (0.7 = 70% vector, 30% keyword)
# results = await client.hybrid_search(
# vector_store_id="your-vector-store-id",
# search_request=search_request
# )
# For KEYWORD SEARCH (exact text matching):
# search_request["queryText"] = query
# search_request["fuzzy"] = True # Enable fuzzy matching
# results = await client.keyword_search(
# vector_store_id="your-vector-store-id",
# search_request=search_request
# )
# Step 4: Handle no results
if not results or not hasattr(results, 'results') or not results.results:
return f"No documents found for '{query}'."
# Step 5: Format results
formatted_results = []
for i, result in enumerate(results.results, 1):
chunk = getattr(result, 'chunk', '')
score = getattr(result, 'score', 0)
result_text = f"**Result {i}** (Relevance: {score:.3f})\n\n{chunk}"
formatted_results.append(result_text)
return "\n\n---\n\n".join(formatted_results)
except Exception as e:
return f"Search failed: {str(e)}. Please try a different query."
Key Components:
- Embedding Generation: Converts the query text to a vector
- Vector Search: Finds semantically similar documents
- Result Formatting: Presents results in agent-friendly format
- Error Handling: Gracefully handles failures
When to Build Custom vs Use Built-in:
| Use Built-in KnowledgeBaseSearchTool | Build Custom Tool |
|---|---|
| Standard RAG use cases | Custom search logic needed |
| Multiple search strategies needed | Specialized formatting required |
| Production-ready solution | Learning/experimentation |
| Metadata filtering sufficient | Complex query pre-processing |
Database Query Tool
@agent.tool_plain
def search_customers(query: str, limit: int = 10) -> str:
"""Search for customers by name or email."""
# In production, query your database
results = db.customers.search(query, limit=limit)
if not results:
return "No customers found matching your query."
output = f"Found {len(results)} customers:\n"
for customer in results:
output += f"- {customer.name} ({customer.email})\n"
return output
API Integration Tool
import httpx
@agent.tool_plain
def get_stock_price(symbol: str) -> str:
"""Get the current stock price for a ticker symbol."""
try:
response = httpx.get(f"https://api.example.com/stocks/{symbol}")
data = response.json()
return f"{symbol}: ${data['price']:.2f} ({data['change']:+.2f}%)"
except Exception as e:
return f"Unable to fetch stock price for {symbol}"
Link Provider Tool
from typing import Literal
@agent.tool_plain
def provide_link(
link_type: Literal["docs", "support", "dashboard", "api"]
) -> str:
"""Provide a link to CAIP resources."""
links = {
"docs": "https://docs.caip.bmw.cloud/",
"support": "https://support.caip.bmw.cloud/",
"dashboard": "https://caip.bmw.cloud/",
"api": "https://api.caip.bmw.cloud/docs"
}
descriptions = {
"docs": "view the documentation",
"support": "get support",
"dashboard": "access the dashboard",
"api": "view the API reference"
}
return f"You can {descriptions[link_type]} here: {links[link_type]}"
Best Practices
1. Write Descriptive Docstrings
# ❌ Bad - Vague description
@agent.tool_plain
def search(q: str) -> str:
"""Search."""
pass
# ✅ Good - Clear description
@agent.tool_plain
def search_knowledge_base(query: str) -> str:
"""
Search the internal knowledge base for articles and documentation.
Use this when users ask questions about company policies, procedures,
or technical documentation.
"""
pass
2. Handle Errors Gracefully
@agent.tool_plain
def fetch_user_data(user_id: str) -> str:
"""Fetch user profile data."""
try:
user = database.get_user(user_id)
return f"User: {user.name}, Email: {user.email}"
except UserNotFoundError:
return f"User with ID {user_id} not found."
except Exception as e:
return f"Error fetching user data: {str(e)}"
3. Use Appropriate Default Values
@agent.tool_plain
def list_orders(
status: str = "all",
limit: int = 10,
sort_by: str = "date"
) -> str:
"""List orders with optional filtering."""
# Defaults make the tool more flexible
pass
4. Keep Tools Focused
# ❌ Bad - Tool does too much
@agent.tool_plain
def handle_order(action: str, order_id: str, ...) -> str:
"""Create, update, delete, or query orders."""
pass
# ✅ Good - Separate focused tools
@agent.tool_plain
def create_order(product_id: str, quantity: int) -> str:
"""Create a new order."""
pass
@agent.tool_plain
def get_order_status(order_id: str) -> str:
"""Get the status of an existing order."""
pass
5. Use Literal Types for Fixed Options
from typing import Literal
@agent.tool_plain
def set_priority(
ticket_id: str,
priority: Literal["low", "medium", "high", "critical"]
) -> str:
"""Set the priority level of a support ticket."""
return f"Ticket {ticket_id} priority set to {priority}"
Next Steps
Now that you understand how to create and use tools with your agents, explore these resources:
- Request Context - Learn how request-scoped metadata works across
run()andstream() - RAG & Vector Stores - Complete guide to KnowledgeBaseSearchTool and RAG implementations
- Search & Filtering - Learn about search strategies and filtering
- Examples - See complete implementations including the RAG agent example
- Quick Start - Build your first agent with tools