Ollama: الممارسة الشاملة للمرحلة 3
الممارسة الشاملة للمرحلة 3 هي القفزة من "الميزات الفردية" إلى "المستوى النظامي" — نشر المكدس الكامل، بناء خط الأنابيب، وتنسيق النماذج في خطوة واحدة.
💡 نصيحة: الممارسة المتقدمة تدمج مهارات الدروس الـ 17 السابقة — نشر دوكر (د15) + خط أنابيب RAG (د14) + توجيه النماذج المتعددة (د17) + اختيار التقدير الكمي (د16) + تنسيق LangChain (د13). اتبع تسلسل "انشر أولاً → ابنِ خط الأنابيب → أضف التوجيه" للتكامل التدريجي، بدلاً من محاولة بناء كل شيء دفعة واحدة.
📋 المتطلبات المسبقة: يجب أن تتقن أولاً ما يلي
- الدرس 1: مفاهيم الذكاء الاصطناعي المحلي والبيئة
- الدرس 2: تثبيت Ollama وإعداد البيئة
- الدرس 3: أساسيات CLI
- الدرس 4: إدارة النماذج
- الدرس 5: أساسيات REST API
- الدرس 6: الممارسة الشاملة للمرحلة 1
- الدرس 7: تكامل Python SDK
- الدرس 8: نماذج Modelfile المخصصة
- الدرس 9: إعدادات GPU وCUDA
- الدرس 10: النماذج متعددة الوسائط
- الدرس 11: API المتوافق مع OpenAI
- الدرس 12: الممارسة الشاملة للمرحلة 2
- الدرس 13: تكامل LangChain
- الدرس 14: خط أنابيب RAG
- الدرس 15: النشر الحاوي باستخدام دوكر
- الدرس 16: تقدير كمي النموذج والتحسين
- الدرس 17: تنسيق النماذج المتعددة
1. ما ستتعلمه
- نشر Docker Compose لمكدس Ollama + Chroma + WebUI الكامل
- بناء خط أنابيب RAG ومقارنة نماذج التضمين
- تنفيذ توجيه نماذج متعددة لتقسيم الحركة التلقائي
- تجارب مقارنة جودة النماذج المقدرة كميًا
- تخطيط بنية SupportBot النهائية
2. قصة حقيقية من رائدة أعمال SaaS
💡 نصيحة: بعد الانتهاء من البنية، استخدم "جدول مصفوفة القدرات" (نموذج × سيناريو × مقاييس) لتوثيق مبرر الاختيار وأساس الأداء لكل مكون. هذا يوفر إمكانية التتبع للتحسين المستقبلي أو تغيير النماذج، ويساعد في شرح القرارات التقنية للفريق.
ℹ️ معلومة: الممارسة الشاملة للمرحلة 3 هي متطلب سابق مباشر للمرحلة 4 (الأمان/المراقبة/نشر الإنتاج). بعد إكمال هذه الممارسة، سيكون لديك SupportBot V4 يعمل (نشر دوكر + RAG + توجيه نماذج متعددة). المرحلة 4 ستعززه وتحسنه أكثر.
(1) المشكلة: القدرات المنفصلة لا تعمل معًا
تعلمت Alice تكامل LangChain وRAG ودوكر والتقدير الكمي والتنسيق، لكن المهارات متفرقة. تحتاج إلى دمجها في بنية SupportBot موحدة وتحديد الاختيار التقني النهائي وخطة النشر.
(2) الحل: تحديد البنية النهائية
flowchart TD
A[استعلام المستخدم] --> B[الموجه<br/>مصنف 3B]
B -->|بسيط| C[أسئلة شائعة RAG<br/>3B + Chroma]
B -->|معقد| D[إجابة عميقة<br/>8B qwen2.5]
C --> E[الاستجابة]
D --> E
E --> F[نشر دوكر<br/>Ollama + FastAPI + Chroma]
3. تمرين 1: نشر المكدس الكامل باستخدام Docker Compose
⚠️ ملاحظة: نشر المكدس الكامل بـ Docker Compose يتضمن 4+ خدمات وتمرير GPU، مما قد يسبب مشاكل متنوعة عند البدء الأول. اختبر كل خدمة على حدة أولاً (ابدأ بـ
docker run ollama، ثم أضف chroma، ثم أضف app)، واستكشاف الأخطاء تدريجيًا، بدلاً من تشغيل docker compose up دفعة واحدة ومواجهة كومة من الأخطاء.
⚠️ تحذير: نشر المكدس الكامل بـ Docker Compose يتضمن 4+ خدمات وتمرير GPU. البدء الأول قد يواجه مشاكل متنوعة. اختبر كل خدمة على حدة أولاً (ابدأ بـ
docker run ollama، ثم أضف chroma، ثم أضف app)، واستكشاف الأخطاء تدريجيًا بدلاً من تشغيل docker compose up دفعة واحدة.
(1) بنية المكدس الكامل
| الخدمة | الصورة | المنفذ | المسؤولية |
|---|---|---|---|
| ollama | ollama/ollama | 11434 | استنتاج النموذج |
| chroma | chromadb/chroma | 8001 | التخزين المتجهي |
| webui | open-webui | 3000 | واجهة الويب |
| app | fastapi مخصص | 8000 | المنطق التجاري |
(1) ▶ مثال: نشر المكدس الكامل والتحقق
YAML
# docker-compose.phase3.yml
version: "3.8"
services:
ollama:
image: ollama/ollama
ports: ["11434:11434"]
volumes: ["ollama_data:/root/.ollama"]
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:11434/api/tags"]
interval: 30s
retries: 5
chroma:
image: chromadb/chroma
ports: ["8001:8000"]
volumes: ["chroma_data:/chroma/chroma"]
environment: [ANONYMIZED_TELEMETRY=FALSE]
webui:
image: ghcr.io/open-webui/open-webui:main
ports: ["3000:8080"]
environment: [OLLAMA_BASE_URL=http://ollama:11434]
volumes: ["webui_data:/app/backend/data"]
depends_on:
ollama: {condition: service_healthy}
volumes:
ollama_data:
chroma_data:
webui_data:
BASH
# Deploy and verify
docker compose -f docker-compose.phase3.yml up -d
# Pull models
docker exec ollama ollama pull qwen2.5
docker exec ollama ollama pull nomic-embed-text
docker exec ollama ollama pull llama3.2:3b
# Verify all services
docker compose -f docker-compose.phase3.yml ps
curl http://localhost:11434/api/tags # Ollama
curl http://localhost:8001/api/v1/heartbeat # Chroma
curl http://localhost:3000 # WebUI
الإخراج:
TEXT
Full-stack deployment verified successfully, all services running normally
4. تمرين 2: خط أنابيب RAG ومقارنة التضمين
(1) أبعاد مقارنة نموذج التضمين
| البُعد | nomic-embed-text | mxbai-embed-large | all-minilm |
|---|---|---|---|
| الأبعاد | 768 | 1024 | 384 |
| الحجم | 274 ميجابايت | 670 ميجابايت | 46 ميجابايت |
| جودة الإنجليزية | عالية | الأعلى | متوسطة |
| جودة الصينية | متوسطة | متوسطة-عالية | منخفضة |
| السرعة | سريعة | متوسطة | سريعة جدًا |
(2) ▶ مثال: مقارنة تأثير نموذج التضمين
PYTHON
from langchain_ollama import OllamaEmbeddings
from langchain_community.vectorstores import Chroma
import numpy as np
def compare_embeddings(query: str, docs: list[str],
models: list[str]) -> dict:
"""Compare retrieval results across embedding models."""
results = {}
for model_name in models:
embeddings = OllamaEmbeddings(model=model_name)
vectorstore = Chroma.from_texts(
texts=docs, embedding=embeddings,
collection_name=f"test_{model_name.replace('-','_')}"
)
retrieved = vectorstore.similarity_search_with_score(query, k=3)
results[model_name] = [
{"text": doc.page_content[:80], "score": round(score, 4)}
for doc, score in retrieved
]
return results
# Test documents
docs = [
"Return policy: 30 days from purchase, original condition required.",
"Shipping: Free for orders over $50, 3-5 business days delivery.",
"Warranty: 1-year manufacturer warranty for all electronics.",
"International shipping available to 50+ countries with import duties.",
"Refund process: 5-7 business days after return received.",
"Product exchange: Available within 14 days for different size/color."
]
# Compare
results = compare_embeddings(
query="How do I get my money back?",
docs=docs,
models=["nomic-embed-text", "all-minilm"]
)
for model, hits in results.items():
print(f"\n{model}:")
for hit in hits:
print(f" [{hit['score']}] {hit['text']}")
الإخراج:
TEXT
# Function defined successfully
5. تمرين 3: توجيه النماذج المتعددة
(1) تنفيذ التوجيه الكامل
flowchart TD
A[سؤال العميل] --> B[مصنف النية<br/>llama3.2:3b]
B --> C{فئة النية}
C -->|أسئلة شائعة/شحن/طلب| D[خط أنابيب أسئلة شائعة RAG<br/>3B + Chroma]
C -->|منتج/مقارنة| E[إجابة عميقة<br/>8B qwen2.5]
C -->|شكوى| F[استجابة متعاطفة<br/>8B qwen2.5 + موجه خاص]
D --> G[الاستجابة]
E --> G
F --> G
(3) ▶ مثال: توجيه كامل + RAG
PYTHON
import ollama
import json
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
class SmartRouter:
def __init__(self, chroma_path: str = "./chroma_kb"):
self.classifier = ChatOllama(model="llama3.2:3b", temperature=0)
self.small_model = ChatOllama(model="llama3.2:3b", temperature=0.3)
self.large_model = ChatOllama(model="qwen2.5", temperature=0.4)
self.embeddings = OllamaEmbeddings(model="nomic-embed-text")
try:
self.vectorstore = Chroma(
persist_directory=chroma_path,
embedding_function=self.embeddings
)
self.retriever = self.vectorstore.as_retriever(search_kwargs={"k": 3})
except Exception:
self.retriever = None
def classify(self, question: str) -> str:
response = self.classifier.invoke(
f"Classify as one word: faq, product, comparison, complaint, other.\nQuestion: {question}"
)
return response.content.strip().lower()
def answer(self, question: str) -> dict:
intent = self.classify(question)
if intent in ("faq", "other") and self.retriever:
# RAG pipeline for FAQ
docs = self.retriever.invoke(question)
context = "\n\n".join(d.page_content for d in docs)
response = self.small_model.invoke(
f"Context:\n{context}\n\nAnswer based on context: {question}"
)
model_used = "3B + RAG"
elif intent == "complaint":
response = self.large_model.invoke(
f"You are an empathetic customer service agent. "
f"Show understanding and offer solutions:\n{question}"
)
model_used = "8B (empathetic)"
else:
response = self.large_model.invoke(question)
model_used = "8B"
return {
"intent": intent,
"model": model_used,
"answer": response.content
}
# Usage
router = SmartRouter()
for q in ["What is the return policy?", "Compare 3 headphone models", "My order arrived damaged!"]:
result = router.answer(q)
print(f"[{result['intent']}|{result['model']}] {result['answer'][:80]}...")
الإخراج:
TEXT
# Function defined successfully
6. تجربة التقدير الكمي
(1) تصميم التجربة
| المجموعة | النموذج | التقدير الكمي | الحجم | الجودة المتوقعة |
|---|---|---|---|---|
| A | llama3.2:3b | Q4_K_M | ~2 جيجابايت | أساس |
| B | qwen2.5 | Q4_K_M | ~5 جيجابايت | أعلى |
| C | qwen2.5 | Q8_0 | ~8 جيجابايت | الأعلى |
(4) ▶ مثال: اختبار مقارنة التقدير الكمي
PYTHON
import ollama
import time
def quantization_benchmark(test_questions: list[str], models: list[str]):
"""Compare response quality across quantizations."""
results = {}
for model in models:
model_results = []
# Warm up
ollama.chat(model=model, messages=[{"role": "user", "content": "hi"}],
stream=False)
for q in test_questions:
start = time.time()
resp = ollama.chat(
model=model,
messages=[{"role": "user", "content": q}],
stream=False,
options={"temperature": 0.3}
)
elapsed = time.time() - start
model_results.append({
"question": q,
"answer": resp["message"]["content"][:200],
"latency_s": round(elapsed, 2)
})
results[model] = model_results
return results
# Run if models are available
# questions = [
# "What is the return policy for electronics?",
# "Translate to French: Free shipping on orders over $50",
# "Write a SQL query for top 5 customers"
# ]
# results = quantization_benchmark(questions, ["llama3.2:3b", "qwen2.5"])
الإخراج:
TEXT
# Function defined successfully
7. مثال شامل: تخطيط بنية SupportBot النهائية
PYTHON
# ============================================
# Comprehensive: SupportBot final architecture
# Integrates all Phase 3 capabilities
# ============================================
from dataclasses import dataclass, field
from typing import Optional
import json
@dataclass
class SupportBotArchitecture:
"""Final SupportBot architecture specification."""
name: str = "SupportBot V4"
version: str = "4.0"
components: dict = field(default_factory=lambda: {
"intent_classifier": {
"model": "llama3.2:3b",
"quantization": "Q4_K_M",
"purpose": "Classify customer intent in <1s",
"vram": "~2GB"
},
"faq_rag": {
"model": "llama3.2:3b",
"embedding": "nomic-embed-text",
"vector_db": "Chroma",
"purpose": "Answer FAQ from knowledge base",
"vram": "~2.3GB shared"
},
"deep_answer": {
"model": "qwen2.5:7b",
"quantization": "Q4_K_M",
"purpose": "Complex product questions and comparisons",
"vram": "~5GB"
},
"complaint_handler": {
"model": "qwen2.5:7b",
"system_prompt": "empathetic mode",
"purpose": "Handle complaints with empathy",
"vram": "shared with deep_answer"
},
"multimodal": {
"model": "llava",
"purpose": "Damage report image analysis",
"vram": "~5GB (on-demand)"
}
})
deployment: dict = field(default_factory=lambda: {
"platform": "Docker Compose",
"services": ["ollama", "chroma", "fastapi", "nginx"],
"vram_total": "8GB minimum, 12GB recommended",
"persistence": "Named volumes for models and Chroma"
})
routing_rules: dict = field(default_factory=lambda: {
"faq": "faq_rag (3B + RAG)",
"order_status": "faq_rag (3B + RAG)",
"shipping": "faq_rag (3B + RAG)",
"product_info": "deep_answer (8B)",
"comparison": "deep_answer (8B)",
"complaint": "complaint_handler (8B empathetic)",
"damage_report": "multimodal (llava)"
})
cost_analysis: dict = field(default_factory=lambda: {
"before_gpt4": "2,000 USD/month",
"after_local": "98 USD/month (hardware amortization + electricity)",
"saving": "1,902 USD/month (95% reduction)",
"payback_months": 1.1
})
def to_report(self) -> str:
"""Generate architecture report."""
report = [f"# {self.name} v{self.version} Architecture\n"]
report.append("## Components")
for name, spec in self.components.items():
report.append(f"- {name}: {spec['model']} - {spec['purpose']}")
report.append("\n## Deployment")
report.append(f"- Platform: {self.deployment['platform']}")
report.append(f"- Services: {', '.join(self.deployment['services'])}")
report.append(f"- VRAM: {self.deployment['vram_total']}")
report.append("\n## Routing Rules")
for intent, target in self.routing_rules.items():
report.append(f"- {intent} → {target}")
report.append("\n## Cost Analysis")
for key, value in self.cost_analysis.items():
report.append(f"- {key}: {value}")
return "\n".join(report)
# Generate report
arch = SupportBotArchitecture()
print(arch.to_report())
❓ أسئلة شائعة
س ما هي متطلبات الأجهزة لنشر المكدس الكامل بـ Docker؟
ج الحد الأدنى 8 جيجابايت VRAM + 16 جيجابايت RAM + 50 جيجابايت قرص. يُوصى بـ 12 جيجابايت VRAM + 32 جيجابايت RAM. وضع وحدة المعالجة المركزية يتطلب 32 جيجابايت RAM.
س كيف أحسّن جودة استرجاع خط أنابيب RAG؟
ج 1) استراتيجية تقسيم أفضل (الحفاظ على الحدود الدلالية)؛ 2) نموذج تضمين أفضل (mxbai-embed-large)؛ 3) تغطية مستندات أكبر؛ 4) بحث MMR للتنوع.
س ماذا لو كانت دقة تصنيف التوجيه ليست عالية بما يكفي؟
ج أضف قواعد تصفية مسبقة بالكلمات المفتاحية لتقليل عبء النموذج. استخدم أمثلة Few-shot لتحسين اتساق التصنيف. فكر في ترقية المصنف إلى نموذج 8B.
س كم يستغرق اختبار مقارنة التقدير الكمي؟
ج 3 نماذج × 3 أسئلة × 3 مرات ≈ حوالي 15 دقيقة. تأكد من الإحماء أولاً لتجنب تحيز البدء البارد.
س ماذا لو كانت بنية SupportBot النهائية تحتاج تغييرات؟
ج البنية كائن حي. المرحلة 4 ستعدل بناءً على الأداء ومتطلبات الأمان أكثر. المشروع العملي في المرحلة 5 سيتحقق من البنية ويحسنها.
س ما المعارف المطلوبة مسبقًا للمرحلة 4؟
ج أساسيات تشغيل خوادم Linux، وكيل Nginx العكسي، مفاهيم مراقبة Prometheus/Grafana. الدروس 19-22 ستغطي كل واحد بالتفصيل.
📖 ملخص
- نشر المكدس الكامل بـ Docker Compose: Ollama + Chroma + WebUI + FastAPI
- مقارنة التضمين: nomic-embed-text لأفضل قيمة عامة، mxbai-embed-large للدقة العالية
- مجموعة التوجيه + RAG: الأسئلة الشائعة تمر عبر مسار RAG السريع، الأسئلة المعقدة تذهب للإجابات العميقة 8B
- تجربة التقدير الكمي تتحقق: فقدان جودة Q4_K_M < 3% في سيناريوهات خدمة العملاء
- بنية SupportBot النهائية: مصنف + أسئلة شائعة RAG + إجابة عميقة + معالجة المشاعر + متعدد الوسائط
- جميع مهارات المرحلة 3 مدمجة في بنية موحدة؛ المرحلة 4 تدخل مرحلة تحسين التشغيل
📝 تمارين
- أساسي (⭐): انشر مكدس Ollama + Chroma + WebUI الكامل باستخدام Docker Compose، وتحقق من أن الخدمات الثلاث تعمل بشكل طبيعي.
- متوسط (⭐⭐): ابنِ خط أنابيب RAG، وقارن جودة الاسترجاع بنموذجي تضمين، وسجل معدلات إصابة Top-3.
- متقدم (⭐⭐⭐): نفّذ بنية SupportBot كاملة — توجيه + RAG + إجابة عميقة، وأخرج وثيقة تصميم البنية وتقرير اختبار الأداء.