llm-chat-sdks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

LLM Chat SDKs — Claude API & Vercel AI SDK

LLM 聊天 SDK — Claude API 与 Vercel AI SDK

Two complementary surfaces:
SDKWhen
Claude API / Anthropic SDKBackend Python/Node calling Claude directly. Prompt caching, thinking, tool use, batch, files, citations, Managed Agents, migrating between Claude versions (4.5 → 4.6 → 4.7).
Vercel AI SDKFrontend React/Next.js chat UI.
useChat
,
streamText
, tool calls, UIMessage, ChatStatus,
addToolOutput
, streaming responses.
Pick by where the code runs. Most real apps need both — Claude API on the server, Vercel AI SDK on the client.

两个互补的适用场景:
SDK适用场景
Claude API / Anthropic SDK后端通过 Python/Node 直接调用 Claude。支持提示缓存、思维过程、工具调用、批量处理、文件处理、引用、托管 Agents、Claude 版本迁移(4.5 → 4.6 → 4.7)。
Vercel AI SDK前端 React/Next.js 聊天界面。支持
useChat
streamText
、工具调用、UIMessage、ChatStatus、
addToolOutput
、响应流式传输。
根据代码运行环境选择。大多数实际应用需要同时使用两者——后端用 Claude API,客户端用 Vercel AI SDK。

Claude API / Anthropic SDK

Claude API / Anthropic SDK



Vercel AI SDK

Vercel AI SDK

Vercel AI SDK

Vercel AI SDK

The Vercel AI SDK provides React hooks and server utilities for building streaming chat interfaces with support for tool calls, file attachments, and multi-step reasoning.
Vercel AI SDK 提供 React Hooks 和服务端工具,用于构建支持工具调用、文件附件和多步推理的流式聊天界面。

Quick Reference

快速参考

Basic useChat Setup

基础 useChat 配置

typescript
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[]
});
typescript
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);
  }
});

// 发送消息
sendMessage({ text: 'Hello', metadata: { createdAt: Date.now() } });

// 附带文件发送
sendMessage({
  text: 'Analyze this',
  files: fileList // FileList 或 FileUIPart[]
});

ChatStatus States

ChatStatus 状态

The
status
field indicates the current state of the chat:
  • ready
    : Chat is idle and ready to accept new messages
  • submitted
    : Message sent to API, awaiting response stream start
  • streaming
    : Response actively streaming from the API
  • error
    : An error occurred during the request
status
字段表示聊天的当前状态:
  • ready
    : 聊天处于空闲状态,可接受新消息
  • submitted
    : 消息已发送至 API,等待响应流启动
  • streaming
    : API 正在主动流式返回响应
  • error
    : 请求过程中发生错误

Message Structure

消息结构

Messages use the
UIMessage
type with a parts-based structure:
typescript
interface UIMessage {
  id: string;
  role: 'system' | 'user' | 'assistant';
  metadata?: unknown;
  parts: Array<UIMessagePart>; // text, file, tool-*, reasoning, etc.
}
Part types include:
  • text
    : Text content with optional streaming state
  • file
    : File attachments (images, documents)
  • tool-{toolName}
    : Tool invocations with state machine
  • reasoning
    : AI reasoning traces
  • data-{typeName}
    : Custom data parts
消息采用
UIMessage
类型,基于部件的结构:
typescript
interface UIMessage {
  id: string;
  role: 'system' | 'user' | 'assistant';
  metadata?: unknown;
  parts: Array<UIMessagePart>; // 文本、文件、tool-*、推理等
}
部件类型包括:
  • text
    : 带有可选流式状态的文本内容
  • file
    : 文件附件(图片、文档)
  • tool-{toolName}
    : 带有状态机的工具调用
  • reasoning
    : AI 推理轨迹
  • data-{typeName}
    : 自定义数据部件

Server-Side Streaming

服务端流式传输

typescript
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
  }
});
typescript
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 }) => {
    // 保存至数据库
  }
});

Tool Handling Patterns

工具处理模式

Client-Side Tool Execution:
typescript
const { addToolOutput } = useChat({
  onToolCall: async ({ toolCall }) => {
    if (toolCall.toolName === 'getLocation') {
      addToolOutput({
        tool: 'getLocation',
        toolCallId: toolCall.toolCallId,
        output: 'San Francisco'
      });
    }
  }
});
Rendering Tool States:
typescript
{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>;
    }
  }
})}
客户端工具执行:
typescript
const { addToolOutput } = useChat({
  onToolCall: async ({ toolCall }) => {
    if (toolCall.toolName === 'getLocation') {
      addToolOutput({
        tool: 'getLocation',
        toolCallId: toolCall.toolCallId,
        output: 'San Francisco'
      });
    }
  }
});
渲染工具状态:
typescript
{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>;
    }
  }
})}

Reference Files

参考文档

Detailed documentation on specific aspects:
  • use-chat.md: Complete useChat API reference
  • messages.md: UIMessage structure and part types
  • streaming.md: Server-side streaming implementation
  • tools.md: Tool definition and execution patterns
特定方面的详细文档:
  • use-chat.md: 完整的 useChat API 参考
  • messages.md: UIMessage 结构和部件类型
  • streaming.md: 服务端流式传输实现
  • tools.md: 工具定义和执行模式

Common Patterns

常见模式

Error Handling

错误处理

typescript
const { error, clearError } = useChat({
  onError: (error) => {
    toast.error(error.message);
  }
});

// Clear error and reset to ready state
if (error) {
  clearError();
}
typescript
const { error, clearError } = useChat({
  onError: (error) => {
    toast.error(error.message);
  }
});

// 清除错误并重置为就绪状态
if (error) {
  clearError();
}

Message Regeneration

消息重新生成

typescript
const { regenerate } = useChat();

// Regenerate last assistant message
await regenerate();

// Regenerate specific message
await regenerate({ messageId: 'msg-123' });
typescript
const { regenerate } = useChat();

// 重新生成最后一条助手消息
await regenerate();

// 重新生成特定消息
await regenerate({ messageId: 'msg-123' });

Custom Transport

自定义传输

typescript
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
      }
    })
  })
});
typescript
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
      }
    })
  })
});

Performance Optimization

性能优化

typescript
// Throttle UI updates to reduce re-renders
const chat = useChat({
  experimental_throttle: 100 // Update max once per 100ms
});
typescript
// 限制 UI 更新频率以减少重渲染
const chat = useChat({
  experimental_throttle: 100 // 每 100ms 最多更新一次
});

Automatic Message Sending

自动消息发送

typescript
import { lastAssistantMessageIsCompleteWithToolCalls } from 'ai';

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

const chat = useChat({
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls
  // 当所有工具调用都有输出时自动重新发送
});

Type Safety

类型安全

The SDK provides full type inference for tools and messages:
typescript
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>();
SDK 为工具和消息提供完整的类型推断:
typescript
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 }, // 元数据类型
  UIDataTypes,
  InferUITools<typeof tools> // 工具类型
>;

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

Key Concepts

核心概念

Parts-Based Architecture

基于部件的架构

Messages use a parts array instead of a single content field. This allows:
  • Streaming text while maintaining other parts
  • Tool calls with independent state machines
  • File attachments and custom data mixed with text
消息使用部件数组而非单一内容字段,这允许:
  • 在流式传输文本的同时保留其他部件
  • 带有独立状态机的工具调用
  • 文件附件和自定义数据与文本混合

Tool State Machine

工具状态机

Tool parts progress through states:
  1. input-streaming
    : Tool input streaming (optional)
  2. input-available
    : Tool input complete
  3. approval-requested
    : Waiting for user approval (optional)
  4. approval-responded
    : User approved/denied (optional)
  5. output-available
    : Tool execution complete
  6. output-error
    : Tool execution failed
  7. output-denied
    : User denied approval
工具部件会经历以下状态:
  1. input-streaming
    : 工具输入流式传输(可选)
  2. input-available
    : 工具输入完成
  3. approval-requested
    : 等待用户批准(可选)
  4. approval-responded
    : 用户已批准/拒绝(可选)
  5. output-available
    : 工具执行完成
  6. output-error
    : 工具执行失败
  7. output-denied
    : 用户拒绝批准

Streaming Protocol

流式传输协议

The SDK uses Server-Sent Events (SSE) with UIMessageChunk types:
  • text-start
    ,
    text-delta
    ,
    text-end
  • tool-input-available
    ,
    tool-output-available
  • reasoning-start
    ,
    reasoning-delta
    ,
    reasoning-end
  • start
    ,
    finish
    ,
    abort
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

Client vs Server Tools

客户端 vs 服务端工具

Server-side tools have an
execute
function and run on the API route.
Client-side tools omit
execute
and are handled via
onToolCall
and
addToolOutput
.
服务端工具包含
execute
函数,在 API 路由上运行。
客户端工具省略
execute
,通过
onToolCall
addToolOutput
处理。

Best Practices

最佳实践

  1. Always handle the
    error
    state and provide user feedback
  2. Use
    experimental_throttle
    for high-frequency updates
  3. Implement proper loading states based on
    status
  4. Type your messages with custom metadata and tools
  5. Use
    sendAutomaticallyWhen
    for multi-turn tool workflows
  6. Handle all tool states in the UI for better UX
  7. Use
    stop()
    to allow users to cancel long-running requests
  8. Validate messages with
    validateUIMessages
    on the server NOTE: claude-api is a plugin-loaded skill (not in local dir). Only vercel-ai-sdk was local.
  1. 始终处理
    error
    状态并向用户提供反馈
  2. 对高频更新使用
    experimental_throttle
  3. 根据
    status
    实现合适的加载状态
  4. 为消息添加自定义元数据和工具的类型定义
  5. 对多轮工具工作流使用
    sendAutomaticallyWhen
  6. 在 UI 中处理所有工具状态以提升用户体验
  7. 使用
    stop()
    允许用户取消长时间运行的请求
  8. 在服务端使用
    validateUIMessages
    验证消息 注意:claude-api 是插件加载的技能(不在本地目录)。只有 vercel-ai-sdk 是本地的。