انتقل إلى المحتوى الرئيسي

الوحدة 4 — الواجهة البرمجيّة المحلّيّة

سطر الأوامر ممتاز للتجريب، لا لبناء تطبيق. Ollama يعرض واجهة HTTP بسيطة على 11434، مع مكتبة Python رسميّة، وواجهة متوافقة مع واجهة OpenAI تسمح بإعادة استعمال شيفرات موجودة بلا تعديل. هذه الوحدة تربط الطرفيّة بالتطبيق.

نقاط النهاية الأصليّة

POST /api/generate

توليد إتمام من تعليمة واحدة، دون رسائل نظام أو تاريخ محادثة.

curl http://localhost:11434/api/generate -d '{
"model": "qwen2.5:7b-instruct-q4_K_M",
"prompt": "لخِّص في نقطتين: مبدأ حسن النيّة في العقود",
"stream": false,
"options": { "temperature": 0.2, "num_ctx": 4096 }
}'

الجواب يحتوي response (النصّ)، وeval_count، وeval_duration. مع stream: true (وهو الافتراضيّ)، يُعاد تسلسل من كائنات JSON، كلّ واحد يحوي رمزًا أو مجموعة رموز.

POST /api/chat

هذه هي النقطة الرئيسيّة للتطبيقات. تقبل قائمة messages بأدوار system وuser وassistant، فتحفظ تاريخ المحادثة على مسؤوليّة العميل:

{
"model": "qwen2.5:7b-instruct-q4_K_M",
"messages": [
{"role": "system", "content": "أنت مساعد المكتب القانونيّ، تجيب بالفصحى بأسلوب رسميّ."},
{"role": "user", "content": "ما شروط فسخ عقد إيجار قبل نهاية مدّته؟"}
]
}

يمكن أيضًا تمرير أدوات (tools) بمخطّط JSON، فيُعيد النموذج tool_calls جاهزة للتنفيذ. هذه هي البنية التي تستعملها LangChain في الوحدة 8.

POST /api/embeddings

يُنتج متّجه تضمين لنصّ، وهو ما نستعمله في الوحدة 9 لبناء RAG محلّيّ:

curl http://localhost:11434/api/embeddings -d '{
"model": "nomic-embed-text",
"prompt": "شروط فسخ عقد الإيجار التجاريّ"
}'

الجواب {"embedding": [0.021, -0.184, ...]} بطول ثابت (768 لـnomic-embed-text). النموذج المستعمل للتضمين مختلف عن نموذج المحادثة، ويجب أن يكون هو نفسه في الفهرسة وفي الاستفسار.

مكتبة Python الرسميّة

pip install ollama
import ollama

reponse = ollama.chat(
model="qwen2.5:7b-instruct-q4_K_M",
messages=[
{"role": "system", "content": "أنت مساعد المكتب القانونيّ."},
{"role": "user", "content": "صنّف هذه الرسالة: ..."},
],
options={"temperature": 0.0, "num_ctx": 4096},
)
print(reponse["message"]["content"])

للتضمينات:

vec = ollama.embeddings(model="nomic-embed-text", prompt="نصّ")["embedding"]

للبثّ:

for morceau in ollama.chat(model="...", messages=[...], stream=True):
print(morceau["message"]["content"], end="", flush=True)

المكتبة ليست إلّا غلافًا رقيقًا على HTTP، فلا سحر ولا حالة داخليّة: تاريخ المحادثة تحفظه أنت.

التوافق مع واجهة OpenAI

Ollama يعرض واجهة توافق تحت /v1. يعني ذلك أنّ أيّ شيفرة تستعمل مكتبة openai تعمل مع Ollama بلا تعديل يذكر:

from openai import OpenAI

client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama", # يُطلب المفتاح شكليًّا وأيّ سلسلة تكفي
)

reponse = client.chat.completions.create(
model="qwen2.5:7b-instruct-q4_K_M",
messages=[{"role": "user", "content": "لخّص هذه الفقرة: ..."}],
temperature=0.2,
)
print(reponse.choices[0].message.content)

قيمة هذا هائلة: تطبيق كُتِب لـ OpenAI في السحابة يُحوَّل إلى تشغيل محلّيّ بتغيير سطرين. الميزات المدعومة تشمل chat.completions، وembeddings، وtools، وstreaming. الميزات النادرة (Batch API، ملفّات دقيقة) غير مدعومة، لكنّ 95 % من الشيفرة الحقيقيّة لا يستعملها.

البثّ ولمَ يهمّ

من دون بثّ، مستخدم مساعد المكتب ينتظر خمس ثوانٍ صامتًا قبل أن يرى أوّل حرف. مع البثّ، يبدأ الجواب في نصف ثانية، ويقرأه المستخدم بينما يُوَلَّد. الأثر النفسيّ فارق: التطبيق نفسه يُدرَك أسرع مرّتين.

قنوات البثّ تختلف: curl مع --no-buffer، iter_lines في Python، EventSource في المتصفّح، text/event-stream وراء نغن-إكس. اختيار الطبقة الشبكيّة يحدّد كيف تُنقَل الرموز إلى الواجهة الأخيرة.

الأمان على شبكة محلّيّة

بمجرّد أن تفتح OLLAMA_HOST=0.0.0.0، تصير الواجهة متاحة لكلّ من على الشبكة. لا مصادقة داخل Ollama نفسه، فالحلّ يمرّ بوسيط عكسيّ:

[العميل] → HTTPS + رأس Authorization → [Nginx] → HTTP محلّيّ → [Ollama]

يفرض Nginx شهادة TLS ومفتاح API في رأس، ويمرّر الطلب. قاعدة: لا تعرِض Ollama مباشرة على الإنترنت أو حتى على شبكة مؤسّسة كبيرة بلا وسيط. الوحدة 8 تُفصّل ذلك.

الخيط الأحمر

في المكتب، سكربت classer_courrier.py يقرأ صندوق بريد IMAP، يرسل كلّ رسالة إلى POST /api/chat مع تعليمة تصنيف، ويضع الرسالة في المجلّد المناسب. الشيفرة ثلاثون سطرًا، وتعمل بلا اتّصال بالإنترنت لأنّ Ollama يعمل على المحطّة نفسها.

النسخة الأولى استعملت واجهة OpenAI في السحابة؛ التحويل إلى Ollama لم يعدّل الشيفرة، فقط base_url وapi_key وmodel. هذه هي قيمة التوافق: بديل مباشر يسمح باختبار الجدوى قبل الالتزام.

اختر الواجهة بحسب المشروع

شيفرة جديدة داخل المكتب: مكتبة ollama الرسميّة أبسط وأخفّ. شيفرة موجودة من عصر OpenAI: واجهة /v1 تسمح بالانتقال بلا إعادة كتابة. تكامل مع LangChain أو LlamaIndex: تلقائيًّا يمرّ عبر الواجهة الأصليّة أو التوافقيّة بحسب الإصدار (الوحدة 8).

الخلاصة

  • ثلاث نقاط أساسيّة: /api/generate للنداء الواحد، /api/chat للمحادثة والأدوات، /api/embeddings للاسترجاع.
  • مكتبة ollama في Python غلاف رقيق، ولا تحفظ حالة؛ تاريخ المحادثة على عاتق التطبيق.
  • واجهة /v1 متوافقة مع OpenAI فيصير التحويل بين السحابة والمحلّيّ تغييرًا لسطرين.
  • البثّ مطلب تجربة استعمال لا رفاهيّة، وفتح الواجهة على الشبكة يستلزم دائمًا وسيطًا بمصادقة.