工具调用

模型擅长写句子,不擅长精确算术和「现在外面几点」。工具是模型可以调用的函数:描述 + Zod inputSchema + 可选的 execute。AI SDK 在服务端跑 execute,把结果塞回上下文,再让模型继续说。

tool() 辅助函数,以便 execute 的参数被正确推断。


定义工具

import { tool } from 'ai';
import { z } from 'zod';

const weather = tool({
  description: 'Get the weather in a location (fahrenheit)',
  inputSchema: z.object({
    location: z.string().describe('The location to get the weather for'),
  }),
  execute: async ({ location }) => {
    const temperature = Math.round(Math.random() * (90 - 32) + 32);
    return { location, temperature };
  },
});
字段作用
description告诉模型何时调用
inputSchemaZod 或 JSON Schema;校验模型填的参数
execute可省略:省略则把调用交给客户端或队列
strict部分厂商支持严格 JSON

tools对象:key 是工具名。


接到 streamText(聊天路由)

默认 stopWhenisStepCount(1):模型调用一次工具后就停,界面上往往只有工具 JSON、没有自然语言总结。允许继续循环:

import {
  convertToModelMessages,
  createUIMessageStreamResponse,
  isStepCount,
  streamText,
  toUIMessageStream,
  tool,
  type UIMessage,
} from 'ai';
import { z } from 'zod';

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();

  const result = streamText({
    model: 'openai/gpt-4.1-mini',
    messages: await convertToModelMessages(messages),
    stopWhen: isStepCount(5),
    tools: {
      weather: tool({
        description: 'Get the weather in a location (fahrenheit)',
        inputSchema: z.object({
          location: z.string().describe('The location to get the weather for'),
        }),
        execute: async ({ location }) => ({
          location,
          temperature: Math.round(Math.random() * 58 + 32),
        }),
      }),
      convertFahrenheitToCelsius: tool({
        description: 'Convert fahrenheit to celsius',
        inputSchema: z.object({
          temperature: z.number().describe('Temperature in fahrenheit'),
        }),
        execute: async ({ temperature }) => ({
          celsius: Math.round((temperature - 32) * (5 / 9)),
        }),
      }),
    },
  });

  return createUIMessageStreamResponse({
    stream: toUIMessageStream({ stream: result.stream }),
  });
}

问「纽约现在多少摄氏度?」时,模型会先 weatherconvertFahrenheitToCelsius,最后用文本回答。onStepEnd 里可以 console.log(toolResults)

无 UI 时同样的 tools + stopWhen 可以交给 generateText


前端渲染工具 part

工具 part 的类型永远是 tool-{toolName}

{message.parts.map((part, i) => {
  switch (part.type) {
    case 'text':
      return <div key={i}>{part.text}</div>;
    case 'tool-weather':
    case 'tool-convertFahrenheitToCelsius':
      return <pre key={i}>{JSON.stringify(part, null, 2)}</pre>;
    default:
      return null;
  }
})}

状态会从 input-streaminginput-availableoutput-available。可在 output-available 之前显示「正在查询天气…」。


审批、安全、反模式

默认带 execute 的工具会 自动跑。危险操作(删数据、付款)应使用 toolApprovaluser-approval 等),不要在生产里对不可信输入 eval

工具在 服务端 执行,可以读数据库、打内网 API。不要把密钥返回给模型或前端。给 description.describe() 写清楚,减少乱调工具。

stopWhen: isStepCount(n) 是步数上限,不是「必须跑满 n 步」。结构化输出本身也算一步,和工具组合时要把 n 留够。


下一步

评论