اختر Auto أو TokenLab Verified أو Official لكل طلب، مع عرض الأسعار مسبقاً.اطلع على الجديد

بناء روبوت محادثة (Chatbot) يعمل بالذكاء الاصطناعي باستخدام مفتاح API واحد: من الصفر إلى مرحلة الإنتاج في 30 دقيقة

·١٩ سبتمبر ٢٠٢٦·6 دقائق قراءة·آخر تحديث ٢ أكتوبر ٢٠٢٦·2786 مشاهدة
#دردشة آلية#دليل تعليمي#Python#FastAPI#بث البيانات
بناء روبوت محادثة (Chatbot) يعمل بالذكاء الاصطناعي باستخدام مفتاح API واحد: من الصفر إلى مرحلة الإنتاج في 30 دقيقة

يمكنك بناء روبوت محادثة يعمل بالذكاء الاصطناعي ويقوم ببث رد واحد في غضون فترة ما بعد الظهيرة. أما الحفاظ على استمراريته بعد ظهور أول خطأ 429 فهو العمل الحقيقي. سنقوم ببناء خدمة FastAPI صغيرة مع خاصية البث SSE، وذاكرة محادثة من جانب الخادم، وقائمة سماح للنماذج، وكل ذلك خلف مفتاح TokenLab واحد. الهدف هو الحصول على واجهة خلفية (backend) يمكنك وضعها خلف واجهة منتج حقيقي وتغيير النماذج فيها دون إعادة كتابة التكامل. إذا لم تقم بتبديل عنوان URL الأساسي بعد، فاقرأ دليل الترحيل أولاً. إذا كان قلقك الرئيسي هو التراجع (backoff) تحت ضغط التحميل، فاقرن هذا الدليل بـ دليل تحديد معدل AI API.

أبرز النقاط

  • يحتاج روبوت المحادثة الجاهز للإنتاج إلى ستة أجزاء: نقطة نهاية متزامنة (sync endpoint)، ونقطة نهاية للبث (streaming endpoint)، وحالة محادثة من جانب الخادم، وقائمة سماح للنماذج، ومعالجة حقيقية للأخطاء، ومسار لترقية التخزين.
  • قم بتحميل المفتاح من TOKENLAB_API_KEY في كل عملية واحتفظ به على الخادم. تنص وثائق المصادقة (تمت ملاحظتها في 2026-10-03) على عدم كشف المفاتيح أبداً في كود جانب العميل أو تضمينها في الكود المصدري.
  • GET /v1/models هي نقطة عامة، لذا فإن الاستجابة الناجحة لا تثبت أن مفتاحك يعمل. أثبت ذلك بطلب محادثة حقيقي واحد (Quickstart، تمت ملاحظتها في 2026-10-03).
  • تغطي SSE معظم منتجات المحادثة. تعامل مع البث الذي يتوقف بعد وصول المخرجات على أنه غير مكتمل، وليس كإجابة نهائية.
  • تربط قائمة السماح في هذا الدليل المستويات بـ deepseek-v4-flash و gpt-5.5 و claude-sonnet-5 و gemini-3.5-flash. تدرج كل منها openai_chat_completions كتنسيق مقبول (تمت ملاحظتها في 2026-10-03).
  • كتقدير من الأسعار أدناه، تكلف جولة واحدة من 2000 توكن للإدخال و 500 توكن للإخراج ما بين 0.0006 دولار إلى 0.0075 دولار، اعتماداً على النموذج.

بناء روبوت محادثة AI: الإعداد والخدمة الأساسية

تحتوي الخدمة النهائية على ستة أجزاء متحركة:

  1. نقطة نهاية /chat متزامنة لاختبارات الدخان (smoke tests).
  2. نقطة نهاية /chat/stream للبث من أجل واجهة المستخدم الحقيقية.
  3. حالة محادثة مفهرسة بواسطة conversation_id.
  4. قائمة سماح للنماذج، حتى لا تتمكن الواجهة الأمامية من طلب معرفات عشوائية.
  5. معالجة أخطاء لا تنهار عند أول خطأ 429.
  6. مسار من النموذج الأولي في الذاكرة إلى Redis أو PostgreSQL.

هذا كافٍ لروبوت دعم، أو مساعد داخلي، أو الإصدار الأول من أداة محادثة مضمنة. يقوم كل كود أدناه بالإلحاق بملف واحد، main.py، بالترتيب الموضح.

الخطوة 1: تعيين المفتاح وتثبيت الحزمة

أنشئ مفتاحاً ضمن API Keys في مساحة العمل الخاصة بك. يتم عرض القيمة الكاملة مرة واحدة فقط، ولا يمكن للبادئة الموجودة في قائمة المفاتيح مصادقة الطلبات. تبدأ جميع مفاتيح TokenLab بـ sk-. تحقق من رصيدك في Billing أيضاً، لأن الطلب الأول يتم محاسبته بالسعر الحالي للنموذج (Quickstart، تمت ملاحظتها في 2026-10-03).

قم بتخزين المفتاح في متغير بيئة، وليس في المصدر أبداً. يحافظ الأمر أدناه على إبقائه خارج سطر الأوامر وسجل الصدفة (shell history)، وهو ينطبق فقط على الجهاز الحالي وعملياته الفرعية. استخدم مدير أسرار للتطبيقات المنشورة. يحتوي Quickstart أيضاً على إصدار PowerShell.

printf 'TokenLab API key: '
read -r -s TOKENLAB_API_KEY
printf '\n'
export TOKENLAB_API_KEY
pip install fastapi uvicorn openai pydantic

أنت لا تحتاج إلى SDK خاص بـ TokenLab. يعمل SDK الرسمي لـ OpenAI بمجرد تغيير المفتاح وعنوان URL الأساسي (دليل SDKs، تمت ملاحظتها في 2026-10-03).

الخطوة 2: تحديد المخطط، وقائمة السماح، والذاكرة

نبدأ بالأجزاء المشتركة. يستخدم مخطط الطلب tier، وليس سلسلة model خام، ويرسل كل مثال curl لاحق في هذا الدليل نفس الحقول.

عادةً ما يحتفظ عرض المحادثة الأول بالنص في المتصفح ويعيد إرساله في كل جولة. على سبيل المثال، هذا يعمل مع نموذج أولي. يصبح الأمر فوضوياً بمجرد احتياجك إلى عمليات إعادة المحاولة، أو الجلسات القابلة للاستئناف، أو الأدوات من جانب الخادم. لذا فإن الخادم يمتلك التاريخ منذ البداية.

import json
import logging
import os
import time
import uuid
from collections import defaultdict

from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from openai import APIConnectionError, APIStatusError, OpenAI, RateLimitError
from pydantic import BaseModel, Field

log = logging.getLogger("chatbot")
app = FastAPI()

client = OpenAI(
    api_key=os.environ["TOKENLAB_API_KEY"],
    base_url="https://api.tokenlab.sh/v1",
    timeout=30.0,
    max_retries=0,
)

ALLOWED_MODELS = {
    "default": "deepseek-v4-flash",
    "flagship": "gpt-5.5",
    "balanced": "claude-sonnet-5",
    "budget": "gemini-3.5-flash",
}
SYSTEM_PROMPT = "You are a helpful assistant. Be concise and direct."
MAX_HISTORY = 20          # عدد الرسائل المحفوظة لكل محادثة (اختيارك)
MAX_REPLY_TOKENS = 1000   # حد المخرجات لكل جولة (اختيارك)

class ChatRequest(BaseModel):
    message: str = Field(min_length=1, max_length=4000)
    tier: str = "default"
    conversation_id: str | None = None

def resolve_model(tier: str) -> str:
    model = ALLOWED_MODELS.get(tier)
    if model is None:
        raise HTTPException(status_code=400, detail=f"unknown tier: {tier}")
    return model

conversations: dict[str, list[dict]] = defaultdict(list)

def build_messages(conv_id: str, user_msg: str) -> list[dict]:
    history = conversations[conv_id][-MAX_HISTORY:]
    return [
        {"role": "system", "content": SYSTEM_PROMPT},
        *history,
        {"role": "user", "content": user_msg},
    ]

def remember(conv_id: str, user_msg: str, reply: str) -> None:
    conversations[conv_id].append({"role": "user", "content": user_msg})
    conversations[conv_id].append({"role": "assistant", "content": reply})

يضبط العميل max_retries=0 ومهلة 30 ثانية، مطابقة لـ Quickstart. نتخذ قرارات إعادة المحاولة بأنفسنا، لأن إعادة محاولة SDK عمياء في جولة مبثوثة يمكن أن تكرر الرسوم.

الخطوة 3: تعيين الأخطاء وإضافة نقطة النهاية المتزامنة

نحتفظ بوظيفة صغيرة واحدة تحول أي استثناء SDK إلى رمز حالة ورسالة آمنة للمستخدم. تذهب التفاصيل إلى سجلاتك، وليس إلى المستخدم.

def describe_error(exc: Exception) -> tuple[int, str]:
    if isinstance(exc, RateLimitError):  # تحقق أولاً: إنها فئة فرعية من APIStatusError
        return 429, "The assistant is busy. Please retry shortly."
    if isinstance(exc, APIConnectionError):
        return 502, "Connection problem. Please retry."
    if isinstance(exc, APIStatusError):
        log.error("upstream status=%s code=%s", exc.status_code, exc.code)
        if exc.status_code in (401, 402, 403):  # مفتاح، رصيد، أو إذن: مشكلة مشغل
            return 503, "The assistant is unavailable right now."
        if exc.status_code in (400, 404):  # حقل سيء أو معرف نموذج: خطأنا
            return 500, "Something went wrong on our side."
        return 502, "The assistant is unavailable right now."
    return 500, "Something went wrong."

def retry_delay(exc: RateLimitError) -> float:
    try:
        return min(float(exc.response.headers.get("retry-after", "2")), 10.0)
    except ValueError:
        return 2.0

def create_with_one_retry(**kwargs):
    try:
        return client.chat.completions.create(**kwargs)
    except RateLimitError as exc:
        time.sleep(retry_delay(exc))
        return client.chat.completions.create(**kwargs)

@app.post("/chat")
def chat(req: ChatRequest):
    model = resolve_model(req.tier)
    conv_id = req.conversation_id or str(uuid.uuid4())
    messages = build_messages(conv_id, req.message)
    try:
        response = create_with_one_retry(
            model=model, messages=messages, max_tokens=MAX_REPLY_TOKENS
        )
    except (APIConnectionError, APIStatusError) as exc:
        status, message = describe_error(exc)
        raise HTTPException(status_code=status, detail=message)
    reply = response.choices[0].message.content or ""
    log.info("model=%s usage=%s", model, response.usage)
    remember(conv_id, req.message, reply)
    return {"conversation_id": conv_id, "reply": reply}

قم بتشغيل طلب واحد قبل بناء أي شيء آخر. إذا فشل هذا، فلا تستمر في البناء فوقه.

بث الردود والحفاظ على استجابة روبوت المحادثة

معظم منتجات روبوتات المحادثة تبدو بطيئة ليس لأن النموذج بطيء، ولكن لأن واجهة المستخدم تظل فارغة حتى تصل الاستجابة الكاملة. نضيف SSE لأنه يغطي معظم منتجات المحادثة ويحمل وزناً تشغيلياً أقل من websockets. إذا كان منتجك يقوم بالفعل بتشغيل عميل متصفح عبر HTTP عادي، فإن SSE يبقي البنية أبسط.

الخطوة 4: إضافة نقطة نهاية البث

تحافظ وثائق البث (دليل البث، تمت ملاحظتها في 2026-10-03) على صلاحية بث Chat Completions للأطر التي تتوقع بالفعل أجزاء SSE من /v1/chat/completions. نحن نبقى على هذا المسار لأن جميع النماذج الأربعة المدرجة في قائمة السماح تقبله. نقوم بتشفير كل حدث كـ JSON، بحيث لا يمكن للأسطر الجديدة داخل الرد كسر التأطير.

def sse(payload: dict) -> str:
    return f"data: {json.dumps(payload)}\n\n"

@app.post("/chat/stream")
def chat_stream(req: ChatRequest):
    model = resolve_model(req.tier)
    conv_id = req.conversation_id or str(uuid.uuid4())
    messages = build_messages(conv_id, req.message)

    def generate():
        parts: list[str] = []
        finish_reason = None
        try:
            with client.chat.completions.create(
                model=model,
                messages=messages,
                max_tokens=MAX_REPLY_TOKENS,
                stream=True,
                stream_options={"include_usage": True},
            ) as stream:
                for chunk in stream:
                    if chunk.usage:
                        log.info("model=%s usage=%s", model, chunk.usage)
                    if not chunk.choices:
                        continue
                    choice = chunk.choices[0]
                    if choice.delta.content:
                        parts.append(choice.delta.content)
                        yield sse({"delta": choice.delta.content})
                    if choice.finish_reason:
                        finish_reason = choice.finish_reason
        except (APIConnectionError, APIStatusError) as exc:
            _, message = describe_error(exc)
            yield sse({"error": message})
            return
        if finish_reason != "stop":
            yield sse({"error": f"Reply incomplete ({finish_reason})."})
            return
        remember(conv_id, req.message, "".join(parts))
        yield sse({"done": True, "conversation_id": conv_id})

    return StreamingResponse(generate(), media_type="text/event-stream")

هناك تفصيلان مهمان هنا. أولاً، يضيف include_usage: true استخدام التوكن إلى أجزاء البث، وجزء الاستخدام النهائي ليس لديه choices، لذا تتخطاه الحلقة. ثانياً، نحفظ الجولة فقط عندما يكون finish_reason هو stop. يعني انتهاء length أن حد التوكن قطع الرد، وتدرج الوثائق كلاهما كظروف إنهاء طبيعية.

بما أن الأخطاء تظهر داخل المولد، فإن حالة HTTP تكون بالفعل 200 بحلول الوقت الذي يحدث فيه الفشل. يجب على العميل قراءة حدث error. تغلق كتلة with البث المنبع عندما يخرج المولد. تنصح الوثائق بإلغاء المنبع عندما يتوقف المستخدم أو يقطع الاتصال، لذا اختبر أن خادم ASGI الخاص بك يوقف المولد فعلياً عند قطع الاتصال.

الخطوة 5: قراءة البث في المتصفح

يجب أن يتصل المتصفح بالخلفية الخاصة بك والمصادق عليها، ولا يتصل بـ TokenLab مباشرة أبداً. قم بالتحليل حسب حدود الحدث، لأن جزء الشبكة ليس بالضرورة حدث SSE كاملاً واحداً.

async function sendMessage(payload, onDelta) {
  const response = await fetch('/chat/stream', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload), // { message, tier, conversation_id }
  });
  if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`);

  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = '';

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const events = buffer.split('\n\n');
    buffer = events.pop();
    for (const raw of events) {
      if (!raw.startsWith('data: ')) continue;
      const event = JSON.parse(raw.slice(6));
      if (event.error) throw new Error(event.error);
      if (event.delta) onDelta(event.delta);
    }
  }
}

ترقية الذاكرة إلى Redis عندما تحتاج إليها

قاموس الذاكرة جيد لعملية واحدة. اتجه إلى Redis عندما تحتاج المحادثات إلى TTL، أو قابلية الاستئناف، أو مثيلات متعددة. اتجه إلى PostgreSQL عندما يكون النص بيانات منتج يجب عليك الاستعلام عنها، أو تدقيقها، أو إعداد تقارير عنها. تغيير Redis هو سباكة تخزين، وليس منطقاً. قم بتشغيل pip install redis، ثم اجعل build_messages و remember تقرأ وتكتب من خلال هذه المساعدات.

import redis

redis_client = redis.Redis(
    host=os.environ.get("REDIS_HOST", "127.0.0.1"), port=6379, decode_responses=True
)

def load_history(conv_id: str) -> list:
    raw = redis_client.get(f"chat:{conv_id}")
    return json.loads(raw) if raw else []

def save_history(conv_id: str, history: list) -> None:
    redis_client.setex(f"chat:{conv_id}", 60 * 60 * 24, json.dumps(history))

معالجة الأخطاء عند بناء روبوت محادثة AI للإنتاج

لا يهتم المستخدم الذي يواجه العميل بما إذا كان الفشل ناتجاً عن تحديد المعدل، أو رصيد فارغ، أو انقطاع في المنبع. إنهم يهتمون بما إذا كانت واجهة المستخدم تتجمد. تخيل أداة دعم يموت بثها بعد أربعين كلمة. تقول وثائق TokenLab أن البث الذي تمت مقاطعته بعد وصول المخرجات ليس استجابة كاملة، وتكراره قد يولد مخرجات مختلفة ورسوماً ثانية (معالجة أخطاء API، تمت ملاحظتها في 2026-10-03). لهذا السبب لا تحفظ الخطوة 4 أبداً جولة غير مكتملة.

الخطوة 6: تطبيق قواعد إعادة المحاولة

تعامل مع الأخطاء حسب حالة HTTP و code، وليس حسب message المقروء للبشر، والذي يمكن أن يتغير دون سابق إنذار. يجمع الجدول الوثائق مع ما تفعله خدمتنا.

الحالة المعنى (حسب الوثائق) ما تفعله هذه الخدمة
400 الحقل، معرف النموذج، أو الإدخال غير صالح إرجاع 500، تسجيله، عدم إعادة المحاولة أبداً
401 المفتاح مفقود، غير صالح، منتهي الصلاحية، أو ملغى إرجاع 503، تنبيه المشغل
402 الرصيد أو حد مفتاح API منخفض جداً (insufficient_balance، quota_exceeded) إرجاع 503، تنبيه المشغل
403 لا يمكن للمفتاح استخدام هذا النموذج إرجاع 503، تحقق من قائمة نماذج المفتاح
404 المورد أو النموذج غير موجود إرجاع 500، تحقق من المعرف مقابل /v1/models
429 تم الوصول إلى حد الطلب انتظر Retry-After، أعد المحاولة مرة واحدة (متزامن فقط)
500–504 الخدمة غير متاحة أو فشل الشبكة أعد المحاولة فقط عندما يكون retryable هو true؛ احترم retry_after

تحذيران من الوثائق. لا يعني 503 all_channels_failed دائماً انقطاعاً مؤقتاً. إذا كان retryable هو false، فلا تكرر نفس الطلب، وتحقق من GET /v1/models قبل اختيار نموذج آخر. أيضاً، تستخدم Chat Completions و Responses كائن error على طراز OpenAI، لكن Anthropic Messages و Gemini تحتفظ بتنسيقاتها الخاصة. تستدعي خدمتنا Chat Completions على كل نموذج مدرج في قائمة السماح، لذا يعمل محلل واحد. إذا قمت بنقل مستوى إلى تنسيق أصلي، فأنت بحاجة إلى محلل ثانٍ.

احفظ معرف الطلب من رؤوس الاستجابة مع نقطة النهاية، والنموذج، والوقت، ومعرف المستخدم أو الوظيفة الخاص بك. لا تسمي الوثائق الرأس في الصفحات التي استخدمناها، لذا تأكد منه مقابل مسار استكشاف الأخطاء وإصلاحها قبل توصيل سجلاتك.

بقية قائمة التحقق للإنتاج قصيرة:

  • أضف معرفات الطلبات الخاصة بك حتى تتمكن من ربط فشل الواجهة الأمامية بسجلات الخلفية.
  • حدد التزامن لكل مستخدم وحجم الطلب. max_length=4000 في message هو نقطة بداية يجب عليك ضبطها.
  • حدد التاريخ حسب عدد الرسائل، كما يفعل MAX_HISTORY. تقول وثائق الفوترة أن الترميز يختلف حسب النموذج، واللغة، وإدخال الوسائط، لذا فإن صيغة عدد الأحرف ليست تقدير فوترة موثوقاً.
  • سجل النموذج، وزمن الوصول، والاستخدام، وسبب الإنهاء في كل جولة.
  • احتفظ بالرسائل المرئية للمستخدم منفصلة عن تفاصيل الخطأ الداخلية.
  • أرسل جولة اختبار واحدة إلى مستوى بديل، حتى تعرف أن التراجع يعمل قبل أول انقطاع حقيقي.

اختيار النماذج وتقدير التكلفة لروبوت المحادثة الخاص بك

لا تدع الواجهة الأمامية تمرر سلسلة نموذج عشوائية إلى API. حقل النص الحر يدعو إلى الأخطاء المطبعية، والنماذج المتقاعدة، والنماذج التي لم تقصد عرضها أبداً. تمنحك خريطة ALLOWED_MODELS مكاناً واحداً لتبديل نموذج عندما يقوم مزود بإيقاف واحد، دون تحديث العميل. تُرجع معرفات النماذج غير المعروفة model_not_found، وتوجهك الوثائق إلى /v1/models للحصول على معرف حالي. تحقق من دليل النماذج قبل قفل قائمة السماح.

الأسعار الحالية لكل 1 مليون توكن

هذه هي أسعار TokenLab بالدولار الأمريكي من إدخال API المباشر لكل نموذج، تمت ملاحظتها في 2026-10-03. تم تحديث التسعير آخر مرة في 2026-10-02T16:53:30.068Z. أظهرت النماذج الأربعة جميعها مرحلة دورة الحياة active.

المستوى معرف النموذج الإدخال / 1M الإخراج / 1M قراءة ذاكرة التخزين المؤقت / 1M المصدر
default deepseek-v4-flash (خارج الذروة) $0.15 $0.60 $0.003 model API
default deepseek-v4-flash (الذروة) $0.30 $1.20 $0.006 model API
flagship gpt-5.5 $1.50 $9.00 $0.15 model API
balanced claude-sonnet-5 $0.90 $4.50 $0.09 model API
budget gemini-3.5-flash $0.75 $4.50 $0.075 model API

بالنسبة لـ deepseek-v4-flash، يدرج الإدخال نوافذ الذروة في أيام الأسبوع، باستثناء العطلات الرسمية في الصين. تشمل خارج الذروة عطلات نهاية الأسبوع وتلك العطلات. يدرج claude-sonnet-5 أيضاً سعر كتابة ذاكرة التخزين المؤقت بقيمة 1.125 دولار لكل 1 مليون توكن.

طلب واحد تم العمل عليه

افترض جولة واحدة مع 2000 توكن إدخال و 500 توكن إخراج. هذا الحجم هو افتراضنا، وليس قياساً. بالنسبة لـ gpt-5.5:

  • الإدخال: 2000 × 1.50 دولار / 1,000,000 = 0.0030 دولار
  • الإخراج: 500 × 9.00 دولار / 1,000,000 = 0.0045 دولار
  • الإجمالي: 0.0075 دولار لكل جولة (تقدير، لا توجد زيارات لذاكرة التخزين المؤقت)

باستخدام نفس الحساب، نحصل على هذه التقديرات: يكلف deepseek-v4-flash 0.0006 دولار خارج الذروة و 0.0012 دولار في الذروة، ويكلف claude-sonnet-5 0.00405 دولار، ويكلف gemini-3.5-flash 0.00375 دولار. على مدى 1000 جولة من هذا القبيل، هذا تقريباً 7.50 دولار على gpt-5.5 مقابل 0.60 إلى 1.20 دولار على deepseek-v4-flash. تنمو الجولات الحقيقية مع تراكم التاريخ، لذا اقرأ usage من الاستجابة أو جزء البث النهائي بدلاً من الوثوق بهذا التحجيم.

من الناحية العملية، نقوم بتوجيه معظم المحادثات إلى المستوى الافتراضي ونحجز gpt-5.5 أو claude-sonnet-5 للجولات التي تحتاج إليها. تحذر وثائق الفوترة من أن أقل سعر لكل توكن ليس دائماً أقل تكلفة لكل مهمة مكتملة. قم بتشغيل نفس المطالبات عبر مستويين وقارن التكلفة النهائية في Usage.

فحصان قبل الشحن. تقول وثائق الفوترة عدم ترميز جدول أسعار منسوخ، لذا اقرأ GET /v1/models/:model/pricing إذا كان تطبيقك يعرض التكاليف (الفوترة والتسعير، تمت ملاحظتها في 2026-10-03). أيضاً، يدرج إدخال deepseek-v4-flash كلاً من json-mode و prompt-cache و tool-use ولكن ليس vision. إذا كان روبوت المحادثة الخاص بك يقبل الصور، فوجه تلك الجولات إلى مستوى يدرج إدخاله vision.

اختبار الدخان وشحن روبوت المحادثة الخاص بك

الخطوة 7: تشغيله من النهاية إلى النهاية

ابدأ الخادم في محطة طرفية حيث يتم تصدير TOKENLAB_API_KEY. تستخدم حمولات curl نفس حقول message و tier و conversation_id مثل ChatRequest.

uvicorn main:app --reload --port 8000
# 1. نقطة النهاية المتزامنة: توقع رداً قصيراً و conversation_id
curl -X POST http://localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Reply only with OK.", "tier": "default"}'

# 2. البث: توقع أسطر data: {"delta": ...}، ثم {"done": true, ...}
curl -N -X POST http://localhost:8000/chat/stream \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello!", "tier": "default"}'

# 3. الذاكرة: أعد استخدام conversation_id الذي تم إرجاعه أعلاه
curl -X POST http://localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "What did I just say?", "tier": "default", "conversation_id": "PASTE_ID_FROM_STEP_1"}'

# 4. فشل قسري: توقع HTTP 400 و "unknown tier: nope"
curl -i -X POST http://localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello!", "tier": "nope"}'

لفرض فشل في المنبع، أعد تشغيل الخادم باستخدام TOKENLAB_API_KEY خاطئ. يجب أن يحصل عميلك على رسالة 503 نظيفة، ويجب أن تظهر سجلاتك 401 الخاصة بالمنبع مع invalid_api_key. ثم افتح Requests لتأكيد النموذج، والحالة، واستخدام المكالمات الناجحة. إذا كان بإمكانك بث جولة واحدة، والحفاظ على محادثة واحدة، وإرجاع خطأ نظيف عند فشل قسري، فلديك الأساس الصحيح.

بعد ذلك، التحديثات التالية هي بنية تحتية مملة بدلاً من المزيد من الذكاء الاصطناعي:

  • المصادقة، حتى لا يتمكن مستخدم واحد من قراءة محادثة مستخدم آخر.
  • الاستمرارية، حتى تنجو الجلسات من عمليات النشر.
  • تحديد المعدل لكل مستخدم، حتى لا يتمكن مستخدم صاخب من حرق حصتك.
  • نسب الاستخدام، إذا كان روبوت المحادثة يواجه العميل.
  • التلخيص في الخلفية، إذا كانت المحادثات تحتاج إلى ذاكرة طويلة المدى.

بمجرد أن يصبح ترحيل عنوان URL الأساسي خلفك، يكون تغيير النموذج عبارة عن تعديل تكوين لـ ALLOWED_MODELS.

الأسئلة الشائعة

هل أحتاج إلى websockets لبناء روبوت محادثة AI؟

لا. SSE، الموضح في الخطوة 4، يغطي معظم منتجات المحادثة. تضيف Websockets قيمة عندما تحتاج إلى دفع ثنائي الاتجاه خارج الطلب والاستجابة، مثل التعاون المباشر أو الأحداث التي يبدأها الخادم. بالنسبة لواجهة محادثة قياسية، SSE أسهل في النشر والتصحيح.

أي نموذج يجب أن يستخدمه روبوت المحادثة الخاص بي افتراضياً؟

ابدأ بمستوى منخفض السعر مثل deepseek-v4-flash وأضف claude-sonnet-5 أو gpt-5.5 خلف قائمة السماح للجولات الأكثر صعوبة. تظهر الأسعار أعلاه، التي تمت ملاحظتها في 2026-10-03، الفجوة. ثم اختبر مطالباتك الخاصة على مستويين وقارن التكلفة النهائية في Usage. تحقق من دليل النماذج قبل الالتزام، حيث تتغير التشكيلات.

لماذا يُرجع روبوت المحادثة الخاص بي 402 أو 429؟

يعني 402 أن رصيد الحساب أو حد استخدام المفتاح نفسه منخفض جداً. الرموز هي insufficient_balance و quota_exceeded، ولن يساعد تكرار الطلب. يعني 429 أنك وصلت إلى حد الطلب، لذا انتظر Retry-After ثم أعد المحاولة. تعيد نقطة النهاية المتزامنة الخاصة بنا المحاولة مرة واحدة وتُبلغ نقطة نهاية البث بالخطأ إلى العميل.

هل يمكن للمتصفح الاتصال بـ TokenLab مباشرة؟

ليس بأمان. تقول وثائق المصادقة عدم كشف مفاتيح API أبداً في كود جانب العميل. احتفظ بالمفتاح في بيئة الخادم واجعل المتصفح يتصل بالخلفية الخاصة بك والمصادق عليها، كما يفعل نموذج الواجهة الأمامية.


أنشئ مفتاح API على tokenlab.sh، وأضف أموالاً، وقم بتشغيل اختبار الدخان أعلاه.

المصادر

تم رصد السعر في 2026-10-03

نماذج ذات صلة

النماذج الصادرة حديثًا

ابدأ البناء بالنماذج في هذا الدليل

قارن الأسعار، اختبر المسارات، وحول البحث إلى طلب API يعمل.