# Cuga Build Agent

> Use when the user wants to write Python code that creates or invokes a CugaAgent or CugaSupervisor, e.g. "build an agent with cuga", "how do I call agent.invoke", "set up a multi-agent supervisor".

- Skill: `cuga-project/cuga-build-agent` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cuga-project/cuga-build-agent`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cuga-project/cuga-build-agent/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: cuga-project (https://skillmd.com/u/cuga-project)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cuga-project/cuga-build-agent

---


# Building with the CugaAgent SDK

## Single agent

```python
from cuga import CugaAgent
from langchain_core.tools import tool
import asyncio

@tool
def add_numbers(a: int, b: int) -> int:
    """Add two numbers together"""
    return a + b

agent = CugaAgent(tools=[add_numbers])

async def main():
    result = await agent.invoke("What is 5 + 3?")
    print(result.answer)

asyncio.run(main())
```

Key points:
- Tools are plain LangChain `@tool`-decorated functions (or an OpenAPI/MCP provider — see `cuga-build-tool`).
- `await agent.invoke(message, thread_id=...)` — `thread_id` isolates conversation state per user/session; omit it for a one-off call.
- `agent.stream()` gives real-time execution events instead of a single final result.
- `agent.policies` is the entry point for attaching Intent Guard / Playbook / Tool Approval / Tool Guide / Output Formatter policies programmatically — see `cuga-author-policy`.
- Knowledge/RAG is on by default (`enable_knowledge=True`) — see `cuga-knowledge-rag`; pass `enable_knowledge=False` to turn it off.
- The underlying LangGraph graph is reachable for advanced use cases (custom nodes, inspecting state) if the simple API isn't enough.

## Multi-agent (CugaSupervisor)

```python
from cuga import CugaAgent, CugaSupervisor
from langchain_core.tools import tool
import asyncio

@tool
def get_customers(limit: int = 10) -> str:
    """Fetch top customers from CRM."""
    return "Alice ($250k); Bob ($180k)"

@tool
def send_email(to: str, body: str) -> str:
    """Send an email."""
    return f"Email sent to {to}"

async def main():
    crm_agent = CugaAgent(tools=[get_customers])
    crm_agent.description = "CRM and customer data"

    email_agent = CugaAgent(tools=[send_email])
    email_agent.description = "Sending emails and notifications"

    supervisor = CugaSupervisor(agents={"crm": crm_agent, "email": email_agent})
    result = await supervisor.invoke("Get our top customer and email them a thank-you")
    print(result.answer)

asyncio.run(main())
```

- Each sub-agent needs a `.description` — the supervisor uses it to decide who handles what.
- Mix local `CugaAgent`s with remote agents via A2A: pass an `"agent_name": {"type": "external", "description": "...", "config": {"a2a_protocol": {...}}}` entry in `agents=`.
- Pass data between sub-agents with `variables=["var_name"]`.
- `CugaSupervisor.from_yaml("path/to/config.yaml")` loads agents from a config file instead of constructing them in code.
- Try it live first: `uv run cuga start demo_supervisor` (see `cuga-install-and-launch`).

## Reference

Full SDK docs: https://docs.cuga.dev/docs/sdk/cuga_agent/ and https://docs.cuga.dev/docs/sdk/cuga_supervisor

