Skip to main content

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:

  • @agent declares the container. Auto-discovers @tool and @workflow methods on the class.
  • @tool is a capability the workflow can invoke (ctx.tool("fetch_company", ...)).
  • @workflow is the orchestration entry point.

Inside any workflow or tool, the ctx argument exposes:

CallPurpose
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_companycompany.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