كل تطبيق ذكاء اصطناعي جاد يصطدم في النهاية بنفس الصداع: تعدد المزودين. تبدأ مع OpenAI، ثم تضيف Anthropic للاستفادة من السياق الطويل لـ Claude، وتوجّه جزءاً من الحركة إلى Google من أجل Gemini Flash بسعر منخفض، وربما ترش القليل من Mistral كحل احتياطي مفتوح المصدر. فجأة يحوي ملف .env ستة مفاتيح API، وعملاء المزودين مبعثرون عبر ملفاتك، وعطل واحد لـ OpenAI يكفي لإسقاط المنتج بأكمله.
بوابة Vercel AI تختصر هذه الفوضى في نقطة نهاية HTTP واحدة ومفتاح API واحد وعلاقة فوترة واحدة. تستمر في كتابة الكود مقابل Vercel AI SDK، لكن كل استدعاء للنموذج يصبح موزّع الحمل وقابلاً للرصد والتخزين المؤقت وجاهزاً لتجاوز الفشل افتراضياً.
في هذا الدرس ستبني تطبيق دردشة بجودة الإنتاج يوجّه الطلبات بذكاء بين المزودين، ويتجاوز الفشل تلقائياً عند تعطل أحدهم، ويخزّن الاستجابات المكلفة، ويتتبع استهلاك الرموز لكل مستخدم — كل ذلك دون تغيير كود التطبيق بأكثر من سطر واحد.
المتطلبات المسبقة
قبل البدء تأكد من توفر:
- Node.js الإصدار 20 أو أحدث
- مشروع Next.js 15 (أو اتبع خطوة الإعداد أدناه)
- حساب Vercel مع تفعيل AI Gateway — سجّل في vercel.com
- إلمام أساسي بـ React و TypeScript و Next.js App Router
- محرر أكواد (يُنصح بـ VS Code)
لا تحتاج حسابات منفصلة لدى OpenAI أو Anthropic أو Google لمتابعة هذا الدرس. توفّر بوابة Vercel AI رصيداً تجريبياً لاختبار جميع المزودين المدعومين من لوحة تحكم واحدة.
ما ستبنيه
تطبيق دردشة متعدد النماذج يقوم بما يلي:
- التوجيه عبر نقطة نهاية واحدة — كل طلب يمر بالبوابة، لا اتصال مباشر بأي مزود
- تجاوز الفشل التلقائي عندما يعيد مزود ما خطأ أو يصطدم بحد المعدل
- تخزين المطالبات المتطابقة مؤقتاً لتقليل استهلاك الرموز للأسئلة المتكررة
- تتبع الاستخدام لكل مستخدم من خلال ترويسات بيانات وصفية مخصصة
- مقارنة النماذج مباشرة عبر لوحة استجابة جنباً إلى جنب لأغراض المقارنة
لماذا البوابة أفضل من الاستدعاءات المباشرة
استدعاء حزم تطوير المزودين مباشرة يصلح في النموذج الأولي، لكنه ينهار حالما يصبح أي مما يلي مهماً:
- الموثوقية. أعطال المزودين تحدث شهرياً. بدون تجاوز الفشل يرى المستخدمون أخطاء بدلاً من إجابات.
- رؤية التكلفة. لكل مزود لوحة تحكم وفوترة وحساب رموز خاصة به. تسوية الإنفاق عبر خمسة مزودين كابوس مالي.
- حدود المعدل. المستوى الأول لـ OpenAI يقيّدك ببضعة آلاف طلب في الدقيقة. توزيع الحمل بين المزودين يرفع سقفك الفعلي.
- تبديل النماذج. تجربة نموذج جديد تعني مطاردة كل استدعاء
openai.chat(). مع البوابة تغيّر سلسلة نصية واحدة. - قابلية الرصد. تحتاج إلى تتبعات الطلبات ومعدلات الأخطاء والكمون لكل نموذج. بناء ذلك بنفسك يستغرق أسابيع.
البوابة تقدّم كل هذا كخدمة مُدارة مقابل تمرير الرموز إضافةً إلى هامش بسيط.
الخطوة 1: إعداد المشروع
أنشئ مشروع Next.js 15 جديداً مع TypeScript و Tailwind:
npx create-next-app@latest ai-gateway-demo \
--typescript --tailwind --app --use-pnpm
cd ai-gateway-demoثبّت Vercel AI SDK v5 وحزمة مزود البوابة:
pnpm add ai @ai-sdk/gateway zodحزمة @ai-sdk/gateway تكشف واجهة مزود واحدة تتوسط لأي نموذج تدعمه بوابة Vercel AI. لا تثبّت @ai-sdk/openai أو @ai-sdk/anthropic أو أي حزمة خاصة بمزود — هذه هي الفكرة الأساسية.
الخطوة 2: تكوين البوابة
داخل مشروع Vercel افتح علامة التبويب AI واضغط Enable AI Gateway. ستزوّد Vercel تلقائياً نقطة نهاية البوابة المربوطة بمشروعك وتكشف مفتاح API كمتغير بيئة باسم AI_GATEWAY_API_KEY.
إذا كنت تطوّر محلياً اربط المشروع واسحب متغيرات البيئة:
pnpm dlx vercel link
pnpm dlx vercel env pull .env.localينبغي أن يحوي .env.local الآن:
AI_GATEWAY_API_KEY=vck_xxxxxxxxxxxxxxxxxxxxهذا المفتاح الوحيد يحل محل كل مفاتيح المزودين التي كنت ستحتاجها عادةً.
الخطوة 3: إنشاء عميل البوابة
أنشئ lib/ai.ts لتمركز الوصول إلى البوابة:
import { createGateway } from "@ai-sdk/gateway";
export const gateway = createGateway({
apiKey: process.env.AI_GATEWAY_API_KEY!,
});
export const MODELS = {
fast: "google/gemini-2.5-flash",
smart: "anthropic/claude-sonnet-4.6",
cheap: "openai/gpt-4o-mini",
reasoning: "openai/o3-mini",
openWeights: "mistral/mistral-large-latest",
} as const;
export type ModelKey = keyof typeof MODELS;كل قيمة في MODELS معرّف نموذج مؤهل بالكامل تعرفه البوابة. تبديل المزودين أصبح تغيير سطر واحد في أي مكان من الكود.
الخطوة 4: بناء مسار دردشة أساسي
أنشئ app/api/chat/route.ts:
import { streamText, convertToCoreMessages } from "ai";
import { gateway, MODELS, type ModelKey } from "@/lib/ai";
export const runtime = "edge";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages, model = "smart" } = await req.json() as {
messages: { role: string; content: string }[];
model?: ModelKey;
};
const result = await streamText({
model: gateway(MODELS[model]),
messages: convertToCoreMessages(messages),
system: "You are a concise, helpful assistant. Keep answers under 200 words unless asked for detail.",
});
return result.toDataStreamResponse();
}لاحظ غياب أي كود خاص بمزود معين. تحل البوابة anthropic/claude-sonnet-4.6 إلى الـ API الصحيح خلف الكواليس.
الخطوة 5: بناء واجهة الدردشة
أنشئ app/page.tsx مع واجهة دردشة بسيطة:
"use client";
import { useChat } from "ai/react";
import { useState } from "react";
const MODEL_OPTIONS = [
{ value: "fast", label: "Gemini 2.5 Flash (سريع ورخيص)" },
{ value: "smart", label: "Claude Sonnet 4.6 (أفضل جودة)" },
{ value: "cheap", label: "GPT-4o Mini (متوازن)" },
{ value: "reasoning", label: "OpenAI o3-mini (تفكير)" },
{ value: "openWeights", label: "Mistral Large (أوزان مفتوحة)" },
];
export default function ChatPage() {
const [model, setModel] = useState("smart");
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: "/api/chat",
body: { model },
});
return (
<main className="mx-auto max-w-2xl p-6">
<h1 className="mb-4 text-2xl font-bold">دردشة AI Gateway</h1>
<select
value={model}
onChange={(e) => setModel(e.target.value)}
className="mb-4 w-full rounded border p-2"
>
{MODEL_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
<div className="mb-4 space-y-3">
{messages.map((m) => (
<div key={m.id} className="rounded border p-3">
<div className="text-xs text-gray-500">{m.role}</div>
<div className="whitespace-pre-wrap">{m.content}</div>
</div>
))}
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="اسأل عن أي شيء..."
className="flex-1 rounded border p-2"
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading || !input}
className="rounded bg-black px-4 py-2 text-white disabled:opacity-50"
>
إرسال
</button>
</form>
</main>
);
}شغّل pnpm dev ويفترض أن تتمكن من الدردشة مع خمسة نماذج مختلفة من نفس الواجهة، جميعها موجهة عبر البوابة.
الخطوة 6: إضافة تجاوز الفشل التلقائي
لا ينبغي لحركة الإنتاج الحقيقية أن ترى أخطاء المزودين أبداً. اضبط سلسلة احتياطية بحيث عند تقييد Claude بحد المعدل تُعاد محاولة الطلب مع GPT-4o، وهكذا.
حدّث app/api/chat/route.ts:
import { streamText, convertToCoreMessages } from "ai";
import { gateway, MODELS, type ModelKey } from "@/lib/ai";
export const runtime = "edge";
const FALLBACK_CHAIN: ModelKey[] = ["smart", "cheap", "fast"];
async function streamWithFailover(
messages: any[],
preferred: ModelKey,
) {
const chain = [preferred, ...FALLBACK_CHAIN.filter((m) => m !== preferred)];
for (const key of chain) {
try {
return await streamText({
model: gateway(MODELS[key]),
messages,
system: "You are a concise, helpful assistant.",
experimental_telemetry: { isEnabled: true, functionId: `chat-${key}` },
});
} catch (err) {
console.warn(`[gateway] ${key} failed, trying next provider`, err);
if (key === chain[chain.length - 1]) throw err;
}
}
throw new Error("All providers failed");
}
export async function POST(req: Request) {
const { messages, model = "smart" } = await req.json();
const result = await streamWithFailover(
convertToCoreMessages(messages),
model as ModelKey,
);
return result.toDataStreamResponse();
}تدعم البوابة كذلك سياسات تجاوز فشل من جهة الخادم يمكنك تكوينها في لوحة التحكم، وهو الأفضل للإنتاج لأن قرار التوجيه يحدث قبل احتساب الرموز.
الخطوة 7: تفعيل التخزين المؤقت للاستجابات
المطالبات المتطابقة الواردة في نافذة زمنية قصيرة يجب ألا تكلفك رموزاً مرتين. تتوفر في بوابة Vercel AI ميزة تخزين دلالي مدمج — فعّلها بإضافة ترويسات التخزين المؤقت:
const result = await streamText({
model: gateway(MODELS[model], {
headers: {
"x-gateway-cache-ttl": "3600",
"x-gateway-cache-mode": "semantic",
},
}),
messages,
});ثلاثة أوضاع متاحة:
exact— مطابقة المطالبات المتطابقة بايت ببايت فقطsemantic— مطابقة المطالبات المتشابهة في المعنى (يستخدم تضمينات)disabled— تجاوز التخزين المؤقت كلياً
لمساعد على نمط الأسئلة المتكررة قد يقلّل التخزين الدلالي إنفاق الرموز بنسبة تتراوح بين 40 و70 بالمائة لأن المستخدمين يطرحون السؤال نفسه بصيغ كثيرة.
الخطوة 8: تتبع الاستخدام لكل مستخدم
في التطبيقات متعددة المستأجرين تريد إسناد استهلاك الرموز إلى مستخدمين أفراد. مرّر ترويسة بيانات وصفية:
const result = await streamText({
model: gateway(MODELS[model], {
headers: {
"x-gateway-user-id": userId,
"x-gateway-tags": "tier:pro,feature:chat",
},
}),
messages,
});تقوم لوحة تحكم البوابة بعد ذلك بتقسيم الإنفاق حسب user-id وأي وسوم تزوّدها بها. يمكنك أيضاً تصدير هذه البيانات عبر واجهة برمجة التحليلات لفوترة العملاء بناءً على الاستخدام الفعلي للنماذج.
الخطوة 9: إضافة لوحة مقارنة مباشرة للنماذج
ميزة قاتلة في التوجيه متعدد المزودين هي السماح للمستخدمين برؤية إجابة المطالبة نفسها من نماذج مختلفة جنباً إلى جنب. أنشئ app/compare/page.tsx:
"use client";
import { useState } from "react";
const COMPARE_MODELS = ["smart", "fast", "reasoning"] as const;
export default function ComparePage() {
const [prompt, setPrompt] = useState("");
const [results, setResults] = useState<Record<string, string>>({});
const [loading, setLoading] = useState(false);
async function runComparison() {
setLoading(true);
setResults({});
await Promise.all(
COMPARE_MODELS.map(async (model) => {
const res = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({
messages: [{ role: "user", content: prompt }],
model,
}),
});
const text = await res.text();
setResults((prev) => ({ ...prev, [model]: text }));
}),
);
setLoading(false);
}
return (
<main className="mx-auto max-w-5xl p-6">
<h1 className="mb-4 text-2xl font-bold">مقارنة النماذج جنباً إلى جنب</h1>
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="أدخل مطالبة لمقارنتها بين النماذج..."
className="mb-4 h-32 w-full rounded border p-3"
/>
<button
onClick={runComparison}
disabled={loading || !prompt}
className="mb-6 rounded bg-black px-6 py-2 text-white disabled:opacity-50"
>
{loading ? "جاري التشغيل..." : "قارن النماذج"}
</button>
<div className="grid gap-4 md:grid-cols-3">
{COMPARE_MODELS.map((model) => (
<div key={model} className="rounded border p-4">
<h3 className="mb-2 font-semibold">{model}</h3>
<div className="whitespace-pre-wrap text-sm">
{results[model] ?? (loading ? "جاري التحميل..." : "")}
</div>
</div>
))}
</div>
</main>
);
}هذا النمط ذو قيمة لا تقدّر في هندسة المطالبات — يمكنك أن ترى فوراً كيف يتعامل Claude مع طلب دقيق مقابل كيفية معالجة Gemini Flash أو o3-mini له.
الخطوة 10: ضبط حدود الإنفاق والتنبيهات
افتح لوحة تحكم AI Gateway في Vercel واضبط:
- الميزانية الشهرية — تتوقف البوابة عن توجيه الطلبات بمجرد بلوغ السقف
- حدود معدل لكل نموذج — للحماية من الحلقات الفارّة أو سوء الاستخدام
- تنبيهات — بريد إلكتروني أو Slack عند تجاوز 50 أو 75 أو 90 بالمائة من الميزانية
هذه الضوابط حاسمة لأي ميزة ذكاء اصطناعي تواجه الجمهور. بدونها يمكن لمستخدم خبيث واحد يملك سكربتاً أن يستنزف آلاف الدولارات من الرموز بين عشية وضحاها.
اختبار التطبيق
تحقق من عمل كل جزء:
- التوجيه الأساسي — تحدّث مع كل نموذج وتأكد من تلقي استجابة
- تجاوز الفشل — اضبط مفتاح API خاطئ مؤقتاً لأحد المزودين في لوحة التحكم، أرسل طلباً، وتأكد أن المزود التالي في السلسلة يخدمه
- التخزين المؤقت — أرسل المطالبة نفسها مرتين وراجع لوحة البوابة لرؤية استدعاء واحد مفوتر وآخر من ذاكرة التخزين
- تتبع المستخدم — أطلق طلبات بترويسات
x-gateway-user-idمختلفة وتأكد من ظهور التقسيم لكل مستخدم - المقارنة — أرسل مطالبة في صفحة المقارنة وتحقق من استجابة النماذج الثلاثة
استكشاف الأخطاء وإصلاحها
أخطاء المصادقة
تأكد من ضبط AI_GATEWAY_API_KEY في .env.local ومن أن المشروع مربوط بمشروع Vercel الذي تم تفعيل البوابة فيه.
النموذج غير موجود
تحقق من معرّف النموذج الدقيق في لوحة تحكم البوابة. بادئات المزودين (anthropic/ و openai/ و google/) حساسة لحالة الأحرف.
البث يتجمد في الإنتاج
تأكد من تصدير runtime = "edge" في مسارك أو ضبط maxDuration على 30 ثانية على الأقل. مهلات الخادم بدون خادم الافتراضية يمكن أن تقطع التوليدات الطويلة.
التخزين المؤقت لا يصيب أبداً يحتاج التخزين الدلالي إلى بضع طلبات ليُحمي فهرس التضمينات. شغّل المطالبة نفسها ثلاث أو أربع مرات قبل الحكم على معدل الإصابة.
الخطوات التالية
- اربط البوابة بمشاريع Vercel AI SDK القائمة لديك — إنها بديل مباشر لاستيرادات المزودين المباشرة
- اقرأ وثائق بوابة Vercel AI للميزات المتقدمة مثل قواعد التوجيه المخصصة
- اجمعها مع Mem0 لبناء مساعدين بحالة دائمة متعددي المزودين
- أضف قابلية رصد Langfuse فوق البوابة للحصول على تتبع على مستوى المطالبة
- اقرنها مع تحديد معدل Arcjet لحماية نقطة نهاية البوابة من سوء الاستخدام
الخلاصة
تحوّل بوابة Vercel AI الذكاء الاصطناعي متعدد المزودين من عبء صيانة إلى تغيير إعداد بسطر واحد. تحصل على الموثوقية وقابلية الرصد والتحكم في التكلفة وحرية تبديل النماذج دون إعادة كتابة الكود. لأي تطبيق Next.js يأخذ الذكاء الاصطناعي بجدية في 2026، هي نقطة البداية الافتراضية — لم يعد هناك سبب يُذكر لاستدعاء حزم تطوير المزودين مباشرة.
النمط الموضح هنا يتسع من مشروع جانبي إلى نظام إنتاج بملايين الطلبات. ابدأ بإعداد التوجيه الأساسي، أضف تجاوز الفشل والتخزين المؤقت مع نمو الحركة، واستخدم وسم المستخدم لإبقاء فريقك المالي راضياً.