Agents
An Agent is a declarative description of behavior. It says what the model is responsible
for, which provider to use, which tools it may request, and (optionally) what shape the final
output must have.
An agent does not execute runs. The Runner does.
import { Agent, tool } from "@shiro-sdk/core";
import { z } from "zod";
const lookupInvoice = tool({
name: "lookupInvoice",
description: "Fetch an invoice by id.",
parameters: z.object({
invoiceId: z.string(),
}),
execute: async ({ invoiceId }) => invoices.get(invoiceId),
});
const support = new Agent({
name: "support",
instructions: "Help users with account and billing questions. Prefer tools over guessing.",
provider: "openai",
tools: [lookupInvoice],
});Why agents are declarative#
If agents mutate themselves per request — rewriting instructions, attaching the current user, stashing conversation history — they stop being reusable configuration. You cannot cache them, test them in isolation, or register them safely for handoffs.
| Put this on the agent | Put this elsewhere |
|---|---|
| Role instructions | User identity → session |
| Tool allowlist | Conversation history → session / memory |
| Output schema | Secrets → env / provider plugin |
| Provider id | One-off overrides → execute options |
Required fields#
| Field | Purpose |
|---|---|
name | Identity in traces, events, and registries |
instructions | Role and policy for the model |
provider | Provider instance or registered provider id (e.g. "openai") |
Optional: tools, output, guardrails, middleware, memory, sessionStore,
humanApproval, handoff, tracer, events, metadata.
Builder API#
Same immutable result, fluent construction:
const agent = Agent.builder()
.name("support")
.instructions("Help users with billing questions.")
.provider("openai")
.tool(lookupInvoice)
.output(
z.object({
summary: z.string(),
nextStep: z.enum(["close", "escalate", "refund_review"]),
})
)
.build();When to create multiple agents#
Create another agent when responsibility changes, not when a prompt feels long.
Good splits:
- triage vs billing vs research
- planner vs executor
- intake vs remediation
Bad splits:
- “short prompt agent” and “long prompt agent” with the same tools
- one agent per user
Register specialists with the engine when using handoffs — see Multi-Agent.
Structured output#
When another system consumes the answer, attach an output schema. Shiro validates before
returning result.output. See Structured Outputs.
Best practices#
- Narrow tools to what the role should actually call
- Prefer explicit tools over burying business rules in a giant prompt
- Keep instructions stable; put user facts in sessions
- Name agents for operators (
billing,refund) — those names show up in Studio
Common mistakes#
- Omitting
provider(required) - Adding every tool “just in case”
- Mutating
instructionsper user - Implementing handoffs by starting a second
execute()manually