mirror of
https://github.com/hanzoai/agent.git
synced 2026-09-11 21:08:51 +00:00
The README runs, and make tests collects only tests/
The old README showed Web3Agent, a marketplace and a cli extension that do not import from hanzo-agent 0.1.1, a HANZO_ROUTER_URL nothing reads, and examples on gpt-4. It now shows a first agent and a function tool that ran on Python 3.9 and 3.12 against a local OpenAI-compatible server, how to point the client at api.hanzo.ai, and the extras whose names import. It also says what the defaults do: the Responses route unless chat_completions is set, traces posted to api.openai.com with the client's key unless tracing is off, the openai client's retry rules, and a <think> block kept in final_output. pytest picked up the stray scripts at the repo root, and test_simple_anthropic.py exits on import, so make tests stopped at collection. testpaths keeps it to tests/: 369 passed. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -2,347 +2,110 @@
|
||||
|
||||
# Hanzo Agent SDK
|
||||
|
||||
A powerful Python framework for building AI agents and multi-agent systems with built-in orchestration.
|
||||
`hanzo-agent` runs agents in Python. An agent is a model reached over an
|
||||
OpenAI-compatible `/v1` API, its instructions, and the Python functions it may
|
||||
call as tools. It installs as `hanzo-agent` and imports as `agents`.
|
||||
|
||||
## ✨ Features
|
||||
## Install
|
||||
|
||||
- 🤖 **Multi-Agent Networks**: Build systems where multiple specialized agents collaborate
|
||||
- 🧠 **Intelligent Routing**: Semantic, rule-based, and load-balanced routing strategies
|
||||
- 🛠️ **Powerful Tools**: Enhanced tool system with MCP (Model Context Protocol) support
|
||||
- 📊 **Shared State**: Agents can share information through network state
|
||||
- 🔄 **Orchestration**: Define complex workflows with parallel, conditional, and loop steps
|
||||
- 💾 **Memory System**: Long-term memory with vector search and reflection capabilities
|
||||
- ⚡ **UI Streaming**: Real-time updates for building responsive interfaces
|
||||
- 🔍 **Observability**: Built-in tracing and monitoring via Hanzo Cloud dashboard
|
||||
- 🌐 **Backend Flexibility**: Use with Hanzo Router for 100+ LLM providers
|
||||
|
||||
### Optional Extensions:
|
||||
|
||||
- 💎 **Web3 Integration** (`[web3]`): Wallet management, transactions, on-chain identity
|
||||
- 🔒 **TEE Support** (`[tee]`): Intel SGX, AMD SEV, NVIDIA H100 attestation and confidential computing
|
||||
- 🛒 **Marketplace** (`[marketplace]`): Decentralized agent service discovery and economics
|
||||
- 💻 **CLI** (`[cli]`): Command-line interface integration
|
||||
|
||||
### Core concepts:
|
||||
|
||||
1. [**Agents**](docs/agents.md): LLMs configured with instructions, tools, and memory
|
||||
2. [**Networks**](docs/networks-and-orchestration.md): Multi-agent systems with intelligent routing
|
||||
3. [**Workflows**](docs/networks-and-orchestration.md#orchestration-and-workflows): Orchestrate complex multi-step processes
|
||||
4. [**State & Memory**](docs/networks-and-orchestration.md#state-management): Shared state and long-term memory
|
||||
5. [**Tools**](docs/tools.md): Enhanced tool system with MCP support
|
||||
6. [**Tracing**](docs/tracing.md): Built-in tracking and observability
|
||||
|
||||
Explore the [examples](examples) directory to see the SDK in action, and read our [documentation](docs/) for more details.
|
||||
|
||||
Notably, our SDK [is compatible](https://openai.github.io/openai-agents-python/models/) with any model providers that support the Open AI Chat Completions API format.
|
||||
|
||||
## Get started
|
||||
|
||||
1. Set up your Python environment
|
||||
|
||||
```
|
||||
python -m venv env
|
||||
source env/bin/activate
|
||||
```
|
||||
|
||||
2. Install Hanzo Agent SDK
|
||||
Python 3.9 or newer.
|
||||
|
||||
```bash
|
||||
# Basic installation
|
||||
pip install hanzo-agent
|
||||
|
||||
# With Web3 support
|
||||
pip install "hanzo-agent[web3]"
|
||||
|
||||
# With TEE support
|
||||
pip install "hanzo-agent[tee]"
|
||||
|
||||
# With Marketplace support
|
||||
pip install "hanzo-agent[marketplace]"
|
||||
|
||||
# With CLI support
|
||||
pip install "hanzo-agent[cli]"
|
||||
|
||||
# Full installation (all extensions)
|
||||
pip install "hanzo-agent[full]"
|
||||
```
|
||||
|
||||
## Quick Examples
|
||||
|
||||
### Simple Agent
|
||||
## First agent
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
from openai import AsyncOpenAI
|
||||
from agents import Agent, Runner, set_default_openai_api, set_default_openai_client, set_tracing_disabled
|
||||
|
||||
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
|
||||
set_default_openai_client(AsyncOpenAI(base_url="http://localhost:11434/v1", api_key="ollama"))
|
||||
set_default_openai_api("chat_completions")
|
||||
set_tracing_disabled(True)
|
||||
|
||||
agent = Agent(name="Assistant", instructions="You are a helpful assistant.", model="qwen3:0.6b")
|
||||
result = Runner.run_sync(agent, "Write a haiku about recursion in programming.")
|
||||
print(result.final_output)
|
||||
|
||||
# Code within the code,
|
||||
# Functions calling themselves,
|
||||
# Infinite loop's dance.
|
||||
```
|
||||
|
||||
### Multi-Agent Network
|
||||
One run printed:
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
from agents.network import create_network, SemanticRouter
|
||||
|
||||
# Create specialized agents
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
instructions="You find and analyze information.",
|
||||
tools=[search_tool, analyze_tool],
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
name="Writer",
|
||||
instructions="You create content based on research.",
|
||||
tools=[format_tool],
|
||||
)
|
||||
|
||||
# Create a network
|
||||
network = create_network(
|
||||
agents=[researcher, writer],
|
||||
router=SemanticRouter(),
|
||||
default_model="gpt-4",
|
||||
)
|
||||
|
||||
# Run the network
|
||||
result = await network.run("Research and write about quantum computing")
|
||||
```
|
||||
Recursive function deferring calls,
|
||||
A loop unrolled with grace,
|
||||
Ends with a loop.
|
||||
```
|
||||
|
||||
### Orchestrated Workflow
|
||||
The three calls before the agent decide where it runs:
|
||||
|
||||
- `set_default_openai_client` names the endpoint and the key. Above it is a
|
||||
local server on port 11434, Ollama with `qwen3:0.6b` pulled. For the hosted
|
||||
API pass `base_url="https://api.hanzo.ai/v1"`, a Hanzo API key as `api_key`,
|
||||
and a model id from `https://api.hanzo.ai/v1/models`.
|
||||
- `set_default_openai_api("chat_completions")` sends `POST /v1/chat/completions`.
|
||||
Without it the SDK sends `POST /v1/responses` instead.
|
||||
- `set_tracing_disabled(True)` stops the trace export described under Tracing.
|
||||
|
||||
## Tools
|
||||
|
||||
A decorated function becomes a tool the model can call:
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
from agents.orchestration import Orchestrator, OrchestrationConfig
|
||||
|
||||
# Define agents (omitted: see Multi-Agent Network above)
|
||||
researcher = Agent(name="researcher", instructions="...")
|
||||
writer = Agent(name="writer", instructions="...")
|
||||
reviewer = Agent(name="reviewer", instructions="...")
|
||||
|
||||
# Orchestrator owns the agent registry and workflow execution
|
||||
orchestrator = Orchestrator(
|
||||
config=OrchestrationConfig(name="Content Pipeline"),
|
||||
)
|
||||
orchestrator.register_agent(researcher, capabilities=["research"])
|
||||
orchestrator.register_agent(writer, capabilities=["writing"])
|
||||
orchestrator.register_agent(reviewer, capabilities=["review"])
|
||||
|
||||
# Build a workflow from registered agents and execute it
|
||||
workflow = orchestrator.create_workflow_from_agents(
|
||||
name="Article Workflow",
|
||||
agents=["researcher", "writer", "reviewer"],
|
||||
)
|
||||
orchestrator.register_workflow(workflow)
|
||||
|
||||
result = await orchestrator.execute_workflow(
|
||||
workflow_id=workflow.id,
|
||||
input="Research and write about AI safety.",
|
||||
)
|
||||
print(result.success, result.output)
|
||||
```
|
||||
|
||||
For lower-level control — composing parallel, conditional, and loop steps directly — see `agents.orchestration.Workflow` plus the `Step` helpers (`Step.agent`, `Step.parallel`, `Step.conditional`, `Step.loop`).
|
||||
|
||||
(_Configure backend with `HANZO_ROUTER_URL` and `HANZO_API_KEY` environment variables_)
|
||||
|
||||
## Handoffs example
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
import asyncio
|
||||
|
||||
spanish_agent = Agent(
|
||||
name="Spanish agent",
|
||||
instructions="You only speak Spanish.",
|
||||
)
|
||||
|
||||
english_agent = Agent(
|
||||
name="English agent",
|
||||
instructions="You only speak English",
|
||||
)
|
||||
|
||||
triage_agent = Agent(
|
||||
name="Triage agent",
|
||||
instructions="Handoff to the appropriate agent based on the language of the request.",
|
||||
handoffs=[spanish_agent, english_agent],
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?")
|
||||
print(result.final_output)
|
||||
# ¡Hola! Estoy bien, gracias por preguntar. ¿Y tú, cómo estás?
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Functions example
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from agents import Agent, Runner, function_tool
|
||||
|
||||
from agents import function_tool
|
||||
|
||||
@function_tool
|
||||
def get_weather(city: str) -> str:
|
||||
return f"The weather in {city} is sunny."
|
||||
|
||||
|
||||
agent = Agent(
|
||||
name="Hello world",
|
||||
instructions="You are a helpful agent.",
|
||||
name="Weather",
|
||||
instructions="Answer with the get_weather tool.",
|
||||
tools=[get_weather],
|
||||
model="qwen3:0.6b",
|
||||
)
|
||||
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(agent, input="What's the weather in Tokyo?")
|
||||
print(result.final_output)
|
||||
# The weather in Tokyo is sunny.
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
print(Runner.run_sync(agent, "What is the weather in Tokyo?").final_output)
|
||||
```
|
||||
|
||||
## The agent loop
|
||||
With the setup above it printed `The weather in Tokyo is sunny.`
|
||||
|
||||
When you call `Runner.run()`, we run a loop until we get a final output.
|
||||
## Errors and retries
|
||||
|
||||
1. We call the LLM, using the model and settings on the agent, and the message history.
|
||||
2. The LLM returns a response, which may include tool calls.
|
||||
3. If the response has a final output (see below for more on this), we return it and end the loop.
|
||||
4. If the response has a handoff, we set the agent to the new agent and go back to step 1.
|
||||
5. We process the tool calls (if any) and append the tool responses messages. Then we go to step 1.
|
||||
|
||||
There is a `max_turns` parameter that you can use to limit the number of times the loop executes.
|
||||
|
||||
### Final output
|
||||
|
||||
Final output is the last thing the agent produces in the loop.
|
||||
|
||||
1. If you set an `output_type` on the agent, the final output is when the LLM returns something of that type. We use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) for this.
|
||||
2. If there's no `output_type` (i.e. plain text responses), then the first LLM response without any tool calls or handoffs is considered as the final output.
|
||||
|
||||
As a result, the mental model for the agent loop is:
|
||||
|
||||
1. If the current agent has an `output_type`, the loop runs until the agent produces structured output matching that type.
|
||||
2. If the current agent does not have an `output_type`, the loop runs until the current agent produces a message without any tool calls/handoffs.
|
||||
|
||||
## Common agent patterns
|
||||
|
||||
The Agent SDK is designed to be highly flexible, allowing you to model a wide range of LLM workflows including deterministic flows, iterative loops, and more. See examples in [`examples/agent_patterns`](examples/agent_patterns).
|
||||
Model calls go through the `openai` client and follow its rules. A 401, 402 or
|
||||
403 raises on the first answer (`AuthenticationError`, `APIStatusError`,
|
||||
`PermissionDeniedError`). A 429 or 5xx is retried twice and then raises;
|
||||
`AsyncOpenAI(..., max_retries=0)` sends it once. A 200 whose body is an error
|
||||
object rather than `choices` ends the run with `TypeError`. A
|
||||
`<think>…</think>` block left in the reply stays in `final_output`.
|
||||
|
||||
## Tracing
|
||||
|
||||
The Agent SDK automatically traces your agent runs, making it easy to track and debug the behavior of your agents. Tracing is extensible by design, supporting custom spans and a wide variety of external destinations, including [Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents), [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk), [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk), [Scorecard](https://docs.scorecard.io/docs/documentation/features/tracing#openai-agents-sdk-integration), and [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent). For more details about how to customize or disable tracing, see [Tracing](docs/tracing.md).
|
||||
Tracing is on by default. After each run the SDK posts the trace and its spans
|
||||
to `https://api.openai.com/v1/traces/ingest`, authenticated with the default
|
||||
client's key. Any other key is refused there, and the run logs
|
||||
`Tracing client error 401: ...`. Turn it off with `set_tracing_disabled(True)`
|
||||
or `OPENAI_AGENTS_DISABLE_TRACING=1`.
|
||||
|
||||
## Development (only needed if you need to edit the SDK/examples)
|
||||
|
||||
0. Ensure you have [`uv`](https://docs.astral.sh/uv/) installed.
|
||||
## Extras
|
||||
|
||||
```bash
|
||||
uv --version
|
||||
pip install "hanzo-agent[web3]" # agents.extensions.web3: AgentWallet, Web3Wallet, MpcClient
|
||||
pip install "hanzo-agent[tee]" # agents.extensions.tee: ConfidentialAgent, TEEProvider
|
||||
```
|
||||
|
||||
1. Install dependencies
|
||||
`agents.network` routes a request among several agents and
|
||||
`agents.orchestration` runs workflows over registered agents; both are in the
|
||||
base package.
|
||||
|
||||
## This repository
|
||||
|
||||
The root is also a Go module, `github.com/hanzoai/agent`, and `sdk/` holds
|
||||
Python, TypeScript and Go clients published under other names. This README
|
||||
covers `hanzo-agent` only.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
make sync
|
||||
make sync # uv sync --all-extras --all-packages --group dev
|
||||
make tests # uv run pytest
|
||||
```
|
||||
|
||||
2. (After making changes) lint/test
|
||||
|
||||
```
|
||||
make tests # run tests
|
||||
make mypy # run typechecker
|
||||
make lint # run linter
|
||||
```
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
We'd like to acknowledge the excellent work of the open-source community, especially:
|
||||
|
||||
- [Pydantic](https://docs.pydantic.dev/latest/) (data validation) and [PydanticAI](https://ai.pydantic.dev/) (advanced agent framework)
|
||||
- [MkDocs](https://github.com/squidfunk/mkdocs-material)
|
||||
- [Griffe](https://github.com/mkdocstrings/griffe)
|
||||
- [uv](https://github.com/astral-sh/uv) and [ruff](https://github.com/astral-sh/ruff)
|
||||
|
||||
We're committed to continuing to build the Agent SDK as an open source framework so others in the community can expand on our approach.
|
||||
|
||||
## Extension Examples
|
||||
|
||||
### Web3 Agent
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
from agents.extensions.web3 import Web3Agent, AgentWallet
|
||||
|
||||
# Create wallet for agent
|
||||
wallet = AgentWallet.from_mnemonic("your mnemonic here")
|
||||
|
||||
# Create Web3-enabled agent
|
||||
trader = Web3Agent(
|
||||
name="Crypto Trader",
|
||||
instructions="You are a cryptocurrency trading agent",
|
||||
wallet=wallet
|
||||
)
|
||||
|
||||
# Agent can now use wallet tools
|
||||
result = await Runner.run(trader, "Check my ETH balance")
|
||||
```
|
||||
|
||||
### TEE (Confidential) Agent
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner
|
||||
from agents.extensions.tee import ConfidentialAgent, TEEProvider
|
||||
|
||||
# Create agent that runs in TEE
|
||||
confidential_agent = ConfidentialAgent(
|
||||
name="Secure Agent",
|
||||
instructions="You handle sensitive data",
|
||||
tee_provider=TEEProvider.INTEL_SGX
|
||||
)
|
||||
|
||||
# Generate attestation
|
||||
attestation = await confidential_agent.generate_attestation()
|
||||
print(f"Attestation: {attestation.quote}")
|
||||
```
|
||||
|
||||
### Marketplace Agent
|
||||
|
||||
```python
|
||||
from agents import Agent
|
||||
from agents.extensions.marketplace import AgentMarketplace, ServiceOffer, ServiceType
|
||||
|
||||
# Create agent that offers services
|
||||
service_agent = Agent(
|
||||
name="Research Agent",
|
||||
instructions="You conduct research"
|
||||
)
|
||||
|
||||
# Create service offer
|
||||
offer = ServiceOffer(
|
||||
id="research-1",
|
||||
agent_address="0x...",
|
||||
agent_name="Research Agent",
|
||||
service_type=ServiceType.RESEARCH,
|
||||
price_eth=0.01,
|
||||
description="Comprehensive research services"
|
||||
)
|
||||
|
||||
# Register on marketplace
|
||||
marketplace = AgentMarketplace()
|
||||
await marketplace.register_offer(offer)
|
||||
```
|
||||
|
||||
|
||||
@@ -160,6 +160,7 @@ exclude_also = [
|
||||
]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
asyncio_mode = "auto"
|
||||
asyncio_default_fixture_loop_scope = "session"
|
||||
filterwarnings = [
|
||||
|
||||
Reference in New Issue
Block a user