Skip to Content
ConceptsAgent loops

Agent loops

An agent loop is a loop that extends Agent instead of Workflow. Instead of a single run() that executes once per trigger, agent loops handle chat turns — each message from the playground or an external channel calls onChatMessage() and streams a reply back.

Use agent loops for conversational experiences: health concierges, intake assistants, triage chatbots, and anything where the user sends multiple messages in a session.

Shape

export class HealthLoop extends Agent { async onChatMessage({ messages, ctx }) { const user = await ctx.USER.getUser({ id: "patient-123" }); const result = await ctx.OLLIE.chat({ thinking: "high", system: "You're a health concierge.", intent: ctx.intent, messages, user, tools: ctx.tools(), }); return result.toUIMessageStreamResponse(); } }
PieceDescription
messagesThe chat transcript so far, in AI SDK UIMessage shape — flows unchanged from the web playground’s useChat.
ctx.OLLIE.chatPlatform-managed chat completion. Returns a stream you hand back with toUIMessageStreamResponse().
ctx.intentOptional intent string from the trigger or playground — use to branch behavior without changing the system prompt.
ctx.bodyThe JSON body from the trigger, if any.
ctx.USERSame org-wide user store as workflow loops. See Users.
this.envLoop variables and secrets, same as workflow loops. See Variables and secrets.

OLLIE.chat options

type OllieChatInput = { thinking?: "low" | "medium" | "high"; system?: string; intent?: string; messages: UIMessage[]; user?: User; tools?: LoopToolSpec[]; options?: { /** Inject the user's most recent completed scan into the system context. */ useLatestScan?: boolean; }; };
OptionDescription
thinkingReasoning depth for the model. Higher = more thorough, slower.
systemSystem prompt for this turn. Combine with the dashboard’s System prompt panel for a base prompt you extend in code.
userA User handle — required for useLatestScan and for threading identity across turns.
toolsTool specs from ctx.tools({ loops }) — see below.
options.useLatestScanWhen true, the platform injects the user’s latest completed facial scan (summary, measurements, intake) into context.

Declare workflow and voice tools

Agent loops can trigger other loops mid-conversation. Declare them with ctx.workflow() or ctx.voice(), then pass them to ctx.tools():

async onChatMessage({ messages, ctx }) { const intakeTool = ctx.workflow({ loopId: "intake-workflow-loop-id", description: "Start a facial scan intake for the patient", inputSchema: z.object({ patientId: z.string(), phoneNumber: z.string(), }), }); const callTool = ctx.voice({ loopId: "intake-voice-loop-id", description: "Start a voice intake call and return a join link", inputSchema: z.object({ reason: z.string() }), }); const result = await ctx.OLLIE.chat({ messages, tools: ctx.tools({ loops: { start_intake: intakeTool, start_call: callTool, }, }), }); return result.toUIMessageStreamResponse(); }

Workflow tools start a background run — the model receives { runId, workflowInstanceId, … }, not the child loop’s output. Voice tools return { runId, sessionId, joinUrl, callSid? } so the model can share the join link. Do not use workflow tools for synchronous API lookups where the model needs the response data inline (search, slots, booking). See Loop-as-tool for return shapes and when to call APIs directly instead.

Knowledge base

Upload documents under Storage → Knowledge base (PDF, TXT, or Markdown). At runtime, the platform exposes a searchKnowledgeBase tool to the model automatically — you don’t declare it in loop code. Use the knowledge base for clinic policies, product FAQs, dosing guides, or any reference material the agent should cite.

System prompt panel

Agent loops (and voice loops) have a System prompt panel on the /code route, separate from the loop’s TypeScript. The saved prompt is applied as the base system context for every turn. In code, pass an additional system string to OLLIE.chat to extend or override it for a specific turn.

Playground and sessions

Agent loops don’t produce runs in the workflow sense — they produce sessions (one per chat thread). Open the Playground tab to test turns interactively, and Sessions to browse past conversations.

To trigger an agent loop from the API, POST to the same loop trigger URL. The body becomes ctx.body; the response is a streaming chat reply rather than a runId.

Where to go next

  • Loop-as-tool — declare other loops as tools, return shapes, and limitations.
  • Users — carry memory and metadata across turns.
  • Voice loops — voice tools and ctx.startVoice.
  • Facial scan — pair with useLatestScan so the agent speaks to scan results.
Last updated on