Ollama: تنسيق النماذج المتعددة
تنسيق النماذج المتعددة هو عمل فريق الذكاء الاصطناعي — النماذج الصغيرة تقود الهجوم، النماذج الكبيرة توفر العمود الفقري، كل يؤدي دوره.
💡 نصيحة: جوهر استراتيجية توجيه النماذج المتعددة هو "التوزيع حسب المهمة" — الأسئلة الشائعة البسيطة تستخدم نموذجًا صغيرًا 3B (استجابة في ثانية واحدة، استهلاك GPU منخفض)، الاستنتاج المعقد يستخدم نموذجًا كبيرًا 8B (استجابة في 5 ثوانٍ، دقة عالية). يمكنك أيضًا "التوزيع حسب الحمل" — استخدم النماذج الصغيرة في ساعات الذروة للتعامل مع المزيد من الطلبات، وانتقل إلى النماذج الكبيرة في فترات الانخفاض للحصول على جودة أعلى. توجيه 80% من الطلبات إلى النماذج الصغيرة يمكن أن يقلل زمن الانتقال المتوسط بنسبة 50%.
📋 المتطلبات المسبقة: يجب أن تتقن أولاً ما يلي
- الدرس 13: تكامل LangChain
1. ما ستتعلمه
- أنماط بنية النماذج المتعددة: التوجيه / التسلسل / التصويت المتوازي
- نمط التوجيه: نموذج صغير يصنف → نموذج كبير يجيب بعمق
- نمط التسلسل: مسودة أولية → تحسين → تنسيق خط الأنابيب
- Python asyncio للاستدعاءات المتزامنة لعدة مثيلات Ollama
- SupportBot V4 الخاص بـ Alice: توجيه قائم على تقسيم الحركة
2. قصة حقيقية من رائدة أعمال SaaS
ℹ️ معلومة: في تنسيق النماذج المتعددة، يجب تحميل كل نموذج بشكل مستقل في VRAM. نموذجان (3B + 8B) يقيمان في وقت واحد يتطلبان حوالي 10 جيجابايت VRAM. إذا كان VRAM غير كافٍ، يفرغ Ollama النماذج غير النشطة تلقائيًا، لكن التبديل يتكبد تأخير إعادة تحميل 5-30 ثانية.
(1) المشكلة: النماذج الكبيرة تهدر الموارد في الأسئلة البسيطة
وجدت Alice أن 80% من أسئلة SupportBot ("ما هي سياسة الإرجاع؟" "كيف أتحقق من الشحن؟") يمكن الإجابة عليها بنموذج صغير، لكن توجيه كل شيء عبر نموذج كبير يهدر موارد GPU وهو أبطأ.
(2) الحل: نمط التوجيه لتقسيم الحركة
نموذج صغير (3B) يصنف النية في ثانية واحدة؛ الأسئلة البسيطة تحصل على إجابات مباشرة، والأسئلة المعقدة تُوجّه إلى نموذج كبير (8B):
PYTHON
# Router pattern: small model classifies, large model handles complex
intent = small_model.classify(question) # 1 second
if intent == "simple":
answer = small_model.answer(question) # 2 seconds
else:
answer = large_model.answer(question) # 5 seconds
3. ثلاثة أنماط تنسيق
⚠️ ملاحظة: نمط التصويت المتوازي لديه أعلى موثوقية لكن أيضًا أعلى تكلفة — كل طلب يتطلب استدعاء 3-5 نماذج، مما يضاعف حساب GPU. استخدمه فقط لسيناريوهات القرارات الحرجة (مثل المشورة الطبية، الأحكام القانونية)؛ نمط التوجيه يكفي لخدمة العملاء اليومية.
💡 نصيحة: نمط التوجيه هو أنجح طريقة تنسيق من حيث التكلفة — 80% من الطلبات البسيطة تستخدم نموذج 3B (استجابة في ثانية واحدة)، و20% فقط من الطلبات المعقدة تُوجّه إلى نموذج 8B. متوسط زمن الاستجابة الكلي ينخفض من 5 ثوانٍ إلى ثانيتين، مع انخفاض استخدام GPU بنسبة 60%.
(1) مقارنة الأنماط
| النمط | التدفق | حالة الاستخدام | زمن الانتقال | التكلفة |
|---|---|---|---|---|
| التوجيه | تصنيف → توزيع | توجيه خدمة العملاء، تصنيف المهام | منخفض | منخفض |
| التسلسل | مسودة → تحسين → تنسيق | توليد الأكواد، إنشاء المحتوى | متوسط | متوسط |
| التصويت المتوازي | نماذج متعددة → تصويت/دمج | قرارات عالية الموثوقية | مرتفع | مرتفع |
flowchart TD
subgraph Router
R1[الإدخال] --> R2[المصنف<br/>نموذج 3B]
R2 -->|بسيط| R3[إجابة النموذج الصغير]
R2 -->|معقد| R4[إجابة النموذج الكبير]
end
subgraph Cascade
C1[الإدخال] --> C2[مسودة<br/>نموذج 3B]
C2 --> C3[تحسين<br/>نموذج 8B]
C3 --> C4[تنسيق<br/>نموذج 3B]
end
subgraph Voting
V1[الإدخال] --> V2[النموذج A]
V1 --> V3[النموذج B]
V1 --> V4[النموذج C]
V2 --> V5[تصويت الأغلبية]
V3 --> V5
V4 --> V5
end
4. نمط التوجيه
(1) بنية التوجيه
sequenceDiagram
participant U as المستخدم
participant C as المصنف (3B)
participant S as النموذج الصغير (3B)
participant L as النموذج الكبير (8B)
U->>C: "أريد استرداد أموالي"
C-->>C: النية: استرداد (بسيط)
C->>S: توجيه إلى النموذج الصغير
S-->>U: إجابة سياسة الاسترداد
U->>C: "قارن شروط الضمان لـ 3 منتجات"
C-->>C: النية: مقارنة (معقد)
C->>L: توجيه إلى النموذج الكبير
L-->>U: مقارنة تفصيلية
(2) تصميم وسوم تصنيف النية
| الوسم | التعقيد | هدف التوجيه | سؤال مثال |
|---|---|---|---|
| faq | بسيط | النموذج الصغير | "ما هي سياسة الإرجاع؟" |
| order_status | بسيط | النموذج الصغير | "أين الطلب #12345؟" |
| product_info | متوسط | النموذج المتوسط | "هل تدعم سماعات الرأس هذه Bluetooth 5.3؟" |
| comparison | معقد | النموذج الكبير | "قارن بين 3 موديلات سماعات رأس" |
| complaint | معقد | النموذج الكبير | "المنتج الذي استلمته مختلف تمامًا عن الوصف" |
(1) ▶ مثال: تنفيذ نمط التوجيه
PYTHON
import ollama
import json
from dataclasses import dataclass
from typing import Optional
@dataclass
class ModelRouter:
classifier_model: str = "llama3.2:3b"
small_model: str = "llama3.2:3b"
large_model: str = "qwen2.5"
INTENTS = {
"faq": "simple",
"order_status": "simple",
"product_info": "medium",
"comparison": "complex",
"complaint": "complex"
}
def classify(self, question: str) -> dict:
response = ollama.chat(
model=self.classifier_model,
messages=[{
"role": "user",
"content": f"""Classify this customer question.
Return JSON: {{"intent": "faq|order_status|product_info|comparison|complaint", "complexity": "simple|medium|complex"}}
Question: {question}"""
}],
format="json",
stream=False,
options={"temperature": 0.1}
)
return json.loads(response["message"]["content"])
def route(self, question: str, system: str = "") -> str:
intent_data = self.classify(question)
complexity = intent_data.get("complexity", "simple")
model = self.small_model if complexity == "simple" else self.large_model
messages = []
if system:
messages.append({"role": "system", "content": system})
messages.append({"role": "user", "content": question})
response = ollama.chat(model=model, messages=messages,
stream=False, options={"temperature": 0.3})
return response["message"]["content"], model, intent_data
# Usage
router = ModelRouter()
questions = [
"What is the return policy?",
"Compare the 3 wireless headphones you sell",
"Where is order #88765?"
]
for q in questions:
answer, model_used, intent = router.route(q, system="You are SupportBot.")
print(f"Q: {q}")
print(f"Intent: {intent}, Model: {model_used}")
print(f"A: {answer[:100]}...\n")
الإخراج:
TEXT
# Function defined successfully
5. نمط التسلسل
(1) تفاصيل بنية التسلسل
| المرحلة | النموذج | المهمة | المعاملات |
|---|---|---|---|
| المسودة | 3B | مسودة سريعة | temperature=0.5 |
| التحسين | 8B | تحسين عميق | temperature=0.3 |
| التنسيق | 3B | تنسيق الإخراج | temperature=0.1 |
(2) ▶ مثال: توليد محتوى متسلسل
PYTHON
import ollama
class CascadePipeline:
def __init__(self):
self.draft_model = "llama3.2:3b"
self.refine_model = "qwen2.5"
self.format_model = "llama3.2:3b"
def generate(self, topic: str) -> dict:
# Stage 1: Draft
draft = ollama.chat(
model=self.draft_model,
messages=[{"role": "user",
"content": f"Write a brief draft about: {topic}"}],
stream=False, options={"temperature": 0.5}
)["message"]["content"]
# Stage 2: Refine
refined = ollama.chat(
model=self.refine_model,
messages=[
{"role": "system", "content": "Improve the following text. Add details and fix errors."},
{"role": "user", "content": draft}
],
stream=False, options={"temperature": 0.3}
)["message"]["content"]
# Stage 3: Format
formatted = ollama.chat(
model=self.format_model,
messages=[
{"role": "system", "content": "Format this text as a professional product description with bullet points."},
{"role": "user", "content": refined}
],
stream=False, options={"temperature": 0.1}
)["message"]["content"]
return {"draft": draft, "refined": refined, "formatted": formatted}
pipeline = CascadePipeline()
result = pipeline.generate("wireless noise-cancelling headphones")
print("=== Final Output ===")
print(result["formatted"])
الإخراج:
TEXT
=== Final Output ===
6. التصويت المتوازي وasyncio المتزامن
(1) بنية التصويت المتوازي
| النمط | عدد النماذج | طريقة القرار | حالة الاستخدام |
|---|---|---|---|
| تصويت الأغلبية | 3+ | أخذ إجابة إجماع الأغلبية | أحكام واقعية |
| متوسط مرجح | 2+ | ترجيح حسب جودة النموذج | مهام التسجيل |
| أفضل اختيار | 2+ | اختيار الأكثر تفصيلًا/مصداقية | أسئلة مفتوحة |
(3) ▶ مثال: استدعاء asyncio المتزامن
PYTHON
import asyncio
import ollama
from dataclasses import dataclass
@dataclass
class ParallelVoter:
models: list[str] = None
def __post_init__(self):
if self.models is None:
self.models = ["llama3.2:3b", "qwen2.5", "mistral"]
async def query_model(self, model: str, question: str) -> dict:
client = ollama.AsyncClient()
response = await client.chat(
model=model,
messages=[{"role": "user", "content": question}],
stream=False,
options={"temperature": 0.3}
)
return {"model": model, "answer": response["message"]["content"]}
async def vote(self, question: str) -> dict:
tasks = [self.query_model(m, question) for m in self.models]
results = await asyncio.gather(*tasks)
# Simple voting: check for consensus
answers = [r["answer"] for r in results]
return {
"question": question,
"results": results,
"consensus": len(set(a[:50] for a in answers)) == 1
}
# Usage
async def main():
voter = ParallelVoter()
result = await voter.vote("Is 2+2 equal to 4?")
for r in result["results"]:
print(f"{r['model']}: {r['answer'][:80]}")
asyncio.run(main())
الإخراج:
TEXT
# Function defined successfully
(4) ▶ مثال: المجموعة المرجحة
PYTHON
import ollama
def weighted_ensemble(question: str, models: list[dict]) -> str:
"""Query multiple models and select the best response."""
responses = []
for m in models:
resp = ollama.chat(
model=m["name"],
messages=[{"role": "user", "content": question}],
stream=False,
options={"temperature": m.get("temperature", 0.3)}
)
responses.append({
"model": m["name"],
"answer": resp["message"]["content"],
"weight": m.get("weight", 1.0),
"length": len(resp["message"]["content"])
})
# Select longest response from highest-weight model
responses.sort(key=lambda x: x["weight"] * x["length"], reverse=True)
return responses[0]["answer"]
models = [
{"name": "qwen2.5", "weight": 1.5, "temperature": 0.3},
{"name": "llama3.2:3b", "weight": 0.8, "temperature": 0.5},
]
result = weighted_ensemble("Explain the benefits of local AI", models)
print(result)
الإخراج:
TEXT
# Function defined successfully
7. مثال شامل: نظام توجيه SupportBot V4
PYTHON
# ============================================
# Comprehensive: SupportBot V4 with Router
# Multi-model routing for e-commerce support
# ============================================
import ollama
import json
import time
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class SupportBotV4:
classifier: str = "llama3.2:3b"
simple_model: str = "llama3.2:3b"
complex_model: str = "qwen2.5"
system_prompt: str = "You are SupportBot for GlobalShop e-commerce. Be polite and concise."
INTENT_MAP: dict = field(default_factory=lambda: {
"faq": "simple",
"order_status": "simple",
"shipping": "simple",
"product_info": "complex",
"comparison": "complex",
"complaint": "complex",
"refund": "complex"
})
def classify_intent(self, question: str) -> tuple[str, str]:
"""Classify intent and determine complexity."""
response = ollama.chat(
model=self.classifier,
messages=[{
"role": "user",
"content": f"""Classify intent (one word): {question}
Categories: faq, order_status, shipping, product_info, comparison, complaint, refund
Reply with just the category word."""
}],
stream=False,
options={"temperature": 0.0}
)
intent = response["message"]["content"].strip().lower()
for key in self.INTENT_MAP:
if key in intent:
return key, self.INTENT_MAP[key]
return "faq", "simple"
def answer(self, question: str) -> dict:
"""Route question and generate answer."""
start = time.time()
intent, complexity = self.classify_intent(question)
model = self.simple_model if complexity == "simple" else self.complex_model
response = ollama.chat(
model=model,
messages=[
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": question}
],
stream=False,
options={"temperature": 0.3 if complexity == "simple" else 0.4}
)
elapsed = time.time() - start
return {
"question": question,
"intent": intent,
"complexity": complexity,
"model_used": model,
"answer": response["message"]["content"],
"latency_s": round(elapsed, 2)
}
def batch_process(self, questions: list[str]) -> list[dict]:
"""Process multiple questions and show routing stats."""
results = [self.answer(q) for q in questions]
simple_count = sum(1 for r in results if r["complexity"] == "simple")
avg_simple = sum(r["latency_s"] for r in results if r["complexity"] == "simple") / max(simple_count, 1)
complex_count = len(results) - simple_count
avg_complex = sum(r["latency_s"] for r in results if r["complexity"] == "complex") / max(complex_count, 1)
print(f"=== Routing Stats ===")
print(f"Simple: {simple_count}/{len(results)} (avg {avg_simple:.1f}s)")
print(f"Complex: {complex_count}/{len(results)} (avg {avg_complex:.1f}s)")
print(f"Cost saving: ~{simple_count * 60}% of queries use small model")
return results
# Usage
if __name__ == "__main__":
bot = SupportBotV4()
questions = [
"What is the return policy?",
"Where is my order #88765?",
"Do you ship internationally?",
"Compare the 3 headphone models in detail",
"I received a completely wrong item, this is unacceptable!",
"What are the warranty terms for electronics?",
"Analyze which laptop is best for a graphic designer under $1000",
]
results = bot.batch_process(questions)
for r in results:
print(f"\n[{r['complexity'].upper()}|{r['model_used']}] {r['intent']}")
print(f" Q: {r['question']}")
print(f" A: {r['answer'][:100]}... ({r['latency_s']}s)")
❓ أسئلة شائعة
س ماذا لو كان تصنيف التوجيه غير دقيق؟
ج استخدم مصنفًا أفضل. النماذج الصغيرة (3B) لديها دقة تصنيف حوالي 85%، ونماذج 8B حوالي 92%. يمكنك أيضًا التصفية المسبقة بمطابقة الكلمات المفتاحية، وإترك للنموذج التعامل فقط مع الحالات الغامضة.
س هل نمط التسلسل أفضل من استخدام نموذج كبير مباشرة؟
ج يعتمد على السيناريو. إجمالي زمن انتقال التسلسل = مجموع جميع المراحل، لكن كل مرحلة أسرع. مناسب للسيناريوهات التي تحتاج مسودة + تحسين (الكتابة، الأكواد). الأسئلة والأجوبة البسيطة لا تحتاج تسلسل.
س هل التصويت المتوازي يستهلك 3 أضعاف موارد GPU؟
ج نعم. 3 نماذج تعمل استنتاجًا في وقت واحد تحتاج 3 أضعاف VRAM. يمكنك استدعاء 3 نماذج بالتسلسل لتجنب OOM، لكنك تفقد ميزة التوازي.
س هل يوجد فرق سرعة كبير بين asyncio المتزامن والاستدعاءات المتسلسلة؟
ج يعتمد على GPU. مع GPU واحد، يصطف Ollama داخليًا، لذا التزامن لا يساعد. مع وحدات GPU متعددة أو مزيج وحدة معالجة مركزية+GPU، يمكن لـ asyncio تشغيل نماذج مختلفة بشكل متزامن.
س كيف أقيّم فعالية تقسيم الحركة للتوجيه؟
ج تتبع نسبة التوجيه، زمن الانتقال لكل فئة، ورضا المستخدم. الهدف: 80% من الطلبات إلى النماذج الصغيرة، انخفاض زمن انتقال متوسط بنسبة 50%+، بدون انخفاض في الرضا.
س هل يجب أن تستخدم النماذج الصغيرة والكبيرة نفس موجه النظام؟
ج يُوصى بالحفاظ على الاتساق لتجربة مستخدم موحدة. لكن يمكنك الضبط الدقيق لمستويات تعقيد مختلفة — موجه النموذج الكبير يمكن أن يكون أكثر تفصيلًا.
📖 ملخص
- ثلاثة أنماط تنسيق: التوجيه (تقسيم الحركة)، التسلسل (خط أنابيب)، التصويت المتوازي (موثوقية عالية)
- نمط التوجيه: تصنيف 3B + إجابات عميقة 8B، 80% من الطلبات إلى النموذج الصغير، زمن انتقال متوسط مخفض بنسبة 50%
- نمط التسلسل: مسودة(3B) → تحسين(8B) → تنسيق(3B)، مناسب لإنشاء المحتوى
- asyncio للاستدعاءات المتزامنة متعددة النماذج؛ انتبه للصف على GPU واحد
- يستخدم SupportBot V4 تقسيمًا قائمًا على التوجيه: ثانيتان للأسئلة البسيطة، 5 ثوانٍ للمعقدة
- قيّم فعالية التوجيه بنسبة التوجيه، تحسين زمن الانتقال، ورضا المستخدم
📝 تمارين
- أساسي (⭐): نفّذ موجه ثنائي بسيط — الأسئلة الشائعة تذهب للنموذج الصغير، وكل شيء آخر للنموذج الكبير. اختبر بـ 5 أسئلة.
- متوسط (⭐⭐): نفّذ خط أنابيب تسلسلي ثلاثي المراحل — مسودة → تحسين → تنسيق. قارن الجودة مع الإخراج المباشر بنموذج واحد.
- متقدم (⭐⭐⭐): نفّذ نظام توجيه كامل لـ SupportBot V4، يتضمن تصنيف النية، إحصائيات التوجيه، مراقبة زمن الانتقال، وإخراج تقرير فعالية التوجيه.