Vercel AI SDK چیست؟ آموزش ساخت چت‌بات با Nex t.js و API درواره

Vercel AI SDK یک ابزار متن‌باز TypeScript برای ساخت اپلیکیشن‌ها، چت‌بات‌ها و Agentهای هوش مصنوعی است. در این آموزش با Next.js، React، Streaming و API سازگار با OpenAI درواره یک چت‌بات فارسی واقعی می‌سازیم. عنوان متا:

Share
Vercel AI SDK چیست؟ آموزش ساخت چت‌بات با Nex t.js و API درواره

ساخت یک رابط ساده برای مدل هوش مصنوعی چندان دشوار نیست. می‌توان یک فرم ایجاد کرد، متن کاربر را به API فرستاد و پاسخ مدل را نمایش داد. پیچیدگی واقعی زمانی آغاز می‌شود که بخواهیم قابلیت‌های زیر را به اپلیکیشن اضافه کنیم:

  • نمایش تدریجی پاسخ مدل
  • نگهداری تاریخچه مکالمه
  • توقف تولید پاسخ
  • نمایش وضعیت در حال پردازش
  • اتصال به مدل‌های مختلف
  • دریافت خروجی ساختاریافته
  • اجرای Tool Calling
  • مدیریت پیام‌های System، User و Assistant
  • پیاده‌سازی رابط چت در React
  • مدیریت خطا و تلاش مجدد
  • ذخیره مکالمات
  • ساخت Agent هوش مصنوعی

Vercel AI SDK برای ساده‌ترکردن همین بخش‌ها طراحی شده است.

در این آموزش ابتدا بررسی می‌کنیم Vercel AI SDK چیست و چه تفاوتی با OpenAI SDK دارد. سپس یک اپلیکیشن چت کامل با Next.js، React، Streaming و API درواره می‌سازیم.

پروژه نهایی دارای این قابلیت‌ها خواهد بود:

  • رابط چت فارسی
  • دریافت تدریجی پاسخ
  • اتصال به API سازگار با OpenAI درواره
  • امکان انتخاب مدل با متغیر محیطی
  • نگهداری کلید API در سمت سرور
  • توقف پاسخ در حال تولید
  • مدیریت وضعیت و خطا
  • نمونه خروجی ساختاریافته با Zod
  • نمونه Tool Calling
  • معماری مناسب برای توسعه محیط Production

Vercel AI SDK چیست؟

Vercel AI SDK یک Toolkit متن‌باز مبتنی بر TypeScript برای ساخت اپلیکیشن‌های هوش مصنوعی و Agentها است.

این SDK مجموعه‌ای از توابع و Hookهای آماده در اختیار توسعه‌دهنده قرار می‌دهد تا بتواند با مدل‌های مختلف کار کند، بدون اینکه برای هر Provider تمام منطق رابط کاربری، Streaming و پردازش پیام‌ها را از ابتدا بنویسد.

طبق مستندات رسمی، AI SDK برای توسعه اپلیکیشن‌های هوش مصنوعی با محیط‌هایی مانند React، Next.js، Vue، Svelte و Node.js طراحی شده است.

بخش‌های اصلی آن عبارت‌اند از:

  • AI SDK Core
  • AI SDK UI
  • Providerها
  • ابزارهای Streaming
  • Structured Output
  • Tool Calling
  • Agent Loop
  • Middleware

AI SDK Core چیست؟

AI SDK Core شامل توابع اصلی ارتباط با مدل است.

توابع پرکاربرد آن عبارت‌اند از:

generateText
streamText
embed
embedMany
generateImage
tool

برای مثال، تابع generateText یک درخواست کامل به مدل ارسال می‌کند و پس از آماده‌شدن کل پاسخ، نتیجه را برمی‌گرداند.

تابع streamText پاسخ مدل را به‌صورت تدریجی دریافت می‌کند. مستندات رسمی این تابع را برای Streaming خروجی متنی مدل معرفی می‌کند.

AI SDK UI چیست؟

AI SDK UI مجموعه‌ای از Hookها و ابزارهای رابط کاربری است.

مهم‌ترین Hook آن useChat است. این Hook بسیاری از وضعیت‌های موردنیاز یک رابط چت را مدیریت می‌کند:

  • پیام‌های مکالمه
  • ارسال پیام جدید
  • وضعیت ارسال
  • دریافت پاسخ Streaming
  • توقف تولید
  • مدیریت خطا
  • تلاش مجدد
  • اتصال رابط کاربری به Endpoint سمت سرور

براساس مستندات رسمی، useChat برای ساخت رابط مکالمه و دریافت Streaming پیام‌ها از Provider هوش مصنوعی استفاده می‌شود.

تفاوت Vercel AI SDK با OpenAI SDK چیست؟

Vercel AI SDK و OpenAI SDK در بعضی پروژه‌ها می‌توانند وظیفه مشابهی انجام دهند، اما هدف یکسانی ندارند.

ویژگیVercel AI SDKOpenAI SDK
زبان اصلیTypeScript و JavaScriptچند زبان، از جمله Python و JavaScript
تمرکزساخت اپلیکیشن و رابط هوش مصنوعیاتصال مستقیم به APIهای OpenAI-compatible
React Hookداردندارد
رابط آماده Streamingداردباید مدیریت شود
Providerهای مختلفمعماری یکپارچه Providerعمدتاً API سازگار با OpenAI
خروجی ساختاریافتهبا Schema و ابزارهای SDKاز طریق قابلیت‌های API
Tool Callingپشتیبانی سطح SDKپشتیبانی سطح API
مناسب Next.jsبسیار مناسبقابل‌استفاده، اما سطح پایین‌تر
وابستگی به Vercel Hostingنداردندارد

برای یک اسکریپت ساده سمت سرور، استفاده مستقیم از OpenAI SDK ممکن است کافی باشد. اما برای ساخت رابط چت، Streaming، Agent و اپلیکیشن React، استفاده از Vercel AI SDK معمولاً کدهای تکراری را کاهش می‌دهد.

آیا Vercel AI SDK فقط روی Vercel کار می‌کند؟

خیر.

نام Vercel در عنوان این پروژه ممکن است این تصور را ایجاد کند که اپلیکیشن فقط باید روی زیرساخت Vercel اجرا شود؛ اما AI SDK را می‌توان در محیط‌های مختلف اجرا کرد:

  • سرور شخصی
  • Docker
  • VPS
  • Kubernetes
  • Cloudflare
  • سرویس‌های Serverless
  • سایر پلتفرم‌های ابری
  • محیط داخلی سازمان

برنامه Next.js ساخته‌شده با این SDK نیز می‌تواند روی هر زیرساخت سازگار با نیازهای Runtime پروژه مستقر شود.

Provider در Vercel AI SDK چیست؟

Provider لایه‌ای است که AI SDK را به API مدل متصل می‌کند.

هر Provider وظیفه دارد درخواست استاندارد AI SDK را به ساختار قابل‌فهم برای سرویس مدل تبدیل کند و پاسخ آن را دوباره به ساختار استاندارد SDK برگرداند.

AI SDK علاوه بر Providerهای اختصاصی، بسته‌ای برای APIهای سازگار با OpenAI ارائه می‌کند. در این بسته می‌توان baseURL، کلید API و شناسه مدل را تنظیم کرد.

از آنجا که API درواره با ساختار OpenAI سازگار است، می‌توان آن را با Provider سازگار با OpenAI به AI SDK متصل کرد.

چرا Vercel AI SDK را به درواره متصل کنیم؟

با اتصال AI SDK به درواره می‌توانید:

  • از یک API برای دسترسی به مدل‌های مختلف استفاده کنید.
  • هزینه را به‌صورت ریالی مدیریت کنید.
  • مدل را بدون بازنویسی رابط چت تغییر دهید.
  • از ساختار OpenAI-compatible استفاده کنید.
  • Providerهای متعدد را پشت یک اتصال مدیریت کنید.
  • از مدل مناسب هر کاربرد استفاده کنید.
  • مصرف API را در داشبورد متمرکز بررسی کنید.

آدرس پایه API درواره:

https://api.darvareh.ir/v1

معماری پروژه

ارتباط مرورگر نباید مستقیماً با API درواره برقرار شود؛ زیرا در این صورت کلید API در اختیار کاربر قرار می‌گیرد.

معماری پیشنهادی:

مرورگر کاربر
      ↓
رابط React و useChat
      ↓
Route Handler در Next.js
      ↓
Vercel AI SDK
      ↓
API درواره
      ↓
مدل انتخاب‌شده

کلید API فقط در Route Handler سمت سرور استفاده می‌شود.

پیش‌نیازهای پروژه

برای اجرای پروژه به موارد زیر نیاز دارید:

  • Node.js
  • npm، pnpm یا yarn
  • آشنایی مقدماتی با React
  • یک حساب درواره
  • کلید API درواره
  • شناسه یک مدل متنی

ساخت پروژه Next.js

دستور زیر را اجرا کنید:

npx create-next-app@latest darvareh-ai-chat \
  --typescript \
  --tailwind \
  --eslint \
  --app

وارد پوشه پروژه شوید:

cd darvareh-ai-chat

بسته‌های لازم را نصب کنید:

npm install ai @ai-sdk/react @ai-sdk/openai-compatible zod

کاربرد هر بسته:

بستهکاربرد
aiتوابع Core مانند generateText و streamText
@ai-sdk/reactHookهای React مانند useChat
@ai-sdk/openai-compatibleاتصال به APIهای سازگار با OpenAI
zodتعریف و اعتبارسنجی Schema

تنظیم متغیرهای محیطی

فایل .env.local را در ریشه پروژه ایجاد کنید:

DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL=YOUR_MODEL_ID
DARVAREH_BASE_URL=https://api.darvareh.ir/v1

مقدار YOUR_MODEL_ID را با شناسه یکی از مدل‌های فعلی درواره جایگزین کنید.

فایل .env.local نباید در Repository عمومی Commit شود.

ساخت Provider درواره

فایل زیر را ایجاد کنید:

lib/ai.ts

محتوای فایل:

import { createOpenAICompatible } from "@ai-sdk/openai-compatible";

const apiKey = process.env.DARVAREH_API_KEY;
const modelId = process.env.DARVAREH_MODEL;

if (!apiKey) {
  throw new Error(
    "متغیر محیطی DARVAREH_API_KEY تنظیم نشده است.",
  );
}

if (!modelId) {
  throw new Error(
    "متغیر محیطی DARVAREH_MODEL تنظیم نشده است.",
  );
}

export const darvareh = createOpenAICompatible({
  name: "darvareh",
  apiKey,
  baseURL:
    process.env.DARVAREH_BASE_URL ??
    "https://api.darvareh.ir/v1",
});

export const DARVAREH_MODEL = modelId;

تابع createOpenAICompatible یک Provider قابل‌استفاده در AI SDK می‌سازد.

سه مقدار اصلی آن:

  • name: نام داخلی Provider
  • apiKey: کلید احراز هویت
  • baseURL: آدرس پایه API

Provider سازگار با OpenAI می‌تواند Chat Model را با متدی مانند chatModel ایجاد کند.

اولین درخواست با generateText

پیش از ساخت رابط چت، یک Endpoint ساده برای تولید متن ایجاد می‌کنیم.

فایل زیر را بسازید:

app/api/generate/route.ts

کد:

import { generateText } from "ai";

import {
  darvareh,
  DARVAREH_MODEL,
} from "@/lib/ai";

export const runtime = "nodejs";

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

  const prompt =
    typeof body.prompt === "string"
      ? body.prompt.trim()
      : "";

  if (!prompt) {
    return Response.json(
      {
        error: {
          code: "invalid_prompt",
          message: "متن ورودی الزامی است.",
        },
      },
      {
        status: 400,
      },
    );
  }

  if (prompt.length > 4000) {
    return Response.json(
      {
        error: {
          code: "prompt_too_long",
          message: "متن ورودی بیش از حد مجاز است.",
        },
      },
      {
        status: 400,
      },
    );
  }

  try {
    const result = await generateText({
      model: darvareh.chatModel(
        DARVAREH_MODEL,
      ),
      system:
        "شما یک دستیار فارسی دقیق و حرفه‌ای هستید.",
      prompt,
      maxOutputTokens: 600,
      temperature: 0.3,
    });

    return Response.json({
      text: result.text,
      finishReason: result.finishReason,
      usage: result.usage,
    });
  } catch (error) {
    console.error("AI generation failed", error);

    return Response.json(
      {
        error: {
          code: "generation_failed",
          message:
            "در حال حاضر امکان تولید پاسخ وجود ندارد.",
        },
      },
      {
        status: 502,
      },
    );
  }
}

در این مثال، generateText تا زمان آماده‌شدن کل پاسخ منتظر می‌ماند و سپس نتیجه را برمی‌گرداند.

آزمایش generateText

سرور را اجرا کنید:

npm run dev

سپس درخواست زیر را ارسال کنید:

curl --request POST \
  --url http://localhost:3000/api/generate \
  --header "Content-Type: application/json" \
  --data '{
    "prompt": "Vercel AI SDK را در سه جمله توضیح بده."
  }'

نمونه پاسخ:

{
  "text": "Vercel AI SDK مجموعه‌ای از ابزارهای TypeScript برای ساخت اپلیکیشن‌های هوش مصنوعی است...",
  "finishReason": "stop",
  "usage": {
    "inputTokens": 35,
    "outputTokens": 92,
    "totalTokens": 127
  }
}

ساختار دقیق usage ممکن است با توجه به نسخه SDK و اطلاعاتی که Provider برمی‌گرداند متفاوت باشد.

تفاوت generateText و streamText

در generateText کاربر باید منتظر بماند تا تمام پاسخ تولید شود.

درخواست
→ انتظار برای کل پاسخ
→ نمایش پاسخ کامل

در streamText بخش‌های پاسخ به‌تدریج نمایش داده می‌شوند:

درخواست
→ دریافت بخش اول
→ نمایش بخش اول
→ دریافت بخش بعدی
→ ادامه نمایش

برای چت‌بات، Streaming تجربه کاربری بهتری ایجاد می‌کند؛ زیرا کاربر خیلی زودتر شروع پاسخ را مشاهده می‌کند.

ساخت Route Handler چت Streaming

فایل زیر را ایجاد کنید:

app/api/chat/route.ts

کد:

import {
  convertToModelMessages,
  streamText,
  type UIMessage,
} from "ai";

import {
  darvareh,
  DARVAREH_MODEL,
} from "@/lib/ai";

export const runtime = "nodejs";

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

    const messages = body.messages as
      | UIMessage[]
      | undefined;

    if (!Array.isArray(messages)) {
      return Response.json(
        {
          error: {
            code: "invalid_messages",
            message:
              "فهرست پیام‌ها معتبر نیست.",
          },
        },
        {
          status: 400,
        },
      );
    }

    if (messages.length > 50) {
      return Response.json(
        {
          error: {
            code: "conversation_too_long",
            message:
              "تعداد پیام‌های مکالمه بیش از حد مجاز است.",
          },
        },
        {
          status: 400,
        },
      );
    }

    const result = streamText({
      model: darvareh.chatModel(
        DARVAREH_MODEL,
      ),
      system: [
        "شما دستیار فارسی یک اپلیکیشن نرم‌افزاری هستید.",
        "پاسخ‌ها را دقیق، روشن و کاربردی بنویسید.",
        "اگر اطلاعات کافی ندارید، از حدس قطعی خودداری کنید.",
      ].join("\n"),
      messages:
        await convertToModelMessages(messages),
      temperature: 0.3,
      maxOutputTokens: 1000,
    });

    return result.toUIMessageStreamResponse({
      onError: (error) => {
        console.error("Chat stream failed", error);

        return "در دریافت پاسخ مدل خطایی رخ داد.";
      },
    });
  } catch (error) {
    console.error("Chat request failed", error);

    return Response.json(
      {
        error: {
          code: "chat_failed",
          message:
            "امکان پردازش درخواست وجود ندارد.",
        },
      },
      {
        status: 500,
      },
    );
  }
}

در این Route:

  1. پیام‌های رابط کاربری دریافت می‌شوند.
  2. پیام‌ها با convertToModelMessages به قالب مناسب مدل تبدیل می‌شوند.
  3. درخواست با streamText به مدل ارسال می‌شود.
  4. پاسخ به شکل UI Message Stream به مرورگر برمی‌گردد.

Route Handlerهای Next.js داخل دایرکتوری app تعریف می‌شوند و از Web Request و Response API استفاده می‌کنند.

ساخت رابط چت با useChat

فایل زیر را بسازید:

components/Chat.tsx

کد:

"use client";

import { useState } from "react";
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";

export default function Chat() {
  const [input, setInput] = useState("");

  const {
    messages,
    sendMessage,
    status,
    stop,
    error,
  } = useChat({
    transport: new DefaultChatTransport({
      api: "/api/chat",
    }),
  });

  const isLoading =
    status === "submitted" ||
    status === "streaming";

  async function handleSubmit(
    event: React.FormEvent<HTMLFormElement>,
  ) {
    event.preventDefault();

    const text = input.trim();

    if (!text || isLoading) {
      return;
    }

    setInput("");

    await sendMessage({
      text,
    });
  }

  return (
    <main
      dir="rtl"
      className="mx-auto flex min-h-screen max-w-3xl flex-col p-4"
    >
      <header className="border-b py-4">
        <h1 className="text-2xl font-bold">
          دستیار هوش مصنوعی
        </h1>

        <p className="mt-2 text-sm text-slate-500">
          ساخته‌شده با Next.js، Vercel AI SDK
          و API درواره
        </p>
      </header>

      <section className="flex-1 space-y-4 py-6">
        {messages.length === 0 && (
          <div className="rounded-2xl bg-slate-50 p-6 text-center">
            <p className="font-medium">
              چگونه می‌توانم کمک کنم؟
            </p>

            <p className="mt-2 text-sm text-slate-500">
              سؤال خود را به زبان فارسی بنویسید.
            </p>
          </div>
        )}

        {messages.map((message) => (
          <article
            key={message.id}
            className={
              message.role === "user"
                ? "mr-auto max-w-[85%] rounded-2xl bg-purple-600 p-4 text-white"
                : "ml-auto max-w-[85%] rounded-2xl bg-slate-100 p-4 text-slate-900"
            }
          >
            <div className="mb-2 text-xs opacity-70">
              {message.role === "user"
                ? "شما"
                : "دستیار"}
            </div>

            <div className="whitespace-pre-wrap leading-8">
              {message.parts.map((part, index) => {
                if (part.type === "text") {
                  return (
                    <span key={index}>
                      {part.text}
                    </span>
                  );
                }

                return null;
              })}
            </div>
          </article>
        ))}

        {status === "submitted" && (
          <div className="text-sm text-slate-500">
            در حال ارسال درخواست...
          </div>
        )}

        {error && (
          <div className="rounded-xl bg-red-50 p-4 text-sm text-red-700">
            دریافت پاسخ با خطا مواجه شد.
          </div>
        )}
      </section>

      <form
        onSubmit={handleSubmit}
        className="sticky bottom-0 border-t bg-white py-4"
      >
        <div className="flex gap-2">
          <textarea
            value={input}
            onChange={(event) =>
              setInput(event.target.value)
            }
            rows={3}
            maxLength={4000}
            placeholder="پیام خود را بنویسید..."
            className="flex-1 resize-none rounded-xl border p-3 outline-none focus:border-purple-500"
          />

          {status === "streaming" ? (
            <button
              type="button"
              onClick={stop}
              className="rounded-xl bg-red-600 px-5 text-white"
            >
              توقف
            </button>
          ) : (
            <button
              type="submit"
              disabled={!input.trim() || isLoading}
              className="rounded-xl bg-purple-600 px-5 text-white disabled:opacity-50"
            >
              ارسال
            </button>
          )}
        </div>
      </form>
    </main>
  );
}

نمایش رابط چت در صفحه اصلی

فایل زیر را ویرایش کنید:

app/page.tsx

محتوا:

import Chat from "@/components/Chat";

export default function HomePage() {
  return <Chat />;
}

اکنون پروژه را اجرا کنید:

npm run dev

وارد آدرس زیر شوید:

http://localhost:3000

پیام کاربر از رابط React به /api/chat ارسال می‌شود. Route Handler درخواست را از طریق درواره به مدل می‌فرستد و پاسخ مدل به‌صورت تدریجی نمایش داده می‌شود.

DefaultChatTransport چیست؟

DefaultChatTransport مسئول ارتباط میان useChat و Endpoint سمت سرور است.

اگر آدرس مشخصی تعیین نشود، useChat به‌صورت پیش‌فرض از Endpoint زیر استفاده می‌کند:

/api/chat

مستندات AI SDK نیز DefaultChatTransport را Transport پیش‌فرض ارتباط HTTP برای useChat معرفی می‌کند.

می‌توان آدرس را تغییر داد:

const { messages, sendMessage } =
  useChat({
    transport: new DefaultChatTransport({
      api: "/api/assistant",
    }),
  });

پیام‌های UIMessage چه ساختاری دارند؟

در نسخه‌های جدید AI SDK، محتوای پیام رابط کاربری معمولاً داخل parts قرار می‌گیرد.

یک پیام متنی نمونه:

{
  "id": "msg_123",
  "role": "user",
  "parts": [
    {
      "type": "text",
      "text": "سلام، چه کمکی می‌توانی بکنی؟"
    }
  ]
}

استفاده از parts اجازه می‌دهد یک پیام فقط متن نباشد و اجزای مختلفی داشته باشد:

  • متن
  • فایل
  • تصویر
  • نتیجه Tool
  • Reasoning
  • داده سفارشی

به همین دلیل بهتر است هنگام نمایش پیام، parts را بررسی کنید و برای هر نوع Part رابط مناسب بسازید.

تولید خروجی ساختاریافته با Zod

بعضی قابلیت‌ها به متن آزاد نیاز ندارند. برای مثال، یک سیستم پشتیبانی ممکن است بخواهد این اطلاعات را از پیام مشتری استخراج کند:

  • موضوع پیام
  • اولویت
  • خلاصه
  • واحد مسئول
  • نیاز به بررسی انسانی

در AI SDK می‌توان Schema خروجی را با Zod تعریف کرد.

فایل زیر را ایجاد کنید:

app/api/analyze/route.ts

کد:

import {
  generateText,
  Output,
} from "ai";
import { z } from "zod";

import {
  darvareh,
  DARVAREH_MODEL,
} from "@/lib/ai";

const analysisSchema = z.object({
  category: z.enum([
    "sales",
    "technical",
    "billing",
    "feedback",
    "other",
  ]),
  priority: z.enum([
    "low",
    "medium",
    "high",
  ]),
  summary: z.string(),
  requiresHuman: z.boolean(),
});

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

  const message =
    typeof body.message === "string"
      ? body.message.trim()
      : "";

  if (!message) {
    return Response.json(
      {
        error: {
          code: "invalid_message",
          message: "متن پیام الزامی است.",
        },
      },
      {
        status: 400,
      },
    );
  }

  try {
    const result = await generateText({
      model: darvareh.chatModel(
        DARVAREH_MODEL,
      ),
      output: Output.object({
        schema: analysisSchema,
      }),
      prompt: [
        "پیام مشتری را تحلیل کن.",
        "خروجی باید دقیقاً مطابق Schema باشد.",
        "",
        `پیام مشتری: ${message}`,
      ].join("\n"),
    });

    return Response.json(result.output);
  } catch (error) {
    console.error(
      "Structured generation failed",
      error,
    );

    return Response.json(
      {
        error: {
          code: "analysis_failed",
          message:
            "تحلیل پیام انجام نشد.",
        },
      },
      {
        status: 502,
      },
    );
  }
}

در نسخه فعلی مستندات AI SDK، می‌توان generateText را همراه Output.object() برای تولید داده ساختاریافته و اعتبارسنجی‌شده استفاده کرد.

نمونه درخواست:

curl --request POST \
  --url http://localhost:3000/api/analyze \
  --header "Content-Type: application/json" \
  --data '{
    "message": "سلام، مبلغ اشتراک از حساب ما کم شده اما اعتبار اضافه نشده است."
  }'

نمونه پاسخ:

{
  "category": "billing",
  "priority": "high",
  "summary": "مبلغ پرداخت شده اما اعتبار حساب افزایش پیدا نکرده است.",
  "requiresHuman": true
}

پشتیبانی عملی از Structured Output به قابلیت مدل و Provider انتخاب‌شده نیز بستگی دارد. مدل را پیش از استفاده در محیط واقعی آزمایش کنید.

Tool Calling در Vercel AI SDK

Tool Calling به مدل اجازه می‌دهد در صورت نیاز یک تابع تعریف‌شده در برنامه را فراخوانی کند.

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

  • بررسی وضعیت سفارش
  • مشاهده موجودی محصول
  • محاسبه هزینه ارسال
  • ثبت درخواست پیگیری
  • جست‌وجو در پایگاه دانش

نمونه ساده:

import {
  generateText,
  stepCountIs,
  tool,
} from "ai";
import { z } from "zod";

import {
  darvareh,
  DARVAREH_MODEL,
} from "@/lib/ai";

const result = await generateText({
  model: darvareh.chatModel(
    DARVAREH_MODEL,
  ),

  system:
    "شما دستیار پشتیبانی فروشگاه هستید.",

  prompt:
    "وضعیت سفارش شماره ۱۲۳۴ را بررسی کن.",

  tools: {
    getOrderStatus: tool({
      description:
        "دریافت وضعیت سفارش با شماره سفارش",

      inputSchema: z.object({
        orderId: z
          .string()
          .describe("شماره سفارش"),
      }),

      execute: async ({ orderId }) => {
        const order = {
          id: orderId,
          status: "shipped",
          trackingCode: "TRK-92314",
        };

        return order;
      },
    }),
  },

  stopWhen: stepCountIs(3),
});

console.log(result.text);

AI SDK از Tool Calling در کنار generateText، streamText و رابط useChat پشتیبانی می‌کند.

پشتیبانی نهایی Tool Calling به مدل انتخاب‌شده نیز وابسته است.

آیا باید تمام عملیات را به مدل واگذار کنیم؟

خیر.

مدل می‌تواند پیشنهاد دهد چه Toolی اجرا شود، اما قوانین قطعی باید در کد برنامه باقی بمانند.

برای مثال، مدل نباید به‌تنهایی بتواند:

  • وجهی را بازپرداخت کند.
  • سفارش را لغو کند.
  • اطلاعات کاربر دیگری را بخواند.
  • سطح دسترسی را تغییر دهد.
  • عملیات مالی انجام دهد.

سمت سرور باید پیش از اجرای Tool موارد زیر را بررسی کند:

  • هویت کاربر
  • سطح دسترسی
  • مالکیت منبع
  • اعتبار ورودی
  • محدودیت عملیات
  • نیاز به تأیید انسانی
  • امکان اجرای مجدد ایمن

مدیریت تاریخچه مکالمه

در نمونه اولیه، پیام‌ها داخل State مرورگر نگهداری می‌شوند. با Refresh صفحه، تاریخچه از بین می‌رود.

در محصول واقعی بهتر است موجودیت‌های زیر ذخیره شوند:

Conversation
Message
User
Model
Usage
ToolCall
Feedback

نمونه جدول پیام:

id
conversation_id
role
content
created_at
model_id
input_tokens
output_tokens
latency_ms

هنگام بارگذاری مکالمه، پیام‌های ذخیره‌شده را به useChat منتقل کنید.

تاریخچه نباید بدون محدودیت رشد کند. در مکالمات طولانی می‌توان:

  • پیام‌های قدیمی را خلاصه کرد.
  • فقط پیام‌های مرتبط را ارسال کرد.
  • سقف تعداد پیام تعریف کرد.
  • بودجه Token تعیین کرد.
  • اطلاعات پایدار را جداگانه ذخیره کرد.

مدیریت هزینه API

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

برای کنترل هزینه:

  • تعداد پیام‌های ارسالی را محدود کنید.
  • پیام‌های قدیمی را خلاصه کنید.
  • خروجی را با maxOutputTokens محدود کنید.
  • مدل متناسب با وظیفه انتخاب کنید.
  • مصرف هر کاربر و قابلیت را ثبت کنید.
  • برای درخواست‌های تکراری Cache در نظر بگیرید.
  • وظایف ساده را به مدل‌های اقتصادی‌تر بسپارید.

همیشه قوی‌ترین مدل، مناسب‌ترین مدل برای تمام درخواست‌ها نیست.

مدیریت خطا در رابط کاربری

خطاهای احتمالی عبارت‌اند از:

  • کلید API نامعتبر
  • موجودی ناکافی
  • Rate Limit
  • Timeout
  • قطع ارتباط
  • شناسه مدل نامعتبر
  • پشتیبانی‌نکردن مدل از قابلیت درخواستی
  • ورودی طولانی
  • پاسخ نامعتبر

در رابط کاربری نباید جزئیات داخلی Provider یا Stack Trace نمایش داده شود.

بهتر است خطاها را به ساختار قابل‌پردازش تبدیل کنید:

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "تعداد درخواست‌ها بیش از حد مجاز است.",
    "requestId": "req_7f91a"
  }
}

کد خطا برای منطق برنامه و پیام فارسی برای نمایش به کاربر استفاده می‌شود.

نکات مهم برای محیط Production

احراز هویت کاربران

پیش از پردازش درخواست مشخص کنید چه کاربری در حال استفاده از قابلیت هوش مصنوعی است.

محدودیت نرخ

برای هر کاربر، کلید API یا سازمان محدودیت تعداد درخواست تعریف کنید.

محدودیت ورودی

اندازه پیام، تعداد فایل، نوع فایل و طول تاریخچه باید محدود شود.

کنترل مصرف

مصرف Token و هزینه هر درخواست را با شناسه کاربر و قابلیت ثبت کنید.

Timeout

برای درخواست‌های طولانی Timeout مشخص داشته باشید و امکان لغو درخواست را فراهم کنید.

ثبت لاگ

حداقل این اطلاعات را ثبت کنید:

  • Request ID
  • User ID
  • Model ID
  • زمان پاسخ
  • وضعیت درخواست
  • تعداد Token
  • Tool Call
  • کد خطا

انتخاب مدل از سمت سرور

اجازه ندهید کاربر هر رشته دلخواهی را به‌عنوان Model ID ارسال کند. مدل‌ها را در سمت سرور به فهرست مجاز محدود کنید.

کنترل Toolها

Tool فقط باید عملیات مشخص و محدود انجام دهد. ورودی هر Tool را با Schema اعتبارسنجی کنید.

ارزیابی قبل از تغییر مدل

پیش از تغییر مدل یا پرامپت، مجموعه‌ای از ورودی‌های ثابت را روی نسخه جدید اجرا و کیفیت، سرعت و هزینه را مقایسه کنید.

ساختار پیشنهادی پروژه واقعی

app/
  api/
    chat/
      route.ts
    analyze/
      route.ts
  page.tsx

components/
  Chat.tsx
  Message.tsx
  Composer.tsx
  ErrorMessage.tsx
  ModelSelector.tsx

lib/
  ai.ts
  auth.ts
  rate-limit.ts
  usage.ts
  validation.ts

services/
  conversation-service.ts
  tool-service.ts
  billing-service.ts

schemas/
  chat.ts
  tools.ts
  output.ts

تفکیک Provider، احراز هویت، مصرف، Toolها و رابط کاربری باعث می‌شود پروژه با افزایش قابلیت‌ها قابل‌مدیریت باقی بماند.

اشتباهات رایج هنگام استفاده از Vercel AI SDK

قرار دادن API Key در کد Client

هر متغیری که در مرورگر قابل‌دسترسی باشد ممکن است توسط کاربر مشاهده شود. کلید درواره را فقط در سمت سرور نگه دارید.

ارسال نامحدود تاریخچه

ارسال تمام پیام‌های قدیمی باعث افزایش هزینه و زمان پاسخ می‌شود.

اعتماد مستقیم به Tool Call

مدل نباید بدون بررسی سمت سرور عملیات حساس انجام دهد.

فرض یکسان‌بودن تمام مدل‌ها

همه مدل‌ها از Tool Calling، تصویر، Structured Output یا پارامترهای یکسان پشتیبانی نمی‌کنند.

نمایش مستقیم خطای Provider

خطا را ثبت کنید، اما پیام کنترل‌شده و قابل‌فهم به کاربر نمایش دهید.

وابسته‌کردن کد به یک مدل ثابت

شناسه مدل را در متغیر محیطی یا تنظیمات برنامه قرار دهید.

نداشتن ارزیابی

قبل از انتشار، پاسخ مدل را روی نمونه‌های واقعی فارسی آزمایش کنید.

نادیده‌گرفتن لغو درخواست

در رابط Streaming بهتر است کاربر بتواند تولید پاسخ را متوقف کند.

Vercel AI SDK برای چه پروژه‌هایی مناسب است؟

این SDK برای پروژه‌های زیر انتخاب مناسبی است:

  • چت‌بات پشتیبانی
  • دستیار داخل نرم‌افزار
  • جست‌وجوی هوشمند
  • تولید محتوا
  • تحلیل و خلاصه‌سازی سند
  • رابط RAG
  • دستیار برنامه‌نویسی
  • ابزارهای دارای Structured Output
  • Agent دارای Tool Calling
  • اپلیکیشن‌های React و Next.js
  • محصولات چندمدلی

اگر فقط یک اسکریپت کوتاه Backend دارید، ممکن است SDK مستقیم OpenAI ساده‌تر باشد. اما برای رابط تعاملی و Streaming، AI SDK امکانات سطح بالاتری ارائه می‌دهد.

پرسش‌های متداول

Vercel AI SDK چیست؟

Vercel AI SDK یک Toolkit متن‌باز TypeScript برای ساخت اپلیکیشن‌ها، چت‌بات‌ها و Agentهای مبتنی بر مدل‌های هوش مصنوعی است.

آیا Vercel AI SDK رایگان است؟

خود SDK متن‌باز است؛ اما استفاده از مدل‌های متصل به آن ممکن است بر اساس مصرف هزینه داشته باشد.

آیا برای استفاده از AI SDK باید برنامه را روی Vercel منتشر کنیم؟

خیر. می‌توانید برنامه را روی سرور شخصی، Docker، VPS یا سایر زیرساخت‌ها اجرا کنید.

آیا AI SDK فقط با Next.js کار می‌کند؟

خیر. این SDK با React، Node.js و چند فریم‌ورک دیگر قابل‌استفاده است؛ بااین‌حال یکپارچگی آن با Next.js بسیار رایج است.

تفاوت generateText و streamText چیست؟

generateText پس از تکمیل پاسخ نتیجه را برمی‌گرداند، اما streamText بخش‌های پاسخ را به‌تدریج ارسال می‌کند.

useChat چیست؟

useChat یک Hook برای مدیریت پیام‌ها، ارسال درخواست، دریافت پاسخ Streaming، وضعیت پردازش، خطا و توقف تولید در رابط‌های React است.

آیا می‌توان Vercel AI SDK را به API درواره متصل کرد؟

بله. با بسته @ai-sdk/openai-compatible می‌توان Base URL را روی آدرس زیر تنظیم کرد:

https://api.darvareh.ir/v1

آیا می‌توان مدل را تغییر داد؟

بله. اگر مدل موردنظر از Endpoint و قابلیت استفاده‌شده پشتیبانی کند، معمولاً کافی است مقدار DARVAREH_MODEL را تغییر دهید.

آیا Structured Output پشتیبانی می‌شود؟

AI SDK امکان تعریف خروجی ساختاریافته با Schema را فراهم می‌کند. پشتیبانی عملی آن باید روی مدل و Provider انتخاب‌شده آزمایش شود.

آیا Tool Calling پشتیبانی می‌شود؟

بله. می‌توان Toolها را با Schema تعریف کرد و مدل‌های سازگار را برای انتخاب و فراخوانی آن‌ها به کار برد.

آیا کلید API را می‌توان در React قرار داد؟

خیر. کلید API باید در Route Handler یا Backend نگهداری شود و نباید در کد قابل‌اجرا در مرورگر قرار گیرد.

جمع‌بندی

Vercel AI SDK یک لایه کاربردی برای ساخت رابط‌ها و برنامه‌های هوش مصنوعی در اکوسیستم TypeScript است.

این SDK قابلیت‌هایی مانند موارد زیر را ساده‌تر می‌کند:

  • تولید متن
  • Streaming
  • ساخت رابط چت
  • مدیریت پیام‌ها
  • اتصال به Providerهای مختلف
  • خروجی ساختاریافته
  • Tool Calling
  • ساخت Agent
  • مدیریت وضعیت رابط کاربری

در این آموزش یک اپلیکیشن واقعی ساختیم که:

  1. با Next.js و React اجرا می‌شود.
  2. از useChat برای مدیریت رابط استفاده می‌کند.
  3. پاسخ را با streamText به‌صورت تدریجی نمایش می‌دهد.
  4. از Provider سازگار با OpenAI استفاده می‌کند.
  5. به آدرس پایه API درواره متصل می‌شود.
  6. کلید API را در سمت سرور نگه می‌دارد.
  7. امکان تولید خروجی ساختاریافته دارد.
  8. می‌تواند با Tool Calling توسعه پیدا کند.

برای شروع، یک قابلیت محدود مانند چت، خلاصه‌سازی یا طبقه‌بندی پیام را پیاده‌سازی کنید. پس از آزمایش کیفیت مدل، مدیریت هزینه، ثبت مصرف و کنترل خطا را به پروژه اضافه کنید.

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

مقالات مرتبط

منابع

این مقاله صرفاً با هدف آموزش و اطلاع‌رسانی تهیه شده است. پیش از استفاده عملی، مستندات رسمی سرویس‌ها و صفحه سلب مسئولیت را مطالعه کنید.

Read more