Skip to content

Building agents

An agent is an AI worker your module ships: an LLM with a system prompt, a set of tools it may call, and a place in a hierarchy of other agents. You declare an agent the same way you declare a model — as a small, declarative Python class. Victor reads the class, registers the agent when your module installs, and renders it as an editable record in the app.

This page covers how to declare an agent, the full set of spec fields, how to compose agents into a hierarchy, and how agents behave as in-app records that operators can tune without touching code.

Declare an agent

Subclass victor.agents.Agent and set class attributes. This is a declarative spec — you set fields, you do not write runtime code. Give the agent a unique name, a model, an instructions prompt, and the tools it is allowed to call.

from victor import agents


class Researcher(agents.Agent):
    name = "researcher"                     # unique key; used in the run URL
    label = "Researcher"                    # human-readable label
    model = "google:gemini-3-flash-preview" # "provider:model-id"
    description = "Searches company contacts and reports what it finds."
    instructions = "You research the company's contacts. Use search_contacts and report concisely."
    tools = ["search_contacts"]             # names of tools this agent may call

A few things to note:

  • name is the agent's identity everywhere — it is how other agents reference it and it appears in the agent's run URL. Keep it unique and stable.
  • model is a "provider:model-id" string, e.g. anthropic:claude-sonnet-4-6 or google:gemini-3-flash-preview. The exact providers and models available depend on your instance.
  • tools lists the names of registered tools the agent may call. The tool code is wired into the module via module.yaml (see Register agents).

Tip

Write instructions like a job description: what the agent is for, which tools to reach for, and how to respond. The model follows this prompt on every run.

Spec fields

Every field is optional except name. Set only the ones you need; the rest fall back to the defaults below.

from victor import agents


class MyAgent(agents.Agent):
    name = "my_agent"          # unique key
    label = "My Agent"         # display label
    model = "anthropic:claude-sonnet-4-6"  # "provider:model-id"
    reasoning = "off"          # thinking level: "off" | "low" | "medium" | "high"
    instructions = ""          # system prompt
    description = ""           # what this agent does — read by a parent to decide when to delegate
    tools = []                 # names of registered tools it may call
    skills = []                # names of SKILL.md capability files it may read
    subagents = []             # agent names nested as sub-agents (delegation)
    capabilities = []          # agent names it may call laterally as peers
    directcontact = False      # True = entry point for end-user conversations
    all_tools = False          # True = also auto-gain every module's read-only tools
Field Type Default Purpose
name str required Unique agent key. Identifies the agent everywhere and appears in its run URL.
label str Human-readable label shown in the app.
model str The LLM, as a "provider:model-id" string.
reasoning str "off" Thinking level: "off", "low", "medium", or "high". In-app this field is labelled Thinking level. "off" disables provider thinking; higher values raise the thinking budget.
instructions str The system prompt / instructions for the agent.
description str Short statement of what the agent does. A parent agent reads this to decide when and how to delegate to it.
tools list[str] [] Names of registered tools the agent may call.
skills list[str] [] Names of SKILL.md capability files the agent may read on demand.
subagents list[str] [] Agent names nested as sub-agents (delegation).
capabilities list[str] [] Agent names the agent may call laterally as peers.
directcontact bool False When True, marks this agent as the entry point for end-user conversations.
all_tools bool False When True, the agent auto-gains every installed module's read-only tools on top of its declared tools, resolved per run.

Thinking level

reasoning controls how much the model deliberates before answering. "off" is the default and turns provider thinking off; "low", "medium", and "high" raise the thinking budget. Use a higher level for agents that plan multi-step work or reason over complex data, and leave it "off" for fast, straightforward responders.

Tools vs. skills

Both extend what an agent can do, but they are different in kind:

  • tools are actions the agent can take — registered functions it may call (read records, send an email, and so on). See Tools.
  • skills are SKILL.md files — instructions and knowledge the agent reads on demand, not executable code. Use a skill to teach the agent a procedure or give it reference material. See Skills.
from victor import agents


class Skillhost(agents.Agent):
    name = "skillhost"
    label = "Skillhost"
    model = "anthropic:claude-sonnet-4-6"
    instructions = "You greet users using the greeter skill."
    skills = ["greeter"]                # names of SKILL.md files this agent may read

all_tools

Setting all_tools = True grants the agent every installed module's read-only tools in addition to whatever it lists in tools, resolved fresh on each run. This is handy for a general-purpose front-door agent that should be able to read across the whole instance without you enumerating every tool by hand. Mutating and approval-gated tools are never auto-granted this way — list those explicitly in tools.

Register agents in module.yaml

Declaring an Agent class is not enough on its own — Victor needs to know which Python submodules to load. List them under the agents: key in your module.yaml, as dotted import paths relative to your module package. If your agents call custom tools, list the tool submodules under tools: as well.

name: assistant
label: Assistant
version: 0.1.0
requires: [contact, mail]        # dependencies installed first
agents: [agents.researcher, agents.manager, agents.frontdesk]
tools: [tools.contacts]          # tool modules an agent's `tools` list can reference

Here agents.frontdesk means the file agents/frontdesk.py inside your module package. Every Agent subclass in a listed submodule is registered when the module installs.

Build an agent hierarchy

Agents compose. A single agent can hand work to others in two distinct ways, and the difference matters:

  • subagentsnested delegation. The parent hands a whole slice of multi-step work down to a specialist sub-agent and gets the result back. Use this to break a big job into focused workers.
  • capabilitieslateral calls. The agent calls another agent as a peer service, the way you would call a shared function, rather than owning it as a child.

In both cases, the description field on the target agent is what a parent reads to decide when and why to route to it. Write descriptions that clearly state what the agent is good for.

Subagents (nested delegation)

Each name in subagents must be another declared agent. The child's description tells the parent when to hand off.

from victor import agents


class Helper(agents.Agent):
    name = "helper"
    label = "Helper"
    model = "anthropic:claude-sonnet-4-6"
    instructions = "You help."
    description = "Helps with things"   # parent reads this to decide when to delegate


class Boss(agents.Agent):
    name = "boss"
    label = "Boss"
    model = "anthropic:claude-sonnet-4-6"
    instructions = "Delegate to helper."
    description = "The boss"
    subagents = ["helper"]              # nests helper as a sub-agent

Capabilities (lateral peers)

List peer agent names in capabilities. The caller may invoke them as services without nesting them as children.

from victor import agents


class Target(agents.Agent):
    name = "target"
    label = "Target"
    model = "anthropic:claude-sonnet-4-6"
    instructions = "You are the target."
    description = "Does the target work"


class Caller(agents.Agent):
    name = "caller"
    label = "Caller"
    model = "anthropic:claude-sonnet-4-6"
    instructions = "Use the target capability."
    capabilities = ["target"]           # may call `target` as a lateral peer

A complete example

This front-desk agent brings the pieces together: a thinking level, a capability it can delegate to, the directcontact flag that makes it the conversation entry point, and all_tools so it can read across every installed module on top of its declared tool list.

from victor import agents


class Frontdesk(agents.Agent):
    name = "frontdesk"
    label = "Front Desk"
    model = "google:gemini-3-flash-preview"
    reasoning = "medium"               # thinking level
    description = "Greets users and answers questions about the company's data."
    instructions = "You are the front desk assistant. ..."
    tools = ["read_records", "create_record", "update_record", "send_email"]
    capabilities = ["researcher"]      # may delegate research laterally
    directcontact = True               # entry point for end-user conversations
    all_tools = True                   # also auto-gain every module's read-only tools

directcontact is the front door

Mark exactly the agent(s) meant to talk to end users with directcontact = True. That flag makes the agent an entry point for conversations; specialist sub-agents and capabilities usually leave it False and are reached only through a front-door agent.

Agents are editable in-app records

Every agent you declare is also a record under Settings → Agents. This lets an operator tune an agent's behaviour without a code change:

  • Module-defined (code) agents are seeded as read-only records — the class is the source of truth.
  • Agents created in the UI are fully editable records.

The in-app form exposes an agent's Model, Thinking level, Prompt (instructions), Tools, Skills, and its Subagents / Capabilities. Edits take effect on the agent's next run — there is nothing to redeploy.

Instance-wide overrides

Two System Parameters let an operator override every agent at once, which is useful for switching the whole instance to a cheaper or faster model during testing:

System Parameter Effect
agent.model Forces every agent's model (e.g. google:gemini-2.5-flash). Leave unset to fall back to each agent's own model.
agent.reasoning Forces every agent's thinking level (off / low / medium / high). Leave unset to fall back to each agent's own reasoning.

When set, these win over the per-agent values; when unset, each agent uses what it declares.

Next steps