Examples

Realistic workflow patterns — support, refunds, research, approvals, structured weather.

Examples

Each example is a pattern, not a toy snippet. Copy the shape, then replace the adapters (invoices, refunds, …) with your systems.

Shared setup for all examples:

import { Agent, Engine, TraceManager, tool } from "@shiro-sdk/core";
import { OpenAIPlugin } from "@shiro-sdk/openai";
import { z } from "zod";

const traces = new TraceManager();
const engine = new Engine({ events: traces });
engine.use(
  new OpenAIPlugin({
    apiKey: process.env.OPENAI_API_KEY!,
    model: "gpt-5",
  })
);

Customer support + invoice lookup#

Why: Support agents guess invoice state when they lack a tool. A typed lookup keeps numbers honest and shows up in Studio’s tool inspector.

When: Read-only account questions before any mutation.

const lookupInvoice = tool({
  name: "lookupInvoice",
  description: "Fetch invoice status and amount.",
  parameters: z.object({ invoiceId: z.string() }),
  execute: async ({ invoiceId }) => invoices.get(invoiceId),
});

const support = new Agent({
  name: "support",
  instructions: "Resolve billing questions. Call lookupInvoice before stating balances.",
  provider: "openai",
  tools: [lookupInvoice],
});

await engine.execute(support, "What is the status of INV-8821?", {
  sessionId: "customer_48",
});

How it works: The runner lets the model request lookupInvoice, validates args, executes, and continues. Session id keeps follow-ups continuous — see Memory & Sessions.


Refund with approval#

Why: Refunds move money. Natural-language “confirmation” is not a control. Mark the tool requiresApproval: true so the runner pauses and Studio can show the gate.

When: Any mutating payment / credit path.

const issueRefund = tool({
  name: "issueRefund",
  description: "Issue a refund for an order.",
  requiresApproval: true,
  approvalDescription: "Credits the customer payment method.",
  parameters: z.object({
    orderId: z.string(),
    amount: z.number().positive(),
  }),
  execute: async (input) => refunds.issue(input),
});

const refundAgent = new Agent({
  name: "refund",
  instructions: "Verify the order, then issue a refund only via issueRefund.",
  provider: "openai",
  tools: [lookupOrder, checkRefundPolicy, issueRefund],
});

See Human-in-the-loop. Make issueRefund idempotent before retries.


Scheduling / travel planning#

Why: Multi-step plans need several tools (weather, flights, hotels). One agent with narrow tools is clearer than one mega-prompt.

When: Itineraries, booking assistance, constraint-heavy planning.

const travel = new Agent({
  name: "travel",
  instructions: "Build a practical itinerary. Prefer tools over inventing schedules.",
  provider: "openai",
  tools: [weatherLookup, flightsSearch, hotelsSearch],
});

await engine.execute(travel, "4 days in Tokyo in late October, rain-aware.");

Inspect tool order and latency in Studio’s timeline.


Research workflow#

Why: Research benefits from retrieval + compaction + a specialist handoff. Dumping the web into the prompt does not scale.

When: Competitive briefs, incident writeups, multi-source summaries.

engine.registerAgent(marketAnalyst);

const researchLead = new Agent({
  name: "research-lead",
  instructions: "Gather sources, then hand off synthesis when evidence is ready.",
  provider: "openai",
  tools: [webResearch],
  handoff: researchHandoffStrategy,
});

Pair with Multi-Agent and memory compaction events in traces.


Approval before deleting resources#

Why: Deletes are irreversible. Gate them explicitly.

const deleteResource = tool({
  name: "deleteResource",
  requiresApproval: true,
  approvalDescription: "Irreversible delete.",
  parameters: z.object({ resourceId: z.string() }),
  execute: async ({ resourceId }) => cloud.delete(resourceId),
});

Operators review args in the approval center before execute runs.


Structured weather output#

Why: Downstream code needs fields, not a paragraph. Attach output on the agent.

When: Any result another system will parse.

const weatherOutput = z.object({
  city: z.string(),
  condition: z.string(),
  temperature: z.number(),
});

const weatherAgent = new Agent({
  name: "weather",
  instructions: "Return structured weather only.",
  provider: "openai",
  output: weatherOutput,
});

const result = await engine.execute(weatherAgent, "Sample weather for Pune: 24C, cloudy.");

console.log(result.output.temperature);

See Structured Outputs and examples/basic-agent in the repo.


Multi-agent delegation (support → billing)#

Why: Billing mutations should not live on the triage agent.

engine.registerAgent(billingAgent);

const triage = new Agent({
  name: "support-triage",
  instructions: "Classify issues. Hand off billing.",
  provider: "openai",
  tools: [classifyIssue],
  handoff: toBillingWhenNeeded,
});

Watch the handoff on Studio’s execution graph.


Exporting for Studio#

import { JsonTraceExporter } from "@shiro-sdk/core";

const json = await traces.export(new JsonTraceExporter());
// Load / replay in Studio, or attach to an issue

See Tracing and Studio.