Build Your First Agent
In this tutorial you'll build a small lead-research agent that looks up a company, drafts an outreach email, and returns the result. By the end you'll have a working agent and understand the four moving parts of every Waxell agent: @agent, @tool, @workflow, and the ctx object.
Prerequisites
- Python 3.10+
- A Waxell account (sign up)
pip install waxell-runtime waxell-observe
wax login
wax login writes your API credentials to ~/.waxell/config. Verify with:
wax whoami
Step 1: Author the agent
Create lead_research.py:
from waxell_runtime import agent, workflow, tool
@agent(name="lead_research", description="Research a lead and draft outreach")
class LeadResearchAgent:
@tool
async def fetch_company(self, ctx, domain: str) -> dict:
"""Look up a company by domain."""
return await ctx.domain("company", "lookup", domain=domain)
@workflow("draft_email")
async def draft_email(self, ctx, lead_id: str):
company = await ctx.tool("fetch_company", domain="acme.com")
email = await ctx.llm.generate(
prompt=f"Write a 3-sentence intro for {company['name']}",
output_format="text",
task="outreach_email",
)
return {"email": email, "company": company["name"]}
That's the whole agent. Three pieces:
@agentdeclares the container. Auto-discovers@tooland@workflowmethods on the class.@toolis a capability the workflow can invoke (ctx.tool("fetch_company", ...)).@workflowis the orchestration entry point.
Inside any workflow or tool, the ctx argument exposes:
| Call | Purpose |
|---|---|
await ctx.tool("name", ...) | Invoke another @tool on this agent |
await ctx.domain("entity", "action", ...) | Call a Waxell domain endpoint |
await ctx.llm.generate(prompt=..., ...) | Routed LLM call (cost-aware, policy-aware) |
ctx.secrets.get("KEY") | Read a secret (don't use os.environ inside a workflow) |
Step 2: Push the agent to Waxell
wax push lead_research.py
wax push validates your code, registers the spec, and prints a link to the agent in the dashboard.
Verify it landed:
wax agents list
wax agents info lead_research
Step 3: Run it
The platform runs your agent — you don't instantiate LeadResearchAgent yourself. After @agent decoration, the class is replaced with an AgentSpec, which is what gets registered. Trigger a run from the dashboard, the API, or CLI:
wax run lead_research --workflow draft_email --input '{"lead_id":"L-001"}'
Open the trace in the dashboard to see the full call tree: fetch_company → company.lookup domain endpoint → llm.generate, with timing, cost, and policy decisions on every span.
Step 4: Iterate with Claude Code
If you use Claude Code, install the bundled skill so Claude understands the SDK conventions when you ask it to extend your agent:
wax claude-init
This drops a project-scoped skill at .claude/skills/waxell-runtime/SKILL.md. Claude activates it whenever it sees from waxell_runtime import ... in your project and will write idiomatic agent code for you.
What's next
- Workflows tutorial — multi-step orchestration, error handling, retries
- Governance tutorial — attach policies to tools, workflows, and agents
- Production tutorial — deploy patterns, secrets, env config
- SDK reference — every decorator and spec type