# Agent Builder

> Create and operate autonomous trading agents the minimal way — create the agent from just its role + purpose, prove it's alive by delegating it a question, then progressively improve it with routines and (optionally) a loop strategy.

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

---


You are helping the user build or operate an **autonomous trading agent**. Agents live
under `agents/{slug}/` and are distinct from you (the interactive Condor assistant). You
drive them via `manage_agents`, `manage_strategies`, `control_agent`, `manage_routines`,
`trading_agent_journal_read` and `delegate`.

## Mental model — start minimal, improve in layers

An **Agent** is a specialist with an **essence**: a domain it understands and a role it
plays. It is defined in `agents/{slug}/AGENT.md` (its brain/system prompt). There is only
ONE kind of thing — an Agent. "Expert" is not a separate type; it's just an agent being
asked something.

**Every agent can do both things from the moment it exists** — be delegated a task
(`delegate`, whether that is a one-line question or a multi-step build) and run on a loop
(`control_agent(action="start")`). There is no capability flag, nothing to enable, and
no such thing as an "advisory-only" or "loop-only" agent. The layers below add *quality*, never capability.

The whole point of this skill is to build the agent in the **smallest useful step first,
then layer capability on only when the user wants it.** Do NOT front-load routines,
strategies, executors, or model questions. The progression is:

1. **Create the agent from just its role + what it's for.** Nothing else required. The
   moment it exists it can already be delegated to and looped.
2. **Delegate it a question to prove it's alive.** Ask it something inside its specialty
   and show the answer. This is the agent working end-to-end.
3. **Improve it with routines** — give it structured market data of its own. Define one,
   create it, run it, look at the output together. This is what turns a guessing LLM into
   a real specialist.
4. **(Optional) Give its loop a dedicated playbook** — a strategy the engine runs on a
   tick. The agent can already loop without one (it ticks a default playbook driven by
   its own brain); a strategy is how you make that loop specific and disciplined. The
   loop does NOT have to trade: it can read a routine's output and decide to trade, send
   a report, or do nothing — at a frequency the user sets.

Each layer is independently valuable. Most agents are worth creating and asking things
long before they ever get a routine, and many never need a loop at all.

```
agents/{slug}/
  AGENT.md                         # identity + role (the brain) — step 1
  routines/*.py                    # agent-scoped analysis scripts — step 3
  skills/{name}/SKILL.md           # the agent's own reusable playbooks
  strategies/{slug}/strategy.md    # OPTIONAL loop playbook — step 4 (a default
                                   # one is created on first start if there is none)
  sessions/session_N/              # run journals/snapshots (created at runtime)
```

Label each message with the current step, e.g. `[Step 2 — Ask it something]`.

## Step 1 — Create the agent (minimal)

When the user asks to create an agent, do NOT open with a config questionnaire
(exchange, pair, strategy, model…). In a sentence, frame how agents work here (create →
ask it something → improve with routines → optionally loop), then settle just two
things in a short conversation:

- **Role / domain** — what is this agent the specialist in? (e.g. "spread & inventory
  judgment for BRL market making", "executor selection for a given regime")
- **What it's used for** — the kind of question Condor should be able to ask it. This
  becomes `when_to_consult`.

That's enough to create it. **Pick the model from what the operator actually has** —
call `get_available_models` once and choose a sensible default for THIS agent's job (see
**Model selection** below for the heuristic); it's the easiest thing to change later.
Then create:

```
manage_agents(
    action="create",
    name="Executor Manager",
    description="Expert in deploying and tuning Hummingbot executors",
    agent_key="openrouter:anthropic/claude-sonnet-4-5",   # chosen from get_available_models; change anytime
    when_to_consult="When the user wants to deploy, tune, or stop an executor",
    tools=[],                              # leave open unless the user named tools
    instructions="<AGENT.md body — the agent's system prompt>"
)
```

> **Never invent an `agent_key`.** You cannot tell which backends are installed,
> running, or authenticated — a guessed key names a model that may not exist, and it
> only fails on its first run, long after creation "succeeded". Pass one *only*
> when the user named a specific model. `manage_servers(action="list")` reports the
> user's `active_agent_key` and their saved `custom_llm_endpoints` if you need to show
> or confirm the choice.

> **Leave `server_name` empty.** Same discipline, different field. An empty
> `server_name` means "follow whichever server the chat is on", which is right for
> almost every agent — and it is the only value that travels, because an agent is
> shared, committed, and read on machines whose server list is nothing like the
> creating operator's. Naming the server you happen to be on **pins** the agent to
> it: its `mcp-hummingbot` subprocess and every strategy it deploys use that server
> forever, regardless of the chat, and on anyone else's install it names a server
> that does not exist. Pass a name **only** when the user explicitly says this agent
> must always trade on that specific server. It is not a field to fill in helpfully
> because `server_required: true` sits next to it. The pin can be set or cleared
> later from the agent's page (the server chip beside its model), so leaving it
> empty costs nothing.

The **AGENT.md body (`instructions`)** is the brain — write it as the agent's own system
prompt, kept tight: **who it is** (its domain + what it explicitly does NOT handle),
**what it knows** (durable domain knowledge), and **how it answers** (lead with the
recommendation, key: value not prose). You can keep it short now and enrich it later with
`manage_agents(action="update")`. Note it owns scoped memory (`manage_memory`) and skills
(`manage_skill`).

`manage_agents(action="create")` returns `agent_slug` — use it for everything after.

Then tell the user plainly: **the agent is created. Now let's ask it something to check
it's alive.**

## Step 2 — Ask it something to prove it's alive

Test it the way another agent will use it. This is the one case where blocking is
right: the answer IS the check, it should be short, and the user is watching for it.

```
delegate(action="ask", agent="<agent_slug>",
         task="…a real question in its specialty…", context="…")
```

Show the answer. This proves the agent runs end-to-end. If the persona or answer is off,
fix the AGENT.md with `manage_agents(action="update", agent_slug=…, instructions=…)` and
ask again.

When the answer looks good, **stop and tell the user the agent already works as an
expert they can hand work to** — and that the next way to make it sharper is to give it
routines so it reasons over real structured data instead of guessing.

## Step 3 — Improve it with routines

A routine is how the agent pre-processes raw market data into the specific view its
specialty needs (a band scanner, a regime classifier, an inventory snapshot). Offer this
as the upgrade, then guide the user through it one routine at a time:

1. **Define** — agree on what this routine should output and why the agent needs it.
2. **Create** — hand the writing to a background worker
   (`delegate(action="start", agent="condor", task="...")`); it follows the
   `routine_cookbook` playbook and tests the routine before reporting. Tell it the
   target agent so it passes the right `agent`. Routines live at the agent level
   and are shared across every run and any future loop — pass the **agent slug**:
   ```
   manage_routines(action="create_routine", agent="<agent_slug>",
                   name="band_scanner", code="<python>")
   ```
3. **Analyze the output** — run it and read it together; iterate until it's clean and
   useful:
   ```
   manage_routines(action="run", agent="<agent_slug>",
                   name="band_scanner", config={…})
   ```

Then update the AGENT.md so the agent knows to call the routine by name and how to read
it, and ask it again to confirm it now reasons over that data. Repeat for each
routine the agent needs. **Stop here unless the user wants the agent to act on its own on
a loop.**

## ⚠️ Background delegation limits — ALWAYS respect these

Background delegate sessions have a **hard wall of 900 seconds (15 minutes)**. A task
that exceeds this is cut off mid-work, losing whatever was not yet committed.

### Sizing rule — one routine per delegation
The safe budget for a background routine-building task is **one routine + one round of
testing**. Building two or three routines in a single delegation will reliably hit the
wall during the test phase of the last routine.

**Always split large routine work across multiple sequential delegations**, one routine
each:
```
# WRONG — will time out:
delegate(task="Build routines A, B, and C for agent X")

# RIGHT — three tasks, triggered one after the other:
delegate(task="Build routine A for agent X. Test it with input Y. Report back.")
# (wait for completion, verify, then:)
delegate(task="Build routine B for agent X. Test it with input Y. Report back.")
```

If you are the background worker and you realize mid-task that you have been given too
much work, **stop, commit what you have**, and instruct the user (via the task result) to
trigger a follow-up delegation for the remaining items rather than racing the clock.

### Timeout is currently fixed at 900s for `delegate`
The `control_agent(action="start")` path exposes `tick_timeout_sec` in its config and
can be raised. The `delegate` shortcut does not yet expose this — it runs at the
system default. Until that is surfaced, the only mitigation is task sizing.

## Step 4 — (Optional) Give its loop a dedicated playbook

Only if the user wants the agent to act autonomously. The agent can already loop without
this step — `control_agent(action="start", strategy_id="<agent_slug>")` ticks a
default playbook driven by
its AGENT.md — but that default is deliberately generic. A **strategy** is the specific
tick playbook the engine runs in a **session**, and it is what you want for anything that
trades.

**How a strategy is authored, dry-run and launched lives in the shared `strategy_builder`
playbook — read it (`manage_skill(action="read", name="strategy_builder")`) and follow it,
passing `agent_slug="<agent_slug>"`.** It is the single source of truth, and it is shared
precisely so an agent can give *itself* a loop without coming back through you. Don't
restate its mechanics here; your job at this step is only to:

- decide **with the user** whether a dedicated strategy is warranted at all,
- make clear the loop does NOT have to trade — it can read routine X's output and decide
  to trade, send a report, or watch a condition — at a **frequency the user sets**
  (`frequency_sec`),
- then run `strategy_builder` for the agent you just created.

If the agent is capable enough to author its own loop, prefer handing it the job:
`delegate(action="start", agent="<agent_slug>", task="give yourself a loop that …")`. It
reads the same shared playbook and knows its own domain better than you do.

## Monitoring existing agents
1. `manage_agents(action="list")` — all agents, with their
   routing hint and owned strategies. Only list that shows agents owning no strategy.
2. `control_agent(action="list")` — running loop instances, with their status.
3. `trading_agent_journal_read(agent_id=…, section="summary"|"runs"|"run:N")`.

## Reference

**Capability rule:** there isn't one. Every agent is delegable and loopable on any
model; `when_to_consult` and owning a strategy are quality, not permission. The model
only changes *how* a run executes: a pydantic-ai key (`ollama:…`/`openai:…`/`groq:…`/
`lmstudio:…`) enforces the `tools` allowlist; an ACP key (`claude-code`/`gemini`/
`copilot`) runs unrestricted. Every run reached through `delegate` — `start` or `ask` —
is unattended: nobody is asked to approve its tool calls, so only hand work to agents
and tasks you trust.

**Writing the `when_to_consult` hint:** it never gates anything — it is how Condor
*picks* this agent over another, and it falls back to the description when unset. A vague
or missing one costs you routing accuracy, so write it well. Rules:
- **Lead with the user's action, not the domain** — "When the user wants to deploy,
  tune, or stop an executor" matches an intent; "Executor expertise" matches nothing.
- **Name the concrete verbs + nouns** the user would actually say (deploy/tune/stop,
  executor, grid, spread, inventory) so keyword overlap is high.
- **State the boundary** when two agents are close ("…executor deployment — NOT
  controller backtesting"). Same shape applies to a skill's `when_to_use`.

**Model selection:** Set per session, not baked in. The agent/strategy `agent_key` is the
default; override at launch via `config={"agent_key": "…"}`. **Recommend from what the
operator actually has — call `get_available_models` and pick for the agent's job. Do NOT
default to a hardcoded model.** The tool reports:
- `acp_clis` — subscription/CLI bridges (`claude-code`, `gemini`, `copilot`, `codex`) and
  whether each CLI is installed. No API key or per-token cost (rides the operator's
  Claude/ChatGPT subscription); runs unrestricted (does NOT enforce the `tools` allowlist).
  **`available` means installed, not signed in** — each bridge needs its own interactive
  login that Condor cannot probe. Never recommend one as if it were ready; name it as an
  option and ask the user to confirm they use it.
- `local` — `ollama` / `lmstudio`, each with the models currently **loaded** (empty =
  server not running). Free, private, offline; addressed as `ollama:<model>` /
  `lmstudio:<model>`. Only offer a local model that is actually loaded.
- `cloud_keys` — which of openrouter / openai / anthropic / groq / google keys are set.
- `custom_endpoints` — the user's own OpenAI-compatible endpoints (Venice, Together,
  a self-hosted vLLM…), each already validated, with the chat models it serves and a
  ready `agent_key` (`custom@<endpoint>:<model-id>`). These are the strongest signal
  in the whole report: the user configured them deliberately and Condor verified them
  reachable, so prefer one when it fits the job.
- `openrouter` — tool-capable catalog, cheapest first, each with a ready `agent_key`
  (`openrouter:<slug>`) and in/out $/Mtok. **The catalog is public — recommendations work
  with no key.** If `openrouter.key_present` is false, those models need `OPENROUTER_API_KEY`
  (web Settings) before they can run — say so and prefer a runnable option (a loaded local
  model, or an installed ACP CLI) unless the user wants to add a key.

Choose by the agent's job, not by habit:
- **Correctness-critical / high-capital** (decides real trades) → a strong model. Lead with
  a credential you can verify: if `openrouter.key_present` (or another `cloud_keys` provider)
  is true, recommend from there; offer an ACP bridge as the alternative to confirm, not the
  default. Pick a capable OpenRouter model (e.g. a Claude/GPT/DeepSeek-V3-class,
  not a tiny "flash" model — lightweight models drop instructions, e.g. answering in the
  wrong language). Validate a cheaper pick with a `dry_run` before going live.
- **Simple report / watch loop, or privacy / offline / zero-cost** → a loaded local model
  (`ollama:…`/`lmstudio:…`) or a cheap OpenRouter model.
- **Must be sandboxed to specific `tools`** → a pydantic-ai key
  (`openrouter:`/`ollama:`/`lmstudio:`/`openai:`/`groq:`); only these enforce the allowlist.
- **A saved custom endpoint** → `custom@<endpoint>:<model-id>` (e.g.
  `custom@Venice:claude-sonnet-4-6`). The URL and API key are resolved from the user's
  saved endpoints at run time — do NOT set `model_base_url` for these. Enforces the
  `tools` allowlist like any other pydantic-ai key.
  Default local URLs: Ollama=localhost:11434, LM Studio=localhost:1234.

Propose one sensible pick with a one-line why; offer the alternatives you saw. Don't turn
it into a questionnaire — it's the easiest thing to change later.

**Generic vs Specific strategies:**
- GENERIC (default): pair/connector are NOT in the instructions — passed at launch via
  `trading_context`. Refer to "the configured trading pair"; keep sensible `default_config`.
- SPECIFIC: pair/connector baked into the instructions (e.g. an ETH/BTC ratio play).

**Agent tools, memory & skills:** `tools` on the AGENT.md is a tool-name allowlist
enforced on pydantic-ai runs and loops (empty = unrestricted; not enforceable on ACP
keys). **A non-empty allowlist must include `run_code`** if the agent reads a market at
all: there is no candle, order book or funding tool to name any more (ARCH-308), so
`get_prices` plus `run_code` over `client.market_data.*` is the whole market data
surface. An allowlist that names neither leaves the agent able to trade a market it
cannot look at.

An agent keeps its own domain memory (`manage_memory`) and reusable playbooks
(`manage_skill`/`agents/{slug}/skills/`) — the agent OWNS skills; it is not itself a skill.
A new agent also reads the **shared** library (`agents/_shared/skills/`) from birth, so it
gets `routine_cookbook` and friends for free — write only what is specific to its domain,
and target it explicitly with `manage_skill(..., agent="<slug>")`.

**Editing & deleting:** read the current brain with `manage_agents(action="get",
agent_slug=…)`, edit with `manage_agents(action="update", agent_slug=…, instructions=…)`.
`manage_agents(action="delete", agent_slug=…)` refuses while the agent still owns
strategies — delete those first (`manage_strategies(action="delete", strategy_id=…)`).

## Rules
- **Minimal first.** Create the agent from just role + purpose; never open with a config
  questionnaire. Layer routines and loops on only when the user wants them.
- After creating, immediately steer to a **`delegate(action="ask")`** to prove it's
  alive before anything else.
- Only the step label as a header. Be direct; status as key: value.
- Every agent is delegable (always set `when_to_consult` — it is the routing hint).
- **Never invent an `agent_key` or a `server_name`.** Both default to "follow the
  operator" — pass either one only when the user named it. A guessed model or a
  helpfully-filled server pin fails on someone else's install, long after creation
  reported success.
- Create the AGENT.md FIRST — routines and strategies require an existing agent_slug.
- **One routine per background delegation.** Never bundle 2+ routines in one `delegate`
  call — always split and sequence them. If handed too much work as the background
  worker, commit what you have and instruct the user to trigger a follow-up.
- One routine per analysis task; run it and show the output before moving on.
- A loop doesn't have to trade — it can report or watch. Always include risk limits when
  it can trade, and dry-run before going live.
- Guide one step at a time and offer concrete proposals.

