AR ▾

تكوين واجهة API الوسيطة في أطر العمل الشائعة: من SDK إلى Dify

الهدف الأساسي من دمج واجهة API الوسيطة هو ثلاثة قيم فقط: عنوان نقطة النهاية، ومفتاح API، واسم النموذج. الصعوبة تكمن في أن كل إطار عمل يسمي هذه القيم بشكل مختلف؛ بعضها يستخدم base_url، وبعضها api_base، وDify يستخدم نموذجًا كاملًا. نعرض هنا جدول المقارنة لكل إطار، مع أكواد جاهزة للتشغيل وقائمة خطوة بخطوة.

تم التحديث في

نقاط رئيسية

  1. ضع العنوان ومفتاح API في متغيرات البيئة API_BASE وAPI_KEY لتجنب ظهورها كنص واضح في الكود.
  2. يجب أن يحتوي عنوان نقطة النهاية على لاحقة /v1، واسم النموذج ثابت على uncensored.
  3. LangChain تستخدم base_url، بينما تستخدم OpenAILike في LlamaIndex api_base؛ الاسم مختلف لكن المعنى واحد.
  4. في Dify، اختر مزود "OpenAI-API-compatible"، ثم أدخل اسم النموذج، والعنوان، وطول نافذة السياق يدويًا.

جهز القيم الثلاث أولاً

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

  • عنوان نقطة النهاية: https://api.llmzhongzhuan.com/v1. انتبه إلى أن /v1 في النهاية يجب أن يبقى، لكن لا تضيف /chat/completions لأن الـ SDK يضيفها تلقائيًا.
  • مفتاح API: يظهر فورًا بعد التسجيل بالبريد الإلكتروني وكلمة المرور في صفحة الحصول على المفتاح. كل حساب له مفتاح واحد، ويمكنك إعادة تعيينه، عندها يصبح المفتاح القديم غير صالح فورًا.
  • اسم النموذج: يوجد نموذج واحد فقط، وهو uncensored. يمكنك التحقق من ذلك بنفسك باستخدام GET /v1/models.

تذكر أيضًا حدودًا أخرى: إجمالي نافذة السياق 100,000 رمز (token)، وقيمة max_tokens الافتراضية هي 2048 والحد الأقصى 32,000، مع حد 300 طلب في الدقيقة لكل مفتاح. ستحتاج إلى هذه الأرقام في إعدادات المعلمات لاحقًا.

صيغة متغيرات البيئة

كتابة مفتاح API مباشرة في الكود هو السبب الأكثر شيوعًا للأعطال. الطريقة الموصى بها هي قراءة المتغيرات البيئية فقط، وإدراجها عند النشر عبر الحاوية أو نظام إدارة المفاتيح. نستخدم المتغيرين API_KEY وAPI_BASE في الأمثلة التالية لجميع الأطر.

# Linux / macOS:写进 ~/.zshrc 或 .env 加载脚本
export API_KEY="替换为你的密钥"
export API_BASE="https://api.llmzhongzhuan.com/v1"

# Windows PowerShell(仅当前会话)
$env:API_KEY = "替换为你的密钥"
$env:API_BASE = "https://api.llmzhongzhuan.com/v1"

إذا كنت تستخدم ملف .env، فلا تنسَ إضافته إلى .gitignore. بالإضافة إلى ذلك، يقرأ الـ Python SDK الرسمي افتراضيًا المتغيرين OPENAI_API_KEY وOPENAI_BASE_URL. يمكنك استخدام هذه الأسماء أو تمرير القيم صراحةً في الدالة الإنشائية كما هو موضح أدناه. الميزة هي عدم تأثر الكود بالمتغيرات القديمة المتبقية في الجهاز، مما يسهل استكشاف الأخطاء.

OpenAI Python SDK وNode SDK

يتطلب Python حزمة openai الإصدار 1 فما فوق، ويتطلب Node.js الإصدار 4 فما فوق. الفرق هو في الصياغة فقط؛ قم بإنشاء العميل بتمرير العنوان ومفتاح API. ابدأ باستدعاء غير متدفق في Python مع طباعة حقل usage للتحقق من الخصم:

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
    api_key=os.environ["API_KEY"],
)

resp = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "写一条 20 字以内的发布公告标题。"}],
    max_tokens=100,
)
print(resp.choices[0].message.content)
print(resp.usage)

يستخدم مثال Node الإخراج المتدفق (streaming)، وهو الأسلوب الأكثر شيوعًا لتأثير الآلة الكاتبة في الواجهات الأمامية. عند انتهاء الطلب المتدفق، سيرسل الخادم تلقائيًا شريحة (chunk) تحتوي على الاستخدام، دون الحاجة إلى معاملات إضافية:

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: process.env.API_BASE ?? "https://api.llmzhongzhuan.com/v1",
  apiKey: process.env.API_KEY,
});

const stream = await client.chat.completions.create({
  model: "uncensored",
  messages: [{ role: "user", content: "列出三个命名服务端日志字段的好习惯。" }],
  stream: true,
  max_tokens: 300,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
process.stdout.write("\n");

يستخدم مثال Node.js الـ await على المستوى الأعلى، مما يتطلب ملفًا بامتداد .mjs أو تعيين "type": "module" في ملف package.json. إذا لم يدعم وقت التشغيل ذلك، قم بتغليف الكود داخل دالة async.

LangChain: base_url في ChatOpenAI

في LangChain، لا تحتاج إلى أي مخصص خاص؛ استخدم ببساطة فئة ChatOpenAI من حزمة langchain_openai، وأشر إلى base_url.

import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="uncensored",
    base_url=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
    api_key=os.environ["API_KEY"],
    temperature=0.7,
    max_tokens=500,
    timeout=60,
)

print(llm.invoke("把“服务已降级”改写成对用户友好的一句话。").content)

تنبيهان صغيران. أولاً، يُفضل تعيين max_tokens وtimeout صراحةً لأن القيم الافتراضية قد لا تناسب عملك. ثانيًا، إذا كنت تستخدم استدعاء الدوال في السلسلة، فإننا ندعم أدوات بصيغة OpenAI، ويمكنك استخدام bind_tools في LangChain بشكل طبيعي؛ في الوضع المتدفق، استهلك البيانات على الكتل داخل stream().

LlamaIndex: OpenAILike

فئة OpenAI المضمنة في LlamaIndex تتحقق مما إذا كان اسم النموذج موجودًا في القائمة الرسمية، مما يسبب خطأ مع الأسماء المخصصة. في هذه الحالة، استخدم بدلاً من ذلك OpenAILike من حزمة llama-index-llms-openai-like، التي لا تقوم بهذا التحقق وتستخدم أسماء معاملات مختلفة، حيث يُسمى العنوان api_base.

import os
from llama_index.llms.openai_like import OpenAILike

llm = OpenAILike(
    model="uncensored",
    api_base=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
    api_key=os.environ["API_KEY"],
    is_chat_model=True,
    context_window=100000,
    max_tokens=500,
)

print(llm.complete("用一句话解释什么是幂等请求。"))

القيمة is_chat_model=True هنا حاسمة؛ فهي تجعل LlamaIndex يستخدم واجهة الدردشة بدلاً من واجهة الإكمال القديمة. اضبط context_window على 100000 حتى لا يقوم LlamaIndex بقص مستنداتك عند تقسيم السياق بناءً على القيم الافتراضية الصغيرة.

Dify: مزود OpenAI-API-compatible

منصات مثل Dify لا تعتمد على الكود، بل على تكوين النماذج. قد تختلف نصوص الواجهة قليلاً بين الإصدارات، لكن الخطوات متشابهة:

  1. ادخل إلى "الإعدادات"، وابحث عن صفحة "مزودي النموذج"، واختر "OpenAI-API-compatible" من القائمة، ثم انقر على إضافة نموذج.
  2. اختر نوع النموذج "LLM"، واكتب اسم النموذج uncensored.
  3. املأ حقل API Key بمفتاحك، واملأ حقل API endpoint URL بـ https://api.llmzhongzhuan.com/v1.
  4. أدخل 100000 لطول سياق النموذج، و32000 للحد الأقصى لعدد الرموز.
  5. إذا كنت تريد استخدام استدعاء الدوال في سير العمل، فعّل خيار دعم استدعاء الدوال؛ واحتفظ بالإخراج المتدفق مفعلاً.
  6. بعد الحفظ، أنشئ تطبيق دردشة بسيطًا، وحدد النموذج المضاف للتو وأرسل رسالة للتأكد من استلام الاستجابة بشكل صحيح.

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

المعاملات التي يجب التحقق منها قبل النشر وطريقة النشر

قدرة إطار العمل على العمل هي الخطوة الأولى فقط. يُنصح بالتحقق من المعاملات التالية قبل النشر، لأنها تحدد التكلفة ومعدل الفشل.

  • max_tokens: القيمة الافتراضية 2048، قم بزيادتها صراحةً للمخرجات الطويلة، والحد الأقصى هو 32,000. انتبه إلى أن مجموع المدخلات والمخرجات لا يجب أن يتجاوز 100,000 رمز، وإلا ستحصل على خطأ 400.
  • timeout: الطلبات ذات المخرجات الطويلة تستغرق وقتًا أطول. في السيناريوهات المتدفقة، يُنصح بضبط مهلة القراءة على 60 ثانية أو أكثر، وفي الحالات غير المتدفقة، قدّر الوقت بناءً على أقصى طول للمخرجات.
  • temperature / top_p / stop: يتم تمرير معاملات أخذ العينات القياسية كما هي، وستُطبق القيم المحددة في الإطار مباشرة دون الحاجة إلى مفاتيح إضافية.
  • الطلبات المتزامنة: الحد هو 300 طلب في الدقيقة لكل مفتاح. عندما تشارك عدة مثيلات الخدمة نفس المفتاح، يتم حساب حدّ المعدل بشكل مجمع، لذا لا تخطط لكل مثيل على أنه 300 طلب.
  • إعادة المحاولة: إعادة المحاولة المضمنة في الأطر غالبًا ما تقتصر على أخطاء الشبكة. يجب عليك إضافة استراتيجيات تجنب الأخطاء (backoff) لرموز 429 و503 بنفسك، انظر قسم الاستقرار للتفاصيل.

# docker-compose.yml 片段
services:
  app:
    image: your-app:latest
    environment:
      API_BASE: https://api.llmzhongzhuan.com/v1
      API_KEY: ${API_KEY}   # 从宿主机环境或 .env 读取,不写进镜像

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

أخيرًا، السجلات. تطبع العديد من الأطر رؤوس الطلب الكاملة في وضع التصحيح، بما في ذلك Authorization. تأكد من إيقاف هذا الإخراج في بيئة الإنتاج أو قم بإخفاء المفتاح في مرشحات السجلات. يُعد تسجيل حقل usage عادة جيدة؛ فهو يساعدك في مطابقة الفواتير واكتشاف ما إذا كان طول الموجّه لوظيفة معينة قد زاد فجأة.

ترتيب استكشاف الأخطاء عند حدوثها

تركز 90% من مشاكل مرحلة الدمج على نقاط محددة؛ أسرع طريقة للتحقق هي اتباع الترتيب التالي:

  1. 401: المفتاح فارغ، أو تم نسخه مع مسافات، أو استخدام مفتاح قديم بعد إعادة التعيين.
  2. 404: تم كتابة العنوان كجذر بدون /v1، أو تمت إضافة /chat/completions مرة أخرى.
  3. 402: رمز الخطأ هو no_credit، مما يعني نفاد الرصيد أو انتهاء صلاحية التجربة، ويتطلب شحن رصيد مسبق الدفع.
  4. 400: السبب الشائع هو تجاوز إجمالي حجم الطلب (المدخلات + max_tokens) لـ 100,000 رمز، أو تجاوز حجم جسم الطلب لـ 8 ميجابايت.
  5. 429 / 503: الأول هو حدّ المعدل (300 طلب في الدقيقة)، والثاني يعني أن الخادم البعيد مشغول. انتظر بضع ثوانٍ وأعد المحاولة، انظر ممارسات الاستقرار للتفاصيل.

هناك فئة أخرى من المشاكل التي يسهل تجاهلها وهي بيئة الشبكة. قد تؤدي شبكات الشركات الداخلية، أو برامج الوكيل (Proxy)، أو قواعد مجموعات الأمان إلى نجاح حل اسم النطاق (DNS) مع عدم القدرة على إنشاء اتصال HTTPS، مما يتجلى في توقف طويل ثم انتهاء المهلة (Timeout) بدلاً من رمز خطأ واضح. عند مواجهة هذا الوضع، قم أولاً باختبار الاتصال عبر عنوان /v1/models باستخدام curl من نفس الجهاز؛ إذا نجح الاتصال، فالمشكلة في طبقة التطبيق، وإذا فشل، فافحص إعدادات الوكيل وجدار الحماية. يُنصح بتوثيق خطوات الاستكشاف في وثائق الفريق لتسهيل انضمام الزملاء الجدد مستقبلاً.

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

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

هل يجب أن يحتوي base_url على /v1؟

يجب أن يحتوي على /. ما عليك سوى كتابة https://api.llmzhongzhuan.com/v1، حيث سيضيف SDK تلقائيًا /chat/completions في النهاية، فلا تضفها مرة أخرى.

لماذا نستخدم OpenAILike في LlamaIndex بدلاً من OpenAI؟

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

هل يمكن تسمية اسم النموذج في Dify بأي طريقة نريدها؟

لا، يجب أن يكون اسم النموذج uncensored لأن الطلب سيرسل هذا المعرف كما هو. يمكن تسمية العرض (Display Name) بشكل مختلف، لكن حقل النموذج يجب أن يطابق.

لماذا لم تُفعّل التغييرات في متغيرات البيئة؟

السبب الأكثر شيوعاً هو عدم إعادة تشغيل الطرفية أو العملية. يُنصح بتمرير base_url و api_key بشكل صريح في الكود، وطباعة عنوان الـ endpoint الفعلي للتأكد.

املأ النموذج للحصول على مفتاح API

أنشئ حساباً، انسخ مفتاح API، وعدّل عنوان الـ Base URL. الإعداد بهذه البساطة.

الحصول على مفتاح API