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:
2026-09-11 11:01:26 -07:00
co-authored by Hanzo Dev
parent 56ff389fc7
commit 56add66c20
2 changed files with 65 additions and 301 deletions
+64 -301
View File
@@ -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)
```
+1
View File
@@ -160,6 +160,7 @@ exclude_also = [
]
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
filterwarnings = [