Vercel AI SDK(Tool Calling)通用工具实践笔记
这是一份基于 TypeScript/Next.js 与 Vercel AI SDK v5/v6 的实践记录,整理了一个通用的 Tool Calling 接入示例,并在前端用
useChat显示工具结果,形成最小闭环。
文中不特指某个具体工具,示例仅作演示(如文本总结 summarize);你可以替换为查询、检索、图片生成等任何符合需求的工具。
目录
- 核心概念速览
- 安装与准备
- 后端:定义工具
img-generate并接入streamText - 前端:用
useChat渲染文本与工具结果(含图片) - Node 脚本版(不依赖 Next.js)
- 常用进阶:多步、强制工具、并行调用、错误处理
- 常见坑与排查清单
- 参考与扩展阅读
核心概念速览
- Tool(工具):用
tool()定义。包含description、inputSchema(Zod/JSON Schema)、execute。模型在需要时会按 schema 产出参数并触发execute。工具的参数/结果会被强类型化,且支持在流式对话中多轮调用。 oai_citation:0‡ai-sdk.dev - Tool Calling 工作流:
streamText/generateText在生成回复时,模型可发起工具调用;服务端自动执行带execute的工具并把结果回流给前端,或由前端接管“交互型/客户端工具”。 oai_citation:1‡v6.ai-sdk.dev - OpenAI Provider:通过
@ai-sdk/openai使用 OpenAI Responses API(AI SDK 5 起默认)。可在providerOptions.openai配置并行工具调用等细节。 oai_citation:2‡ai-sdk.dev - 生成图片:AI SDK 提供
experimental_generateImage帮助方法;OpenAI 的图像生成模型为gpt-image-1,支持 1024×1024、1024×1536、1536×1024 等尺寸。 oai_citation:3‡ai-sdk.dev - UIMessage/parts:前端
useChat以 message.parts(包括文本、工具调用、工具结果、文件等)来渲染消息,建议用parts而非旧的content。 oai_citation:4‡ai-sdk.dev
安装与准备
# 必装:
npm i ai zod @ai-sdk/openai
# 或 pnpm add ai zod @ai-sdk/openai
在环境变量中配置:
# .env / Vercel 项目环境变量
OPENAI_API_KEY=sk-...
@ai-sdk/openai会默认走 OpenAI Responses API;也可用createOpenAI自定义 baseURL、headers、project 等。 oai_citation:5‡ai-sdk.dev
后端:定义工具 img-generate 并接入 streamText
下面的示例适用于 Next.js App Router,将接口放在
app/api/chat/route.ts。返回 UIMessage Stream,前端useChat可直接消费。 oai_citation:6‡AI SDK
// app/api/chat/route.ts
import { z } from 'zod';
import {
streamText,
convertToModelMessages,
tool,
stepCountIs,
} from 'ai';
import { openai } from '@ai-sdk/openai';
// Image 生成:实验 API,用法见官方“Image Generation”页面
import { experimental_generateImage as generateImage } from 'ai';
export const maxDuration = 30; // 可选:限制流式生成最长 30s
// 1) 定义一个可以被模型“自动调用”的服务端工具
const tools = {
'img-generate': tool({
description:
'根据文字描述生成图片。仅在用户明确需要图片时调用。',
inputSchema: z.object({
prompt: z.string().describe('要生成内容的详细中文/英文描述'),
size: z
.enum(['1024x1024', '1024x1536', '1536x1024'])
.default('1024x1024')
.describe('输出分辨率'),
n: z.number().int().min(1).max(4).default(1).describe('生成张数'),
}),
// execute 会在服务器上运行(安全、可保密 API Key)
async execute({ prompt, size, n }, { abortSignal }) {
const { images } = await generateImage({
model: openai.image('gpt-image-1'),
prompt,
size,
n,
abortSignal,
});
// 返回精简后的结果,前端按需渲染
return images.map((img) => ({
base64: img.base64,
mediaType: img.mediaType, // 如 'image/png'
}));
},
}),
};
// 2) 接入 streamText(多轮 + 工具)
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'), // 可换 gpt-4.1 / 其它 Responses 模型
system:
'你是图文助手。用户有明确“生成图/图片/海报/logo”等需求时,调用工具 img-generate,拿到图后再用一句话说明要点。',
messages: convertToModelMessages(messages),
tools,
// 多步:允许“工具调用→拿结果→继续解释”后再停
stopWhen: stepCountIs(2),
// 进阶:也可以用 toolChoice 强制选用某个工具(见下方“常用进阶”)
});
// 返回 UIMessage 流式响应,前端 useChat 可直接消费
return result.toUIMessageStreamResponse();
}
-
tool()的inputSchema会指导模型产出正确参数,并在运行期做严格校验。execute()支持AbortSignal、返回值或可迭代结果(可用于“处理中→成功”等状态)。 oai_citation:7‡ai-sdk.dev -
experimental_generateImage为 AI SDK 的图像生成辅助方法,建议与openai.image('gpt-image-1')搭配使用;OpenAI 对应模型说明见官方文档。 oai_citation:8‡ai-sdk.dev -
streamText().toUIMessageStreamResponse()会输出 UIMessage 流协议,天然配合@ai-sdk/react的useChat。 oai_citation:9‡ai-sdk.dev
前端:用 useChat 渲染文本与工具结果(含图片)
要点:渲染 message.parts;当遇到工具结果(类型名以
tool-<工具名>)时,将我们在execute返回的base64图片展示出来。官方建议使用parts渲染,并可结合“自动提交”等辅助函数。 oai_citation:10‡v6.ai-sdk.dev
// app/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
export default function Page() {
const { messages, input, setInput, sendMessage } = useChat({
api: '/api/chat',
});
return (
<div className="p-4 space-y-4">
<div className="flex gap-2">
<input
className="border px-3 py-2 rounded w-full"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && sendMessage({ text: input })}
placeholder="比如:生成一张赛博朋克风格的成都夜景 1024x1536"
/>
<button
className="px-3 py-2 rounded bg-black text-white"
onClick={() => sendMessage({ text: input })}
>
发送
</button>
</div>
{messages.map((m) => (
<div key={m.id} className="space-y-3">
<div className="text-xs opacity-70">{m.role}</div>
{m.parts.map((p, i) => {
// 1) 常规文本
if (p.type === 'text') return <p key={i}>{p.text}</p>;
// 2) 我们的“工具:img-generate”结果(AI SDK 会给出带工具名的类型标识)
if (p.type === 'tool-img-generate') {
// p.result 为 execute 返回值(此处是数组)
return (
<div key={i} className="grid grid-cols-2 gap-3">
{p.result.map(
(
img: { base64: string; mediaType: string },
idx: number,
) => (
<img
key={idx}
className="rounded"
alt="generated"
src={`data:${img.mediaType};base64,${img.base64}`}
/>
),
)}
</div>
);
}
// 3) 其他类型(如文件/file、工具调用提示等)按需扩展
return null;
})}
</div>
))}
</div>
);
}
- 以上演示了“typed tool parts”:在前端
parts里,你会看到类似tool-img-generate的片段名(官方示例中展示了tool-askForConfirmation的渲染方式)。 oai_citation:11‡v6.ai-sdk.dev - 如果你使用“图片文件(file part)”的方式回传,也可以检测
part.type === 'file' && part.mimeType.startsWith('image/')直接<img/>渲染(AI SDK 4.2 博文示例)。 oai_citation:12‡Vercel
Node 脚本版(不依赖 Next.js)
import { z } from 'zod';
import { generateText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { experimental_generateImage as generateImage } from 'ai';
const imgGenerate = tool({
description: '按文字描述生成图片(gpt-image-1)',
inputSchema: z.object({
prompt: z.string(),
size: z.enum(['1024x1024', '1024x1536', '1536x1024']).default('1024x1024'),
n: z.number().int().min(1).max(4).default(1),
}),
async execute({ prompt, size, n }) {
const { images } = await generateImage({
model: openai.image('gpt-image-1'),
prompt,
size,
n,
});
return images.map((img) => ({ base64: img.base64, mediaType: img.mediaType }));
},
});
const { text, toolCalls, toolResults } = await generateText({
model: openai('gpt-4o'),
tools: { 'img-generate': imgGenerate },
prompt: '生成两张武侠风场景图,竖图 1024x1536',
// 也可用 messages,多步、toolChoice 等见下节
});
console.log('LLM 总结:', text);
console.log('工具调用:', toolCalls);
console.log('工具结果:', toolResults);
@ai-sdk/openai会自动选择 Responses API;也可通过providerOptions.openai配置parallelToolCalls、serviceTier等选项。 oai_citation:13‡ai-sdk.dev
常用进阶:多步、强制工具、并行调用、错误处理
1) 多步(在拿到工具结果后继续总结)
在 streamText/generateText 里用 stopWhen: stepCountIs(n) 约束最大步数,例如:工具→总结两步。 oai_citation:14‡ai-sdk.dev
import { stepCountIs } from 'ai';
const result = streamText({
model: openai('gpt-4o'),
messages,
tools,
stopWhen: stepCountIs(2),
});
2) 强制工具选择(toolChoice)
可设为 'auto' | 'required' | 'none' | { type: 'tool', toolName: '...' }。例如强制调用 img-generate: oai_citation:15‡ai-sdk.dev
const result = await generateText({
model: openai('gpt-4o'),
tools,
toolChoice: { type: 'tool', toolName: 'img-generate' },
prompt: '来一张...',
});
3) 并行工具调用
OpenAI Responses API 在 AI SDK 中 默认开启并行工具调用,可通过 providerOptions.openai.parallelToolCalls 关闭或开启(默认 true)。 oai_citation:16‡ai-sdk.dev
const result = await generateText({
model: openai('gpt-4o'),
tools,
providerOptions: {
openai: {
parallelToolCalls: true, // 默认即为 true
// serviceTier: 'flex' | 'priority' | 'auto' ...
},
},
});
4) 观测每一步(回调)
onStepFinish 可拿到每步的 toolCalls、toolResults;prepareStep 可针对不同 step 切换模型/强制工具/限制 activeTools: oai_citation:17‡ai-sdk.dev
const result = await generateText({
model: openai('gpt-4o'),
tools,
stopWhen: stepCountIs(2),
onStepFinish: ({ step, toolCalls, toolResults }) => {
console.log('step:', step, toolCalls, toolResults);
},
prepareStep: async ({ stepNumber }) => {
if (stepNumber === 0) {
return { toolChoice: { type: 'tool', toolName: 'img-generate' } };
}
},
});
5) 服务端 → 前端流(UIMessage Stream)
后端用 toUIMessageStreamResponse() 返回;前端 useChat 自动解析 parts,无需手写 SSE 底层细节。 oai_citation:18‡ai-sdk.dev
6) 错误展示
默认会屏蔽具体错误,可在 toUIMessageStreamResponse 里自定义 onError 将错误文本返回(只在安全场景开启)。 oai_citation:19‡v6.ai-sdk.dev
常见坑与排查清单
- 没用
convertToModelMessages:useChat的 UIMessage 与 Core 的 ModelMessage 结构不同,API 路由要转换。 oai_citation:20‡AI SDK - 前端仍渲染旧的
content字段:请改为遍历message.parts(文本、tool-...、file等都在这里)。 oai_citation:21‡ai-sdk.dev - 工具不触发:检查
description是否清晰、inputSchema是否贴合需求;必要时用toolChoice: 'required'或指定{ type: 'tool', toolName: '...' }。 oai_citation:22‡ai-sdk.dev - 图片无法显示:确认
mediaType与base64前缀拼接正确;或改为回传file类型的消息部件再渲染。 oai_citation:23‡Vercel - 多步没生效:确认设置了
stopWhen: stepCountIs(n),并让工具执行在服务器端(带execute)。 oai_citation:24‡v6.ai-sdk.dev - 并发/速率:需要限制并发或关闭并行调用时,设置
providerOptions.openai.parallelToolCalls = false。 oai_citation:25‡ai-sdk.dev
参考与扩展阅读
- Tool/Tool Calling(核心):AI SDK Core 文档(
tool()、多步、回调、toolChoice 等) oai_citation:26‡ai-sdk.dev - Image Generation(AI SDK):
experimental_generateImage用法与示例 oai_citation:27‡ai-sdk.dev - OpenAI Provider:
@ai-sdk/openai配置、Responses API 默认、并行工具调用等 oai_citation:28‡ai-sdk.dev - UIMessage Stream & useChat:返回流、前端渲染
parts、工具在聊天里的使用方式 oai_citation:29‡ai-sdk.dev - OpenAI 图像模型:
gpt-image-1官方说明与尺寸支持 oai_citation:30‡platform.openai.com
结语
以上就是一套“模型自动调用生成图片工具”的端到端实践:
- 用
tool()定义img-generate,execute内部调用gpt-image-1。 - 在
streamText中交给模型自由决定是否触发(或toolChoice强制)。 - 前端用
useChat渲染parts,拿到tool-img-generate结果直接<img/>显示。
把这些拼起来,你的对话式“文生图”体验就齐活了 🚀