mcpField
V
vercel-ai-sdk

vercel-ai-sdk

github.com/existential-birds开发85101 次安装12 周前更新
代码仓库

关于 vercel-ai-sdk

Vercel AI SDK 提供 React Hooks 和服务端工具,用于构建流式聊天界面。它支持工具调用、文件附件、多步骤流程,以及客户端和服务端执行的类型化消息部分。

主要功能

  1. 01React 聊天 Hooks
  2. 02服务端流式处理工具
  3. 03带执行状态的工具调用
  4. 04文件与自定义数据消息部分
  5. 05类型化消息与工具

适用场景

  1. 01构建流式聊天界面
  2. 02呈现工具调用进度与结果
  3. 03在聊天中处理文件附件
  4. 04实现服务端和客户端工具

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/existential-birds/beagle --skill vercel-ai-sdk

安装后在 Claude Code 中输入/vercel-ai-sdk确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
messages.md
streaming.md
tools.md
use-chat.md

详细介绍

Vercel AI SDK

Vercel AI SDK 提供 React 钩子和服务器工具,用于构建支持工具调用、文件附件和多步推理的流式聊天界面。

快速参考

基本的 useChat 设置
import { useChat } from '@ai-sdk/react';

const { messages, status, sendMessage, stop, regenerate } = useChat({
  id: 'chat-id',
  messages: initialMessages,
  onFinish: ({ message, messages, isAbort, isError }) => {
    console.log('Chat finished');
  },
  onError: (error) => {
    console.error('Chat error:', error);
  }
});

// Send a message
sendMessage({ text: 'Hello', metadata: { createdAt: Date.now() } });

// Send with files
sendMessage({
  text: 'Analyze this',
  files: fileList // FileList or FileUIPart[]
});
ChatStatus 状态

status 字段表示聊天的当前状态:

  • ready:聊天处于空闲状态,可以接收新消息
  • submitted:消息已发送至 API,正在等待响应流开始
  • streaming:正在从 API 接收流式响应
  • error:请求过程中发生错误
消息结构

消息使用基于 parts 的结构,类型为 UIMessage:

interface UIMessage {
  id: string;
  role: 'system' | 'user' | 'assistant';
  metadata?: unknown;
  parts: Array<UIMessagePart>; // text, file, tool-*, reasoning, etc.
}

Part 类型包括:

  • text:文本内容,可包含可选的流式状态
  • file:文件附件(图像、文档)
  • tool-{toolName}:带状态机的工具调用
  • reasoning:AI 推理轨迹
  • data-{typeName}:自定义数据 part
服务端流式传输
import { streamText } from 'ai';
import { convertToModelMessages } from 'ai';

const result = streamText({
  model: openai('gpt-4'),
  messages: convertToModelMessages(uiMessages),
  tools: {
    getWeather: tool({
      description: 'Get weather',
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => {
        return { temperature: 72, weather: 'sunny' };
      }
    })
  }
});

return result.toUIMessageStreamResponse({
  originalMessages: uiMessages,
  onFinish: ({ messages }) => {
    // Save to database
  }
});
工具处理模式

客户端工具执行:

const { addToolOutput } = useChat({
  onToolCall: async ({ toolCall }) => {
    if (toolCall.toolName === 'getLocation') {
      addToolOutput({
        tool: 'getLocation',
        toolCallId: toolCall.toolCallId,
        output: 'San Francisco'
      });
    }
  }
});

渲染工具状态:

{message.parts.map(part => {
  if (part.type === 'tool-getWeather') {
    switch (part.state) {
      case 'input-streaming':
        return <pre>{JSON.stringify(part.input, null, 2)}</pre>;
      case 'input-available':
        return <div>Getting weather for {part.input.city}...</div>;
      case 'output-available':
        return <div>Weather: {part.output.weather}</div>;
      case 'output-error':
        return <div>Error: {part.errorText}</div>;
    }
  }
})}

参考文件

有关特定方面的详细文档:

常见模式

错误处理
const { error, clearError } = useChat({
  onError: (error) => {
    toast.error(error.message);
  }
});

// Clear error and reset to ready state
if (error) {
  clearError();
}
重新生成消息
const { regenerate } = useChat();

// Regenerate last assistant message
await regenerate();

// Regenerate specific message
await regenerate({ messageId: 'msg-123' });
自定义传输
import { DefaultChatTransport } from 'ai';

const { messages } = useChat({
  transport: new DefaultChatTransport({
    api: '/api/chat',
    prepareSendMessagesRequest: ({ id, messages, trigger, messageId }) => ({
      body: {
        chatId: id,
        lastMessage: messages[messages.length - 1],
        trigger,
        messageId
      }
    })
  })
});
性能优化
// Throttle UI updates to reduce re-renders
const chat = useChat({
  experimental_throttle: 100 // Update max once per 100ms
});
自动发送消息
import { lastAssistantMessageIsCompleteWithToolCalls } from 'ai';

const chat = useChat({
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls
  // Automatically resend when all tool calls have outputs
});

类型安全

SDK 为工具和消息提供完整的类型推断:

import { InferUITools, UIMessage } from 'ai';

const tools = {
  getWeather: tool({
    inputSchema: z.object({ city: z.string() }),
    execute: async ({ city }) => ({ weather: 'sunny' })
  })
};

type MyMessage = UIMessage<
  { createdAt: number }, // Metadata type
  UIDataTypes,
  InferUITools<typeof tools> // Tool types
>;

const { messages } = useChat<MyMessage>();

核心概念

基于 Parts 的架构

消息使用 parts 数组,而非单一的 content 字段。这样可以:

  • 在保留其他 parts 的同时流式传输文本
  • 使用独立状态机处理工具调用
  • 将文件附件和自定义数据与文本混合
工具状态机

工具 part 会经历以下状态:

  1. input-streaming:工具输入正在流式传输(可选)
  2. input-available:工具输入已完成
  3. approval-requested:正在等待用户批准(可选)
  4. approval-responded:用户已批准或拒绝(可选)
  5. output-available:工具执行已完成
  6. output-error:工具执行失败
  7. output-denied:用户拒绝了批准请求
流式传输协议

SDK 使用 Server-Sent Events(SSE)和 UIMessageChunk 类型:

  • text-start、text-delta、text-end
  • tool-input-available、tool-output-available
  • reasoning-start、reasoning-delta、reasoning-end
  • start、finish、abort
客户端工具与服务端工具

服务端工具包含 execute 函数,并在 API 路由中运行。

客户端工具不包含 execute,而是通过 onToolCall 和 addToolOutput 处理。

检查关卡

按以下顺序执行;只有当代码或 UI 中满足通过条件时,才算完成该步骤(而不是“应该能用”)。

  1. 流式路由 — 通过条件: 聊天处理程序按顺序调用 convertToModelMessages → streamText(或应用标准化的 SDK 模式)→ toUIMessageStreamResponse(或等效的流式响应)。未通过条件: 响应是普通 JSON 字符串,没有遵循 UI 消息流约定。
  2. 客户端 ↔ 路由 — 通过条件: useChat 的 id / DefaultChatTransport 的 api(以及 prepareSendMessagesRequest 的请求体)与路由路径及服务器读取的请求体一致。未通过条件: 客户端发送请求的路径或请求体结构与处理程序的预期不一致。
  3. 工具闭环 — 通过条件: tools 中的每个工具都有服务器端 execute,或 onToolCall + addToolOutput,且使用相同的 toolCallId;UI 也会处理你呈现的 tool-* 部分状态。未通过条件: tools 中存在某个工具名称,但没有对应的处理程序,或渲染器缺少相应状态。
  4. 持久化(如有) — 通过条件: 保存前,服务器会运行 validateUIMessages(或更严格的验证)。未通过条件: 未经验证的客户端请求内容被写入存储。

最佳实践

  1. 始终处理 error 状态并向用户提供反馈
  2. 对高频更新使用 experimental_throttle
  3. 根据 status 实现适当的加载状态
  4. 使用自定义元数据和工具为消息添加类型
  5. 使用 sendAutomaticallyWhen 处理多轮工具工作流
  6. 在 UI 中处理所有工具状态,以提供更好的用户体验
  7. 使用 stop() 允许用户取消长时间运行的请求
  8. 在服务器上使用 validateUIMessages 验证消息

评论

支持 Markdown