Skip to main content

Agents

Create, configure, and run agents bound to a provider and a set of tools.

Overview

An agent is created from a provider plus its runtime config — model, instructions, sampling and step limits — and is a thin surface onto a runtime agent. Everything an agent is lives on the runtime (the source of truth) and is read from there when shaping responses; naturali stores nothing of its own about it, timestamps included.

Every Channels binding and customer app consumes agents from this registry. Runs happen through a Session; each model loop inside a session is a Generation.

See the OpenAPI spec for the full endpoint and schema reference, or browse it rendered under API Reference → Agents.

Data Model

Agent

FieldTypeDescription
idstringPublic agent ID (agent_ prefix) — the runtime agent id.
project_idstringThe owning project (proj_ prefix).
provider_idstringThe provider this agent is bound to (aip_ prefix).
namestringHuman-readable label.
modelstring, nullableModel the agent generates with; null falls back to the provider default.
instructionsstring, nullableSystem instructions guiding the agent's behavior.
temperaturenumber, nullableSampling temperature (0–2).
max_stepsinteger, nullableMaximum agent loop steps before stopping.
max_context_messagesinteger, nullableMaximum recent messages included in the context window.
tool_bindingsobject[]The tools bound to this agent — each { "tool_id": "tool_…" }.
tool_choicestring | object, nullableTool choice strategy — "auto" / "required" / "none", or { type: "tool", name }.
step_rulesarray, nullablePer-step tool_choice overrides — see Per-step tool choice.
output_schemaobject, nullableJSON Schema the model's output is constrained to — see Structured output. null leaves it unconstrained.
trace_content_modestring, nullableAgent-scope zero-retention setting: none never records this agent's content, full records it, null (the default) inherits the project — see Zero-retention agents.
created_atstring (date-time)
updated_atstring (date-time)

Key Concepts

Attaching tools

Attach tools to an agent with the agent's tool_bindings (and optional tool_choice) on create or update. A binding names one tool by reference and carries nothing else:

{ "tool_bindings": [{ "tool_id": "tool_V1StGXR8Z5jdHi6B" }] }

Each tool_id must be a tool registered in the same project; the set is replaced wholesale on every update — [] detaches all tools. Any other field inside a binding is a 400 naming it, so a misspelling cannot be stored as a binding that resolves to nothing.

Per-step tool choice

tool_choice governs every step of a generation's tool loop, with no relaxation once a step has called a tool — "required" forces a tool call on every step, including once the model already has everything it needs, so the run continues all the way to max_steps before it can stop. step_rules lets one step override tool_choice on its own: setting [{ step: 1, tool_choice: "required" }] forces a tool call on the first step only, leaving the agent's own tool_choice (e.g. "auto") in effect from the second step on — a model that skips the tool it needs on turn one no longer gets the chance to, without forcing every later turn into a tool call it doesn't need.

Both fields have exactly one spelling, and it is checked when you write them. A rule carries step and tool_choice and nothing else; the object form of tool_choice is { "type": "tool", "name": "…" } and nothing else. Anything outside that — a camelCase toolChoice or toolName, a strategy string other than auto, required or none — is a 400 naming the offending field, not a value that is stored and then ignored. This matters more here than elsewhere: a rule the runtime does not recognize is dropped rather than corrected, so a misspelled one would leave the agent running normally while quietly never forcing the tool call it was configured to force.

Structured output

Set output_schema to a JSON Schema and the agent stops returning prose: a generation is constrained to that schema and the parsed value comes back as object on the result.

naturali create-agent \
--project-id proj_V1StGXR8Z5jdHi6B \
--provider-id aip_V1StGXR8Z5jdHi6B \
--name triage-classifier \
--output-schema '{"type":"object","properties":{"approved":{"type":"boolean"}},"required":["approved"]}'

A generation from that agent carries the parsed value:

{ "status": "completed", "text": "{\"approved\":true}", "object": { "approved": true } }

Three things are worth knowing before you bind a schema:

The schema itself is validated by the model provider at generation time, not on write — the subset of JSON Schema accepted there is narrower than the standard, so a schema this API accepts can still be rejected when the agent runs. Only the JSON type is checked up front: an object or null, else 400.

Zero-retention agents

trace_content_mode controls whether this agent's trace and generation content — prompts, tool arguments, tool results, error payloads — is recorded at all.

ValueMeaning
null (default)Inherit the project's mode
noneZero-retention — this agent's content is never written
fullRecord content, even when other agents in the project do not

Set it on POST /v1/projects/{project_id}/agents or PATCH /v1/projects/{project_id}/agents/{agent_id}. This is the per-agent knob for the common case where one agent in a project handles sensitive material and the rest do not — you get zero-retention where you need it without giving up observability everywhere else.

An agent can tighten the project's setting, never loosen it

Setting full on an agent whose project is none is refused with 400 invalid_trace_content_mode. The project mode is a floor: if it were loosenable, a project-wide zero-retention mandate could be escaped simply by creating a new agent.

Sending null restores inheritance — which is why null and omitting the field mean different things on PATCH: omitting it leaves the current setting alone. Tightening to none stops future writes but does not erase content already recorded; purge that explicitly with DELETE /v1/projects/{project_id}/traces/{trace_id}/content.

Deletion and dependent resources

Deleting an agent that still has dependent generations or traces returns 409 conflict. Pass force=true to delete those along with the agent — this is destructive and irreversible.

Runtime defaults

Only provider_id is required on create; model, temperature, max_steps, and max_context_messages all fall back to the provider or platform default when omitted, so an agent can be created with a single field and tuned later.

Examples

Create an agent

naturali create-agent \
--project-id proj_V1StGXR8Z5jdHi6B \
--provider-id aip_V1StGXR8Z5jdHi6B \
--name support-triage \
--tool-bindings '[{"tool_id":"tool_V1StGXR8Z5jdHi6B"}]'

Get an agent

naturali get-agent \
--project-id proj_V1StGXR8Z5jdHi6B \
--agent-id agent_V1StGXR8Z5jdHi6B

Make an agent zero-retention

naturali update-agent \
--project-id proj_V1StGXR8Z5jdHi6B \
--agent-id agent_V1StGXR8Z5jdHi6B \
--trace-content-mode none

Send trace_content_mode: null to go back to inheriting the project's setting.