Skip to main content

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:

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_plain for cross-framework compatibility.

Key SDK Patterns

PatternPydanticAILangChainLangGraph
Create agentframework="pydantic_ai"framework="langchain"framework="langgraph"
Context schemadeps_type=MyDepscontext_schema=MyContextStateGraph(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 contextctx.deps.my_fieldctx.context.my_fieldruntime.context.my_field
Run with contextagent.run("...", deps=deps)agent.run("...", context=context)agent.run("...", context=context)
Tool execution nodeManaged by frameworkManaged by frameworkAdd 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
)
Complete Documentation

For comprehensive KnowledgeBaseSearchTool documentation including:

  • All search strategies and parameters
  • Metadata filtering examples
  • Troubleshooting guide
  • Advanced usage patterns

See RAG & Vector Stores and Search & Filtering

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 file
  • index.pkl — a (docstore, index_to_docstore_id) tuple (e.g. from LangChain's FAISS.save_local())

How It Works:

  1. You load your FAISS index and extract texts + metadata from the companion pickle
  2. Pass them to KnowledgeBaseSearchTool along with the embedding model and dimension used to build the index
  3. At search time, the SDK generates a query embedding via the CAIP API and searches your local index
  4. Use search_type="faiss" when calling kb_tool.search()

Constructor Parameters (FAISS mode):

ParameterTypeDescription
clientCAIPAgentsClientSDK client (used for embedding generation)
faiss_indexfaiss.IndexYour loaded FAISS index object
faiss_textslist[str]Document texts aligned with index vectors
faiss_metadatalist[dict]Metadata dicts aligned with index vectors
embedding_modelstrModel name matching the one used to build the index
embedding_dimensionintDimension matching the index vectors
Model Compatibility

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.

Full Example

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_url support — agent only needs to provide the path
  • allowed_hosts allowlist for security
  • ✅ Cross-framework compatibility (PydanticAI, LangChain, and LangGraph)

Import:

from caip_agents_sdk.tools import HTTPRequestTool

Constructor Parameters

ParameterTypeDefaultDescription
namestr"http_request"Tool name shown to the agent
descriptionstrauto-generatedTool description used by the agent to decide when to call it
base_urlstr | NoneNoneBase URL prepended to all requests; agent provides only the path
allowed_hostslist[str] | NoneNoneRestrict requests to these hostnames only
default_headersdict | NoneNoneHeaders added to every request (e.g., auth tokens)
timeoutfloat30.0Request 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:

ModelProviderBest ForDefault Dimension
text-embedding-3-smallOpenAIFast, cost-effective, general purpose1536
text-embedding-3-largeOpenAIHigher accuracy, better semantic understanding3072
titan-text-embeddings-v2AWS BedrockAWS-native, multilingual support1024

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
note

Not all models support all dimensions. Choose a dimension that is compatible with your selected embedding model and vector store configuration.

Model Compatibility

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:

  1. Embedding Generation: Converts the query text to a vector
  2. Vector Search: Finds semantically similar documents
  3. Result Formatting: Presents results in agent-friendly format
  4. Error Handling: Gracefully handles failures

When to Build Custom vs Use Built-in:

Use Built-in KnowledgeBaseSearchToolBuild Custom Tool
Standard RAG use casesCustom search logic needed
Multiple search strategies neededSpecialized formatting required
Production-ready solutionLearning/experimentation
Metadata filtering sufficientComplex 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}"
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() and stream()
  • 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