文档
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
类引用
函数引用
接口引用
类型别名引用
变量引用
指南

工具架构

TanStack AI 工具系统提供了一个强大、灵活的架构,使 AI 代理能够与外部系统交互

  • 服务器工具 在后端安全地执行,并自动处理

  • 客户端工具 在浏览器中执行,用于 UI 更新和本地操作

  • 代理循环 能够实现多步骤推理和复杂的工作流程

  • 工具状态 提供实时反馈并实现强大的 UI

  • 审批流程 赋予用户对敏感操作的控制权。这种架构能够构建复杂的 AI 应用程序,可以

  • 从 API 和数据库中获取数据

  • 执行计算和转换

  • 更新 UI 并管理状态

  • 执行多步骤工作流程

  • 需要用户批准敏感操作

调用流程:客户端到 LLM 服务

当用户发送需要使用工具的消息时,将发生以下流程

mermaid

逐步分解

  1. 用户输入:用户在聊天界面中输入消息
  2. 客户端请求:浏览器向服务器发送 POST 请求,其中包含
    • 当前的对话历史记录 (messages)
    • 可选的数据有效负载 (body)
  3. 服务器处理:服务器
    • 接收请求
    • 从请求主体中提取消息
    • 将工具定义转换为 LLM 期望的格式
    • 将请求发送到 LLM 服务(OpenAI、Anthropic 等)
  4. LLM 决策:LLM 服务
    • 分析对话和可用工具
    • 根据用户的请求决定是否调用工具
    • 生成带有参数的工具调用
  5. 流式响应:LLM 流式传输块
    • tool_call 块,包含工具名称和参数
    • content 块,包含文本响应
    • done 块,表示完成
  6. 客户端更新:浏览器接收块并实时更新 UI

代码示例

服务器(API 路由)

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

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

  // Create streaming chat with tools
  const stream = chat({
    adapter: openaiText("gpt-5.2"),
    messages,
    tools: [getWeather, sendEmail], // Tool definitions passed here
  });

  return toServerSentEventsResponse(stream);
}

客户端(React 组件)

typescript
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";

function ChatComponent() {
  const { messages, sendMessage, isLoading } = useChat({
    connection: fetchServerSentEvents("/api/chat"),
  });

  return (
    <div>
      {messages.map((message) => (
        <div key={message.id}>{/* Render message */}</div>
      ))}
      <input onSubmit={(e) => sendMessage(e.target.value)} />
    </div>
  );
}

状态和生命周期

工具在其生命周期内会经历不同的状态。了解这些状态有助于构建强大的 UI 并调试工具执行。

mermaid

调用状态

状态描述客户端操作
awaiting-input已接收工具调用,但尚未接收到参数显示加载
input-streaming正在接收部分参数显示进度
input-complete已接收所有参数准备执行
approval-requested正在等待用户批准显示审批 UI
approval-responded用户已批准/拒绝执行或取消

结果状态

状态描述客户端操作
streaming正在流式传输结果(未来功能)显示进度
complete结果已完成显示结果
error执行过程中发生错误显示错误消息

在 React 中监控工具状态

typescript
function ChatComponent() {
  const { messages } = useChat({
    connection: fetchServerSentEvents("/api/chat"),
  });

  return (
    <div>
      {messages.map((message) => (
        <div key={message.id}>
          {message.parts.map((part) => {
            if (part.type === "tool-call") {
              return (
                <div key={part.id} className="tool-status">
                  {/* Show state-specific UI */}
                  {part.state === "awaiting-input" && (
                    <div>🔄 Calling {part.name}...</div>
                  )}
                  {part.state === "input-streaming" && (
                    <div>📥 Receiving arguments...</div>
                  )}
                  {part.state === "input-complete" && (
                    <div>✓ Arguments ready</div>
                  )}
                  {part.state === "approval-requested" && (
                    <ApprovalUI part={part} />
                  )}
                </div>
              );
            }
            if (part.type === "tool-result") {
              return (
                <div key={part.toolCallId}>
                  {part.state === "complete" && (
                    <div>✓ Tool completed</div>
                  )}
                  {part.state === "error" && (
                    <div>❌ Error: {part.error}</div>
                  )}
                </div>
              );
            }
          })}
        </div>
      ))}
    </div>
  );
}

审批流程

对于敏感操作,工具在执行前可能需要用户批准

mermaid

使用审批定义工具

typescript
const sendEmailDef = toolDefinition({
  name: "send_email",
  description: "Send an email",
  inputSchema: z.object({
    to: z.string().email(),
    subject: z.string(),
    body: z.string(),
  }),
  needsApproval: true, // Requires user approval
});

const sendEmail = sendEmailDef.server(async ({ to, subject, body }) => {
  await emailService.send({ to, subject, body });
  return { success: true };
});

在客户端中处理审批

typescript
const { messages, addToolApprovalResponse } = useChat({
  connection: fetchServerSentEvents("/api/chat"),
});

// In your render:
{part.state === "approval-requested" && (
  <div>
    <p>Approve sending email to {part.arguments.to}?</p>
    <button
      onClick={() =>
        addToolApprovalResponse({
          id: part.approval.id,
          approved: true,
        })
      }
    >
      Approve
    </button>
    <button
      onClick={() =>
        addToolApprovalResponse({
          id: part.approval.id,
          approved: false,
        })
      }
    >
      Deny
    </button>
  </div>
)}

混合工具(服务器 + 客户端)

有些工具需要在两个环境中执行

typescript
// Server: Fetch data from database
const fetchUserPrefsDef = toolDefinition({
  name: "fetch_user_preferences",
  description: "Get user preferences from server",
  inputSchema: z.object({
    userId: z.string(),
  }),
});

const fetchUserPreferences = fetchUserPrefsDef.server(async ({ userId }) => {
  const prefs = await db.userPreferences.findUnique({ where: { userId } });
  return prefs;
});

// Client: Apply preferences to UI
const applyPrefsDef = toolDefinition({
  name: "apply_preferences",
  description: "Apply user preferences to the UI",
  inputSchema: z.object({
    theme: z.string(),
    language: z.string(),
  }),
});

// On client, create client implementation
const applyPreferences = applyPrefsDef.client(async ({ theme, language }) => {
  // Update UI state with preferences
  document.body.className = theme;
  i18n.changeLanguage(language);
  return { applied: true };
});

// Usage: LLM can chain these together
// 1. Call fetchUserPreferences (server)
// 2. Call applyPreferences with the result (client)

并行工具执行

为了提高效率,LLM 可以并行调用多个工具

mermaid

示例

User: "Compare the weather in NYC, SF, and LA"

LLM calls:
- get_weather({city: "NYC"}) [index: 0]
- get_weather({city: "SF"}) [index: 1]
- get_weather({city: "LA"}) [index: 2]

All execute simultaneously, then LLM generates comparison.

最佳实践

工具设计

  • 单一职责:每个工具应该做好一件事情
  • 清晰的描述:帮助 LLM 了解何时使用该工具
  • 类型安全:使用 Zod 模式进行输入/输出验证
  • 错误处理:返回有意义的错误消息,不要抛出异常
  • 幂等性:工具应该可以安全地多次调用

安全性

  • 服务器与客户端:将敏感操作放在服务器上
  • 审批流程:对破坏性操作使用 needsApproval
  • 输入验证:始终验证工具输入
  • 速率限制:为昂贵的工具实施速率限制
  • 审计日志:记录工具执行情况以进行调试和安全

性能

  • 缓存:在适当的情况下缓存工具结果
  • 并行执行:尽可能启用并行工具调用
  • 流式传输:对长时间运行的操作使用流式传输
  • 超时:为外部 API 调用设置超时
  • 延迟加载:仅在需要时加载工具

下一步