قابلیت Tool Calling در AI SDK


در AI SDK، یک tool می‌تواند هم در generateText استفاده شود، هم در streamText. این کار با قرار دادن یک یا چند Tool در پارامتری به نام tools، انجام می‌شود. یک tool، شامل سه خصیصه (property)، می‌باشد:

  • description: توضیحات اختیاری در مورد ابزار که هنگام استفاده مدل از ابزار، می‌تواند تاثیرگذار باشد.
  • parameters: یک Zod Schema یا یک JSON Schema که پارامترهای مورد نیاز tool را تعریف می‌کند. این اسکیما توسط مدل استفاده می‌شود.
  • execute: یک تابع async اختیاری که با آرگومان‌های داده شده در فراخوانی tool، صدا زده می‌شود. این خصیصه، یک مقدار از نوع RESULT ایجاد می‌کند.

پارامتر tools در generateText و streamText، یک آبجکت است که باید در آن، اسم toolها را به عنوان کلید تعریف کرده و خود toolها را به‌عنوان مقدار، به کلید تعریف شده، بدهید:

کپی
// npm i @ai-sdk/openai-compatible
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText, tool } from 'ai';
import { z } from 'zod';

const result = await generateText({
model: createOpenAICompatible({
baseURL: "<baseUrl>",
name: 'example',
apiKey: "<LIARA_API_KEY>",
}).chatModel("openai/gpt-4o-mini"),

tools: {
weather: tool({
description: 'Get the weather in a location',
parameters: z.object({
location: z.string().describe('The location to get the weather for'),
}),
execute: async ({ location }) => ({
location,
temperature: 72 + Math.floor(Math.random() * 21) - 10,
}),
}),
},

prompt: 'What is the weather in San Francisco?',
});

در قطعه کد فوق، در فیلد tools، یک tool به نام weather تعریف شده است که در آن، پارامتر location با استفاده از ماژول zod مشخص شده است. وقتی که این tool فراخوانی شود. تابع تعریف شده در بخش execute فراخوانی می‌شود و خروجی که بر اساس location است، یک دما را به‌صورت تصادفی (بین 62 تا 82 درجه فارنهایت)، تولید می‌کند.

وقتی که مدل تصمیم می‌گیرد از یک tool استفاده کند؛ یک tool call ایجاد می‌کند. در صورتی که فیلد execute، در یک tool تعریف شده باشد، در حین tool calling، تابع آن فیلد، اجرا می‌شود. در نهایت، خروجی تابع اجرا شده توسط tool calling، با استفاده از tool result object، برگردانده می‌شود.

شما می‌توانید با استفاده از قابلیت فراخوانی چند مرحله‌ای (multi-step calls)، خروجی یک tool را مجدداً به LLM برگردانید.

فراخوانی چندمرحله‌ای (با استفاده از maxSteps)

با maxSteps، می‌توانید فراخوانی چندمرحله‌ای را در generateText و streamText، فعال کنید. زمانی که مقدار maxSteps عددی بزرگ‌تر از 1 باشد و مدل، یک tool call ایجاد کند؛ AI SDK، با ارسال نتیجه Tool به مدل، یک پاسخ جدید ایجاد می‌کند و این کار تا زمانی که دیگر هیچ tool call جدیدی ایجاد نشود یا حد مقدار maxSteps زده نشود؛ ادامه پیدا می‌کند.

برای تنظیم مقدار maxSteps، به‌جای در نظر گرفتن تعداد Toolهای موجود، تعداد مراحل پاسخ دادن به پیچیده‌ترین تسکی که قرار است به مدل، ارسال شود را، در نظر بگیرید.

به‌صورت پیش‌فرض، وقتی که از generateText یا streamText استفاده می‌کنید؛ مدل یک پاسخ برای شما، تولید می‌کند (که به آن generation گفته می‌شود). در این حالت، مقدار پیش‌فرض maxSteps، برابر با 1 است. این حالت، در زمانی که شما به داده‌های خود مدل اکتفا می‌کنید، در بسیاری از موارد، قابل‌قبول است. با این حال، وقتی که از Toolها استفاده می‌کنید، مدل تصمیم می‌گیرد که یک پیام متنی معمولی تولید کند یا یک tool call. اگر که برای مدل، پارامتر maxSteps را تنظیم کرده باشید و مدل، یک tool call ایجاد کند؛ مرحله اول آن به‌صورت کامل انجام شده و وارد مرحله دوم می‌شود.

ممکن است که بخواهید مدل، پس از اجرای یک Tool، یک متن تولید کند یا حتی، نتایج Tool استفاده شده را خلاصه کند و در شکلی خوانا، به شما، تحویل دهد. در بسیاری از موارد، ممکن است شما بخواهید که مدل، در یک پاسخ، از چند تا Tool مختلف، استفاده کند. این، همانجایی است که مرحله دوم (و به مراتب، مراحل بعدی) در maxSteps، معنا پیدا می‌کند و نیاز به فراخوانی چندمرحله‌ای، احساس می‌شود.

مثالی از فراخوانی چند مرحله‌ای با maxSteps

مثال دو مرحله‌ای زیر را، در نظر بگیرید:

۱

مرحله اول

پرامپتِ 'What is the weather in San Francisco'، به‌ مدل، ارسال می‌شود. مدل، یک tool call، ایجاد می‌کند. در نهایت، tool call، اجرا می‌شود.

۲

مرحله دوم

نتایج Tool، به مدل، ارسال می‌شود. مدل، با در نظر گرفتن نتایج Tool، یک پاسخ ایجاد می‌کند.

قطعه کد دو مرحله فوق، در ادامه، قرار گرفته است:

کپی
import { generateText, tool  } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { z } from 'zod';

const model = createOpenAI({
  baseURL: "<baseUrl>",
  apiKey: "<LIARA_API_KEY>",
  compatibility: "strict",
});

const { text, steps } = await generateText({
  model: model('<model_name>'),
 tools: {
    weather: tool({
      description: 'Get the weather in a location',
      parameters: z.object({
        location: z.string().describe('The location to get the weather for'),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 72 + Math.floor(Math.random() * 21) - 10,
      }),
    }),
  },
  maxSteps: 2, // allow up to 2 steps
  prompt: 'What is the weather in San Francisco',
});

در قطعه کد فوق، می‌توانید به‌طور مشابه، از streamText استفاده کنید.

Stepها

برای دسترسی به tool callهای میانی و نتایج آن‌ها، می‌توانید از ویژگی steps یا یک callback به‌نام onFinish، استفاده کنید. این ویژگی، شامل تمامی متن‌ها، tool callها، نتایج Tool و ... در هر مرحله، می‌باشد

در مثال زیر، نتایج Toolها، از تمامی مراحل، استخراج می‌شود:

کپی
import { generateText, tool  } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { z } from 'zod';

const model = createOpenAI({
  baseURL: "<baseUrl>",
  apiKey: "<LIARA_API_KEY>",
  compatibility: "strict",
});

const { steps } = await generateText({
  model: model('<model_name>'),
 tools: {
    weather: tool({
      description: 'Get the weather in a location',
      parameters: z.object({
        location: z.string().describe('The location to get the weather for'),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 72 + Math.floor(Math.random() * 21) - 10,
      }),
    }),
  },
  maxSteps: 5, 
  prompt: 'What is the weather in San Francisco?',
});

const allToolCalls = steps.flatMap(step => step.toolCalls);
console.log(allToolCalls)

کال‌بک onStepFinish

زمانی که از generateText یا streamText استفاده می‌کنید؛ می‌توانید یک callback از نوع onStepFinish تعریف کنید. این callback پس از پایان هر مرحله، فراخوانی می‌شود؛ یعنی زمانی که تمام موارد (از جمله متن، tool callها و نتایج tool) برای مرحله بعدی، آماده هستند. در صورت وجود چند مرحله، این callback، برای هر مرحله، به‌صورت جداگانه، اجرا می‌شود.

به شکل زیر، می‌توانید از این callback استفاده کنید:

کپی
import { generateText, tool  } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { z } from 'zod';

const model = createOpenAI({
  baseURL: "<baseUrl>",
  apiKey: "<LIARA_API_KEY>",
  compatibility: "strict",
});

const result = await generateText({
 model: model('<model_name>'),
 tools: {
    weather: tool({
      description: 'Get the weather in a location',
      parameters: z.object({
        location: z.string().describe('The location to get the weather for'),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 72 + Math.floor(Math.random() * 21) - 10,
      }),
    }),
  },
  maxSteps: 5, 
  prompt: 'What is the weather in San Francisco?',
  onStepFinish({ text, toolCalls, toolResults, finishReason, usage }) {
  // your own logic, e.g. for saving the chat history or recording usage
  console.log(text, toolCalls, toolResults, finishReason, usage)
  },
});

کال‌بک experimental_prepareStep

experimental_prepareStep در AI SDK، آزمایشی است و ممکن است در آینده، تغییر کند. این callback، فقط در generateText، موجود است.

کال‌بک experimental_prepareStep، قبل از اجرای یک مرحله (step)، صدا زده می‌شود. این callback، با پارامترهای زیر، فراخوانی می‌شود:

  • model: مشابه همان مدل تعریف شده در generateText
  • maxSteps: مشابه همان maxSteps تعریف شده در generateText
  • stepNumber: شماره مرحله‌ای که در حال اجراست
  • steps: شماره نشان‌دهنده مراحلی که تا الان، اجرا شده‌اند

می‌توانید مانند قطعه کد زیر، از این callback استفاده کنید تا برای یک مرحله، تنظیمات مختلفی، درج کنید:

کپی
import { generateText, tool  } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';
import { z } from 'zod';

const model = createOpenAI({
  baseURL: "<baseUrl>",
  apiKey: "<LIARA_API_KEY>",
  name: 'my-provider',
  compatibility: "strict",
});

const result = await generateText({
 model: model('<model_name>'),
 tools: {
    weather: tool({
      description: 'Get the weather in a location',
      parameters: z.object({
        location: z.string().describe('The location to get the weather for'),
      }),
      execute: async ({ location }) => ({
        location,
        temperature: 72 + Math.floor(Math.random() * 21) - 10,
      }),
    }),
  },
  maxSteps: 5, 
  prompt: 'What is the weather in San Francisco?',
  
  experimental_prepareStep: async ({ model, stepNumber, maxSteps, steps }) => {
    if (stepNumber === 0) {
      return {
        model,
        stepNumber,
        maxSteps,
        steps
      };
    }

    // when nothing is returned, the default settings are used
  },
});