Skip to content

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 自动执行:

  1. 调用 weather 工具获取华氏温度
  2. 调用 convertFahrenheitToCelsius 转换为摄氏度
  3. 生成最终的文本响应

为什么使用 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 应用的开发者。

前端面试知识库