mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
docs: replace all placeholder content with real documentation
MCP docs: - quickstart.md - Installation, basic usage, transport modes - configuration.md - CLI options, env vars, config files - vscode.md - VS Code, Cursor, Windsurf integration Agent SDK docs: - agents.md - Creating and configuring agents - running_agents.md - Runner, RunConfig, async/sync - tools.md - Function tools, context, custom tools - guardrails.md - Input/output validation - handoffs.md - Agent delegation patterns - streaming.md - Stream events and responses - tracing.md - Observability and debugging - context.md - Mutable run context - config.md - RunConfig and ModelSettings - results.md - RunResult, items, usage - models.md - Providers and model selection - multi_agent.md - Complex multi-agent patterns
This commit is contained in:
+170
-2
@@ -1,2 +1,170 @@
|
||||
# config
|
||||
Documentation coming soon.
|
||||
# Configuration
|
||||
|
||||
Configure agent behavior with `RunConfig` and `ModelSettings`.
|
||||
|
||||
## RunConfig
|
||||
|
||||
Global settings for an agent run:
|
||||
|
||||
```python
|
||||
from agents import Runner, RunConfig, ModelSettings
|
||||
|
||||
config = RunConfig(
|
||||
model="gpt-4o",
|
||||
model_settings=ModelSettings(temperature=0.7),
|
||||
max_turns=20,
|
||||
)
|
||||
|
||||
result = await Runner.run(agent, "Hello!", run_config=config)
|
||||
```
|
||||
|
||||
## RunConfig Options
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `model` | `str \| Model` | None | Override all agent models |
|
||||
| `model_provider` | `ModelProvider` | OpenAI | Model provider |
|
||||
| `model_settings` | `ModelSettings` | None | Global model settings |
|
||||
| `max_turns` | `int` | 10 | Max conversation turns |
|
||||
| `input_guardrails` | `list` | None | Global input guardrails |
|
||||
| `output_guardrails` | `list` | None | Global output guardrails |
|
||||
| `handoff_input_filter` | `HandoffInputFilter` | None | Global handoff filter |
|
||||
| `tracing_disabled` | `bool` | False | Disable tracing |
|
||||
|
||||
## ModelSettings
|
||||
|
||||
Fine-tune model behavior:
|
||||
|
||||
```python
|
||||
from agents import Agent, ModelSettings
|
||||
|
||||
settings = ModelSettings(
|
||||
temperature=0.7,
|
||||
top_p=0.9,
|
||||
max_tokens=1000,
|
||||
presence_penalty=0.0,
|
||||
frequency_penalty=0.0,
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
name="creative",
|
||||
instructions="Be creative.",
|
||||
model_settings=settings,
|
||||
)
|
||||
```
|
||||
|
||||
## ModelSettings Options
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `temperature` | `float` | 1.0 | Randomness (0-2) |
|
||||
| `top_p` | `float` | 1.0 | Nucleus sampling |
|
||||
| `max_tokens` | `int` | None | Max output tokens |
|
||||
| `presence_penalty` | `float` | 0.0 | Presence penalty |
|
||||
| `frequency_penalty` | `float` | 0.0 | Frequency penalty |
|
||||
| `stop` | `list[str]` | None | Stop sequences |
|
||||
|
||||
## Model Selection
|
||||
|
||||
### By String
|
||||
|
||||
```python
|
||||
agent = Agent(name="gpt4", model="gpt-4o")
|
||||
agent = Agent(name="claude", model="claude-3-5-sonnet-20241022")
|
||||
```
|
||||
|
||||
### Override at Runtime
|
||||
|
||||
```python
|
||||
config = RunConfig(model="gpt-4o-mini")
|
||||
result = await Runner.run(agent, "Hello!", run_config=config)
|
||||
```
|
||||
|
||||
## Model Providers
|
||||
|
||||
### OpenAI (default)
|
||||
|
||||
```python
|
||||
from agents import OpenAIProvider
|
||||
|
||||
provider = OpenAIProvider(
|
||||
api_key="sk-...",
|
||||
base_url="https://api.openai.com/v1",
|
||||
)
|
||||
|
||||
config = RunConfig(model_provider=provider)
|
||||
```
|
||||
|
||||
### Hanzo Node
|
||||
|
||||
```python
|
||||
from agents import create_hanzo_node_provider
|
||||
|
||||
provider = create_hanzo_node_provider(
|
||||
api_key="your-hanzo-key",
|
||||
base_url="https://api.hanzo.ai/v1",
|
||||
)
|
||||
|
||||
config = RunConfig(model_provider=provider)
|
||||
```
|
||||
|
||||
### Custom Provider
|
||||
|
||||
```python
|
||||
from agents import ModelProvider, Model
|
||||
|
||||
class MyProvider(ModelProvider):
|
||||
def get_model(self, model_name: str) -> Model:
|
||||
return MyCustomModel(model_name)
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `OPENAI_API_KEY` | OpenAI API key |
|
||||
| `ANTHROPIC_API_KEY` | Anthropic API key |
|
||||
| `HANZO_API_KEY` | Hanzo API key |
|
||||
|
||||
## Default Model
|
||||
|
||||
Set a default model for all agents:
|
||||
|
||||
```python
|
||||
import agents
|
||||
|
||||
# Set default
|
||||
agents.set_default_model("gpt-4o")
|
||||
|
||||
# All agents use gpt-4o unless overridden
|
||||
agent = Agent(name="default", instructions="...")
|
||||
```
|
||||
|
||||
## Configuration Hierarchy
|
||||
|
||||
Settings are applied in order (later overrides earlier):
|
||||
|
||||
1. Default settings
|
||||
2. Agent-level settings (`agent.model_settings`)
|
||||
3. RunConfig settings (`config.model_settings`)
|
||||
|
||||
```python
|
||||
# Agent settings
|
||||
agent = Agent(
|
||||
model_settings=ModelSettings(temperature=0.5),
|
||||
)
|
||||
|
||||
# RunConfig overrides
|
||||
config = RunConfig(
|
||||
model_settings=ModelSettings(temperature=0.9),
|
||||
)
|
||||
|
||||
# Result uses temperature=0.9
|
||||
result = await Runner.run(agent, "Hello!", run_config=config)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Agents](agents.md) - Agent configuration
|
||||
- [Models](models.md) - Model details
|
||||
- [Running Agents](running_agents.md) - Using RunConfig
|
||||
|
||||
+187
-2
@@ -1,2 +1,187 @@
|
||||
# context
|
||||
Documentation coming soon.
|
||||
# Context
|
||||
|
||||
The context is a mutable object passed throughout an agent run.
|
||||
|
||||
## Basic Context
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from agents import Agent, Runner
|
||||
|
||||
@dataclass
|
||||
class MyContext:
|
||||
user_id: str
|
||||
session_id: str
|
||||
request_count: int = 0
|
||||
|
||||
context = MyContext(user_id="123", session_id="abc")
|
||||
result = await Runner.run(agent, "Hello!", context=context)
|
||||
```
|
||||
|
||||
## Accessing Context in Tools
|
||||
|
||||
```python
|
||||
from agents import function_tool, RunContextWrapper
|
||||
|
||||
@function_tool
|
||||
def get_user_data(ctx: RunContextWrapper[MyContext]) -> str:
|
||||
"""Get data for the current user."""
|
||||
user_id = ctx.context.user_id
|
||||
return f"Data for user {user_id}"
|
||||
|
||||
@function_tool
|
||||
def increment_count(ctx: RunContextWrapper[MyContext]) -> str:
|
||||
"""Increment request count."""
|
||||
ctx.context.request_count += 1
|
||||
return f"Count: {ctx.context.request_count}"
|
||||
```
|
||||
|
||||
## Context in Instructions
|
||||
|
||||
Dynamic instructions based on context:
|
||||
|
||||
```python
|
||||
from agents import Agent, RunContextWrapper
|
||||
|
||||
def dynamic_instructions(ctx: RunContextWrapper[MyContext], agent: Agent) -> str:
|
||||
user = ctx.context.user_id
|
||||
count = ctx.context.request_count
|
||||
return f"""You are helping user {user}.
|
||||
This is request #{count} in this session.
|
||||
Be concise and helpful."""
|
||||
|
||||
agent = Agent(
|
||||
name="contextual",
|
||||
instructions=dynamic_instructions,
|
||||
)
|
||||
```
|
||||
|
||||
## Context in Guardrails
|
||||
|
||||
```python
|
||||
from agents import input_guardrail, InputGuardrailResult, RunContextWrapper
|
||||
|
||||
@input_guardrail
|
||||
async def check_permissions(
|
||||
ctx: RunContextWrapper[MyContext],
|
||||
agent,
|
||||
input_text: str
|
||||
) -> InputGuardrailResult:
|
||||
"""Check user permissions."""
|
||||
if ctx.context.user_id not in allowed_users:
|
||||
return InputGuardrailResult(
|
||||
tripwire_triggered=True,
|
||||
output_info="User not authorized."
|
||||
)
|
||||
return InputGuardrailResult(tripwire_triggered=False)
|
||||
```
|
||||
|
||||
## Context in Handoffs
|
||||
|
||||
```python
|
||||
from agents import handoff, Handoff, RunContextWrapper
|
||||
|
||||
@handoff
|
||||
def conditional_handoff(ctx: RunContextWrapper[MyContext]) -> Handoff | None:
|
||||
"""Hand off based on context."""
|
||||
if ctx.context.user_id.startswith("vip_"):
|
||||
return Handoff(target=vip_agent)
|
||||
return None
|
||||
```
|
||||
|
||||
## RunContextWrapper
|
||||
|
||||
The wrapper provides additional functionality:
|
||||
|
||||
```python
|
||||
from agents import RunContextWrapper
|
||||
|
||||
@function_tool
|
||||
def example(ctx: RunContextWrapper[MyContext]) -> str:
|
||||
# Access your context
|
||||
user = ctx.context.user_id
|
||||
|
||||
# Access run metadata
|
||||
run_id = ctx.run_id
|
||||
|
||||
# Check current agent
|
||||
agent_name = ctx.current_agent.name
|
||||
|
||||
return f"User: {user}, Run: {run_id}, Agent: {agent_name}"
|
||||
```
|
||||
|
||||
## Context Types
|
||||
|
||||
### Dataclass (Recommended)
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
@dataclass
|
||||
class AppContext:
|
||||
user_id: str
|
||||
permissions: list[str] = field(default_factory=list)
|
||||
metadata: dict = field(default_factory=dict)
|
||||
```
|
||||
|
||||
### Pydantic Model
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
|
||||
class AppContext(BaseModel):
|
||||
user_id: str
|
||||
permissions: list[str] = []
|
||||
|
||||
class Config:
|
||||
extra = "allow" # Allow additional fields
|
||||
```
|
||||
|
||||
### Simple Dict
|
||||
|
||||
```python
|
||||
# Works but not type-safe
|
||||
context = {"user_id": "123", "data": {}}
|
||||
result = await Runner.run(agent, "Hello!", context=context)
|
||||
```
|
||||
|
||||
## Context Mutation
|
||||
|
||||
Context can be modified during the run:
|
||||
|
||||
```python
|
||||
@function_tool
|
||||
def update_context(ctx: RunContextWrapper[MyContext], key: str, value: str) -> str:
|
||||
"""Update context metadata."""
|
||||
ctx.context.metadata[key] = value
|
||||
return f"Updated {key}"
|
||||
```
|
||||
|
||||
## Context Persistence
|
||||
|
||||
For multi-turn conversations:
|
||||
|
||||
```python
|
||||
# Store context between requests
|
||||
contexts: dict[str, MyContext] = {}
|
||||
|
||||
async def handle_message(session_id: str, message: str):
|
||||
# Get or create context
|
||||
if session_id not in contexts:
|
||||
contexts[session_id] = MyContext(
|
||||
user_id="user_123",
|
||||
session_id=session_id,
|
||||
)
|
||||
|
||||
context = contexts[session_id]
|
||||
result = await Runner.run(agent, message, context=context)
|
||||
|
||||
# Context is updated in place
|
||||
return result.final_output
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tools](tools.md) - Using context in tools
|
||||
- [Guardrails](guardrails.md) - Context in guardrails
|
||||
- [Running Agents](running_agents.md) - Passing context
|
||||
|
||||
+185
-2
@@ -1,2 +1,185 @@
|
||||
# guardrails
|
||||
Documentation coming soon.
|
||||
# Guardrails
|
||||
|
||||
Guardrails validate agent inputs and outputs to ensure safety and quality.
|
||||
|
||||
## Input Guardrails
|
||||
|
||||
Validate user input before the agent processes it:
|
||||
|
||||
```python
|
||||
from agents import Agent, input_guardrail, InputGuardrailResult
|
||||
|
||||
@input_guardrail
|
||||
async def check_input_length(ctx, agent, input_text: str) -> InputGuardrailResult:
|
||||
"""Reject inputs that are too long."""
|
||||
if len(input_text) > 10000:
|
||||
return InputGuardrailResult(
|
||||
tripwire_triggered=True,
|
||||
output_info="Input too long. Please shorten your message."
|
||||
)
|
||||
return InputGuardrailResult(tripwire_triggered=False)
|
||||
|
||||
agent = Agent(
|
||||
name="guarded",
|
||||
instructions="Be helpful.",
|
||||
input_guardrails=[check_input_length],
|
||||
)
|
||||
```
|
||||
|
||||
## Output Guardrails
|
||||
|
||||
Validate agent output before returning to user:
|
||||
|
||||
```python
|
||||
from agents import Agent, output_guardrail, OutputGuardrailResult
|
||||
|
||||
@output_guardrail
|
||||
async def check_pii(ctx, agent, output: str) -> OutputGuardrailResult:
|
||||
"""Block outputs containing PII."""
|
||||
pii_patterns = ["SSN:", "credit card:", "password:"]
|
||||
|
||||
for pattern in pii_patterns:
|
||||
if pattern.lower() in output.lower():
|
||||
return OutputGuardrailResult(
|
||||
tripwire_triggered=True,
|
||||
output_info="Response contained sensitive information."
|
||||
)
|
||||
|
||||
return OutputGuardrailResult(tripwire_triggered=False)
|
||||
|
||||
agent = Agent(
|
||||
name="safe_agent",
|
||||
instructions="Help users with their accounts.",
|
||||
output_guardrails=[check_pii],
|
||||
)
|
||||
```
|
||||
|
||||
## Guardrail Results
|
||||
|
||||
### Input Guardrail Result
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class InputGuardrailResult:
|
||||
tripwire_triggered: bool # True to block
|
||||
output_info: str | None = None # Reason for blocking
|
||||
```
|
||||
|
||||
### Output Guardrail Result
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class OutputGuardrailResult:
|
||||
tripwire_triggered: bool # True to block
|
||||
output_info: str | None = None # Reason for blocking
|
||||
```
|
||||
|
||||
## Guardrail with Context
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from agents import input_guardrail, InputGuardrailResult, RunContextWrapper
|
||||
|
||||
@dataclass
|
||||
class AppContext:
|
||||
user_tier: str
|
||||
rate_limit: int
|
||||
|
||||
@input_guardrail
|
||||
async def rate_limit_check(
|
||||
ctx: RunContextWrapper[AppContext],
|
||||
agent,
|
||||
input_text: str
|
||||
) -> InputGuardrailResult:
|
||||
"""Check rate limits based on user tier."""
|
||||
if ctx.context.user_tier == "free" and ctx.context.rate_limit <= 0:
|
||||
return InputGuardrailResult(
|
||||
tripwire_triggered=True,
|
||||
output_info="Rate limit exceeded. Please upgrade."
|
||||
)
|
||||
return InputGuardrailResult(tripwire_triggered=False)
|
||||
```
|
||||
|
||||
## LLM-Based Guardrails
|
||||
|
||||
Use another LLM to check content:
|
||||
|
||||
```python
|
||||
from agents import input_guardrail, InputGuardrailResult, Agent, Runner
|
||||
|
||||
moderation_agent = Agent(
|
||||
name="moderator",
|
||||
instructions="Return 'SAFE' or 'UNSAFE: reason' for the given text.",
|
||||
)
|
||||
|
||||
@input_guardrail
|
||||
async def llm_moderation(ctx, agent, input_text: str) -> InputGuardrailResult:
|
||||
"""Use an LLM to moderate content."""
|
||||
result = await Runner.run(moderation_agent, input_text)
|
||||
|
||||
if result.final_output.startswith("UNSAFE"):
|
||||
return InputGuardrailResult(
|
||||
tripwire_triggered=True,
|
||||
output_info=result.final_output
|
||||
)
|
||||
|
||||
return InputGuardrailResult(tripwire_triggered=False)
|
||||
```
|
||||
|
||||
## Global Guardrails
|
||||
|
||||
Apply guardrails to all runs:
|
||||
|
||||
```python
|
||||
from agents import Runner, RunConfig
|
||||
|
||||
config = RunConfig(
|
||||
input_guardrails=[check_input_length, rate_limit_check],
|
||||
output_guardrails=[check_pii],
|
||||
)
|
||||
|
||||
result = await Runner.run(agent, user_input, run_config=config)
|
||||
```
|
||||
|
||||
## Handling Tripwires
|
||||
|
||||
```python
|
||||
from agents import (
|
||||
Runner,
|
||||
InputGuardrailTripwireTriggered,
|
||||
OutputGuardrailTripwireTriggered,
|
||||
)
|
||||
|
||||
try:
|
||||
result = await Runner.run(agent, user_input)
|
||||
except InputGuardrailTripwireTriggered as e:
|
||||
print(f"Input blocked: {e.guardrail_result.output_info}")
|
||||
except OutputGuardrailTripwireTriggered as e:
|
||||
print(f"Output blocked: {e.guardrail_result.output_info}")
|
||||
```
|
||||
|
||||
## Multiple Guardrails
|
||||
|
||||
Guardrails run in order. First tripwire stops execution:
|
||||
|
||||
```python
|
||||
agent = Agent(
|
||||
name="multi_guard",
|
||||
instructions="...",
|
||||
input_guardrails=[
|
||||
check_length, # Runs first
|
||||
check_language, # Runs second
|
||||
check_content, # Runs third
|
||||
],
|
||||
output_guardrails=[
|
||||
check_pii,
|
||||
check_formatting,
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Agents](agents.md) - Creating agents
|
||||
- [Running Agents](running_agents.md) - Execution
|
||||
- [Tracing](tracing.md) - Debug guardrails
|
||||
|
||||
+155
-2
@@ -1,2 +1,155 @@
|
||||
# handoffs
|
||||
Documentation coming soon.
|
||||
# Handoffs
|
||||
|
||||
Handoffs allow agents to delegate tasks to specialized sub-agents.
|
||||
|
||||
## Basic Handoffs
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
|
||||
# Specialist agents
|
||||
billing_agent = Agent(
|
||||
name="billing",
|
||||
instructions="Handle billing inquiries, refunds, and payments.",
|
||||
handoff_description="Handles billing, payments, and refunds.",
|
||||
)
|
||||
|
||||
tech_agent = Agent(
|
||||
name="tech_support",
|
||||
instructions="Help with technical issues and troubleshooting.",
|
||||
handoff_description="Handles technical support and troubleshooting.",
|
||||
)
|
||||
|
||||
# Main agent with handoffs
|
||||
main_agent = Agent(
|
||||
name="support",
|
||||
instructions="Route customers to the appropriate specialist.",
|
||||
handoffs=[billing_agent, tech_agent],
|
||||
)
|
||||
```
|
||||
|
||||
## Handoff Decorator
|
||||
|
||||
For more control over handoff behavior:
|
||||
|
||||
```python
|
||||
from agents import Agent, handoff, Handoff
|
||||
|
||||
@handoff
|
||||
def to_billing(context) -> Handoff:
|
||||
"""Transfer to billing specialist."""
|
||||
return Handoff(
|
||||
target=billing_agent,
|
||||
input_filter=lambda items: items[-5:], # Last 5 messages
|
||||
)
|
||||
|
||||
main_agent = Agent(
|
||||
name="support",
|
||||
instructions="Route to specialists as needed.",
|
||||
handoffs=[to_billing],
|
||||
)
|
||||
```
|
||||
|
||||
## Handoff Input Filter
|
||||
|
||||
Control what conversation history transfers:
|
||||
|
||||
```python
|
||||
from agents import Handoff, HandoffInputFilter
|
||||
|
||||
def last_n_messages(n: int) -> HandoffInputFilter:
|
||||
"""Only send last N messages to new agent."""
|
||||
def filter_fn(items):
|
||||
return items[-n:]
|
||||
return filter_fn
|
||||
|
||||
billing_handoff = Handoff(
|
||||
target=billing_agent,
|
||||
input_filter=last_n_messages(3),
|
||||
)
|
||||
```
|
||||
|
||||
## Conditional Handoffs
|
||||
|
||||
```python
|
||||
from agents import Agent, handoff, Handoff, RunContextWrapper
|
||||
|
||||
@handoff
|
||||
def smart_handoff(ctx: RunContextWrapper) -> Handoff | None:
|
||||
"""Conditionally hand off based on context."""
|
||||
if ctx.context.get("is_vip"):
|
||||
return Handoff(target=vip_agent)
|
||||
elif ctx.context.get("issue_type") == "billing":
|
||||
return Handoff(target=billing_agent)
|
||||
return None # No handoff
|
||||
```
|
||||
|
||||
## Handoff Data
|
||||
|
||||
Pass data to the new agent:
|
||||
|
||||
```python
|
||||
from agents import Handoff, HandoffInputData
|
||||
|
||||
handoff = Handoff(
|
||||
target=specialist_agent,
|
||||
input_data=HandoffInputData(
|
||||
summary="Customer needs help with order #12345",
|
||||
metadata={"order_id": "12345", "priority": "high"},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Tracking Handoffs
|
||||
|
||||
```python
|
||||
from agents import Runner, RunHooks
|
||||
|
||||
class HandoffTracker(RunHooks):
|
||||
async def on_handoff(self, context, from_agent, to_agent):
|
||||
print(f"Handoff: {from_agent.name} -> {to_agent.name}")
|
||||
|
||||
result = await Runner.run(
|
||||
main_agent,
|
||||
"I need help with my bill",
|
||||
run_hooks=HandoffTracker(),
|
||||
)
|
||||
|
||||
# Check which agent completed the run
|
||||
print(f"Handled by: {result.last_agent.name}")
|
||||
```
|
||||
|
||||
## Recursive Handoffs
|
||||
|
||||
Agents can hand off to agents that also have handoffs:
|
||||
|
||||
```python
|
||||
level1 = Agent(name="level1", handoffs=[level2])
|
||||
level2 = Agent(name="level2", handoffs=[level3])
|
||||
level3 = Agent(name="level3", instructions="Final handler")
|
||||
```
|
||||
|
||||
## Preventing Infinite Loops
|
||||
|
||||
Use `max_turns` to prevent circular handoffs:
|
||||
|
||||
```python
|
||||
from agents import Runner, RunConfig
|
||||
|
||||
config = RunConfig(max_turns=10)
|
||||
result = await Runner.run(agent, input_text, run_config=config)
|
||||
```
|
||||
|
||||
## Handoff vs Tools
|
||||
|
||||
| Feature | Handoffs | Tools |
|
||||
|---------|----------|-------|
|
||||
| Control flow | Transfers to new agent | Returns to same agent |
|
||||
| Context | New agent takes over | Same agent continues |
|
||||
| Use case | Specialization | Actions/data retrieval |
|
||||
|
||||
## See Also
|
||||
|
||||
- [Agents](agents.md) - Creating agents
|
||||
- [Multi-Agent](multi_agent.md) - Complex workflows
|
||||
- [Running Agents](running_agents.md) - Execution
|
||||
|
||||
+203
-2
@@ -1,2 +1,203 @@
|
||||
# models
|
||||
Documentation coming soon.
|
||||
# Models
|
||||
|
||||
Configure and use different LLM providers.
|
||||
|
||||
## Supported Models
|
||||
|
||||
### OpenAI
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
|
||||
# GPT-4o (recommended)
|
||||
agent = Agent(name="gpt4", model="gpt-4o")
|
||||
|
||||
# GPT-4o mini (faster, cheaper)
|
||||
agent = Agent(name="mini", model="gpt-4o-mini")
|
||||
|
||||
# GPT-4 Turbo
|
||||
agent = Agent(name="turbo", model="gpt-4-turbo")
|
||||
```
|
||||
|
||||
### Anthropic (via Hanzo)
|
||||
|
||||
```python
|
||||
agent = Agent(name="claude", model="claude-3-5-sonnet-20241022")
|
||||
agent = Agent(name="opus", model="claude-3-opus-20240229")
|
||||
```
|
||||
|
||||
### Other Providers
|
||||
|
||||
```python
|
||||
# Gemini
|
||||
agent = Agent(name="gemini", model="gemini-pro")
|
||||
|
||||
# Mistral
|
||||
agent = Agent(name="mistral", model="mistral-large")
|
||||
```
|
||||
|
||||
## Model Providers
|
||||
|
||||
### OpenAI Provider (default)
|
||||
|
||||
```python
|
||||
from agents import OpenAIProvider, RunConfig
|
||||
|
||||
provider = OpenAIProvider(
|
||||
api_key="sk-...", # Or use OPENAI_API_KEY env var
|
||||
)
|
||||
|
||||
config = RunConfig(model_provider=provider)
|
||||
```
|
||||
|
||||
### Hanzo Node Provider
|
||||
|
||||
```python
|
||||
from agents import create_hanzo_node_provider, RunConfig
|
||||
|
||||
provider = create_hanzo_node_provider(
|
||||
api_key="your-key", # Or use HANZO_API_KEY env var
|
||||
)
|
||||
|
||||
config = RunConfig(model_provider=provider)
|
||||
result = await Runner.run(agent, "Hello!", run_config=config)
|
||||
```
|
||||
|
||||
### Custom Base URL
|
||||
|
||||
```python
|
||||
from agents import OpenAIProvider
|
||||
|
||||
# Use Azure OpenAI
|
||||
provider = OpenAIProvider(
|
||||
api_key="azure-key",
|
||||
base_url="https://your-resource.openai.azure.com/",
|
||||
)
|
||||
|
||||
# Use local model (Ollama, vLLM, etc.)
|
||||
provider = OpenAIProvider(
|
||||
api_key="not-needed",
|
||||
base_url="http://localhost:11434/v1",
|
||||
)
|
||||
```
|
||||
|
||||
## Custom Model Implementation
|
||||
|
||||
```python
|
||||
from agents import Model, ModelProvider
|
||||
|
||||
class MyModel(Model):
|
||||
async def complete(
|
||||
self,
|
||||
messages: list[dict],
|
||||
tools: list[dict] | None = None,
|
||||
**kwargs,
|
||||
) -> dict:
|
||||
# Your implementation
|
||||
response = await my_api_call(messages, tools)
|
||||
return {
|
||||
"content": response.text,
|
||||
"tool_calls": response.tool_calls,
|
||||
}
|
||||
|
||||
class MyProvider(ModelProvider):
|
||||
def get_model(self, model_name: str) -> Model:
|
||||
return MyModel(model_name)
|
||||
```
|
||||
|
||||
## Model Settings
|
||||
|
||||
```python
|
||||
from agents import Agent, ModelSettings
|
||||
|
||||
agent = Agent(
|
||||
name="creative",
|
||||
model="gpt-4o",
|
||||
model_settings=ModelSettings(
|
||||
temperature=0.9, # More creative
|
||||
top_p=0.95,
|
||||
max_tokens=2000,
|
||||
presence_penalty=0.1,
|
||||
frequency_penalty=0.1,
|
||||
),
|
||||
)
|
||||
|
||||
agent = Agent(
|
||||
name="precise",
|
||||
model="gpt-4o",
|
||||
model_settings=ModelSettings(
|
||||
temperature=0.1, # More deterministic
|
||||
max_tokens=500,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Model Tracing
|
||||
|
||||
Enable detailed model tracing:
|
||||
|
||||
```python
|
||||
from agents import ModelTracing
|
||||
|
||||
class TracedModel(Model):
|
||||
tracing: ModelTracing = ModelTracing.ENABLED
|
||||
|
||||
async def complete(self, messages, tools=None, **kwargs):
|
||||
# Automatically traced
|
||||
...
|
||||
```
|
||||
|
||||
## Response Models
|
||||
|
||||
### Chat Completions
|
||||
|
||||
Standard OpenAI-compatible response:
|
||||
|
||||
```python
|
||||
from agents import OpenAIChatCompletionsModel
|
||||
|
||||
model = OpenAIChatCompletionsModel("gpt-4o")
|
||||
```
|
||||
|
||||
### Responses API
|
||||
|
||||
For models supporting the newer responses format:
|
||||
|
||||
```python
|
||||
from agents import OpenAIResponsesModel
|
||||
|
||||
model = OpenAIResponsesModel("gpt-4o")
|
||||
```
|
||||
|
||||
## Model Selection Strategy
|
||||
|
||||
```python
|
||||
from agents import Agent, RunConfig
|
||||
|
||||
# Development: faster, cheaper
|
||||
dev_config = RunConfig(model="gpt-4o-mini")
|
||||
|
||||
# Production: best quality
|
||||
prod_config = RunConfig(model="gpt-4o")
|
||||
|
||||
# Use based on environment
|
||||
import os
|
||||
config = prod_config if os.environ.get("ENV") == "prod" else dev_config
|
||||
|
||||
result = await Runner.run(agent, "Hello!", run_config=config)
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `OPENAI_API_KEY` | OpenAI API key |
|
||||
| `OPENAI_BASE_URL` | Custom OpenAI-compatible endpoint |
|
||||
| `ANTHROPIC_API_KEY` | Anthropic API key |
|
||||
| `HANZO_API_KEY` | Hanzo API key |
|
||||
|
||||
## See Also
|
||||
|
||||
- [Configuration](config.md) - Model settings
|
||||
- [Agents](agents.md) - Using models with agents
|
||||
- [Running Agents](running_agents.md) - Runtime model selection
|
||||
|
||||
+227
-2
@@ -1,2 +1,227 @@
|
||||
# multi agent
|
||||
Documentation coming soon.
|
||||
# Multi-Agent Systems
|
||||
|
||||
Build complex workflows with multiple cooperating agents.
|
||||
|
||||
## Agent Teams
|
||||
|
||||
Create specialized agents that work together:
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
|
||||
# Specialist agents
|
||||
researcher = Agent(
|
||||
name="researcher",
|
||||
instructions="Research topics thoroughly and provide facts.",
|
||||
handoff_description="Researches information and facts.",
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="writer",
|
||||
instructions="Write clear, engaging content based on research.",
|
||||
handoff_description="Writes content and articles.",
|
||||
)
|
||||
|
||||
editor = Agent(
|
||||
name="editor",
|
||||
instructions="Review and improve written content.",
|
||||
handoff_description="Edits and improves content quality.",
|
||||
)
|
||||
|
||||
# Coordinator
|
||||
coordinator = Agent(
|
||||
name="coordinator",
|
||||
instructions="""Coordinate the team to produce quality content.
|
||||
1. Use researcher for facts
|
||||
2. Use writer to draft content
|
||||
3. Use editor to polish""",
|
||||
handoffs=[researcher, writer, editor],
|
||||
)
|
||||
```
|
||||
|
||||
## Sequential Workflow
|
||||
|
||||
Agents pass work in sequence:
|
||||
|
||||
```python
|
||||
from agents import Agent, handoff, Handoff
|
||||
|
||||
@handoff
|
||||
def to_next_stage(ctx) -> Handoff:
|
||||
"""Move to the next processing stage."""
|
||||
stages = ctx.context.get("stages", [])
|
||||
current = ctx.context.get("current_stage", 0)
|
||||
|
||||
if current < len(stages):
|
||||
ctx.context["current_stage"] = current + 1
|
||||
return Handoff(target=stages[current])
|
||||
return None
|
||||
|
||||
stage1 = Agent(name="stage1", handoffs=[to_next_stage])
|
||||
stage2 = Agent(name="stage2", handoffs=[to_next_stage])
|
||||
stage3 = Agent(name="stage3", instructions="Final stage.")
|
||||
```
|
||||
|
||||
## Parallel Execution
|
||||
|
||||
Run multiple agents simultaneously:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from agents import Agent, Runner
|
||||
|
||||
agents = [
|
||||
Agent(name="analyst1", instructions="Analyze from perspective A"),
|
||||
Agent(name="analyst2", instructions="Analyze from perspective B"),
|
||||
Agent(name="analyst3", instructions="Analyze from perspective C"),
|
||||
]
|
||||
|
||||
async def parallel_analysis(prompt: str):
|
||||
tasks = [Runner.run(agent, prompt) for agent in agents]
|
||||
results = await asyncio.gather(*tasks)
|
||||
return [r.final_output for r in results]
|
||||
|
||||
# Combine results
|
||||
outputs = await parallel_analysis("Analyze this data")
|
||||
```
|
||||
|
||||
## Supervisor Pattern
|
||||
|
||||
One agent oversees others:
|
||||
|
||||
```python
|
||||
from agents import Agent, function_tool, Runner
|
||||
|
||||
workers = {
|
||||
"data": Agent(name="data_worker", instructions="Process data."),
|
||||
"analysis": Agent(name="analysis_worker", instructions="Analyze results."),
|
||||
}
|
||||
|
||||
@function_tool
|
||||
async def delegate(task_type: str, task: str) -> str:
|
||||
"""Delegate a task to a worker agent."""
|
||||
if task_type not in workers:
|
||||
return f"Unknown worker type: {task_type}"
|
||||
|
||||
result = await Runner.run(workers[task_type], task)
|
||||
return result.final_output
|
||||
|
||||
supervisor = Agent(
|
||||
name="supervisor",
|
||||
instructions="Coordinate workers to complete complex tasks.",
|
||||
tools=[delegate],
|
||||
)
|
||||
```
|
||||
|
||||
## Debate Pattern
|
||||
|
||||
Agents discuss and reach consensus:
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
|
||||
pro_agent = Agent(
|
||||
name="pro",
|
||||
instructions="Argue in favor of the proposition.",
|
||||
)
|
||||
|
||||
con_agent = Agent(
|
||||
name="con",
|
||||
instructions="Argue against the proposition.",
|
||||
)
|
||||
|
||||
judge_agent = Agent(
|
||||
name="judge",
|
||||
instructions="Evaluate arguments and reach a conclusion.",
|
||||
)
|
||||
|
||||
async def debate(topic: str, rounds: int = 3):
|
||||
history = [f"Topic: {topic}"]
|
||||
|
||||
for _ in range(rounds):
|
||||
# Pro argument
|
||||
pro_result = await Runner.run(pro_agent, "\n".join(history))
|
||||
history.append(f"Pro: {pro_result.final_output}")
|
||||
|
||||
# Con argument
|
||||
con_result = await Runner.run(con_agent, "\n".join(history))
|
||||
history.append(f"Con: {con_result.final_output}")
|
||||
|
||||
# Final judgment
|
||||
verdict = await Runner.run(judge_agent, "\n".join(history))
|
||||
return verdict.final_output
|
||||
```
|
||||
|
||||
## Router Pattern
|
||||
|
||||
Route requests to appropriate specialists:
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
|
||||
specialists = {
|
||||
"billing": Agent(name="billing", instructions="Handle billing."),
|
||||
"technical": Agent(name="technical", instructions="Handle tech issues."),
|
||||
"sales": Agent(name="sales", instructions="Handle sales inquiries."),
|
||||
}
|
||||
|
||||
router = Agent(
|
||||
name="router",
|
||||
instructions="""Route customer requests to the right specialist:
|
||||
- Billing questions → billing
|
||||
- Technical issues → technical
|
||||
- Purchase inquiries → sales""",
|
||||
handoffs=list(specialists.values()),
|
||||
)
|
||||
```
|
||||
|
||||
## Shared Context
|
||||
|
||||
Agents share state through context:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass, field
|
||||
from agents import Agent, Runner, function_tool
|
||||
|
||||
@dataclass
|
||||
class SharedState:
|
||||
findings: list[str] = field(default_factory=list)
|
||||
decisions: list[str] = field(default_factory=list)
|
||||
|
||||
@function_tool
|
||||
def add_finding(ctx, finding: str) -> str:
|
||||
"""Add a finding to shared state."""
|
||||
ctx.context.findings.append(finding)
|
||||
return f"Added finding: {finding}"
|
||||
|
||||
@function_tool
|
||||
def add_decision(ctx, decision: str) -> str:
|
||||
"""Record a decision."""
|
||||
ctx.context.decisions.append(decision)
|
||||
return f"Recorded: {decision}"
|
||||
|
||||
state = SharedState()
|
||||
result = await Runner.run(coordinator, "Analyze and decide", context=state)
|
||||
print(f"Findings: {state.findings}")
|
||||
print(f"Decisions: {state.decisions}")
|
||||
```
|
||||
|
||||
## Error Recovery
|
||||
|
||||
Handle agent failures:
|
||||
|
||||
```python
|
||||
from agents import Runner, AgentsException
|
||||
|
||||
async def run_with_fallback(primary: Agent, fallback: Agent, prompt: str):
|
||||
try:
|
||||
return await Runner.run(primary, prompt)
|
||||
except AgentsException:
|
||||
return await Runner.run(fallback, prompt)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Agents](agents.md) - Creating agents
|
||||
- [Handoffs](handoffs.md) - Agent delegation
|
||||
- [Context](context.md) - Shared state
|
||||
|
||||
+183
-2
@@ -1,2 +1,183 @@
|
||||
# results
|
||||
Documentation coming soon.
|
||||
# Results
|
||||
|
||||
Understanding and working with agent run results.
|
||||
|
||||
## RunResult
|
||||
|
||||
The result of a completed agent run:
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
|
||||
agent = Agent(name="assistant", instructions="Be helpful.")
|
||||
result = await Runner.run(agent, "Hello!")
|
||||
|
||||
# Access the result
|
||||
print(result.final_output) # Final text output
|
||||
print(result.last_agent) # Agent that produced output
|
||||
print(result.new_items) # All conversation items
|
||||
print(result.usage) # Token usage
|
||||
```
|
||||
|
||||
## RunResult Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `final_output` | `str` | Final text response |
|
||||
| `last_agent` | `Agent` | Agent that completed the run |
|
||||
| `new_items` | `list[RunItem]` | All items from the run |
|
||||
| `usage` | `Usage` | Token usage statistics |
|
||||
| `input_guardrail_results` | `list` | Input guardrail results |
|
||||
| `output_guardrail_results` | `list` | Output guardrail results |
|
||||
|
||||
## Conversation Items
|
||||
|
||||
Access all items from the conversation:
|
||||
|
||||
```python
|
||||
from agents.items import (
|
||||
MessageOutputItem,
|
||||
ToolCallItem,
|
||||
ToolCallOutputItem,
|
||||
HandoffCallItem,
|
||||
)
|
||||
|
||||
for item in result.new_items:
|
||||
match item:
|
||||
case MessageOutputItem(content=content):
|
||||
print(f"Message: {content}")
|
||||
case ToolCallItem(tool_name=name, arguments=args):
|
||||
print(f"Tool call: {name}({args})")
|
||||
case ToolCallOutputItem(output=output):
|
||||
print(f"Tool result: {output}")
|
||||
case HandoffCallItem(target_agent=agent):
|
||||
print(f"Handoff to: {agent.name}")
|
||||
```
|
||||
|
||||
## Token Usage
|
||||
|
||||
```python
|
||||
usage = result.usage
|
||||
|
||||
print(f"Input tokens: {usage.input_tokens}")
|
||||
print(f"Output tokens: {usage.output_tokens}")
|
||||
print(f"Total tokens: {usage.total_tokens}")
|
||||
```
|
||||
|
||||
## Structured Output
|
||||
|
||||
When using output schemas:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from agents import Agent, Runner
|
||||
|
||||
class Response(BaseModel):
|
||||
answer: str
|
||||
confidence: float
|
||||
|
||||
agent = Agent(
|
||||
name="structured",
|
||||
instructions="Always provide confidence.",
|
||||
output_type=Response,
|
||||
)
|
||||
|
||||
result = await Runner.run(agent, "What is 2+2?")
|
||||
|
||||
# Parsed output
|
||||
response: Response = result.final_output_parsed
|
||||
print(response.answer) # "4"
|
||||
print(response.confidence) # 0.99
|
||||
```
|
||||
|
||||
## Guardrail Results
|
||||
|
||||
```python
|
||||
# Input guardrail results
|
||||
for gr in result.input_guardrail_results:
|
||||
print(f"Guardrail: {gr.guardrail_name}")
|
||||
print(f"Triggered: {gr.tripwire_triggered}")
|
||||
print(f"Info: {gr.output_info}")
|
||||
|
||||
# Output guardrail results
|
||||
for gr in result.output_guardrail_results:
|
||||
print(f"Guardrail: {gr.guardrail_name}")
|
||||
print(f"Triggered: {gr.tripwire_triggered}")
|
||||
```
|
||||
|
||||
## Streaming Results
|
||||
|
||||
For streaming runs:
|
||||
|
||||
```python
|
||||
from agents import Runner
|
||||
|
||||
stream = Runner.run_streamed(agent, "Hello!")
|
||||
|
||||
# Collect events
|
||||
async for event in stream:
|
||||
print(event)
|
||||
|
||||
# Get final result
|
||||
result = stream.result
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
## Multi-Agent Results
|
||||
|
||||
When handoffs occur:
|
||||
|
||||
```python
|
||||
result = await Runner.run(main_agent, "Help with billing")
|
||||
|
||||
# Which agent finished?
|
||||
print(f"Completed by: {result.last_agent.name}")
|
||||
|
||||
# Trace the path
|
||||
agents_involved = set()
|
||||
for item in result.new_items:
|
||||
if hasattr(item, "agent"):
|
||||
agents_involved.add(item.agent.name)
|
||||
print(f"Agents involved: {agents_involved}")
|
||||
```
|
||||
|
||||
## Error Results
|
||||
|
||||
Handle errors gracefully:
|
||||
|
||||
```python
|
||||
from agents import Runner, MaxTurnsExceeded
|
||||
|
||||
try:
|
||||
result = await Runner.run(agent, user_input)
|
||||
print(result.final_output)
|
||||
except MaxTurnsExceeded as e:
|
||||
# Partial result available
|
||||
partial = e.partial_result
|
||||
print(f"Partial output: {partial.final_output}")
|
||||
print(f"Turns used: {len(partial.new_items)}")
|
||||
```
|
||||
|
||||
## Result Serialization
|
||||
|
||||
```python
|
||||
# To dict
|
||||
result_dict = {
|
||||
"output": result.final_output,
|
||||
"agent": result.last_agent.name,
|
||||
"usage": {
|
||||
"input": result.usage.input_tokens,
|
||||
"output": result.usage.output_tokens,
|
||||
},
|
||||
}
|
||||
|
||||
# To JSON
|
||||
import json
|
||||
json.dumps(result_dict)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Running Agents](running_agents.md) - Get results
|
||||
- [Streaming](streaming.md) - Streaming results
|
||||
- [Tracing](tracing.md) - Debug results
|
||||
|
||||
@@ -1,2 +1,178 @@
|
||||
# running agents
|
||||
Documentation coming soon.
|
||||
# Running Agents
|
||||
|
||||
Execute agents with the `Runner` class.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Synchronous
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
|
||||
agent = Agent(name="assistant", instructions="Be helpful.")
|
||||
|
||||
result = Runner.run_sync(agent, "Hello!")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
### Asynchronous
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from agents import Agent, Runner
|
||||
|
||||
agent = Agent(name="assistant", instructions="Be helpful.")
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(agent, "Hello!")
|
||||
print(result.final_output)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Run Configuration
|
||||
|
||||
```python
|
||||
from agents import Runner, RunConfig, ModelSettings
|
||||
|
||||
config = RunConfig(
|
||||
model="gpt-4o",
|
||||
model_settings=ModelSettings(temperature=0.5),
|
||||
max_turns=20,
|
||||
tracing_disabled=False,
|
||||
)
|
||||
|
||||
result = await Runner.run(agent, "Hello!", run_config=config)
|
||||
```
|
||||
|
||||
## RunConfig Options
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `model` | None | Override agent model |
|
||||
| `model_provider` | OpenAI | Model provider |
|
||||
| `model_settings` | None | Global model settings |
|
||||
| `max_turns` | 10 | Maximum conversation turns |
|
||||
| `input_guardrails` | None | Global input guardrails |
|
||||
| `output_guardrails` | None | Global output guardrails |
|
||||
| `tracing_disabled` | False | Disable tracing |
|
||||
|
||||
## Context
|
||||
|
||||
Pass custom context to tools and guardrails:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from agents import Agent, Runner, function_tool
|
||||
|
||||
@dataclass
|
||||
class MyContext:
|
||||
user_id: str
|
||||
permissions: list[str]
|
||||
|
||||
@function_tool
|
||||
def get_user_data(ctx: MyContext) -> str:
|
||||
return f"Data for user {ctx.user_id}"
|
||||
|
||||
agent = Agent(
|
||||
name="contextual",
|
||||
instructions="Access user data as needed.",
|
||||
tools=[get_user_data],
|
||||
)
|
||||
|
||||
context = MyContext(user_id="123", permissions=["read"])
|
||||
result = await Runner.run(agent, "Get my data", context=context)
|
||||
```
|
||||
|
||||
## Run Result
|
||||
|
||||
The `RunResult` contains:
|
||||
|
||||
```python
|
||||
result = await Runner.run(agent, "Hello!")
|
||||
|
||||
# Final output text
|
||||
print(result.final_output)
|
||||
|
||||
# All conversation items
|
||||
for item in result.new_items:
|
||||
print(item)
|
||||
|
||||
# Last agent that ran (for handoffs)
|
||||
print(result.last_agent.name)
|
||||
|
||||
# Input/output guardrail results
|
||||
print(result.input_guardrail_results)
|
||||
print(result.output_guardrail_results)
|
||||
|
||||
# Token usage
|
||||
print(result.usage)
|
||||
```
|
||||
|
||||
## Max Turns
|
||||
|
||||
Limit conversation turns to prevent infinite loops:
|
||||
|
||||
```python
|
||||
from agents import Runner, RunConfig
|
||||
|
||||
config = RunConfig(max_turns=5)
|
||||
|
||||
try:
|
||||
result = await Runner.run(agent, "Complex task", run_config=config)
|
||||
except MaxTurnsExceeded:
|
||||
print("Agent hit max turns limit")
|
||||
```
|
||||
|
||||
## Run Hooks
|
||||
|
||||
Monitor run lifecycle:
|
||||
|
||||
```python
|
||||
from agents import Runner, RunHooks
|
||||
|
||||
class MyRunHooks(RunHooks):
|
||||
async def on_agent_start(self, context, agent):
|
||||
print(f"Starting: {agent.name}")
|
||||
|
||||
async def on_tool_start(self, context, agent, tool):
|
||||
print(f"Calling tool: {tool.name}")
|
||||
|
||||
async def on_handoff(self, context, from_agent, to_agent):
|
||||
print(f"Handoff: {from_agent.name} -> {to_agent.name}")
|
||||
|
||||
result = await Runner.run(
|
||||
agent,
|
||||
"Hello!",
|
||||
run_hooks=MyRunHooks(),
|
||||
)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
from agents import (
|
||||
Runner,
|
||||
AgentsException,
|
||||
MaxTurnsExceeded,
|
||||
InputGuardrailTripwireTriggered,
|
||||
OutputGuardrailTripwireTriggered,
|
||||
)
|
||||
|
||||
try:
|
||||
result = await Runner.run(agent, user_input)
|
||||
except MaxTurnsExceeded:
|
||||
print("Too many turns")
|
||||
except InputGuardrailTripwireTriggered as e:
|
||||
print(f"Input blocked: {e}")
|
||||
except OutputGuardrailTripwireTriggered as e:
|
||||
print(f"Output blocked: {e}")
|
||||
except AgentsException as e:
|
||||
print(f"Agent error: {e}")
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Agents](agents.md) - Creating agents
|
||||
- [Streaming](streaming.md) - Stream responses
|
||||
- [Tracing](tracing.md) - Debug runs
|
||||
|
||||
+156
-2
@@ -1,2 +1,156 @@
|
||||
# streaming
|
||||
Documentation coming soon.
|
||||
# Streaming
|
||||
|
||||
Stream agent responses for real-time output.
|
||||
|
||||
## Basic Streaming
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
|
||||
agent = Agent(name="assistant", instructions="Be helpful.")
|
||||
|
||||
async def stream_response():
|
||||
async for event in Runner.run_streamed(agent, "Tell me a story"):
|
||||
if event.type == "raw_response_event":
|
||||
# Token-by-token output
|
||||
print(event.data, end="", flush=True)
|
||||
elif event.type == "agent_updated_event":
|
||||
# Agent changed (handoff)
|
||||
print(f"\n[Agent: {event.new_agent.name}]")
|
||||
```
|
||||
|
||||
## Stream Events
|
||||
|
||||
| Event Type | Description |
|
||||
|------------|-------------|
|
||||
| `raw_response_event` | Raw LLM response chunks |
|
||||
| `agent_updated_event` | Agent changed (handoff) |
|
||||
| `tool_call_event` | Tool being called |
|
||||
| `tool_output_event` | Tool returned result |
|
||||
| `run_item_event` | New run item added |
|
||||
|
||||
## Processing Events
|
||||
|
||||
```python
|
||||
from agents import Runner
|
||||
from agents.stream_events import (
|
||||
RawResponsesStreamEvent,
|
||||
AgentUpdatedStreamEvent,
|
||||
)
|
||||
|
||||
async for event in Runner.run_streamed(agent, user_input):
|
||||
match event:
|
||||
case RawResponsesStreamEvent(data=chunk):
|
||||
# Handle text chunk
|
||||
print(chunk, end="")
|
||||
|
||||
case AgentUpdatedStreamEvent(new_agent=new_agent):
|
||||
# Handle agent switch
|
||||
print(f"\n[Switched to: {new_agent.name}]")
|
||||
```
|
||||
|
||||
## Streaming with Context
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from agents import Agent, Runner
|
||||
|
||||
@dataclass
|
||||
class MyContext:
|
||||
user_id: str
|
||||
|
||||
context = MyContext(user_id="123")
|
||||
|
||||
async for event in Runner.run_streamed(
|
||||
agent,
|
||||
"Hello!",
|
||||
context=context,
|
||||
):
|
||||
print(event)
|
||||
```
|
||||
|
||||
## Streaming Result
|
||||
|
||||
Get the final result after streaming:
|
||||
|
||||
```python
|
||||
from agents import Runner, RunResultStreaming
|
||||
|
||||
stream = Runner.run_streamed(agent, "Hello!")
|
||||
result: RunResultStreaming = None
|
||||
|
||||
async for event in stream:
|
||||
print(event)
|
||||
result = stream.result
|
||||
|
||||
# After streaming completes
|
||||
print(f"Final output: {result.final_output}")
|
||||
print(f"Usage: {result.usage}")
|
||||
```
|
||||
|
||||
## Buffered Streaming
|
||||
|
||||
Collect output while streaming:
|
||||
|
||||
```python
|
||||
from agents import Runner
|
||||
|
||||
buffer = []
|
||||
|
||||
async for event in Runner.run_streamed(agent, "Hello!"):
|
||||
if event.type == "raw_response_event":
|
||||
buffer.append(event.data)
|
||||
print(event.data, end="")
|
||||
|
||||
full_response = "".join(buffer)
|
||||
```
|
||||
|
||||
## Streaming with Tools
|
||||
|
||||
Tool calls appear as events:
|
||||
|
||||
```python
|
||||
from agents import Runner
|
||||
|
||||
async for event in Runner.run_streamed(agent, "What's the weather?"):
|
||||
match event.type:
|
||||
case "tool_call_event":
|
||||
print(f"Calling: {event.tool_name}")
|
||||
case "tool_output_event":
|
||||
print(f"Result: {event.output}")
|
||||
case "raw_response_event":
|
||||
print(event.data, end="")
|
||||
```
|
||||
|
||||
## Streaming with Handoffs
|
||||
|
||||
```python
|
||||
from agents import Runner
|
||||
|
||||
current_agent = None
|
||||
|
||||
async for event in Runner.run_streamed(main_agent, "Help me"):
|
||||
if event.type == "agent_updated_event":
|
||||
current_agent = event.new_agent
|
||||
print(f"\n--- Transferred to {current_agent.name} ---\n")
|
||||
elif event.type == "raw_response_event":
|
||||
print(event.data, end="")
|
||||
```
|
||||
|
||||
## Error Handling in Streams
|
||||
|
||||
```python
|
||||
from agents import Runner, AgentsException
|
||||
|
||||
try:
|
||||
async for event in Runner.run_streamed(agent, user_input):
|
||||
print(event)
|
||||
except AgentsException as e:
|
||||
print(f"Stream error: {e}")
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Running Agents](running_agents.md) - Non-streaming execution
|
||||
- [Tracing](tracing.md) - Debug streams
|
||||
- [Results](results.md) - Result handling
|
||||
|
||||
+190
-2
@@ -1,2 +1,190 @@
|
||||
# tools
|
||||
Documentation coming soon.
|
||||
# Tools
|
||||
|
||||
Tools give agents the ability to take actions and access external data.
|
||||
|
||||
## Function Tools
|
||||
|
||||
The simplest way to create a tool:
|
||||
|
||||
```python
|
||||
from agents import Agent, function_tool
|
||||
|
||||
@function_tool
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get current weather for a city.
|
||||
|
||||
Args:
|
||||
city: The city name to get weather for
|
||||
"""
|
||||
# Your implementation
|
||||
return f"Weather in {city}: Sunny, 72°F"
|
||||
|
||||
agent = Agent(
|
||||
name="weather_bot",
|
||||
instructions="Help users with weather information.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
```
|
||||
|
||||
## Async Tools
|
||||
|
||||
```python
|
||||
@function_tool
|
||||
async def fetch_data(url: str) -> str:
|
||||
"""Fetch data from a URL."""
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as response:
|
||||
return await response.text()
|
||||
```
|
||||
|
||||
## Tools with Context
|
||||
|
||||
Access the run context in tools:
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from agents import function_tool, RunContextWrapper
|
||||
|
||||
@dataclass
|
||||
class AppContext:
|
||||
user_id: str
|
||||
api_key: str
|
||||
|
||||
@function_tool
|
||||
def get_user_profile(ctx: RunContextWrapper[AppContext]) -> str:
|
||||
"""Get the current user's profile."""
|
||||
user_id = ctx.context.user_id
|
||||
# Fetch profile using user_id
|
||||
return f"Profile for user {user_id}"
|
||||
```
|
||||
|
||||
## Tool Parameters
|
||||
|
||||
Pydantic models for complex parameters:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
from agents import function_tool
|
||||
|
||||
class SearchParams(BaseModel):
|
||||
query: str = Field(description="Search query")
|
||||
max_results: int = Field(default=10, description="Max results to return")
|
||||
include_metadata: bool = Field(default=False)
|
||||
|
||||
@function_tool
|
||||
def search(params: SearchParams) -> str:
|
||||
"""Search the knowledge base."""
|
||||
# Use params.query, params.max_results, etc.
|
||||
return f"Found results for: {params.query}"
|
||||
```
|
||||
|
||||
## Custom Tool Class
|
||||
|
||||
For more control, extend the `Tool` class:
|
||||
|
||||
```python
|
||||
from agents import Tool
|
||||
|
||||
class DatabaseTool(Tool):
|
||||
name = "query_database"
|
||||
description = "Query the application database"
|
||||
|
||||
def __init__(self, connection_string: str):
|
||||
self.conn = connect(connection_string)
|
||||
|
||||
async def run(self, query: str) -> str:
|
||||
result = await self.conn.execute(query)
|
||||
return str(result)
|
||||
|
||||
@property
|
||||
def parameters_schema(self) -> dict:
|
||||
return {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "SQL query to execute"
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Return Types
|
||||
|
||||
### String (default)
|
||||
|
||||
```python
|
||||
@function_tool
|
||||
def simple() -> str:
|
||||
return "Hello"
|
||||
```
|
||||
|
||||
### Structured (Pydantic)
|
||||
|
||||
```python
|
||||
class ToolResult(BaseModel):
|
||||
success: bool
|
||||
data: dict
|
||||
|
||||
@function_tool
|
||||
def structured() -> ToolResult:
|
||||
return ToolResult(success=True, data={"key": "value"})
|
||||
```
|
||||
|
||||
### List/Dict
|
||||
|
||||
```python
|
||||
@function_tool
|
||||
def get_items() -> list[str]:
|
||||
return ["item1", "item2", "item3"]
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
from agents import function_tool, ToolError
|
||||
|
||||
@function_tool
|
||||
def risky_operation(param: str) -> str:
|
||||
"""An operation that might fail."""
|
||||
try:
|
||||
result = do_something(param)
|
||||
return result
|
||||
except Exception as e:
|
||||
raise ToolError(f"Operation failed: {e}")
|
||||
```
|
||||
|
||||
## Tool Metadata
|
||||
|
||||
```python
|
||||
@function_tool(
|
||||
name="custom_name", # Override function name
|
||||
description="Custom description", # Override docstring
|
||||
)
|
||||
def my_tool(x: int) -> int:
|
||||
return x * 2
|
||||
```
|
||||
|
||||
## Multiple Tools
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
|
||||
agent = Agent(
|
||||
name="multi_tool",
|
||||
instructions="Use available tools to help users.",
|
||||
tools=[
|
||||
get_weather,
|
||||
search_web,
|
||||
calculate,
|
||||
send_email,
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Agents](agents.md) - Creating agents
|
||||
- [Running Agents](running_agents.md) - Execution
|
||||
- [Context](context.md) - Run context
|
||||
|
||||
+210
-2
@@ -1,2 +1,210 @@
|
||||
# tracing
|
||||
Documentation coming soon.
|
||||
# Tracing
|
||||
|
||||
Built-in observability for debugging and monitoring agent runs.
|
||||
|
||||
## Automatic Tracing
|
||||
|
||||
Tracing is enabled by default:
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
|
||||
agent = Agent(name="assistant", instructions="Be helpful.")
|
||||
result = await Runner.run(agent, "Hello!")
|
||||
|
||||
# Traces are automatically collected
|
||||
```
|
||||
|
||||
## Trace Structure
|
||||
|
||||
Each run creates a trace with spans:
|
||||
|
||||
```
|
||||
Trace (run_id)
|
||||
├── Agent Span (assistant)
|
||||
│ ├── LLM Call
|
||||
│ ├── Tool Call (get_weather)
|
||||
│ └── LLM Call
|
||||
└── Agent Span (specialist) # if handoff
|
||||
└── LLM Call
|
||||
```
|
||||
|
||||
## Custom Spans
|
||||
|
||||
Add custom spans for your code:
|
||||
|
||||
```python
|
||||
from agents import trace, Span
|
||||
|
||||
@trace("my_operation")
|
||||
async def my_function():
|
||||
# Automatically traced
|
||||
pass
|
||||
|
||||
# Or manually
|
||||
async def manual_trace():
|
||||
with Span("custom_span") as span:
|
||||
span.set_attribute("key", "value")
|
||||
# Your code here
|
||||
```
|
||||
|
||||
## Span Attributes
|
||||
|
||||
```python
|
||||
from agents import Span
|
||||
|
||||
with Span("process_data") as span:
|
||||
span.set_attribute("input_size", len(data))
|
||||
span.set_attribute("user_id", user_id)
|
||||
|
||||
result = process(data)
|
||||
|
||||
span.set_attribute("output_size", len(result))
|
||||
```
|
||||
|
||||
## Error Tracking
|
||||
|
||||
```python
|
||||
from agents import Span, SpanError
|
||||
|
||||
with Span("risky_operation") as span:
|
||||
try:
|
||||
result = risky_call()
|
||||
except Exception as e:
|
||||
span.record_error(SpanError(
|
||||
message=str(e),
|
||||
type=type(e).__name__,
|
||||
))
|
||||
raise
|
||||
```
|
||||
|
||||
## Agent Span Data
|
||||
|
||||
Access agent-specific span data:
|
||||
|
||||
```python
|
||||
from agents.tracing.span_data import AgentSpanData
|
||||
|
||||
# In hooks or custom code
|
||||
span_data = AgentSpanData(
|
||||
agent_name="assistant",
|
||||
model="gpt-4o",
|
||||
input_tokens=100,
|
||||
output_tokens=50,
|
||||
)
|
||||
```
|
||||
|
||||
## Accessing Current Trace
|
||||
|
||||
```python
|
||||
from agents import get_current_trace
|
||||
|
||||
trace = get_current_trace()
|
||||
if trace:
|
||||
print(f"Trace ID: {trace.trace_id}")
|
||||
print(f"Spans: {len(trace.spans)}")
|
||||
```
|
||||
|
||||
## Trace Export
|
||||
|
||||
### Console (default)
|
||||
|
||||
```python
|
||||
from agents import Runner, RunConfig
|
||||
|
||||
config = RunConfig(
|
||||
tracing_disabled=False, # Default
|
||||
)
|
||||
```
|
||||
|
||||
### Custom Exporter
|
||||
|
||||
```python
|
||||
from agents.tracing import TraceExporter
|
||||
|
||||
class MyExporter(TraceExporter):
|
||||
async def export(self, trace):
|
||||
# Send to your observability platform
|
||||
await send_to_datadog(trace)
|
||||
|
||||
# Register exporter
|
||||
from agents.tracing import register_exporter
|
||||
register_exporter(MyExporter())
|
||||
```
|
||||
|
||||
## Disabling Tracing
|
||||
|
||||
```python
|
||||
from agents import Runner, RunConfig
|
||||
|
||||
# Disable for a single run
|
||||
config = RunConfig(tracing_disabled=True)
|
||||
result = await Runner.run(agent, "Hello!", run_config=config)
|
||||
|
||||
# Disable globally
|
||||
import agents
|
||||
agents.tracing.disable()
|
||||
```
|
||||
|
||||
## Trace Context
|
||||
|
||||
Propagate trace context across services:
|
||||
|
||||
```python
|
||||
from agents import get_current_trace
|
||||
|
||||
# Get context to pass to another service
|
||||
trace = get_current_trace()
|
||||
context = {
|
||||
"trace_id": trace.trace_id,
|
||||
"span_id": trace.current_span.span_id,
|
||||
}
|
||||
|
||||
# In the other service
|
||||
from agents import trace_from_context
|
||||
with trace_from_context(context):
|
||||
# Operations here are linked to parent trace
|
||||
pass
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
Tracing adds minimal overhead:
|
||||
|
||||
| Operation | Overhead |
|
||||
|-----------|----------|
|
||||
| Span creation | ~1μs |
|
||||
| Attribute set | ~0.5μs |
|
||||
| Export (async) | Non-blocking |
|
||||
|
||||
## Integration
|
||||
|
||||
### OpenTelemetry
|
||||
|
||||
```python
|
||||
from agents.tracing.otel import OTelExporter
|
||||
|
||||
exporter = OTelExporter(
|
||||
endpoint="http://localhost:4317",
|
||||
service_name="my-agent-app",
|
||||
)
|
||||
register_exporter(exporter)
|
||||
```
|
||||
|
||||
### LangSmith
|
||||
|
||||
```python
|
||||
from agents.tracing.langsmith import LangSmithExporter
|
||||
|
||||
exporter = LangSmithExporter(
|
||||
api_key=os.environ["LANGSMITH_API_KEY"],
|
||||
project="my-project",
|
||||
)
|
||||
register_exporter(exporter)
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Running Agents](running_agents.md) - Execution
|
||||
- [Streaming](streaming.md) - Stream with traces
|
||||
- [Results](results.md) - Access trace data in results
|
||||
|
||||
Reference in New Issue
Block a user