Технологии0 阅读

От функции к Tool: как Vercel AI SDK открывает дверь к вызову инструментов

Если вы уже привыкли писать API Route и вызывать сторонние API в Next.js, то до создания «помощника с большой языковой моделью, который может сам вызывать интерфейсы» остался всего полшага: обернуть эти функции в Tool и передать модели решение, когда их использовать.Tool, и передать модели решение, когда их использовать.

В этой статье мы сначала не будем обсуждать «большие слова» вроде MCP, Agent frameworks, а сделаем только одно:

помочь вам перейти от «написания обычной функции» к «написанию Tool, который может вызываться большой языковой моделью», заодно объяснив ментальную модель, стоящую за этим.

После прочтения вы сможете:

1. Зачем нужен слой «инструментов»?

Сначала представьте простую задачу: вы хотите сделать чат-бота для проверки погоды.

Самый наивный подход:

  1. Пользователь вводит: «Проверь погоду в Пекине на сегодня».

  2. Вы получаете на сервере эту фразу на естественном языке.

  3. Пишете свой if/else или регулярное выражение, чтобы извлечь название города.

  4. Вызываете API погоды, получаете результат.

  5. Вставляете результат обратно в prompt, даёте модели сформировать ответ пользователю.

Этот подход работает, но есть очевидные проблемы:

Слой Tool поднимает ваш «if/else + вызов API» до уровня способности, видимой модели.

Другими словами:

Это абстракция, которую делает Tool в Vercel AI SDK.

2. Что такое Tool в Vercel AI SDK?

Можно сначала понять Tool как:

функция с инструкцией — инструкция для модели, а тело функции для вашего кода.

В Vercel AI SDK Tool обычно содержит три части информации:

  1. description: одно предложение, объясняющее, что делает этот инструмент.

  2. parameters: описание необходимых входных параметров с помощью JSON Schema (или Zod).

  3. execute: функция, которая фактически выполняется, например, вызов HTTP API или запрос к базе данных.

Выглядит это примерно так (псевдокод, чтобы почувствовать определение):

ts
import { tool } from 'ai';

const getWeather = tool({
  description: '查询指定城市的实时天气',
  parameters: {
    type: 'object',
    properties: {
      city: { type: 'string', description: '城市名称,比如 Beijing' },
    },
    required: ['city'],
  },
  execute: async ({ city }) => {
    // 这里就是你平时会写的业务代码
    const res = await fetch(`https://api.example.com/weather?city=${city}`);
    return await res.json();
  },
});

После этого при вызове модели вы передаёте этот getWeather в AI SDK:

Вам больше не нужно писать логику «извлечения названия города» и «решения, когда вызывать API» — вы передаёте «инициативу» модели.

3. Пример минимально рабочего Tool

Давайте напишем минимальный реально работающий пример: получение текущего времени.

3.1 Определяем простой Tool

Предположим, вы уже установили пакет ai (Vercel AI SDK) в проект Next.js. На сервере создадим простой Tool:

ts
// app/api/chat/tools.ts
import { tool } from 'ai';

export const getCurrentTime = tool({
  description: '获取当前服务器时间(ISO 字符串)',
  parameters: {
    type: 'object',
    properties: {},
  },
  async execute() {
    return {
      now: new Date().toISOString(),
    };
  },
});

Здесь стоит обратить внимание на несколько моментов:

3.2 Используем Tool в API Route

Далее напишем простейший чат API, позволяющий модели вызывать этот Tool. Конкретные детали вызова могут немного различаться в зависимости от версии, здесь мы акцентируем только «структуру» и «идею», псевдокод выглядит примерно так:

ts
// app/api/chat/route.ts
import { NextRequest } from 'next/server';
import { streamText } from 'ai'; // 假设使用 AI SDK 的某个调用方法
import { getCurrentTime } from './tools';
import { openai } from '@ai-sdk/openai';

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

  const response = await streamText({
    model: openai('gpt-4o'), // 你选的模型
    messages,
    tools: {
      getCurrentTime,
    },
  });

  return response.toAIStreamResponse();
}

Вот несколько ключевых моментов:

3.3 Простой чат-интерфейс на фронтенде

На фронтенде можно использовать Hook типа useChat для создания минимального чат-интерфейса. Эта часть не является фокусом статьи, поэтому подробно останавливаться не будем. Важно: с точки зрения фронтенда нет отличий от обычного Chat API, только модель теперь «сама может проверять время».

4. Принципиальное различие между Tool и обычной функцией

Здесь вы можете спросить: разве я не могу просто написать new Date().toISOString() в роуте и получить время? Зачем оборачивать в Tool?

Ключевое различие в том, кто отвечает за «принятие решения»:

Это даёт несколько практических преимуществ:

  1. Меньше if/else

    • Вам не нужно разбирать естественный язык, сопоставлять ключевые слова, выбирать ветви.

    • Модель сама на основе контекста решает, стоит ли использовать какой-либо Tool.

  2. Поддержка автоматической оркестровки нескольких Tool

    • Определить несколько инструментов (получение времени, погоды, расписания), и модель может вызывать их по необходимости в одном диалоге.

    • По мере увеличения количества инструментов вам не нужно поддерживать кучу «таблиц маршрутизации бизнес-логики» — модель сама «разбирается».

  3. Единообразие между моделями

    • У разных провайдеров моделей интерфейсы Tool / Function Calling различаются, а AI SDK унифицирует эти детали.

    • Вам нужно поддерживать только один набор определений Tool для переключения между разными моделями.

Можно рассматривать Tool как:

добавление слоя «функции со структурированными метаданными» между «моделью и вашим кодом», делая этот канал одновременно умным и управляемым.

5. Проектирование удобного Tool: параметры и возвращаемые значения

Написать Tool — это не просто «чтобы работало». Правильно спроектированные параметры и возвращаемые значения позволяют модели использовать его более стабильно и управляемо.

5.1 Параметры: помогаем модели чётко формулировать

Рекомендуется следовать нескольким принципам при определении параметров:

Пример:

ts
const getWeather = tool({
  description: '查询指定城市在指定日期的天气',
  parameters: {
    type: 'object',
    properties: {
      city: {
        type: 'string',
        description: '城市名称,比如 Beijing、Shanghai',
      },
      date: {
        type: 'string',
        description: '日期,格式为 YYYY-MM-DD,比如 2025-05-17',
      },
      unit: {
        type: 'string',
        enum: ['celsius', 'fahrenheit'],
        description: '温度单位,默认 celsius',
      },
    },
    required: ['city', 'date'],
  },
  async execute({ city, date, unit = 'celsius' }) {
    // ...
  },
});

5.2 Возвращаемые значения: предпочитайте структурированные, а не строки

Хотя вы можете напрямую вернуть из execute строку вроде «В Пекине сегодня 25 градусов, ясно», это потеряет много гибкости:

Более предпочтительный подход — возвращать чёткий объект:

ts
async execute({ city, date, unit = 'celsius' }) {
  const data = await fetchWeather(city, date, unit);
  return {
    city,
    date,
    unit,
    temperature: data.temp,
    condition: data.condition, // sunny, cloudy, etc.
  };
}

Так модель сможет как самостоятельно сформулировать ответ пользователю, так и использовать эти поля в последующих шагах (например, вызывать другой Tool).

6. Подключение нескольких Tool сразу: пусть модель выбирает сама

Ранее мы использовали только getCurrentTime, теперь можно добавить getWeather и передать их модели вместе.

ts
// app/api/chat/tools.ts
export const tools = {
  getCurrentTime,
  getWeather,
};

При вызове модели:

ts
const response = await streamText({
  model: openai('gpt-4o'),
  messages,
  tools,
});

Теперь, если пользователь скажет:

«Проверь погоду в Шанхае на завтра, если солнечно, заодно напомни текущее время.»

У модели будет возможность сделать следующее (на логическом уровне):

  1. Сначала вызвать getWeather, определить, «солнечно ли».

  2. Затем вызвать getCurrentTime, получить текущее время.

  3. Сгенерировать окончательный ответ, объединив результаты двух вызовов.

Вам не нужно писать никакую «бизнес-блок-схему» для этой логики, достаточно:

В этом и заключается истинная сила «вызова инструментов»: вы переходите от «написания процессов» к «определению возможностей».

7. Завершение этой статьи: сначала освойте Tool, затем думайте об уровне протоколов

В этой статье мы сделали три вещи:

  1. Разобрали Tool концептуально: это «функция с инструкцией», инструкция предназначена для модели.

  2. Написали минимальный пример Tool и показали, как использовать его в API Route Next.js.

  3. Рассказали, как проектировать параметры и возвращаемые значения, чтобы модель лучше использовала ваш инструмент.

Только с помощью Tool от Vercel AI SDK вы уже можете создавать:

Причём на этом этапе вы совершенно можете не беспокоиться о протокольных уровнях вроде MCP — они в основном решают проблемы «повторного использования инструментов между хостами и проектами» и лучше внедрять их постепенно, когда количество инструментов и среда вызова станут сложнее.

В следующей статье мы на основе «только Tool» отдельно обсудим: с какими границами и болями сталкивается один только Tool, когда инструментов становится больше и среда вызова сложнее, и почему появляются такие протоколы, как MCP.

Поделиться

Поделиться

Поделиться этой статьей.