---
name: "gaia-build-agent"
description: "Build a new GAIA agent with the SDK end-to-end: scaffold the package, write the Agent subclass, register @tool functions, compose reusable tool mixins, set the model + system prompt, and test it locally. Use when creating a NEW agent (a Python class inheriting from the base Agent) for the GAIA repo or a user-authored package, not for tuning an existing agent's prompt (prompt-engineer), adding one tool to an existing class (python-developer). GAIA ships one agent (the flagship) through the TUI and Agent UI and has no public agent catalog, so there is no publish step for a user-built agent."
---

# Building a GAIA Agent

How to take an idea to a working, testable GAIA agent — a Python class that runs
locally on AI PCs. The full prose walkthrough is [`docs/guides/custom-agent.mdx`](../../../docs/guides/custom-agent.mdx).

> Read [`CLAUDE.md`](../../../CLAUDE.md) first — the "No Silent Fallbacks", code-reuse,
> testing, and eval rules all apply to a new agent.

## The mental model

A GAIA agent is **one Python class** that inherits from the base `Agent`
(`src/gaia/agents/base/agent.py`) and exposes capabilities as `@tool`-decorated
methods. The base class owns the agent loop, tool registry, state, error recovery,
and (via mixins) MCP / OpenAI-API exposure — you write the prompt + the tools, not
the plumbing. There are no YAML agent manifests for in-core agents (removed in
v0.17.5); an in-repo package under `hub/agents/` adds a `gaia-agent.yaml` manifest on top.

## Steps

1. **Scaffold.** `gaia agent init my-agent` generates a starter package at
   `./my-agent/`. For a publishable hub package in this repo, add `--layout hub`
   so it lands at `hub/agents/<id>/python/`:

   ```bash
   gaia agent init my-agent -o hub/agents/ --layout hub
   ```

   Then mirror an existing package for structure: `hello-world` or `word-count`
   for a minimal one, `email` for a full published agent.

2. **Write the `Agent` subclass.** Inherit from `Agent` (or a closer base like
   `ChatAgent`, which the flagship `GaiaAgent` extends). **Leave `model_id` unset** — that inherits the base default,
   `Gemma-4-E4B-it-GGUF` (`DEFAULT_MODEL_NAME`), which is right for nearly every agent.
   Every agent sharing one model is what keeps a single model resident, so switching
   agents never triggers an eviction + cold reload. Only set `model_id` when the agent
   genuinely needs a different model (e.g. the email agent picks the NPU model
   `gemma4-it-e2b-FLM` when one is present and servable), and say why. Keep the constructor thin.

3. **Register tools with `@tool`.** Each `@tool` method is a capability the LLM can
   call; its **docstring is the schema the model sees**, so write it for the model
   (one line of intent + each arg). Return structured data or an actionable error —
   never swallow exceptions into a placeholder (No Silent Fallbacks).

4. **Reuse, don't reinvent — compose mixins.** Before writing file/web/RAG/shell/SQL
   logic, check `KNOWN_TOOLS` in [`src/gaia/agents/registry.py`](../../../src/gaia/agents/registry.py):
   `rag`, `file_io`, `file_search`, `shell`, `browser`, `scratchpad`, `code_index`,
   `vlm`, `sd`, … Compose them by name instead of duplicating. New shared logic →
   add a mixin and register it in `KNOWN_TOOLS`.

5. **System prompt.** Implement `_get_system_prompt()` — state the agent's job, when
   to use which tool, and the output contract. This is an LLM-affecting surface, so
   it's covered by the eval rule below.

6. **Make it discoverable.** An **in-core** agent is added to
   [`src/gaia/agents/registry.py`](../../../src/gaia/agents/registry.py) (and a
   `gaia <cmd>` subparser in `src/gaia/cli.py` if it's a CLI command). A **hub
   package** is **auto-discovered from its `gaia-agent.yaml` manifest** — no
   `registry.py` edit; just make sure the manifest's `python.entry_module` /
   `entry_class` point at your class.

7. **Test it — actually run it.** Unit tests with a mocked LLM for tool logic
   (`tests/`), then run the **real CLI** a user would (`gaia <cmd> ...` or
   `python my_agent.py`) — never only the Python module. Follow the
   [`gaia-testing`](../gaia-testing/SKILL.md) skill for real-world evidence.

8. **Eval if it touches LLM behavior.** If you wrote/changed a system prompt, tool
   docstrings, the tool schema, the model, or error classification, you MUST run
   `gaia eval agent` against the relevant category before calling it done, and
   compare to a baseline only if one is committed for the same agent — none is
   yet, so report the scores you measured (CLAUDE.md eval rule). Unit tests don't
   catch LLM regressions.

## After it works

There is no publish step: GAIA ships one agent (the flagship) through the TUI and
the Agent UI, and has no public catalog to list a new agent in. A user-authored
agent runs from `~/.gaia/agents/<id>/`; an in-repo one is loaded through its entry
point. Keep its docs in sync per the CLAUDE.md doc-sync rule. Releasing one of the
agents GAIA itself ships (the email sidecar's frozen binary + npm client) is
maintainer work — the [`agent-hub-release`](../agent-hub-release/SKILL.md) skill.

## Reference

- [`docs/guides/custom-agent.mdx`](../../../docs/guides/custom-agent.mdx) — the full build walkthrough.
- [`src/gaia/agents/base/`](../../../src/gaia/agents/base/) — `Agent`, `MCPAgent`, `ApiAgent`, `@tool`, console, errors.
- [`src/gaia/agents/registry.py`](../../../src/gaia/agents/registry.py) — agent registry + `KNOWN_TOOLS`.
- The `gaia-agent-builder` agent (`.claude/agents/`) — a specialist for this work.

## Output style

Follow [CLAUDE.md](../../../CLAUDE.md) → "How You Communicate".
