3.7 KiB
3.7 KiB
ToolLoopAgent Patterns
Deep dive on v6 agent workflows.
When to Use ToolLoopAgent
| Use Case | Approach |
|---|---|
| Single tool call | streamText with tools |
| Multi-step reasoning | ToolLoopAgent |
| Autonomous workflows | ToolLoopAgent with stopWhen |
| Complex orchestration | ToolLoopAgent with custom stop conditions |
Agent Configuration
import { ToolLoopAgent, stepCountIs } from 'ai';
const agent = new ToolLoopAgent({
// Required
model: 'anthropic/claude-sonnet-4.5',
// Optional
instructions: 'You are a research assistant.',
tools: { search, calculate, summarize },
// Stop conditions
stopWhen: stepCountIs(10),
// Or custom: async ({ steps }) => steps.length >= 10
// Tool selection
toolChoice: 'auto', // 'auto' | 'required' | 'none'
// Token limits
maxOutputTokens: 4096,
});
Execution Patterns
Non-Streaming (Simple)
const result = await agent.generate({
prompt: 'Research quantum computing breakthroughs.',
});
console.log(result.text);
console.log(result.steps); // Array of all steps
console.log(result.steps.length, 'steps executed');
Streaming (Real-time)
const stream = agent.stream({
prompt: 'Analyze this data and provide insights.',
});
for await (const chunk of stream.textStream) {
process.stdout.write(chunk);
}
UI Streaming (React/Next.js)
import { createAgentUIStream } from 'ai';
const stream = await createAgentUIStream({
agent,
messages: [{ role: 'user', content: 'What is the weather?' }],
abortSignal: controller.signal,
});
for await (const chunk of stream) {
// Yield to client
}
Stop Conditions
Built-in: Step Count
import { stepCountIs } from 'ai';
stopWhen: stepCountIs(5) // Stop after 5 steps
Custom: Finish Reason
stopWhen: async ({ steps }) =>
steps.at(-1)?.finishReason === 'stop'
Custom: Combined
stopWhen: async ({ steps }) =>
steps.length >= 10 || steps.at(-1)?.finishReason === 'stop'
Tool Choice Control
const agent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
tools: { search, calculate },
// Force tool use every step
toolChoice: 'required',
// Disable tools (text only)
toolChoice: 'none',
// Let model decide (default)
toolChoice: 'auto',
});
Agent with Constraints
const customerSupportAgent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-4.5',
instructions: `You are a customer support specialist.
Rules:
- Never promise refunds without checking policy
- Always be empathetic and professional
- If unsure, offer to escalate
- Keep responses concise
- Never share internal company information`,
tools: {
checkOrderStatus,
lookupPolicy,
createTicket,
},
});
Accessing Step History
const result = await agent.generate({ prompt: '...' });
for (const step of result.steps) {
if (step.type === 'tool-call') {
console.log(`Called ${step.tool} with`, step.input);
} else if (step.type === 'text-generation') {
console.log('Generated:', step.output);
}
}
Error Handling
try {
const result = await agent.generate({ prompt: '...' });
} catch (error) {
if (error.name === 'AbortError') {
console.log('Agent execution aborted');
} else {
console.error('Agent error:', error);
}
}
Best Practices
DO:
- Set reasonable
stopWhenlimits - Use typed tool schemas with Zod
- Handle abort signals for long-running agents
- Log step history for debugging
DON'T:
- Leave agents unbounded (no stop condition)
- Use synchronous blocking operations in tools
- Ignore error states
- Skip tool validation