API سازگار با OpenAI چیست؟ راهنمای کامل OpenAI-Compatible API و اتصال به مدل‌های مختلف

API سازگار با OpenAI به توسعه‌دهندگان اجازه می‌دهد با SDK و ساختار درخواست واحد به مدل‌های مختلف متصل شوند. در این راهنما Base URL، Chat Completions، Streaming، Tool Calling و اتصال به درواره را بررسی می‌کنیم.

Share
Darvareh OpenAI API
Darvareh OpenAI API

API سازگار با OpenAI یا OpenAI-Compatible API به رابطی گفته می‌شود که ساختار Endpointها، روش احراز هویت، قالب درخواست‌ها و شکل پاسخ‌های آن با قرارداد رایج OpenAI API یکسان یا بسیار نزدیک است.

این سازگاری باعث می‌شود توسعه‌دهندگان بتوانند با همان SDKها، کتابخانه‌ها و کدهایی که برای OpenAI نوشته شده‌اند، به مدل‌ها و ارائه‌دهندگان دیگری متصل شوند. در بسیاری از موارد، برای تغییر سرویس تنها کافی است Base URL، مقدار API Key و شناسه مدل تغییر کنند.

برای مثال، یک برنامه می‌تواند به‌جای اتصال مستقیم به یک ارائه‌دهنده، درخواست خود را به API سازگار با OpenAI درواره ارسال کند:

https://api.darvareh.ir/v1

برنامه همچنان از همان ساختار messages، همان Endpoint مربوط به chat/completions و حتی SDK رسمی OpenAI استفاده می‌کند؛ اما مدل انتخاب‌شده می‌تواند متعلق به ارائه‌دهنده‌ای متفاوت باشد.

این معماری فرایند اتصال به مدل‌های مختلف را ساده‌تر می‌کند، حجم کد اختصاصی را کاهش می‌دهد و وابستگی نرم‌افزار به یک ارائه‌دهنده را کمتر می‌کند.

پاسخ کوتاه: API سازگار با OpenAI چیست؟

API سازگار با OpenAI یک رابط برنامه‌نویسی است که قرارداد عمومی OpenAI API را پیاده‌سازی می‌کند. این قرارداد معمولاً شامل موارد زیر است:

  • احراز هویت با Bearer Token
  • استفاده از Base URL قابل‌تغییر
  • Endpointهایی مانند /chat/completions
  • ارسال نام مدل در فیلد model
  • ارسال مکالمه در آرایۀ messages
  • دریافت پاسخ در آرایۀ choices
  • پشتیبانی از Streaming
  • گزارش مصرف در فیلد usage
  • پشتیبانی احتمالی از Tool Calling
  • پشتیبانی احتمالی از Structured Output
  • نمایش مدل‌ها از طریق Endpoint مربوط به /models

عبارت «سازگار با OpenAI» به این معنی نیست که سرویس فقط مدل‌های OpenAI را ارائه می‌کند. این عبارت به سازگاری فنی رابط API اشاره دارد، نه شرکت سازندۀ مدل.

چرا OpenAI API به یک قرارداد رایج تبدیل شد؟

با گسترش استفاده از مدل‌های زبانی، توسعه‌دهندگان به یک قالب مشخص برای ارسال پیام و دریافت پاسخ نیاز داشتند. ساختار Chat Completions به‌دلیل سادگی و استفادۀ گسترده، به‌تدریج در ابزارها، کتابخانه‌ها و پلتفرم‌های متعددی پشتیبانی شد.

تعداد زیادی از ابزارهای توسعه هوش مصنوعی امکان تنظیم این دو مقدار را فراهم کردند:

API Key
Base URL

در نتیجه، اگر یک سرویس رابطی شبیه OpenAI API ارائه کند، می‌توان آن را به بسیاری از ابزارهای موجود متصل کرد؛ بدون اینکه برای هر ارائه‌دهنده یک Integration کاملاً جدید نوشته شود.

امروزه مفهوم OpenAI-Compatible API در بخش‌های مختلف اکوسیستم هوش مصنوعی دیده می‌شود:

  • AI Gatewayها
  • سرویس‌های چندمدلی
  • سرورهای استنتاج مدل‌های متن‌باز
  • ابزارهای اجرای مدل محلی
  • Agentهای کدنویسی
  • رابط‌های دسکتاپ هوش مصنوعی
  • پلتفرم‌های RAG
  • ابزارهای اتوماسیون
  • کتابخانه‌های Agent
  • برنامه‌های چت سازمانی

API سازگار با OpenAI چگونه کار می‌کند؟

یک برنامۀ معمولی برای اتصال به OpenAI-Compatible API به سه مقدار اصلی نیاز دارد:

Base URL

Base URL آدرس پایۀ سرویسی است که درخواست‌ها باید به آن ارسال شوند.

Base URL درواره:

https://api.darvareh.ir/v1

هنگامی که SDK مسیر /chat/completions را به این آدرس اضافه کند، Endpoint نهایی به‌صورت زیر خواهد بود:

https://api.darvareh.ir/v1/chat/completions

API Key

API Key هویت برنامه یا حساب مصرف‌کننده را مشخص می‌کند. این کلید معمولاً در Header درخواست ارسال می‌شود:

Authorization: Bearer YOUR_API_KEY

API Key باید در محیط امن سرور نگهداری شود و نباید داخل کد Frontend، اپلیکیشن عمومی، Repository یا فایل قابل‌دسترسی کاربران قرار گیرد.

Model ID

فیلد model مشخص می‌کند درخواست باید به کدام مدل ارسال شود:

{
  "model": "MODEL_ID"
}

شناسه مدل باید دقیقاً مطابق مقداری باشد که سرویس ارائه می‌کند. نام عمومی یا تجاری یک مدل لزوماً با Model ID قابل‌استفاده در API یکسان نیست.

اجزای اصلی یک درخواست Chat Completions

نمونۀ پایه یک درخواست:

{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "system",
      "content": "شما یک دستیار فنی دقیق هستید."
    },
    {
      "role": "user",
      "content": "API سازگار با OpenAI را توضیح بده."
    }
  ]
}

مهم‌ترین بخش‌های این درخواست عبارت‌اند از:

  • model: شناسه مدل
  • messages: تاریخچۀ مکالمه و دستورها
  • role: نقش هر پیام
  • content: محتوای پیام
  • پارامترهای اختیاری تولید پاسخ

نقش‌های System، User، Assistant و Tool

نقش System

پیام System رفتار کلی مدل را مشخص می‌کند:

{
  "role": "system",
  "content": "همیشه پاسخ را به زبان فارسی و با لحن تخصصی بنویس."
}

این پیام معمولاً برای تعریف موارد زیر استفاده می‌شود:

  • شخصیت دستیار
  • زبان پاسخ
  • لحن
  • محدودیت‌ها
  • قالب خروجی
  • سیاست‌های ثابت
  • دامنۀ کاری

پشتیبانی و میزان اثرگذاری پیام System ممکن است بین مدل‌ها متفاوت باشد.

نقش User

پیام User درخواست یا ورودی کاربر را دربرمی‌گیرد:

{
  "role": "user",
  "content": "تفاوت REST و GraphQL را توضیح بده."
}

نقش Assistant

برای ارسال پاسخ‌های قبلی مدل و حفظ زمینۀ مکالمه استفاده می‌شود:

{
  "role": "assistant",
  "content": "REST یک سبک معماری برای طراحی API است."
}

نقش Tool

پس از درخواست مدل برای اجرای یک ابزار، نتیجۀ ابزار معمولاً در پیامی با نقش Tool به مدل بازگردانده می‌شود:

{
  "role": "tool",
  "tool_call_id": "call_123",
  "content": "{\"temperature\": 24}"
}

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

اولین درخواست به API درواره با cURL

curl "https://api.darvareh.ir/v1/chat/completions" \
  -H "Authorization: Bearer $DARVAREH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ID",
    "messages": [
      {
        "role": "system",
        "content": "شما یک دستیار دقیق و حرفه‌ای هستید."
      },
      {
        "role": "user",
        "content": "سه مزیت استفاده از API سازگار با OpenAI را بنویس."
      }
    ]
  }'

در این نمونه باید:

  • متغیر DARVAREH_API_KEY با کلید معتبر مقداردهی شود.
  • مقدار MODEL_ID با شناسه مدل موردنظر جایگزین شود.
  • Content-Type روی application/json قرار گیرد.
  • کلید از طریق Header استاندارد Authorization ارسال شود.

ساختار پاسخ Chat Completions

پاسخ معمولاً ساختاری مشابه نمونۀ زیر دارد:

{
  "id": "chatcmpl-example",
  "object": "chat.completion",
  "created": 1783950000,
  "model": "MODEL_ID",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "پاسخ تولیدشده توسط مدل"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 48,
    "completion_tokens": 92,
    "total_tokens": 140
  }
}

بخش‌های مهم پاسخ عبارت‌اند از:

شناسه درخواست

فیلد id برای شناسایی Completion استفاده می‌شود. برای عیب‌یابی بهتر است Request ID ارائه‌شده در Header یا پاسخ نیز ثبت شود.

آرایۀ Choices

پاسخ مدل معمولاً در choices قرار دارد:

response.choices[0].message.content

نباید همیشه فرض کرد choices[0] وجود دارد. در کد Production بهتر است ساختار پاسخ اعتبارسنجی شود.

Finish Reason

فیلد finish_reason علت پایان تولید را نشان می‌دهد. مقادیر رایج می‌توانند شامل موارد زیر باشند:

  • stop: مدل به‌صورت عادی تولید را متوقف کرده است.
  • length: محدودیت خروجی یا Context مانع ادامۀ تولید شده است.
  • tool_calls: مدل درخواست اجرای یک یا چند ابزار را داده است.
  • content_filter: تولید پاسخ به‌دلیل سیاست محتوایی متوقف شده است.

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

Usage

فیلد usage اطلاعات مصرف توکن را ارائه می‌کند:

{
  "prompt_tokens": 48,
  "completion_tokens": 92,
  "total_tokens": 140
}

نام یا جزئیات فیلدهای Usage ممکن است در مدل‌های استدلالی، Cache یا ارائه‌دهندگان مختلف متفاوت باشد. همچنین در برخی Streamها، Usage فقط در رویداد پایانی یا با تنظیمات اختصاصی برگردانده می‌شود.

آموزش اتصال با Python

ابتدا کتابخانۀ رسمی OpenAI را نصب کنید:

pip install openai

کلید API را در متغیر محیطی قرار دهید:

export DARVAREH_API_KEY="YOUR_API_KEY"

در Windows PowerShell:

$env:DARVAREH_API_KEY="YOUR_API_KEY"

سپس Client را با Base URL درواره ایجاد کنید:

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DARVAREH_API_KEY"],
    base_url="https://api.darvareh.ir/v1",
)

response = client.chat.completions.create(
    model="MODEL_ID",
    messages=[
        {
            "role": "system",
            "content": "شما یک دستیار برنامه‌نویسی دقیق هستید."
        },
        {
            "role": "user",
            "content": "یک تابع Python برای اعتبارسنجی ایمیل بنویس."
        }
    ],
)

content = response.choices[0].message.content
print(content)

کتابخانۀ رسمی Python امکان تعریف base_url سفارشی را در Client فراهم می‌کند. مخزن رسمی OpenAI Python

پیاده‌سازی Async در Python

در برنامه‌های وب با تعداد درخواست هم‌زمان، بهتر است از Client غیرهم‌زمان استفاده شود:

import os
import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI(
    api_key=os.environ["DARVAREH_API_KEY"],
    base_url="https://api.darvareh.ir/v1",
)

async def main():
    response = await client.chat.completions.create(
        model="MODEL_ID",
        messages=[
            {
                "role": "user",
                "content": "معماری Event-Driven را توضیح بده."
            }
        ],
    )

    print(response.choices[0].message.content)

asyncio.run(main())

آموزش اتصال با Node.js و TypeScript

ابتدا SDK را نصب کنید:

npm install openai

API Key را در متغیر محیطی قرار دهید:

export DARVAREH_API_KEY="YOUR_API_KEY"

نمونۀ JavaScript:

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DARVAREH_API_KEY,
  baseURL: "https://api.darvareh.ir/v1",
});

const response = await client.chat.completions.create({
  model: "MODEL_ID",
  messages: [
    {
      role: "system",
      content: "شما یک دستیار فنی دقیق هستید.",
    },
    {
      role: "user",
      content: "مزایای معماری Microservices را توضیح بده.",
    },
  ],
});

console.log(response.choices[0]?.message?.content);

SDK رسمی JavaScript و TypeScript نیز پارامتر baseURL را در تنظیمات Client پشتیبانی می‌کند. توجه کنید که در SDK جاوااسکریپت نام این پارامتر baseURL و در SDK پایتون base_url است. مخزن رسمی OpenAI Node.js

نمونۀ TypeScript

import OpenAI from "openai";

const apiKey = process.env.DARVAREH_API_KEY;

if (!apiKey) {
  throw new Error("DARVAREH_API_KEY is not configured");
}

const client = new OpenAI({
  apiKey,
  baseURL: "https://api.darvareh.ir/v1",
});

async function generateAnswer(prompt: string): Promise<string> {
  const response = await client.chat.completions.create({
    model: "MODEL_ID",
    messages: [
      {
        role: "user",
        content: prompt,
      },
    ],
  });

  const content = response.choices[0]?.message?.content;

  if (!content) {
    throw new Error("The model returned an empty response");
  }

  return content;
}

const answer = await generateAnswer(
  "API سازگار با OpenAI چه کاربردی دارد؟",
);

console.log(answer);

آموزش اتصال با JavaScript Fetch

اگر نمی‌خواهید از SDK استفاده کنید، می‌توانید مستقیماً درخواست HTTP بفرستید:

const response = await fetch(
  "https://api.darvareh.ir/v1/chat/completions",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.DARVAREH_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "MODEL_ID",
      messages: [
        {
          role: "user",
          content: "یک توضیح کوتاه درباره API Gateway بنویس.",
        },
      ],
    }),
  },
);

if (!response.ok) {
  const errorBody = await response.text();

  throw new Error(
    `AI request failed with ${response.status}: ${errorBody}`,
  );
}

const data = await response.json();
console.log(data.choices?.[0]?.message?.content);

آموزش اتصال با PHP

<?php

$apiKey = getenv('DARVAREH_API_KEY');

if (!$apiKey) {
    throw new RuntimeException('DARVAREH_API_KEY is not configured');
}

$payload = [
    'model' => 'MODEL_ID',
    'messages' => [
        [
            'role' => 'system',
            'content' => 'شما یک دستیار فنی دقیق هستید.',
        ],
        [
            'role' => 'user',
            'content' => 'مفهوم OpenAI-Compatible API را توضیح بده.',
        ],
    ],
];

$ch = curl_init('https://api.darvareh.ir/v1/chat/completions');

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 60,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(
        $payload,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    ),
]);

$responseBody = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($responseBody === false) {
    $error = curl_error($ch);
    curl_close($ch);

    throw new RuntimeException('Connection error: ' . $error);
}

curl_close($ch);

if ($statusCode < 200 || $statusCode >= 300) {
    throw new RuntimeException(
        "AI API returned HTTP {$statusCode}: {$responseBody}"
    );
}

$data = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
$content = $data['choices'][0]['message']['content'] ?? null;

if (!is_string($content)) {
    throw new RuntimeException('Invalid response structure');
}

echo $content;

آموزش اتصال در Laravel

متغیرهای محیطی:

DARVAREH_API_KEY=YOUR_API_KEY
DARVAREH_BASE_URL=https://api.darvareh.ir/v1
DARVAREH_MODEL=MODEL_ID

تنظیمات config/services.php:

'darvareh' => [
    'api_key' => env('DARVAREH_API_KEY'),
    'base_url' => env(
        'DARVAREH_BASE_URL',
        'https://api.darvareh.ir/v1'
    ),
    'model' => env('DARVAREH_MODEL'),
],

سرویس Laravel:

<?php

namespace App\Services;

use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
use RuntimeException;

class DarvarehService
{
    private function client(): PendingRequest
    {
        return Http::baseUrl(config('services.darvareh.base_url'))
            ->withToken(config('services.darvareh.api_key'))
            ->acceptJson()
            ->asJson()
            ->connectTimeout(5)
            ->timeout(60)
            ->retry(
                times: 2,
                sleepMilliseconds: 500,
                throw: false
            );
    }

    public function chat(array $messages): string
    {
        $response = $this->client()->post('/chat/completions', [
            'model' => config('services.darvareh.model'),
            'messages' => $messages,
        ]);

        if ($response->failed()) {
            throw new RuntimeException(
                'Darvareh API error: ' . $response->body()
            );
        }

        $content = $response->json(
            'choices.0.message.content'
        );

        if (!is_string($content)) {
            throw new RuntimeException(
                'Invalid response received from AI model'
            );
        }

        return $content;
    }
}

استفاده در Controller:

<?php

namespace App\Http\Controllers;

use App\Services\DarvarehService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class AssistantController extends Controller
{
    public function __invoke(
        Request $request,
        DarvarehService $darvareh
    ): JsonResponse {
        $validated = $request->validate([
            'prompt' => ['required', 'string', 'max:5000'],
        ]);

        $answer = $darvareh->chat([
            [
                'role' => 'system',
                'content' => 'پاسخ را به فارسی بنویس.',
            ],
            [
                'role' => 'user',
                'content' => $validated['prompt'],
            ],
        ]);

        return response()->json([
            'answer' => $answer,
        ]);
    }
}

آموزش اتصال در Next.js

API Key نباید در Component سمت مرورگر استفاده شود. یک Route Handler سروری ایجاد کنید:

import OpenAI from "openai";
import { NextRequest, NextResponse } from "next/server";

const client = new OpenAI({
  apiKey: process.env.DARVAREH_API_KEY,
  baseURL: "https://api.darvareh.ir/v1",
});

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

  if (typeof prompt !== "string" || prompt.trim().length === 0) {
    return NextResponse.json(
      { error: "Prompt is required" },
      { status: 400 },
    );
  }

  if (prompt.length > 5000) {
    return NextResponse.json(
      { error: "Prompt is too long" },
      { status: 413 },
    );
  }

  try {
    const response = await client.chat.completions.create({
      model: "MODEL_ID",
      messages: [
        {
          role: "system",
          content: "پاسخ را دقیق و به زبان فارسی بنویس.",
        },
        {
          role: "user",
          content: prompt,
        },
      ],
    });

    return NextResponse.json({
      answer: response.choices[0]?.message?.content ?? "",
    });
  } catch (error) {
    console.error("AI request failed", error);

    return NextResponse.json(
      { error: "AI service is temporarily unavailable" },
      { status: 502 },
    );
  }
}

متغیر DARVAREH_API_KEY نباید با پیشوند NEXT_PUBLIC_ تعریف شود؛ زیرا متغیرهای دارای این پیشوند ممکن است در کد مرورگر قرار گیرند.

Streaming چیست؟

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

این قابلیت برای کاربردهای زیر مفید است:

  • چت‌بات
  • دستیار کدنویسی
  • تولید متن طولانی
  • پاسخ‌های تعاملی
  • Agentهایی که وضعیت اجرا را نمایش می‌دهند

Chat Completions معمولاً Streaming را با تنظیم زیر فعال می‌کند:

{
  "stream": true
}

Streaming با cURL

curl -N "https://api.darvareh.ir/v1/chat/completions" \
  -H "Authorization: Bearer $DARVAREH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ID",
    "stream": true,
    "messages": [
      {
        "role": "user",
        "content": "یک مقاله کوتاه درباره معماری نرم‌افزار بنویس."
      }
    ]
  }'

گزینۀ -N در cURL از Buffer شدن خروجی جلوگیری می‌کند.

در Streaming مبتنی بر Server-Sent Events، داده‌ها معمولاً به شکل رویدادهای متوالی دریافت می‌شوند:

data: {"choices":[{"delta":{"content":"سلام"}}]}

data: {"choices":[{"delta":{"content":"، خوش"}}]}

data: {"choices":[{"delta":{"content":" آمدید"}}]}

data: [DONE]

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

Streaming با Python

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DARVAREH_API_KEY"],
    base_url="https://api.darvareh.ir/v1",
)

stream = client.chat.completions.create(
    model="MODEL_ID",
    stream=True,
    messages=[
        {
            "role": "user",
            "content": "ده نکته برای طراحی API امن بنویس."
        }
    ],
)

for chunk in stream:
    if not chunk.choices:
        continue

    content = chunk.choices[0].delta.content

    if content:
        print(content, end="", flush=True)

Streaming با Node.js

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DARVAREH_API_KEY,
  baseURL: "https://api.darvareh.ir/v1",
});

const stream = await client.chat.completions.create({
  model: "MODEL_ID",
  stream: true,
  messages: [
    {
      role: "user",
      content: "اصول طراحی یک چت‌بات حرفه‌ای را بنویس.",
    },
  ],
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;

  if (content) {
    process.stdout.write(content);
  }
}

مدیریت قطع Streaming

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

نمونۀ ساده با AbortController:

const controller = new AbortController();

const stream = await client.chat.completions.create(
  {
    model: "MODEL_ID",
    stream: true,
    messages: [
      {
        role: "user",
        content: "یک پاسخ طولانی تولید کن.",
      },
    ],
  },
  {
    signal: controller.signal,
  },
);

setTimeout(() => {
  controller.abort();
}, 30_000);

در برنامه واقعی، Abort باید به قطع ارتباط کلاینت متصل شود، نه یک زمان ثابت.

Tool Calling چیست؟

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

فرض کنید برنامه یک تابع برای دریافت وضعیت آب‌وهوا دارد:

{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "دریافت وضعیت آب‌وهوا برای یک شهر",
    "parameters": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string",
          "description": "نام شهر"
        }
      },
      "required": ["city"],
      "additionalProperties": false
    }
  }
}

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

{
  "model": "MODEL_ID",
  "messages": [
    {
      "role": "user",
      "content": "هوای شیراز چطور است؟"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "دریافت وضعیت آب‌وهوا برای یک شهر",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string"
            }
          },
          "required": ["city"],
          "additionalProperties": false
        }
      }
    }
  ]
}

مدل ممکن است پاسخی شبیه این تولید کند:

{
  "tool_calls": [
    {
      "id": "call_weather_1",
      "type": "function",
      "function": {
        "name": "get_weather",
        "arguments": "{\"city\":\"شیراز\"}"
      }
    }
  ]
}

مدل تابع را مستقیماً اجرا نمی‌کند. برنامه باید:

  1. نام ابزار را بررسی کند.
  2. آرگومان‌ها را Parse کند.
  3. داده‌ها را با Schema اعتبارسنجی کند.
  4. مجوز کاربر را بررسی کند.
  5. تابع واقعی را اجرا کند.
  6. نتیجۀ ابزار را به مدل بازگرداند.
  7. پاسخ نهایی را دریافت کند.

مستندات رسمی OpenAI بر این چرخه چندمرحله‌ای برای Function Calling تأکید دارند. راهنمای رسمی Function Calling

نمونه Tool Calling با Node.js

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DARVAREH_API_KEY,
  baseURL: "https://api.darvareh.ir/v1",
});

const tools = [
  {
    type: "function",
    function: {
      name: "get_order_status",
      description: "دریافت وضعیت یک سفارش",
      parameters: {
        type: "object",
        properties: {
          order_id: {
            type: "string",
          },
        },
        required: ["order_id"],
        additionalProperties: false,
      },
    },
  },
];

const messages = [
  {
    role: "user",
    content: "وضعیت سفارش ORD-1004 را بررسی کن.",
  },
];

const response = await client.chat.completions.create({
  model: "MODEL_ID",
  messages,
  tools,
});

const message = response.choices[0]?.message;
const toolCall = message?.tool_calls?.[0];

if (toolCall?.function?.name === "get_order_status") {
  const args = JSON.parse(toolCall.function.arguments);

  if (typeof args.order_id !== "string") {
    throw new Error("Invalid order_id");
  }

  // مجوز کاربر باید پیش از دسترسی به سفارش بررسی شود.
  const result = {
    order_id: args.order_id,
    status: "processing",
  };

  messages.push(message);
  messages.push({
    role: "tool",
    tool_call_id: toolCall.id,
    content: JSON.stringify(result),
  });

  const finalResponse = await client.chat.completions.create({
    model: "MODEL_ID",
    messages,
    tools,
  });

  console.log(
    finalResponse.choices[0]?.message?.content,
  );
}

نوع داده‌های TypeScript ممکن است با نسخۀ SDK تغییر کند. هنگام پیاده‌سازی، تعریف Typeهای همان نسخۀ نصب‌شده را بررسی کنید.

اصول امنیتی Tool Calling

خروجی مدل نباید به‌عنوان فرمان قابل‌اعتماد در نظر گرفته شود. حتی اگر آرگومان‌ها JSON معتبر باشند، باید کنترل‌های زیر انجام شوند:

  • استفاده از Allowlist ابزارها
  • اعتبارسنجی Schema
  • کنترل Authentication
  • کنترل Authorization
  • محدودیت زمان اجرا
  • محدودیت تعداد فراخوانی
  • جلوگیری از Path Traversal
  • جلوگیری از SQL Injection
  • جلوگیری از Command Injection
  • عدم ارسال Secret به مدل
  • ثبت عملیات حساس
  • تأیید انسانی برای تراکنش‌های مهم

مدل نباید بتواند فقط با تولید نام یک تابع، عملیات مالی، حذف داده یا تغییر دسترسی انجام دهد.

Structured Output چیست؟

Structured Output برای دریافت پاسخ در قالب یک ساختار قابل‌اعتبارسنجی استفاده می‌شود.

برای مثال، یک سیستم دسته‌بندی تیکت ممکن است چنین خروجی‌ای نیاز داشته باشد:

{
  "category": "billing",
  "priority": "high",
  "requires_human": true,
  "summary": "اعتراض به مبلغ کسرشده از کیف پول"
}

درخواست ساده برای «فقط JSON تولید کن» همیشه قابل‌اعتماد نیست. مدل ممکن است:

  • قبل یا بعد از JSON توضیح بنویسد.
  • فیلدی را حذف کند.
  • مقدار خارج از Enum تولید کند.
  • نوع داده اشتباه برگرداند.
  • JSON ناقص تولید کند.
  • نام فیلدها را تغییر دهد.

Structured Output و JSON Schema برای کاهش این مشکلات طراحی شده‌اند. جزئیات پشتیبانی آن ممکن است بین مدل‌ها و APIهای سازگار متفاوت باشد. راهنمای رسمی Structured Outputs

تفاوت JSON Mode و Structured Output

Prompt عادی برای JSON

در Prompt از مدل خواسته می‌شود JSON تولید کند. هیچ تضمین فنی مشخصی وجود ندارد.

JSON Mode

مدل ملزم می‌شود خروجی JSON معتبر تولید کند، اما لزوماً از Schema موردنظر شما پیروی نمی‌کند.

Structured Output

خروجی باید با JSON Schema تعیین‌شده سازگار باشد؛ البته فقط در مدل‌ها و سرویس‌هایی که این قابلیت را واقعاً پشتیبانی می‌کنند.

حتی در Structured Output نیز برنامۀ مصرف‌کننده باید پاسخ را اعتبارسنجی کند.

نمونه اعتبارسنجی خروجی با Zod

import { z } from "zod";

const TicketSchema = z.object({
  category: z.enum([
    "billing",
    "technical",
    "account",
    "other",
  ]),
  priority: z.enum(["low", "medium", "high"]),
  requires_human: z.boolean(),
  summary: z.string().min(1).max(500),
});

const rawContent = response.choices[0]?.message?.content;

if (!rawContent) {
  throw new Error("Empty model response");
}

const parsedJson = JSON.parse(rawContent);
const ticket = TicketSchema.parse(parsedJson);

console.log(ticket);

ورودی تصویر در API سازگار با OpenAI

در مدل‌های چندوجهی، محتوای پیام User می‌تواند به‌جای یک رشته، آرایه‌ای از بخش‌های متنی و تصویری باشد:

{
  "model": "VISION_MODEL_ID",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "محتوای این تصویر را توضیح بده."
        },
        {
          "type": "image_url",
          "image_url": {
            "url": "https://example.com/image.jpg"
          }
        }
      ]
    }
  ]
}

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

  • URL عمومی
  • Data URL حاوی Base64

ارسال تصویر Base64

{
  "type": "image_url",
  "image_url": {
    "url": "data:image/jpeg;base64,BASE64_DATA"
  }
}

استفاده از URL برای تصویر عمومی معمولاً حجم درخواست را کاهش می‌دهد. Base64 برای فایل محلی یا خصوصی کاربرد دارد، اما اندازۀ درخواست را بیشتر می‌کند.

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

نمونه ورودی تصویر با Python

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DARVAREH_API_KEY"],
    base_url="https://api.darvareh.ir/v1",
)

response = client.chat.completions.create(
    model="VISION_MODEL_ID",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "این تصویر را به فارسی تحلیل کن."
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://example.com/image.jpg"
                    }
                }
            ],
        }
    ],
)

print(response.choices[0].message.content)

پارامترهای رایج تولید پاسخ

Temperature

Temperature میزان تصادفی بودن انتخاب توکن‌ها را کنترل می‌کند. معمولاً:

  • مقدار پایین‌تر برای پاسخ‌های باثبات و دقیق
  • مقدار بالاتر برای پاسخ‌های متنوع و خلاقانه

اما رفتار دقیق Temperature میان مدل‌ها یکسان نیست. برخی مدل‌های جدید یا استدلالی ممکن است این پارامتر را نپذیرند یا محدود کنند.

Top P

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

معمولاً بهتر است بدون دلیل مشخص، temperature و top_p را هم‌زمان به‌شدت تغییر ندهید.

Max Tokens

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

max_tokens
max_completion_tokens
max_output_tokens

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

Stop

با Stop Sequence می‌توان تولید را هنگام رسیدن به رشته‌ای خاص متوقف کرد:

{
  "stop": ["END_OF_ANSWER"]
}

همۀ مدل‌ها این قابلیت را به یک شکل پشتیبانی نمی‌کنند.

Seed

برخی مدل‌ها پارامتر seed را برای افزایش تکرارپذیری نسبی می‌پذیرند، اما این پارامتر معمولاً تضمین‌کنندۀ خروجی کاملاً یکسان نیست.

Chat Completions و Responses API چه تفاوتی دارند؟

Chat Completions بر ساختار مکالمه‌ای messages متمرکز است و به‌دلیل پشتیبانی گسترده در اکوسیستم، همچنان یکی از رایج‌ترین قراردادها برای APIهای سازگار محسوب می‌شود.

Responses API رابط جدیدتر OpenAI برای ترکیب ورودی‌ها، خروجی‌ها و ابزارهای مختلف است. OpenAI برای پروژه‌های جدید خود Responses API را توصیه می‌کند، اما Chat Completions نیز همچنان در مستندات و API رسمی حضور دارد. راهنمای رسمی مهاجرت به Responses API

تفاوت کلی:

موضوعChat CompletionsResponses API
ساختار اصلی ورودیmessagesinput و Itemها
خروجی متنیدر choicesدر Output Itemها
پشتیبانی اکوسیستم ثالثبسیار گستردهوابسته به سرویس
سازگاری Gatewayهارایجباید بررسی شود
ابزارهای داخلی OpenAIمحدودتریکپارچه‌تر
کاربرد متداولچت و API چندمدلیمحصولات جدید مبتنی بر OpenAI

یک OpenAI-Compatible API ممکن است فقط Chat Completions را پیاده‌سازی کرده باشد. سازگاری با Chat Completions به معنی پشتیبانی خودکار از Responses API نیست.

آیا «OpenAI-Compatible» یک استاندارد رسمی است؟

OpenAI-Compatible API یک برچسب کاربردی در صنعت است، نه گواهی رسمی و واحدی که همۀ ارائه‌دهندگان دقیقاً بر اساس آن آزموده شوند.

دو سرویس ممکن است هر دو خود را سازگار با OpenAI معرفی کنند، اما سطح سازگاری متفاوتی داشته باشند:

  • یکی فقط Text Chat را پشتیبانی کند.
  • دیگری Streaming نیز داشته باشد.
  • یکی Tool Calling را با همان Schema ارائه کند.
  • دیگری فقط بخشی از Tool Calling را پشتیبانی کند.
  • یکی Usage دقیق برگرداند.
  • دیگری فیلد Usage نداشته باشد.
  • یکی Responses API را پیاده‌سازی کند.
  • دیگری فقط /chat/completions داشته باشد.

بنابراین سازگاری یک ویژگی صفر و یک نیست؛ بهتر است آن را یک طیف در نظر بگیریم.

سطوح سازگاری با OpenAI API

سازگاری پایه

  • Bearer Token
  • /chat/completions
  • model
  • messages
  • choices
  • پاسخ متنی

سازگاری کاربردی

علاوه بر سطح پایه:

  • Streaming
  • Usage
  • پارامترهای تولید
  • خطاهای استاندارد
  • Endpoint مدل‌ها

سازگاری پیشرفته

علاوه بر موارد قبل:

  • Tool Calling
  • Structured Output
  • ورودی تصویر
  • Logprobs
  • Embedding
  • مدیریت چند انتخاب
  • جزئیات Usage

سازگاری گسترده

  • Responses API
  • Audio
  • Image Generation
  • Files
  • Batch
  • Realtime
  • Endpointهای تخصصی دیگر

سرویسی که Chat Completions را پشتیبانی می‌کند لزوماً با تمام APIهای OpenAI سازگار نیست.

ماتریس سازگاری پیشنهادی

پیش از انتخاب سرویس، این جدول را برای مدل و Gateway موردنظر تکمیل کنید:

قابلیتموردنیاز محصولپشتیبانی سرویسنتیجۀ تست
Chat Completionsبلهبررسی شودثبت شود
Streamingبلهبررسی شودثبت شود
System Messageبلهبررسی شودثبت شود
Tool Callingاختیاریبررسی شودثبت شود
Parallel Tool Callsاختیاریبررسی شودثبت شود
Structured Outputبلهبررسی شودثبت شود
Image Inputاختیاریبررسی شودثبت شود
Usageبلهبررسی شودثبت شود
Request IDبلهبررسی شودثبت شود
Embeddingاختیاریبررسی شودثبت شود
Responses APIاختیاریبررسی شودثبت شود

Endpoint مدل‌ها

برخی APIهای سازگار، فهرست مدل‌ها را از مسیر زیر ارائه می‌کنند:

GET /v1/models

نمونۀ cURL برای درواره:

curl "https://api.darvareh.ir/v1/models" \
  -H "Authorization: Bearer $DARVAREH_API_KEY"

پاسخ ممکن است ساختاری مشابه زیر داشته باشد:

{
  "object": "list",
  "data": [
    {
      "id": "MODEL_ID",
      "object": "model"
    }
  ]
}

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

Embedding در API سازگار با OpenAI

Embedding یک بردار عددی است که معنای متن را نمایش می‌دهد و در کاربردهای زیر استفاده می‌شود:

  • جست‌وجوی معنایی
  • RAG
  • خوشه‌بندی
  • پیشنهاد محتوا
  • تشخیص شباهت
  • بازیابی سند

ساختار رایج درخواست:

curl "https://api.darvareh.ir/v1/embeddings" \
  -H "Authorization: Bearer $DARVAREH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "EMBEDDING_MODEL_ID",
    "input": [
      "API سازگار با OpenAI چیست؟",
      "AI Gateway چگونه کار می‌کند؟"
    ]
  }'

مدل انتخاب‌شده باید از Embedding پشتیبانی کند. ابعاد بردار، محدودیت ورودی و روش قیمت‌گذاری به مدل بستگی دارند.

بردارهای تولیدشده توسط دو مدل Embedding متفاوت معمولاً نباید در یک Index بدون مهاجرت یا بازسازی ترکیب شوند.

مدیریت خطاها

APIهای سازگار معمولاً از کدهای استاندارد HTTP استفاده می‌کنند.

خطای 400

درخواست نامعتبر است:

  • JSON اشتباه
  • فیلد ضروری حذف شده
  • پارامتر پشتیبانی‌نشده
  • قالب Message نادرست
  • ورودی بیش از محدودیت

خطای 401

احراز هویت انجام نشده است:

  • API Key ارسال نشده
  • کلید نامعتبر است
  • قالب Authorization اشتباه است

خطای 403

هویت مشخص است، اما دسترسی مجاز نیست:

  • مدل برای حساب مجاز نیست
  • کلید محدود شده است
  • سیاست امنیتی درخواست را رد کرده است

خطای 404

منبع یا مسیر پیدا نشده است:

  • Base URL اشتباه
  • مسیر دوبار دارای /v1 شده است
  • Model ID نادرست است
  • Endpoint توسط سرویس پیاده‌سازی نشده است

خطای 413

حجم درخواست بیش از حد مجاز است:

  • تصویر Base64 بزرگ
  • فایل حجیم
  • Request Body بیش از محدودیت Gateway

خطای 422

ساختار درخواست از نظر نحوی قابل‌خواندن است، اما اعتبار معنایی ندارد.

خطای 429

محدودیت مصرف یا نرخ درخواست رد شده است:

  • RPM
  • TPM
  • تعداد اتصال هم‌زمان
  • Quota
  • محدودیت بودجه

خطای 500

خطای داخلی سرویس رخ داده است.

خطای 502

Gateway از سرویس بالادستی پاسخ معتبر دریافت نکرده است.

خطای 503

سرویس یا مدل موقتاً در دسترس نیست.

خطای 504

پاسخ سرویس بالادستی پیش از پایان Timeout دریافت نشده است.

ساختار مناسب برای مدیریت خطا

async function createCompletion(messages) {
  try {
    return await client.chat.completions.create({
      model: "MODEL_ID",
      messages,
    });
  } catch (error) {
    const status = error?.status;
    const requestId = error?.request_id;

    console.error("AI request failed", {
      status,
      requestId,
      message: error?.message,
    });

    if (status === 401) {
      throw new Error("AI API authentication failed");
    }

    if (status === 429) {
      throw new Error("AI API rate limit exceeded");
    }

    if (status && status >= 500) {
      throw new Error("AI provider is temporarily unavailable");
    }

    throw error;
  }
}

در پاسخ عمومی به کاربر نباید موارد زیر افشا شوند:

  • API Key
  • Headerهای داخلی
  • Stack Trace
  • آدرس ارائه‌دهنده بالادستی
  • Secretها
  • ساختار داخلی زیرساخت

Retry صحیح

Retry فقط برای خطاهای موقت مناسب است:

  • خطای اتصال
  • 408
  • 429
  • 500
  • 502
  • 503
  • 504

برای خطاهایی مانند 400، 401 یا Model ID اشتباه، Retry معمولاً فایده‌ای ندارد.

روش مناسب Exponential Backoff همراه با Jitter است:

تلاش اول: 500ms
تلاش دوم: 1000ms
تلاش سوم: 2000ms

با Jitter مقدار کوچکی تصادفی به زمان انتظار اضافه می‌شود تا تمام کلاینت‌ها هم‌زمان درخواست مجدد نفرستند.

SDKهای رسمی ممکن است برخی خطاهای موقت را به‌صورت پیش‌فرض Retry کنند. پیش از افزودن Retry در لایۀ برنامه، رفتار SDK را بررسی کنید تا Retryهای تو‌در‌تو ایجاد نشوند.

Timeout

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

نمونۀ ساده با Fetch:

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 60_000);

try {
  const response = await fetch(
    "https://api.darvareh.ir/v1/chat/completions",
    {
      method: "POST",
      signal: controller.signal,
      headers: {
        Authorization: `Bearer ${process.env.DARVAREH_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        model: "MODEL_ID",
        messages: [
          {
            role: "user",
            content: "پاسخ بده.",
          },
        ],
      }),
    },
  );

  return response;
} finally {
  clearTimeout(timeout);
}

مقدار Timeout باید با نوع مدل و کاربرد هماهنگ باشد. مدل استدلالی، تولید تصویر یا تولید ویدئو ممکن است به زمان بیشتری نیاز داشته باشد.

اشتباه رایج Base URL

یکی از خطاهای متداول، قرار دادن مسیر کامل Endpoint به‌جای Base URL است.

درست:

https://api.darvareh.ir/v1

اشتباه:

https://api.darvareh.ir/v1/chat/completions

اگر SDK خودش /chat/completions را اضافه کند، حالت اشتباه ممکن است چنین آدرسی بسازد:

https://api.darvareh.ir/v1/chat/completions/chat/completions

خطای رایج دیگر، دوبار اضافه کردن /v1 است:

https://api.darvareh.ir/v1/v1/chat/completions

خطاهای رایج اتصال

دریافت 401

بررسی کنید:

  • API Key معتبر باشد.
  • قبل یا بعد از کلید فضای اضافی نباشد.
  • از Bearer استفاده شده باشد.
  • کلید منقضی یا لغو نشده باشد.
  • متغیر محیطی واقعاً بارگذاری شده باشد.

دریافت 404

بررسی کنید:

  • Base URL صحیح باشد.
  • /v1 دوبار اضافه نشده باشد.
  • مسیر Chat Completions پشتیبانی شود.
  • Model ID دقیق باشد.
  • SDK از Endpoint دیگری مانند Responses استفاده نکند.

دریافت 429

بررسی کنید:

  • محدودیت RPM
  • محدودیت TPM
  • سقف بودجه
  • موجودی حساب
  • تعداد درخواست‌های هم‌زمان
  • Retry بیش از حد

پاسخ خالی

بررسی کنید:

  • choices وجود داشته باشد.
  • finish_reason چیست.
  • مدل Tool Call تولید نکرده باشد.
  • محتوا توسط سیاست ایمنی متوقف نشده باشد.
  • پاسخ Streaming به‌درستی تجمیع شده باشد.

کار نکردن Streaming

بررسی کنید:

  • stream: true ارسال شده باشد.
  • Proxy پاسخ را Buffer نکند.
  • Server-Sent Events پشتیبانی شود.
  • فشرده‌سازی یا Middleware باعث تجمیع پاسخ نشده باشد.
  • از curl -N استفاده شده باشد.

امنیت API Key

API Key را فقط در Backend نگهداری کنید:

DARVAREH_API_KEY=YOUR_API_KEY

فایل .env باید در .gitignore قرار گیرد:

.env
.env.local
.env.production

اگر کلید به Repository عمومی Commit شد، حذف آن از آخرین Commit کافی نیست؛ زیرا ممکن است در تاریخچۀ Git باقی مانده باشد. کلید را فوراً لغو و کلید جدید ایجاد کنید.

محل‌های نامناسب نگهداری کلید

  • JavaScript مرورگر
  • Source اپلیکیشن موبایل
  • فایل APK
  • افزونۀ عمومی مرورگر
  • HTML
  • GitHub عمومی
  • کد نمونه واقعی
  • Query String
  • لاگ
  • ابزار Analytics

چرا Query String نامناسب است؟

این روش:

https://api.example.com?api_key=SECRET

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

  • تاریخچۀ مرورگر
  • لاگ سرور
  • Proxy
  • Analytics
  • Referrer
  • ابزارهای مانیتورینگ

کلید باید در Header ارسال شود.

معماری صحیح برای وب‌سایت

Browser
   ↓
Backend Application
   ↓
Darvareh OpenAI-Compatible API
   ↓
Selected AI Model

Backend باید:

  • کاربر را احراز هویت کند.
  • طول ورودی را محدود کند.
  • Rate Limit اختصاصی اعمال کند.
  • مدل‌های مجاز را مشخص کند.
  • درخواست را به درواره ارسال کند.
  • خطاها را مدیریت کند.
  • مصرف هر کاربر را ثبت کند.
  • خروجی را پیش از استفاده حساس اعتبارسنجی کند.

مرورگر نباید مستقیماً API Key اصلی را دریافت کند.

کاهش وابستگی به ارائه‌دهنده

OpenAI-Compatible API مهاجرت را ساده‌تر می‌کند، اما Vendor Lock-in را کاملاً از بین نمی‌برد.

وابستگی ممکن است همچنان در این موارد وجود داشته باشد:

  • نام مدل
  • کیفیت Prompt
  • Tool Calling
  • قالب Structured Output
  • محدودیت Context
  • پارامترهای اختصاصی
  • سیاست محتوایی
  • رفتار Streaming
  • شکل خطا
  • قابلیت‌های چندوجهی

برای کاهش وابستگی:

  1. Client هوش مصنوعی را در یک ماژول مرکزی قرار دهید.
  2. Model ID را در Config نگهداری کنید.
  3. از Alias مدل استفاده کنید.
  4. پاسخ‌ها را با Schema داخلی تبدیل کنید.
  5. خطاها را به Error Code داخلی نگاشت کنید.
  6. برای مدل‌های جایگزین تست رگرسیون داشته باشید.
  7. Promptها را نسخه‌بندی کنید.
  8. قابلیت‌های اختصاصی را پشت Feature Flag قرار دهید.

لایۀ انتزاع پیشنهادی

type GenerateOptions = {
  system?: string;
  prompt: string;
  temperature?: number;
};

type GenerateResult = {
  text: string;
  model: string;
  inputTokens?: number;
  outputTokens?: number;
  requestId?: string;
};

interface AIClient {
  generate(options: GenerateOptions): Promise<GenerateResult>;
}

کد اصلی محصول به این Interface وابسته می‌شود، نه مستقیماً به SDK یک ارائه‌دهنده.

Adapter درواره:

import OpenAI from "openai";

export class DarvarehAIClient {
  private readonly client: OpenAI;

  constructor(
    private readonly model: string,
    apiKey: string,
  ) {
    this.client = new OpenAI({
      apiKey,
      baseURL: "https://api.darvareh.ir/v1",
    });
  }

  async generate({
    system,
    prompt,
    temperature,
  }: GenerateOptions): Promise<GenerateResult> {
    const messages = [];

    if (system) {
      messages.push({
        role: "system",
        content: system,
      });
    }

    messages.push({
      role: "user",
      content: prompt,
    });

    const response =
      await this.client.chat.completions.create({
        model: this.model,
        messages,
        temperature,
      });

    return {
      text:
        response.choices[0]?.message?.content ?? "",
      model: response.model,
      inputTokens: response.usage?.prompt_tokens,
      outputTokens: response.usage?.completion_tokens,
    };
  }
}

این کد برای نمایش الگوی معماری است و ممکن است به Typeهای دقیق‌تر متناسب با نسخۀ SDK نیاز داشته باشد.

انتخاب مدل در API سازگار

به‌جای انتخاب مدل صرفاً بر اساس شهرت، این معیارها را بررسی کنید:

  • نوع وظیفه
  • زبان فارسی
  • کیفیت استدلال
  • کیفیت تولید کد
  • سرعت پاسخ
  • قیمت ورودی
  • قیمت خروجی
  • Context Window
  • Structured Output
  • Tool Calling
  • ورودی تصویر
  • Streaming
  • سیاست داده
  • محدودیت نرخ
  • پایداری ارائه‌دهنده

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

مدل سریع و اقتصادی

مناسب برای:

  • طبقه‌بندی
  • بازنویسی ساده
  • استخراج اطلاعات
  • پاسخ‌های کوتاه
  • پردازش پرتعداد

مدل عمومی متعادل

مناسب برای:

  • چت‌بات
  • تولید محتوا
  • خلاصه‌سازی
  • تحلیل عمومی
  • دستیار سازمانی

مدل استدلالی

مناسب برای:

  • مسائل چندمرحله‌ای
  • تحلیل پیچیده
  • برنامه‌ریزی
  • کدنویسی دشوار
  • تصمیم‌سازی با قیود متعدد

مدل چندوجهی

مناسب برای:

  • تحلیل تصویر
  • OCR معنایی
  • بررسی نمودار
  • پردازش اسکرین‌شات
  • تحلیل سند تصویری

استفاده از قوی‌ترین مدل برای تمام درخواست‌ها معمولاً بهترین تصمیم فنی و اقتصادی نیست.

کنترل هزینه

هزینه مدل‌های زبانی معمولاً بر اساس توکن محاسبه می‌شود:

هزینۀ ورودی =
تعداد توکن ورودی × قیمت توکن ورودی

هزینۀ خروجی =
تعداد توکن خروجی × قیمت توکن خروجی

هزینۀ کل =
هزینۀ ورودی + هزینۀ خروجی

راهکارهای کاهش هزینه:

  • حذف تاریخچۀ غیرضروری
  • خلاصه‌سازی مکالمات طولانی
  • انتخاب مدل متناسب با وظیفه
  • محدود کردن خروجی
  • استفاده از Structured Output مختصر
  • Cache کردن نتایج مناسب
  • جلوگیری از Retry غیرضروری
  • کاهش مثال‌های تکراری Prompt
  • مدیریت Context
  • ثبت مصرف در سطح کاربر

نباید فقط total_tokens را ثبت کرد. برای تحلیل دقیق‌تر بهتر است این موارد ذخیره شوند:

  • مدل درخواست‌شده
  • مدل واقعی
  • توکن ورودی
  • توکن خروجی
  • زمان پاسخ
  • وضعیت درخواست
  • تعداد Retry
  • کاربر یا پروژه
  • Request ID
  • هزینه نهایی

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

Chat Completions معمولاً Stateless است؛ یعنی API به‌صورت خودکار تمام تاریخچۀ مکالمۀ برنامۀ شما را نگهداری نمی‌کند. برای حفظ Context باید پیام‌های لازم را در هر درخواست ارسال کنید:

{
  "messages": [
    {
      "role": "system",
      "content": "پاسخ را فارسی بنویس."
    },
    {
      "role": "user",
      "content": "نام من امیر است."
    },
    {
      "role": "assistant",
      "content": "خوشوقتم امیر."
    },
    {
      "role": "user",
      "content": "نام من چیست؟"
    }
  ]
}

ارسال کل تاریخچه در تمام درخواست‌ها می‌تواند هزینه را افزایش دهد و از Context Window عبور کند.

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

  • نگهداری تعداد محدودی پیام اخیر
  • خلاصه‌سازی پیام‌های قدیمی
  • بازیابی پیام‌های مرتبط
  • حذف محتوای تکراری
  • ذخیرۀ اطلاعات پایدار در حافظۀ ساختاریافته
  • تعیین بودجۀ توکن برای تاریخچه

آیا API سازگار با OpenAI برای RAG مناسب است؟

بله. در یک سیستم RAG معمولاً از دو نوع مدل استفاده می‌شود:

  • مدل Embedding برای تبدیل متن به بردار
  • مدل زبانی برای تولید پاسخ

جریان ساده:

پرسش کاربر
    ↓
تولید Embedding
    ↓
جست‌وجو در Vector Database
    ↓
بازیابی متن‌های مرتبط
    ↓
ارسال Context و پرسش به مدل
    ↓
تولید پاسخ

OpenAI-Compatible API می‌تواند اتصال به مدل زبانی و در صورت پشتیبانی، مدل Embedding را یکپارچه کند.

اما موارد زیر همچنان بر عهدۀ برنامۀ RAG است:

  • Chunking
  • Indexing
  • Metadata
  • کنترل دسترسی سند
  • Retrieval
  • Reranking
  • Citation
  • ارزیابی کیفیت پاسخ

آیا API سازگار با OpenAI برای Agent مناسب است؟

بله، به‌ویژه اگر مدل از Tool Calling پشتیبانی کند. Agent می‌تواند:

  1. پیام کاربر را دریافت کند.
  2. از مدل تصمیم بخواهد.
  3. ابزار مناسب را اجرا کند.
  4. نتیجۀ ابزار را به مدل بازگرداند.
  5. پاسخ نهایی تولید کند.

بااین‌حال، API Gateway یا مدل به‌تنهایی یک Agent کامل نیست. Agent به اجزای دیگری نیاز دارد:

  • Loop اجرا
  • مدیریت وضعیت
  • حافظه
  • Tool Registry
  • کنترل دسترسی
  • محدودیت هزینه
  • محدودیت تعداد مراحل
  • مدیریت خطا
  • تأیید انسانی
  • Observability

API سازگار با OpenAI و AI Gateway

OpenAI-Compatible API یک قرارداد ارتباطی است، درحالی‌که AI Gateway یک لایۀ زیرساختی برای مدیریت درخواست‌های هوش مصنوعی است.

AI Gateway می‌تواند API سازگار با OpenAI ارائه کند و در پشت آن قابلیت‌های زیر را اجرا کند:

  • انتخاب ارائه‌دهنده
  • مسیریابی مدل
  • Fallback
  • Load Balancing
  • کنترل هزینه
  • محاسبۀ توکن
  • مدیریت API Key
  • Rate Limiting
  • ثبت لاگ
  • مانیتورینگ
  • Guardrail
  • یکسان‌سازی پاسخ‌ها

بنابراین:

OpenAI-Compatible API = قرارداد ارتباطی
AI Gateway = لایۀ مدیریت و مسیریابی

درواره این دو مفهوم را در کنار هم قرار می‌دهد: یک لایۀ اتصال به مدل‌های مختلف که از رابط آشنا و سازگار با OpenAI برای ساده‌تر شدن توسعۀ نرم‌افزار استفاده می‌کند.

مزایای استفاده از API سازگار با OpenAI

کاهش زمان توسعه

نیازی نیست برای هر ارائه‌دهنده یک SDK و Integration جداگانه پیاده‌سازی شود.

استفاده از ابزارهای موجود

بسیاری از برنامه‌ها امکان تنظیم Base URL و API Key را دارند.

مهاجرت ساده‌تر

در موارد سازگار، تغییر سرویس با تغییر محدود Config انجام می‌شود.

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

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

کاهش کد اختصاصی

ساختار Message، Streaming و پاسخ‌ها تا حد زیادی یکسان می‌شوند.

ساده‌تر شدن آزمایش مدل‌ها

می‌توان مدل را با تغییر فیلد model عوض و عملکرد گزینه‌های مختلف را مقایسه کرد.

کاهش Vendor Lock-in

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

محدودیت‌های API سازگار با OpenAI

سازگاری کامل تضمین‌شده نیست

ممکن است فقط Endpoint اصلی Chat Completions پیاده‌سازی شده باشد.

رفتار مدل‌ها متفاوت است

یک Prompt ثابت می‌تواند در مدل‌های مختلف نتایج متفاوتی تولید کند.

پارامترهای پشتیبانی‌شده یکسان نیستند

برخی مدل‌ها Temperature، Stop، Logprobs یا Seed را نمی‌پذیرند.

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

Tool Calling، Structured Output، Vision و Streaming باید جداگانه آزمایش شوند.

قالب خطاها ممکن است تفاوت داشته باشد

کد HTTP مشابه لزوماً به معنی Error Body کاملاً یکسان نیست.

تعداد توکن‌ها متفاوت است

مدل‌ها ممکن است Tokenizerهای متفاوت داشته باشند؛ بنابراین یک متن ثابت در همۀ مدل‌ها تعداد توکن یکسانی ندارد.

چک‌لیست مهاجرت از API دیگر به درواره

  1. یک API Key معتبر در درواره ایجاد کنید.
  2. کلید را در Secret یا متغیر محیطی قرار دهید.
  3. Base URL را روی مقدار زیر تنظیم کنید:
https://api.darvareh.ir/v1
  1. Model ID معتبر را از فهرست مدل‌ها انتخاب کنید.
  2. ابتدا یک درخواست غیر Streaming ساده آزمایش کنید.
  3. ساختار پاسخ و Usage را بررسی کنید.
  4. Streaming را جداگانه آزمایش کنید.
  5. پارامترهای موردنیاز مدل را بررسی کنید.
  6. Tool Calling را با تست واقعی ارزیابی کنید.
  7. Structured Output را اعتبارسنجی کنید.
  8. Timeout و Retry را تنظیم کنید.
  9. خطاهای 401، 404 و 429 را آزمایش کنید.
  10. مصرف و هزینه را ثبت کنید.
  11. کلید قبلی را فقط پس از اطمینان از مهاجرت لغو کنید.
  12. تغییر را ابتدا روی درصد محدودی از ترافیک منتشر کنید.

چک‌لیست Production

  • API Key فقط در Backend قرار دارد.
  • Secretها در Repository نیستند.
  • مدل در Config تعریف شده است.
  • ورودی کاربر اعتبارسنجی می‌شود.
  • طول Prompt محدود شده است.
  • برای هر درخواست Timeout وجود دارد.
  • Retry فقط برای خطاهای موقت انجام می‌شود.
  • Exponential Backoff و Jitter فعال است.
  • Request ID ثبت می‌شود.
  • API Key در لاگ نمایش داده نمی‌شود.
  • Promptهای حساس ثبت نمی‌شوند.
  • پاسخ مدل پیش از عملیات حساس اعتبارسنجی می‌شود.
  • Rate Limit در سطح کاربر اعمال می‌شود.
  • بودجۀ مصرف تعریف شده است.
  • خطاها به شکل امن به کاربر نمایش داده می‌شوند.
  • Streaming هنگام قطع کاربر لغو می‌شود.
  • Tool Callها Allowlist دارند.
  • آرگومان Toolها با Schema بررسی می‌شوند.
  • برای مدل جایگزین تست وجود دارد.
  • مصرف توکن و هزینه مانیتور می‌شود.

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

API سازگار با OpenAI چیست؟

API سازگار با OpenAI رابطی است که از ساختار Endpointها، احراز هویت، درخواست و پاسخ مشابه OpenAI API استفاده می‌کند. توسعه‌دهنده می‌تواند در بسیاری از موارد با تغییر Base URL، API Key و Model ID از همان کد و SDK استفاده کند.

آیا OpenAI-Compatible API فقط مدل‌های OpenAI را ارائه می‌کند؟

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

Base URL چیست؟

Base URL آدرس پایۀ API است. SDK مسیرهایی مانند /chat/completions را به آن اضافه می‌کند. Base URL درواره عبارت است از:

https://api.darvareh.ir/v1

آیا می‌توان از SDK رسمی OpenAI برای درواره استفاده کرد؟

بله، برای Endpointها و قابلیت‌های سازگار می‌توان Client را با API Key درواره و Base URL زیر مقداردهی کرد:

https://api.darvareh.ir/v1

سپس باید Model ID معتبر درواره را در درخواست قرار داد.

تفاوت base_url و baseURL چیست؟

در SDK رسمی Python پارامتر به شکل base_url نوشته می‌شود. در SDK رسمی JavaScript و TypeScript از baseURL استفاده می‌شود.

آیا فقط با تغییر Base URL همه‌چیز کار می‌کند؟

برای Chat Completions پایه معمولاً تغییر Base URL، API Key و Model ID کافی است. قابلیت‌های پیشرفته مانند Tool Calling، Structured Output، Vision و Responses API باید برای مدل و سرویس موردنظر آزمایش شوند.

آیا Chat Completions منسوخ شده است؟

Chat Completions همچنان در API و مستندات OpenAI وجود دارد. OpenAI برای پروژه‌های جدید خود Responses API را توصیه می‌کند، اما پشتیبانی سرویس‌های ثالث از Responses API به اندازۀ Chat Completions یکسان نیست.

آیا Responses API بخشی از هر OpenAI-Compatible API است؟

خیر. یک سرویس ممکن است Chat Completions را پشتیبانی کند، اما Endpoint مربوط به Responses API را پیاده‌سازی نکرده باشد.

آیا API سازگار با OpenAI از Streaming پشتیبانی می‌کند؟

بسیاری از سرویس‌ها پشتیبانی می‌کنند، اما این موضوع باید در سطح Endpoint و مدل بررسی شود. در Chat Completions معمولاً با stream: true فعال می‌شود.

آیا می‌توان تصویر ارسال کرد؟

اگر مدل انتخاب‌شده و Gateway از ورودی چندوجهی پشتیبانی کنند، می‌توان تصویر را با URL یا Base64 در محتوای پیام ارسال کرد.

آیا می‌توان خروجی JSON دریافت کرد؟

بله، اما سطح اطمینان به قابلیت مدل بستگی دارد. Prompt ساده، JSON Mode و Structured Output سطوح متفاوتی از کنترل را فراهم می‌کنند. خروجی همیشه باید در برنامه اعتبارسنجی شود.

آیا Tool Calling به معنی اجرای مستقیم تابع است؟

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

چرا خطای 404 دریافت می‌کنم؟

رایج‌ترین دلایل عبارت‌اند از:

  • Base URL اشتباه
  • تکرار /v1
  • قرار دادن مسیر کامل به‌جای Base URL
  • Model ID نامعتبر
  • پشتیبانی نشدن Endpoint

چرا خطای 401 دریافت می‌کنم؟

API Key ممکن است ارسال نشده، نامعتبر، منقضی، لغوشده یا با قالب اشتباه در Header قرار گرفته باشد.

چرا خطای 429 دریافت می‌کنم؟

احتمالاً از محدودیت درخواست، توکن، اتصال هم‌زمان، بودجه یا موجودی عبور کرده‌اید. Headerها و بدنۀ خطا را بررسی و Retry را بر اساس Retry-After یا Backoff انجام دهید.

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

خیر. هر Secret موجود در کد مرورگر قابل‌استخراج است. درخواست باید ابتدا به Backend برنامه ارسال شود.

آیا API سازگار با OpenAI باعث کاهش هزینه می‌شود؟

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

آیا تغییر مدل نیاز به بازنویسی Prompt دارد؟

گاهی بله. مدل‌ها در نحوۀ پیروی از دستور، Tool Calling، Structured Output و زبان فارسی تفاوت دارند. هر مدل جدید باید با مجموعۀ ارزیابی واقعی محصول آزمایش شود.

آیا می‌توان از یک API برای مدل‌های متن، تصویر، صوت و ویدئو استفاده کرد؟

یک AI Gateway ممکن است انواع مختلف مدل را ارائه کند، اما Endpoint، ساختار درخواست و روش قیمت‌گذاری آن‌ها لزوماً یکسان نیست. سازگاری Chat Completions معمولاً بیشتر به مدل‌های متنی و چندوجهی مرتبط است.

جمع‌بندی

API سازگار با OpenAI یک قرارداد ارتباطی مشترک برای اتصال نرم‌افزارها به مدل‌های هوش مصنوعی است. در این معماری، توسعه‌دهنده از ساختاری آشنا شامل Base URL، API Key، Model ID، آرایۀ messages و پاسخ choices استفاده می‌کند.

مهم‌ترین مزیت آن این است که کد برنامه کمتر به API اختصاصی یک ارائه‌دهنده وابسته می‌شود. در بسیاری از موارد می‌توان با تغییر سه مقدار، اتصال را جابه‌جا کرد:

Base URL
API Key
Model ID

اما عبارت OpenAI-Compatible نباید به معنی سازگاری صددرصد با تمام قابلیت‌های OpenAI تفسیر شود. پشتیبانی از Streaming، Tool Calling، Structured Output، Vision، Embedding و Responses API باید برای هر سرویس و مدل به‌صورت جداگانه بررسی شود.

درواره با فراهم کردن یک API یکپارچه و سازگار با OpenAI، اتصال نرم‌افزارها به مدل‌های مختلف را ساده‌تر می‌کند. برای شروع، API Key خود را ایجاد کنید، مدل موردنظر را انتخاب کنید و Base URL را روی آدرس زیر قرار دهید:

https://api.darvareh.ir/v1

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

مقالات مرتبط پیشنهادی

Read more

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

اتوماسیون هوش مصنوعی چیست؟ کاربردها و آموزش ساخت AI Automation

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

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce چیست؟ آینده خرید با ایجنت هوش مصنوعی

Agentic Commerce شیوه‌ای جدید برای خرید اینترنتی است که در آن ایجنت هوش مصنوعی می‌تواند نیاز کاربر را بفهمد، محصولات را جست‌وجو و مقایسه کند و فرایند خرید را پیش ببرد. در این راهنما با معماری، UCP، ACP و پیاده‌سازی آن با API درواره آشنا می‌شوید.