文档
CodeRabbit
Cloudflare
AG Grid
Netlify
Neon
WorkOS
Clerk
Convex
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
CodeRabbit
Cloudflare
AG Grid
Netlify
Neon
WorkOS
Clerk
Convex
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
类引用
函数引用
接口引用
类型别名引用
变量引用
指南

工具

工具(也称为“函数调用”)允许 AI 模型与外部系统、API 或执行计算进行交互。TanStack AI 提供了一个同构工具系统,可以实现类型安全、框架无关的工具定义,这些定义可以在服务器和客户端上工作。

工具使您的 AI 应用程序能够

  • 获取数据来自 API 或数据库
  • 执行计算或数据转换
  • 与服务交互,如电子邮件、日历或支付系统
  • 执行客户端操作,如更新 UI 或本地存储
  • 创建混合工具,在服务器和客户端环境中执行

框架支持

TanStack AI 与任何 JavaScript 框架一起工作

  • Next.js、Express、Remix、Fastify 等
  • React、Vue、Solid、Svelte、vanilla JS 等

TanStack AI 与任何 JavaScript 框架一起工作。

同构工具架构

TanStack AI 使用一个两步工具定义过程

  1. 一次定义使用 toolDefinition() - 创建共享工具模式
  2. 实现使用 .server().client() - 为每个环境添加执行逻辑

这种方法提供

  • 类型安全:来自 Zod 模式的完整 TypeScript 推断
  • 代码重用:一次定义模式,随处使用
  • 灵活性:工具可以在服务器、客户端或两者上执行
  • 模式选项:使用 Zod 模式或原始 JSON Schema 对象

模式选项

TanStack AI 支持两种定义工具模式的方式

Zod 模式提供完整的 TypeScript 类型推断和运行时验证

typescript
import { z } from "zod";

const inputSchema = z.object({
  location: z.string().describe("City name"),
  unit: z.enum(["celsius", "fahrenheit"]).optional(),
});

选项 2:JSON Schema 对象

对于您已经拥有 JSON Schema 定义或更喜欢不使用 Zod 的情况,您可以直接传递原始 JSON Schema 对象

typescript
import type { JSONSchema } from "@tanstack/ai";

const inputSchema: JSONSchema = {
  type: "object",
  properties: {
    location: {
      type: "string",
      description: "City name",
    },
    unit: {
      type: "string",
      enum: ["celsius", "fahrenheit"],
    },
  },
  required: ["location"],
};

注意:使用 JSON Schema 时,TypeScript 将为输入/输出类型推断 any,因为 JSON Schema 无法提供编译时类型信息。建议使用 Zod 模式以获得完整的类型安全。

工具定义

工具使用 toolDefinition()@tanstack/ai 定义

typescript
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";

// Step 1: Define the tool schema
const getWeatherDef = toolDefinition({
  name: "get_weather",
  description: "Get the current weather for a location",
  inputSchema: z.object({
    location: z.string().describe("The city and state, e.g. San Francisco, CA"),
    unit: z.enum(["celsius", "fahrenheit"]).optional(),
  }),
  outputSchema: z.object({
    temperature: z.number(),
    conditions: z.string(),
    location: z.string(),
  }),
});

// Step 2: Create a server implementation
const getWeatherServer = getWeatherDef.server(async ({ location, unit }) => {
  const response = await fetch(
    `https://api.weather.com/v1/current?location=${location}&unit=${
      unit || "fahrenheit"
    }`
  );
  const data = await response.json();
  return {
    temperature: data.temperature,
    conditions: data.conditions,
    location: data.location,
  };
});

使用 JSON Schema

如果您更喜欢 JSON Schema 或有现有的模式定义

typescript
import { toolDefinition } from "@tanstack/ai";
import type { JSONSchema } from "@tanstack/ai";

// Define schemas using JSON Schema
const inputSchema: JSONSchema = {
  type: "object",
  properties: {
    location: {
      type: "string",
      description: "The city and state, e.g. San Francisco, CA",
    },
    unit: {
      type: "string",
      enum: ["celsius", "fahrenheit"],
    },
  },
  required: ["location"],
};

const outputSchema: JSONSchema = {
  type: "object",
  properties: {
    temperature: { type: "number" },
    conditions: { type: "string" },
    location: { type: "string" },
  },
  required: ["temperature", "conditions", "location"],
};

// Create the tool definition
const getWeatherDef = toolDefinition({
  name: "get_weather",
  description: "Get the current weather for a location",
  inputSchema,
  outputSchema,
});

// Create server implementation (args is typed as `any` with JSON Schema)
const getWeatherServer = getWeatherDef.server(async (args) => {
  const { location, unit } = args;
  const response = await fetch(
    `https://api.weather.com/v1/current?location=${location}&unit=${unit || "fahrenheit"}`
  );
  return await response.json();
});

在聊天中使用工具

服务器端

typescript
import { chat, toServerSentEventsResponse } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { getWeatherDef } from "./tools";

export async function POST(request: Request) {
  const { messages } = await request.json();

  // Create server implementation
  const getWeather = getWeatherDef.server(async ({ location, unit }) => {
    const response = await fetch(`https://api.weather.com/v1/current?...`);
    return await response.json();
  });

  const stream = chat({
    adapter: openaiText("gpt-5.2"),
    messages,
    tools: [getWeather], // Pass server tools
  });

  return toServerSentEventsResponse(stream);
}

具有类型安全的客户端

typescript
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
import { 
  clientTools, 
  createChatClientOptions, 
  type InferChatMessages 
} from "@tanstack/ai-client";
import { updateUIDef, saveToStorageDef } from "./tools";

// Create client implementations
const updateUI = updateUIDef.client((input) => {
  // Update UI state
  setNotification(input.message);
  return { success: true };
});

const saveToStorage = saveToStorageDef.client((input) => {
  localStorage.setItem("data", JSON.stringify(input));
  return { saved: true };
});

// Create typed tools array (no 'as const' needed!)
const tools = clientTools(updateUI, saveToStorage);

const textOptions = createChatClientOptions({
  connection: fetchServerSentEvents("/api/chat"),
  tools,
});

// Infer message types for full type safety
type ChatMessages = InferChatMessages<typeof textOptions>;

function ChatComponent() {
  const { messages, sendMessage } = useChat(textOptions);
  
  // messages is now fully typed with tool names and outputs!
  return <Messages messages={messages} />;
}

混合工具

工具可以针对服务器和客户端实现,从而实现灵活的执行模式

typescript
// Define once
const addToCartDef = toolDefinition({
  name: "add_to_cart",
  description: "Add item to shopping cart",
  inputSchema: z.object({
    itemId: z.string(),
    quantity: z.number(),
  }),
  outputSchema: z.object({
    success: z.boolean(),
    cartId: z.string(),
  }),
  needsApproval: true,
});

// Server implementation - Store in database
const addToCartServer = addToCartDef.server(async (input) => {
  const cart = await db.carts.create({
    data: { itemId: input.itemId, quantity: input.quantity },
  });
  return { success: true, cartId: cart.id };
});

// Client implementation - Update local wishlist
const addToCartClient = addToCartDef.client((input) => {
  const wishlist = JSON.parse(localStorage.getItem("wishlist") || "[]");
  wishlist.push(input.itemId);
  localStorage.setItem("wishlist", JSON.stringify(wishlist));
  return { success: true, cartId: "local" };
});

在服务器上,传递定义(用于客户端执行)或服务器实现

typescript
chat({
  adapter: openaiText("gpt-5.2"),
  messages,
  tools: [addToCartDef], // Client will execute, or
  tools: [addToCartServer], // Server will execute
});

类型安全优势

同构架构提供完整的类型安全

typescript
// In your React component
messages.forEach((message) => {
  message.parts.forEach((part) => {
    if (part.type === 'tool-call' && part.name === 'add_to_cart') {
      // ✅ TypeScript knows part.name is literally 'add_to_cart'
      // ✅ part.input is typed as { itemId: string, quantity: number }
      // ✅ part.output is typed as { success: boolean, cartId: string } | undefined
      
      if (part.output) {
        console.log(part.output.cartId); // ✅ Fully typed!
      }
    }
  });
});

工具执行流程

  1. 模型决定调用工具 - 基于用户输入和工具描述
  2. 识别工具 - 服务器或客户端实现
  3. 工具执行 - 自动在服务器或客户端上
  4. 返回结果 - 作为工具结果消息返回给模型
  5. 模型继续 - 使用结果生成响应

工具状态

工具在执行过程中会经历不同的状态

  • awaiting-input - 收到工具调用,正在等待参数
  • input-streaming - 正在流式传输部分参数
  • input-complete - 收到所有参数
  • approval-requested - 工具需要用户批准(如果 needsApproval: true
  • approval-responded - 用户已批准/拒绝

下一步