Tool calling

Tools are how agents reach outside the prompt — to query the knowledge graph, look up records, delegate to a team lead, or plan a write. This page covers the mechanics: what a tool definition looks like, how the multi-round loop works, and how errors flow back to the agent.

Tool definition shape

Each tool is declared in the registry (agents/registry.py) as a ToolDef and converted to the provider's tool-call format at dispatch. A definition carries its parameters, how it executes, and — for writes — whether it needs confirmation:

ToolDef(
    name="quick_lookup",
    description="Search any module's records directly without delegating.",
    params=[
        ToolParam("table", "string", "Table to query", required=True,
                  enum=["companies", "contacts", "deals", "projects", "invoices", ...]),
        ToolParam("query", "string", "Search term (name, title, etc.)"),
        ToolParam("limit", "integer", "Max results (default 10)"),
    ],
    method="GET",            # GET reads · POST/PATCH/DELETE write
    requires_confirmation=False,   # True for writes → plan-and-confirm
    autonomy="gated",        # gated | autonomous (writes only)
)

The description field is what the model reads to decide when to use the tool. Write it like a help line: short, action-oriented, explicit about what the tool returns.

Multi-round semantics

An agent can call tools across several rounds in a single user turn, serialized as a tool-use loop:

user: "Which of my late invoices are on stalled projects?"
  └─ round 1
       └─ tool_use(graph_search, {query: "overdue invoices"})
            ← tool_result: [3 nodes]
       └─ tool_use(graph_traverse, {from: invoice, edge: "billed_to", ...})
            ← tool_result: [companies → projects]
  └─ round 2
       └─ tool_use(quick_lookup, {table: "projects", status: "on_hold"})
            ← tool_result: [1 project]
  └─ final text + citations

The cap is 5 tool-use rounds per turn. Hitting it forces a final response — the model can't chain indefinitely. The same cap applies at every tier (orchestrator, lead, specialist), so a delegation chain has a bounded, predictable ceiling.

Several tools in one round

The model may emit multiple tool calls in a single response when they're independent. The platform runs them in order and returns one combined tool_result block on the next round, preserving order. From the model's view it's a single batched step, which keeps the context window compact. (Execution is sequential, not concurrent — the ceiling is the round cap, not wall-clock parallelism.)

Core orchestrator tools

ToolPurposeWhen it's picked
quick_lookupSingle-table read"How many open deals?" / "find company Acme"
graph_searchSemantic + fuzzy entity searchAlmost every run — the discovery primitive
graph_traverseWalk typed edges (multi-hop)Cross-module questions
graph_contextNeighbourhood around an entityGrounding an answer
graph_add_edgeRecord a typed relationshipLinking entities
project_marginsPer-project profitability in one call"Which retainers lose money?"
delegate_to_<module>_leadHand work to a domain leadAnything that touches a module's data

Module write tools (crm_create_company, finance_create_invoice, task_create, …) live on the team leads and specialists, not the orchestrator — see Subagents. The orchestrator reasons and routes; the leads own the writes.

Error handling

Tool failures return as tool_result blocks with an error flag and, when possible, a hint:

{
  "type": "tool_result",
  "tool_use_id": "toolu_01...",
  "is_error": true,
  "content": "quick_lookup found no matches. Try a broader query or a different table."
}

The agent can then adjust parameters and retry, substitute a different tool, answer with partial data (flagging the gap), or bail out to the user with what it tried. Transient LLM errors (rate limits, 5xx) are retried inside the client before the agent sees them — it gets a clean response or a final exhausted-retries error.

Writes never execute inline

No tool mutates real state during a turn. A tool with requires_confirmation=True (any POST/PATCH/DELETE) produces a planned action, not a live write. The plan is serialized and SHA-256 hashed at preview; the /confirm endpoint re-hashes at execution and rejects on any mismatch — so the write that runs is exactly the one you approved, byte for byte. This is the platform's only write path; there is no auto-execute tool.

Autonomy is a second axis on write tools:

  • gated (default) — withheld entirely from unattended runs (automations). Money, people, publishing, and deletes stay here.
  • autonomous — reversible, internal writes that an automation may run on its own.

An unattended run never even sees a gated tool, so it can't plan a call it has no human to confirm. See Subagents → Autonomy.

Telemetry

Every tool call is recorded in the turn trace with:

  • Tool name + input.
  • Latency.
  • Result size.
  • Success / error / retry count.

Visible in Settings → Agent runs → <run> → Tool calls — useful when a run "feels slow" and you want to see which call dominated.

Read next

  • Orchestrator — the root agent and its toolset.
  • Subagents — the leads and specialists that own the writes.