Vercel AI SDK Agent 开发指南
使用 AI SDK 构建生产级 AI Agent 系统的完整指南
📋 概述
Vercel AI SDK 是一个用于构建 AI 应用的 TypeScript 库。其 Agent 模块提供了一套完整的工具,用于构建能够自主调用工具完成任务的智能代理系统。
Agent 的核心概念
Agent 是一个在循环中使用工具来完成任务的 LLM 系统。三个核心组件协同工作:
| 组件 | 职责 |
|---|---|
| LLM | 处理输入,决定下一步操作 |
| Tools | 扩展能力,执行超出文本生成的操作(读取文件、调用 API、写入数据库等) |
| Loop | 编排执行流程,管理上下文和停止条件 |
🏗️ ToolLoopAgent 类
ToolLoopAgent 类是 AI SDK 构建 Agent 的推荐方法,它封装了 LLM 配置、工具和行为,自动处理 Agent 循环。
基础示例
typescript
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';
const weatherAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: {
weather: tool({
description: 'Get the weather in a location (in Fahrenheit)',
inputSchema: z.object({
location: z.string().describe('The location to get the weather for'),
}),
execute: async ({ location }) => ({
location,
temperature: 72 + Math.floor(Math.random() * 21) - 10,
}),
}),
convertFahrenheitToCelsius: tool({
description: 'Convert temperature from Fahrenheit to Celsius',
inputSchema: z.object({
temperature: z.number().describe('Temperature in Fahrenheit'),
}),
execute: async ({ temperature }) => {
const celsius = Math.round((temperature - 32) * (5 / 9));
return { celsius };
},
}),
},
});
const result = await weatherAgent.generate({
prompt: 'What is the weather in San Francisco in celsius?',
});
console.log(result.text); // Agent 的最终回答
console.log(result.steps); // Agent 执行的步骤Agent 自动执行:
- 调用
weather工具获取华氏温度 - 调用
convertFahrenheitToCelsius转换为摄氏度 - 生成最终的文本响应
为什么使用 ToolLoopAgent?
| 优势 | 说明 |
|---|---|
| 减少样板代码 | 自动管理循环和消息数组 |
| 提高复用性 | 定义一次,在整个应用中使用 |
| 简化维护 | 统一的配置管理点 |
| 类型安全 | 完整的 TypeScript 支持 |
⚙️ 配置选项
模型与系统指令
typescript
import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: 'You are an expert software engineer.',
});工具配置
typescript
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';
const codeAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: {
runCode: tool({
description: 'Execute Python code',
inputSchema: z.object({
code: z.string(),
}),
execute: async ({ code }) => {
return { output: 'Code executed successfully' };
},
}),
},
});工具选择模式
typescript
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: { /* your tools */ },
toolChoice: 'required', // 强制使用工具
// toolChoice: 'none' // 禁用工具
// toolChoice: 'auto' // 让模型决定(默认)
});
// 强制使用特定工具
const specificToolAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: {
weather: weatherTool,
cityAttractions: attractionsTool,
},
toolChoice: {
type: 'tool',
toolName: 'weather',
},
});结构化输出
typescript
import { ToolLoopAgent, Output, stepCountIs } from 'ai';
import { z } from 'zod';
const analysisAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
output: Output.object({
schema: z.object({
sentiment: z.enum(['positive', 'neutral', 'negative']),
summary: z.string(),
keyPoints: z.array(z.string()),
}),
}),
stopWhen: stepCountIs(10),
});
const { output } = await analysisAgent.generate({
prompt: 'Analyze customer feedback from the last quarter',
});📝 系统指令设计
系统指令定义 Agent 的行为、个性和约束,是设计良好 Agent 的关键。
基础角色设定
typescript
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: 'You are an expert data analyst. You provide clear insights from complex data.',
});详细行为指令
typescript
const codeReviewAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: `You are a senior software engineer conducting code reviews.
Your approach:
- Focus on security vulnerabilities first
- Identify performance bottlenecks
- Suggest improvements for readability and maintainability
- Be constructive and educational in your feedback
- Always explain why something is an issue and how to fix it`,
});约束规则
typescript
const customerSupportAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: `You are a customer support specialist for an e-commerce platform.
Rules:
- Never make promises about refunds without checking the policy
- Always be empathetic and professional
- If you don't know something, say so and offer to escalate
- Keep responses concise and actionable
- Never share internal company information`,
tools: {
checkOrderStatus,
lookupPolicy,
createTicket,
},
});工具使用指导
typescript
const researchAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: `You are a research assistant with access to search and document tools.
When researching:
1. Always start with a broad search to understand the topic
2. Use document analysis for detailed information
3. Cross-reference multiple sources before drawing conclusions
4. Cite your sources when presenting information
5. If information conflicts, present both viewpoints`,
tools: {
webSearch,
analyzeDocument,
extractQuotes,
},
});🎯 使用 Agent
生成文本
typescript
const result = await myAgent.generate({
prompt: 'What is the weather like?',
});
console.log(result.text);流式响应
typescript
const stream = myAgent.stream({
prompt: 'Tell me a story',
});
for await (const chunk of stream.textStream) {
console.log(chunk);
}响应 UI 消息(API 路由)
typescript
// app/api/chat/route.ts
import { createAgentUIStreamResponse } from 'ai';
export async function POST(request: Request) {
const { messages } = await request.json();
return createAgentUIStreamResponse({
agent: myAgent,
messages,
});
}端到端类型安全
typescript
import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
const myAgent = new ToolLoopAgent({
// ... configuration
});
// 推断 UIMessage 类型
export type MyAgentUIMessage = InferAgentUIMessage<typeof myAgent>;
// 在客户端组件中使用
'use client';
import { useChat } from '@ai-sdk/react';
import type { MyAgentUIMessage } from '@/agent/my-agent';
export function Chat() {
const { messages } = useChat<MyAgentUIMessage>();
// 完整的类型安全
}🔄 循环控制(Loop Control)
停止条件
Agent 循环在以下情况下停止:
- 返回的完成原因不是
tool-calls - 调用的工具没有
execute函数 - 工具调用需要审批
- 满足停止条件
默认情况下,Agent 在 20 步后停止(stepCountIs(20))。
内置停止条件
typescript
import { ToolLoopAgent, stepCountIs } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: { /* your tools */ },
stopWhen: stepCountIs(20),
});组合多个条件
typescript
import { ToolLoopAgent, stepCountIs, hasToolCall } from 'ai';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: { /* your tools */ },
stopWhen: [
stepCountIs(20), // 最多 20 步
hasToolCall('someTool'), // 调用特定工具后停止
],
});自定义停止条件
typescript
import { ToolLoopAgent, StopCondition, ToolSet } from 'ai';
const tools = { /* your tools */ } satisfies ToolSet;
const hasAnswer: StopCondition<typeof tools> = ({ steps }) => {
return steps.some(step => step.text?.includes('ANSWER:')) ?? false;
};
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools,
stopWhen: hasAnswer,
});基于成本的停止条件
typescript
const budgetExceeded: StopCondition<typeof tools> = ({ steps }) => {
const totalUsage = steps.reduce(
(acc, step) => ({
inputTokens: acc.inputTokens + (step.usage?.inputTokens ?? 0),
outputTokens: acc.outputTokens + (step.usage?.outputTokens ?? 0),
}),
{ inputTokens: 0, outputTokens: 0 },
);
const costEstimate =
(totalUsage.inputTokens * 0.01 + totalUsage.outputTokens * 0.03) / 1000;
return costEstimate > 0.5; // 超过 $0.50 时停止
};prepareStep:每步执行前的钩子
prepareStep 回调在循环的每一步之前运行,可用于动态修改设置。
动态模型选择
typescript
const agent = new ToolLoopAgent({
model: 'openai/gpt-4o-mini',
tools: { /* your tools */ },
prepareStep: async ({ stepNumber, messages }) => {
if (stepNumber > 2 && messages.length > 10) {
return {
model: "anthropic/claude-sonnet-4.5",
};
}
return {};
},
});上下文管理
typescript
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: { /* your tools */ },
prepareStep: async ({ messages }) => {
if (messages.length > 20) {
return {
messages: [
messages[0], // 保留系统指令
...messages.slice(-10), // 保留最后 10 条消息
],
};
}
return {};
},
});工具阶段控制
typescript
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: {
search: searchTool,
analyze: analyzeTool,
summarize: summarizeTool,
},
prepareStep: async ({ stepNumber }) => {
// 搜索阶段 (步骤 0-2)
if (stepNumber <= 2) {
return {
activeTools: ['search'],
toolChoice: 'required',
};
}
// 分析阶段 (步骤 3-5)
if (stepNumber <= 5) {
return {
activeTools: ['analyze'],
};
}
// 总结阶段 (步骤 6+)
return {
activeTools: ['summarize'],
toolChoice: 'required',
};
},
});强制工具调用模式
结合 toolChoice: 'required' 和无 execute 函数的 done 工具:
typescript
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
tools: {
search: searchTool,
analyze: analyzeTool,
done: tool({
description: 'Signal that you have finished your work',
inputSchema: z.object({
answer: z.string().describe('The final answer'),
}),
// 无 execute 函数 - 调用时停止 Agent
}),
},
toolChoice: 'required',
});
const result = await agent.generate({
prompt: 'Research and analyze this topic, then provide your answer.',
});
const toolCall = result.staticToolCalls[0];
if (toolCall?.toolName === 'done') {
console.log(toolCall.input.answer);
}手动循环控制
对于需要完全控制的场景:
typescript
import { generateText, ModelMessage } from 'ai';
const messages: ModelMessage[] = [{ role: 'user', content: '...' }];
let step = 0;
const maxSteps = 10;
while (step < maxSteps) {
const result = await generateText({
model: "anthropic/claude-sonnet-4.5",
messages,
tools: { /* your tools */ },
});
messages.push(...result.response.messages);
if (result.text) {
break; // 模型生成文本时停止
}
step++;
}🔧 调用选项(Call Options)
调用选项允许在运行时传递类型安全的输入来动态配置 Agent 行为。
基础示例
typescript
import { ToolLoopAgent } from 'ai';
import { z } from 'zod';
const supportAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
callOptionsSchema: z.object({
userId: z.string(),
accountType: z.enum(['free', 'pro', 'enterprise']),
}),
instructions: 'You are a helpful customer support agent.',
prepareCall: ({ options, ...settings }) => ({
...settings,
instructions: settings.instructions + `
User context:
- Account type: ${options.accountType}
- User ID: ${options.userId}
Adjust your response based on the user's account level.`,
}),
});
const result = await supportAgent.generate({
prompt: 'How do I upgrade my account?',
options: {
userId: 'user_123',
accountType: 'free',
},
});动态模型选择
typescript
const agent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
callOptionsSchema: z.object({
complexity: z.enum(['simple', 'complex']),
}),
prepareCall: ({ options, ...settings }) => ({
...settings,
model: options.complexity === 'simple'
? 'openai/gpt-4o-mini'
: 'openai/o1-mini',
}),
});
await agent.generate({
prompt: 'What is 2+2?',
options: { complexity: 'simple' },
});RAG 集成
typescript
const ragAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
callOptionsSchema: z.object({
query: z.string(),
}),
prepareCall: async ({ options, ...settings }) => {
const documents = await vectorSearch(options.query);
return {
...settings,
instructions: `Answer questions using the following context:
${documents.map(doc => doc.content).join('\n\n')}`,
};
},
});
await ragAgent.generate({
prompt: 'What is our refund policy?',
options: { query: 'refund policy' },
});在 API 路由中使用
typescript
import { createAgentUIStreamResponse } from 'ai';
import { myAgent } from '@/ai/agents/my-agent';
export async function POST(request: Request) {
const { messages, userId, accountType } = await request.json();
return createAgentUIStreamResponse({
agent: myAgent,
messages,
options: {
userId,
accountType,
},
});
}🔀 工作流模式(Workflow Patterns)
当需要比自由 Agent 更可靠、可预测的结果时,使用结构化工作流模式。
模式选择指南
| 因素 | 考量 |
|---|---|
| 灵活性 vs 控制 | LLM 需要多少自由度?需要多严格地约束其行为? |
| 错误容忍度 | 您的用例中错误的后果是什么? |
| 成本考量 | 更复杂的系统通常意味着更多的 LLM 调用和更高的成本 |
| 可维护性 | 更简单的架构更容易调试和修改 |
1. 顺序处理(Sequential Chains)
步骤按预定顺序执行,每步的输出成为下一步的输入。
typescript
import { generateText, generateObject } from 'ai';
import { z } from 'zod';
async function generateMarketingCopy(input: string) {
const model = "anthropic/claude-sonnet-4.5";
// 第一步:生成营销文案
const { text: copy } = await generateText({
model,
prompt: `Write persuasive marketing copy for: ${input}`,
});
// 第二步:质量检查
const { object: qualityMetrics } = await generateObject({
model,
schema: z.object({
hasCallToAction: z.boolean(),
emotionalAppeal: z.number().min(1).max(10),
clarity: z.number().min(1).max(10),
}),
prompt: `Evaluate this marketing copy: ${copy}`,
});
// 第三步:如果质量不达标,重新生成
if (!qualityMetrics.hasCallToAction || qualityMetrics.emotionalAppeal < 7) {
const { text: improvedCopy } = await generateText({
model,
prompt: `Rewrite with stronger call to action: ${copy}`,
});
return { copy: improvedCopy, qualityMetrics };
}
return { copy, qualityMetrics };
}2. 路由(Routing)
根据上下文和中间结果决定工作流路径。
typescript
import { generateObject, generateText } from 'ai';
import { z } from 'zod';
async function handleCustomerQuery(query: string) {
// 分类查询类型
const { object: classification } = await generateObject({
model: "anthropic/claude-sonnet-4.5",
schema: z.object({
type: z.enum(['general', 'refund', 'technical']),
complexity: z.enum(['simple', 'complex']),
}),
prompt: `Classify this query: ${query}`,
});
// 根据分类路由
const { text: response } = await generateText({
model: classification.complexity === 'simple'
? 'openai/gpt-4o-mini'
: 'openai/o4-mini',
system: {
general: 'You are a customer service agent.',
refund: 'You specialize in refund requests.',
technical: 'You are a technical support specialist.',
}[classification.type],
prompt: query,
});
return { response, classification };
}3. 并行处理(Parallel Processing)
独立任务同时执行,提高效率。
typescript
import { generateText, generateObject } from 'ai';
import { z } from 'zod';
async function parallelCodeReview(code: string) {
const model = "anthropic/claude-sonnet-4.5";
// 并行运行多个审查
const [securityReview, performanceReview, maintainabilityReview] =
await Promise.all([
generateObject({
model,
system: 'Focus on security vulnerabilities.',
schema: z.object({
vulnerabilities: z.array(z.string()),
riskLevel: z.enum(['low', 'medium', 'high']),
}),
prompt: `Review: ${code}`,
}),
generateObject({
model,
system: 'Focus on performance bottlenecks.',
schema: z.object({
issues: z.array(z.string()),
impact: z.enum(['low', 'medium', 'high']),
}),
prompt: `Review: ${code}`,
}),
generateObject({
model,
system: 'Focus on code quality.',
schema: z.object({
concerns: z.array(z.string()),
qualityScore: z.number().min(1).max(10),
}),
prompt: `Review: ${code}`,
}),
]);
// 汇总结果
const { text: summary } = await generateText({
model,
system: 'Summarize multiple code reviews.',
prompt: `Synthesize: ${JSON.stringify({
security: securityReview.object,
performance: performanceReview.object,
maintainability: maintainabilityReview.object,
})}`,
});
return { reviews: [securityReview, performanceReview, maintainabilityReview], summary };
}4. 编排者-工作者(Orchestrator-Worker)
主模型(编排者)协调专业工作者的执行。
typescript
import { generateObject } from 'ai';
import { z } from 'zod';
async function implementFeature(featureRequest: string) {
// 编排者:规划实现
const { object: plan } = await generateObject({
model: "anthropic/claude-sonnet-4.5",
schema: z.object({
files: z.array(z.object({
purpose: z.string(),
filePath: z.string(),
changeType: z.enum(['create', 'modify', 'delete']),
})),
}),
system: 'You are a software architect.',
prompt: `Plan implementation for: ${featureRequest}`,
});
// 工作者:执行变更
const changes = await Promise.all(
plan.files.map(async file => {
const workerPrompt = {
create: 'You implement new files.',
modify: 'You modify existing code.',
delete: 'You safely remove code.',
}[file.changeType];
const { object: change } = await generateObject({
model: "anthropic/claude-sonnet-4.5",
schema: z.object({
explanation: z.string(),
code: z.string(),
}),
system: workerPrompt,
prompt: `Implement for ${file.filePath}: ${file.purpose}`,
});
return { file, implementation: change };
}),
);
return { plan, changes };
}5. 评估-优化器(Evaluator-Optimizer)
专门的评估步骤评估中间结果,根据评估进行改进。
typescript
import { generateText, generateObject } from 'ai';
import { z } from 'zod';
async function translateWithFeedback(text: string, targetLanguage: string) {
let currentTranslation = '';
const MAX_ITERATIONS = 3;
// 初始翻译
const { text: translation } = await generateText({
model: "anthropic/claude-sonnet-4.5",
system: 'You are an expert literary translator.',
prompt: `Translate to ${targetLanguage}: ${text}`,
});
currentTranslation = translation;
// 评估-优化循环
for (let i = 0; i < MAX_ITERATIONS; i++) {
const { object: evaluation } = await generateObject({
model: "anthropic/claude-sonnet-4.5",
schema: z.object({
qualityScore: z.number().min(1).max(10),
preservesTone: z.boolean(),
specificIssues: z.array(z.string()),
}),
prompt: `Evaluate: Original: ${text}, Translation: ${currentTranslation}`,
});
if (evaluation.qualityScore >= 8 && evaluation.preservesTone) {
break;
}
// 根据反馈改进
const { text: improved } = await generateText({
model: "anthropic/claude-sonnet-4.5",
prompt: `Improve based on: ${evaluation.specificIssues.join('\n')}\n\nOriginal: ${text}\nCurrent: ${currentTranslation}`,
});
currentTranslation = improved;
}
return { finalTranslation: currentTranslation };
}📊 模式对比
| 模式 | 适用场景 | 复杂度 | 成本 |
|---|---|---|---|
| 顺序处理 | 内容生成管道、数据转换 | ⭐ | 💰 |
| 路由 | 客服分类、多场景处理 | ⭐⭐ | 💰💰 |
| 并行处理 | 代码审查、多维度分析 | ⭐⭐ | 💰💰 |
| 编排者-工作者 | 功能实现、复杂任务分解 | ⭐⭐⭐ | 💰💰💰 |
| 评估-优化器 | 翻译、质量敏感任务 | ⭐⭐⭐ | 💰💰💰 |
🎓 最佳实践
1. 从简单开始
typescript
// ✅ 先验证基础功能
const simpleAgent = new ToolLoopAgent({
model: "anthropic/claude-sonnet-4.5",
instructions: 'You are a helpful assistant.',
});
// 然后逐步添加复杂性2. 明确的系统指令
typescript
// ✅ 具体、可操作的指令
instructions: `You are a code reviewer.
1. Focus on security vulnerabilities first
2. Then check performance issues
3. Finally suggest readability improvements
4. Always explain why something is a problem`
// ❌ 模糊的指令
instructions: 'Review the code well'3. 类型安全至上
typescript
// ✅ 使用 Zod 定义明确的 schema
const tools = {
search: tool({
inputSchema: z.object({
query: z.string().min(1).describe('Search query'),
limit: z.number().optional().default(10),
}),
// ...
}),
};4. 控制成本
typescript
// ✅ 设置合理的停止条件
stopWhen: [
stepCountIs(10), // 步数限制
budgetExceeded, // 成本限制
]5. 优雅的错误处理
typescript
// ✅ 在工具中处理错误
execute: async ({ input }) => {
try {
const result = await riskyOperation(input);
return { success: true, data: result };
} catch (error) {
return { success: false, error: error.message };
}
},📚 延伸阅读
本教程基于 Vercel AI SDK 文档整理,适合希望使用 TypeScript 构建 AI Agent 应用的开发者。