进阶 📋 7 个步骤 第 430 / 470 篇

用 Vercel AI SDK 做工具调用 Agent:tool() 与多步循环

用 Vercel AI SDK(ai 包)搭会调用工具的 Agent:tool() + Zod 定义函数、generateText 单步调用、streamText + stepCountIs 多步循环、Next.js Route Handler 暴露接口、useChat 接前端聊天。

2026.09.14· 19 分钟阅读· 约 1077 字· 🔧 Vercel AI SDK / 🤖 工具调用

Vercel 的 AI SDK(ai 包)是 TypeScript 里调用大模型的事实标准之一:统一了各家模型的接口,还原生支持"工具调用"。本教程带你搭一个会查天气的 Agent——从定义工具、单步调用,到用 stepCountIs 跑多步循环,最后封装成 HTTP 接口并接上 React 聊天界面。

🔧 本教程适合:会 TypeScript、想在新项目或 Next.js 里给模型接工具的开发者。纯 Node 脚本也能跑前几步;聊天 UI 需要 React。

先搞懂:工具调用是怎么循环的?

用一句话理解:你给模型一组"函数说明书",模型自己决定调哪个、传什么参数;SDK 执行函数把结果喂回模型,模型再决定继续调还是直接回答。这个"调函数→喂结果→再思考"的循环,就是 Agent 的核心。AI SDK 用 tools 定义函数、用 stopWhen 控制循环上限。

环节谁来做
选函数 + 填参数大模型
执行函数你的 execute 代码
判断是否继续SDK 循环,直到无工具调用或达上限

Step 1:安装依赖

1 装 ai + 模型适配器 + zod
npm install ai @ai-sdk/openai zod
# 若用 Next.js:npx create-next-app@latest my-app
💡 纯 Node 脚本也能用 generateText / streamText;聊天 UI 部分才需要 React 与 @ai-sdk/react。

Step 2:用 tool() + Zod 定义工具

2 描述 + 参数 schema + 执行体

每个工具三件套:description(模型读它决定何时用)、parameters(Zod schema,既给模型也用于校验)、execute(真正干活的异步函数):

import { tool } from "ai";
import { z } from "zod";

const getWeather = tool({
  description: "获取某城市的当前天气",
  parameters: z.object({
    city: z.string().describe("城市名,如 上海"),
  }),
  execute: async ({ city }) => {
    // 这里接真实天气 API;示例返回固定结构
    return { city, temperature: 22, condition: "晴" };
  },
});

Step 3:单步工具调用(generateText)

3 跑一次,看 toolCalls
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";

const { text, toolCalls } = await generateText({
  model: openai("gpt-4o"),
  tools: { getWeather },
  prompt: "上海现在天气怎么样?",
});
console.log(text, toolCalls);

先配置 OPENAI_API_KEY。在项目根目录 .env 写 OPENAI_API_KEY=sk-...,@ai-sdk/openai 会自动读取。模型名以你账号可用模型为准。

Step 4:多步 Agent 循环(streamText + stepCountIs)

4 让模型连续调多次工具

当用户问题需要多个工具结果(如"上海和东京天气各怎样"),用 stopWhen: stepCountIs(N) 允许 SDK 自动跑多轮,直到模型产出纯文本:

import { streamText, stepCountIs } from "ai";

const result = streamText({
  model: openai("gpt-4o"),
  tools: { getWeather },
  stopWhen: stepCountIs(5), // 最多 5 轮工具调用,防止死循环
  prompt: "上海和东京的天气各是怎样?做个对比。",
});

for await (const part of result.textStream) {
  process.stdout.write(part);
}

务必加 stopWhen: stepCountIs(N)。没有上限时,一个"犯迷糊"的模型可能反复调工具直到烧光额度。大多数 Agent 设 5,研究类可设 10。

Step 5:封装成 HTTP 接口(Next.js Route Handler)

5 流式返回给前端
// app/api/agent/route.ts
import { streamText, convertToModelMessages, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";

export async function POST(req: Request) {
  const { messages } = await req.json();
  const result = streamText({
    model: openai("gpt-4o"),
    messages: convertToModelMessages(messages),
    tools: { getWeather },
    stopWhen: stepCountIs(5),
  });
  return result.toUIMessageStreamResponse();
}

公网接口要鉴权。本示例 Route Handler 任何人都能调用并消耗你的模型额度。上线前加用户登录校验 / rate limit,敏感工具(写数据库、发消息)务必加人工确认。

Step 6:前端聊天 UI(useChat)

6 几行接上流式聊天
"use client";
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";

const { messages, input, handleSubmit, setInput } = useChat({
  transport: new DefaultChatTransport({ api: "/api/agent" }),
});
// <form onSubmit={handleSubmit}>
//   <input value={input} onChange={(e) => setInput(e.target.value)} />
// </form>
💬 useChat 会把工具调用事件作为 typed parts 流式传回前端,你可以在 UI 上显示"正在调用 getWeather…"这类进度提示,体验更顺。

Step 7:模型路由与兜底(可选)

7 主模型挂了自动换备胎

若走 Vercel AI Gateway,可在 providerOptions.gateway.models 里列备用模型,主模型报错时按顺序兜底:

const result = streamText({
  model: openai("gpt-4o"),
  prompt,
  providerOptions: {
    gateway: {
      models: ["anthropic/claude-xxx", "google/gemini-xxx"],
    },
  },
});
🛡️ Agent 每轮都要多次调模型,单点故障概率更高,兜底能显著提升可用性。具体模型名与开关以 Vercel AI Gateway 文档为准。

常见问题速查

现象原因 & 解决
401 / 无响应OPENAI_API_KEY 未配置或模型名不可用
工具不被调用description 不够清楚,或问题本不需要工具
无限循环烧钱漏了 stopWhen: stepCountIs(N)
前端收不到流Route Handler 没返回 toUIMessageStreamResponse
← 返回教程中心