نقاط رئيسية
- يعمل ChatOpenAI دون تغيير: اضبط base_url وapi_key وmodel على
uncensored. - تدعم واجهة API استدعاء الدوال، لذا يعمل كل من bind_tools ووكيل مدمج.
- تحديد عدد تكرارات الحلقة والتحقق من صحة حجج الأدوات؛ أحياناً يخطئ نموذج مفتوح الأوزان في هذه الحجج.
- احترم حد 8 طلبات متزامنة للمفتاح الكامل عند تشغيل الوكلاء بالتوازي.
ما تحتاجه قبل البدء
تحتاج إلى Python 3.10 أو أحدث، ومفتاح API، وبضع حزم. يأتي المفتاح من صفحة مفتاح API: سجّل باستخدام Google أو البريد الإلكتروني وكلمة المرور، ثم احصل على رصيد التجربة المجاني بقيمة $0.50 بنقرة واحدة. يسمح مفتاح التجربة بطلبين متزامنين و60 في الدقيقة، وهو كافٍ لكل شيء في هذا المقال إذا قمت بتشغيل وكيل واحد في كل مرة.
pip install langchain-openai langchain-core langgraph
export API_KEY="paste-your-key-here"تُعرّض واجهة API POST /v1/chat/completions و GET /v1/models. يحتاج LangChain فقط إلى الأول. النموذج هو نموذج واحد مفتوح الأوزان مُعدّ للإجابة دون رفض المحتوى للاستخدام القانوني للبالغين؛ وتشغله جهة مستقلة وليس واجهة API الرسمية لأي بائع نماذج. يُدرج الدليل الترويسات (headers) والحدود إذا أردت الاطلاع عليها بجانب محرر النصوص.
كيف تتطابق الأجزاء مع واجهة API
من المفيد معرفة ما يفعله LangChain عبر الشبكة. يتحوّل HumanMessage إلى رسالة ذات دور user. يتحوّل AIMessage إلى assistant، وعندما يحمل استدعاءات الدوال، تظهر هذه الاستدعاءات في رسالة المساعد كمصفوفة tool_calls. يتحوّل ToolMessage إلى دور tool مع معرّف الاستدعاء المطابق. يذهب موجّه النظام أولاً كدور system.
يُعد هذا التحويل مهماً في تصحيح الأخطاء. إذا تصرف الوكيل بشكل غريب، اطبع قائمة الرسائل قبل كل استدعاء واقرأها كنصّ محواري. ستكتشف المشكلة عادةً خلال دقيقة: نتيجة أداة فارغة، أو معرف استدعاء لا يتطابق، أو سجل توسّع أكثر مما توقعت. قد يوحي مصطلح "الوكيل" (agentic) بأن الأمر سحري، لكن كل خطوة هي مجرد طلب إكمال محادثة عادي.
الخطوة 1: توصيل ChatOpenAI
تكتمل عملية التكامل بثلاثة وسائط. تبني LangChain طلبات على طراز OpenAI القياسي، لذا لا يمكن للخادم التمييز بينها.
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.uncensoredvideo.cc/v1",
api_key=os.environ["API_KEY"],
temperature=0.7,
max_retries=2,
)
print(llm.invoke("Name three beats of a heist scene, one line each.").content)إذا طُبعت ثلاثة أسطر، فإن الاتصال يعمل. إذا حصلت على خطأ 401، تحقق من المفتاح. إذا حصلت على خطأ 404، تأكد من أن الرابط ينتهي بـ /v1 ولا يحتوي على مسار إضافي. يُعد max_retries خياراً على جانب العميل، وهو مفيد مع استجابات 429 المؤقتة التي تحمل Retry-After: 5.
يعمل البث المتدفق بنفس الطريقة التي تستخدمها مع أي نموذج OpenAI: استدعاء llm.stream(...) والتكرار. تحمل الجزء الأخير (chunk) بيانات الاستخدام، لذا يمكنك تسجيل عدد الرموز لكل استدعاء.
الخطوة 2: تعريف الأدوات
الوكيل عبارة عن حلقة: يقرأ النموذج المحادثة، إما يجيب أو يطلب أداة، يشغّل الكود الخاص بك الأداة، ويعود الناتج إلى النموذج. الأدوات أدناه هي دوال بايثون عادية. هي متعمدة في بساطتها، لأن الهدف هو رؤية الآليات.
from langchain_core.tools import tool
@tool
def word_count(text: str) -> int:
"""Count the words in a piece of text."""
return len(text.split())
@tool
def spoken_minutes(words: int, words_per_minute: int = 150) -> float:
"""Estimate how many minutes a script takes to read aloud."""
return round(words / words_per_minute, 2)
tools = [word_count, spoken_minutes]تصبح سلسلة docstring وتلميحات الأنواع وصف الأداة ومخططها الذي يراه النموذج. اكتبها كما لو كنت توجّه زميلاً. docstring الغامضة هو السبب الأكثر شيوعاً لاختيار الوكيل للأداة الخاطئة. إذا كنت تكتب نصوصاً للفيديو، فإن نفس النمط يتوسع ليشمل أدوات البحث عن قوائم اللقطات أو التحقق من ميزانية الطول، وهو موضوع مُغطى في صفحة مولّد نصوص Sora 2.
الخطوة 3: تشغيل حلقة الوكيل يدوياً
تخفي الأدوات الجاهزة الحلقة. كتابة واحدة منها بنفسك يجعل العثور على أي خطأ لاحقاً أسهل.
from langchain_core.messages import HumanMessage, ToolMessage
by_name = {t.name: t for t in tools}
llm_tools = llm.bind_tools(tools)
messages = [HumanMessage(
"Here is a draft: 'The vault door hums. Nina counts to three, "
"then cuts the power.' How many words is it, and how long is it read aloud?"
)]
for step in range(6):
ai = llm_tools.invoke(messages)
messages.append(ai)
if not ai.tool_calls:
break
for call in ai.tool_calls:
result = by_name[call["name"]].invoke(call["args"])
messages.append(ToolMessage(content=str(result), tool_call_id=call["id"]))
print(messages[-1].content)ثلاث تفاصيل مهمة. يوقف الحد range(6) النموذج المربك من الدخول في حلقة لا نهائية، كما يحد من إنفاقك. يجب أن يتطابق tool_call_id مع الاستدعاء الذي يجيب عليه. ويمكن للنموذج طلب عدة أدوات في رد واحد، ولهذا السبب تتعامل الحلقة الداخلية مع قائمة.
من الداخل، يحمل الطلب مصفوفة tools وتحمل الاستجابة tool_calls، مع إرجاع النتائج كرسائل تحمل role: "tool". يمكنك إجبار أداة محددة باستخدام tool_choice، وهو ما تعرضه LangChain كوسيط لـ bind_tools.
الخطوة 4: استخدام وكيل LangGraph الجاهز
بمجرد أن تصبح الحلقة اليدوية مفهومة، يوفر الوكيل الجاهز أسطراً من الكود. يستخدم نفس النموذج ونفس الأدوات.
import os
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.uncensoredvideo.cc/v1",
api_key=os.environ["API_KEY"],
)
agent = create_react_agent(llm, tools)
out = agent.invoke({"messages": [("user", "How long is a 450 word script read aloud?")]})
print(out["messages"][-1].content)تغير LangChain وLangGraph واجهات البرمجة الخاصة بها بين الإصدارات، وتمت إعادة تسمية بعض الأدوات أو نقلها. إذا فشل الاستيراد، تحقق من وثائق LangChain الحالية للحصول على منشئ وكيل مكافئ. جزء الاتصال، ChatOpenAI مع Base URL، يبقى كما هو.
الاختيار بين حلقة ورسم بياني (graph)
الحلقة المكتوبة يدوياً كافية للوكلاء الذين يختارون من بين عدد قليل من الأدوات ويتوقفون. هي سهلة القراءة، وسهلة التسجيل، ولا تحتوي على مفاجآت من إطار العمل. انتقل إلى LangGraph عندما تحتاج إلى فروع تعتمد على الحالة: خطوة مراجعة ترسل العمل للمعالجة مرة أخرى، موافقة بشرية قبل تشغيل أداة محفوفة بالمخاطر، أو عدة وكلاء فرعيين متخصصين يتبادلون النتائج.
مهما اخترت، اجعل كل عقدة صغيرة وأعطها مهمة واحدة. عقدة تخطيط تُخرج قائمة مرقمة قصيرة بصيغة JSON، وعقدة تنفيذ تشغل الأدوات، وعقدة كتابة تنتج النص النهائي، أسهل في الاختبار من موجّه واحد ضخم. كما تتيح لك استخدام درجات حرارة مختلفة: منخفضة للتخطيط واستخدام الأدوات، وأعلى لخطوة الكتابة. بما أن كل عقدة هي استدعاء إكمال محادثة عادي، فإن نفس المفتاح والرابط واسم النموذج يخدم جميعها، والتكلفة هي ببساطة مجموع الاستدعاءات.
عادة أخيرة: اطبع إجمالي عدد الرموز قيد التشغيل. قد يبدو الوكلاء رخيصين في عرض تجريبي، لكنهم قد ينفقون عشرة أضعاف المبلغ في مهمة حقيقية فوضوية، وأرقام الاستخدام في كل استجابة تجعل ذلك واضحاً في التشغيل الأول، قبل أن يتحول إلى مفاجأة في رصيدك.
الذاكرة، الاختبار والإخراج المهيكَل
تنسى الحلقة أعلاه كل شيء عند انتهاء السكربت. أبسط ذاكرة هي قائمة الرسائل نفسها: احتفظ بها بين أدوار المستخدم، وأضف كل سؤال جديد، وقص الرسائل الأقدم عندما تصبح طويلة. للجلسات الطويلة، استبدل الرسائل القديمة بملخص قصير يكتبه النموذج. بما أن الإدخال تُحتسب تكلفته بحسب الرمز، فإن ملخص بـ 500 رمز يتفوق على 20,000 رمز من السجل الخام من حيث التكلفة والتركيز.
اختبار الوكلاء يتعلّق أساساً بفحص السلوك القابل للملاحظة. اضبط درجة الحرارة على 0 أو مرّر seed ثابتاً، الذي تمرّره واجهة برمجة التطبيقات كما هي، لتصبح التشغيلات أكثر قابلية للتكرار. ثم اكتب مجموعة من الأسئلة التي تعرف فيها أي أداة يجب استدعاؤها، وتحقق (assert) من أسماء الأدوات والحجج في قائمة الرسائل، وليس من الصياغة الدقيقة للإجابة. شغّل هذه الاختبارات بعد كل تغيير في الموجّه أو وصف الأداة. تغيير docstring يمكن أن يغيّر الأداة التي يختارها النموذج، والاختبار هو أرخص طريقة لملاحظة ذلك.
سجّل ثلاثة أشياء لكل تشغيل: عدد الخطوات، استدعاءات الأدوات مع حججها، واستخدام الرموز. عندما يفشل تشغيل، تخبرك هذه الأسطر الثلاثة ما إذا كان النموذج قد اختار بشكل سيء، أو فشلت أداة، أو انتهت خطوات الحلقة.
الإخراج المهيكَل ووضع JSON
غالباً ما تحتاج الوكلاء إلى إرجاع بيانات، وليس نصوصاً. تدعم واجهة برمجة التطبيقات وضع JSON من خلال response_format: {"type":"json_object"}. في LangChain يمكنك طلب ذلك بـ llm.bind(response_format={"type": "json_object"}). أخبر النموذج في الموجّه أن الإجابة يجب أن تكون كائن JSON واذكر المفاتيح التي تتوقعها، ثم قم بتحليل النص والتحقق منه باستخدام Pydantic. لا تفترض أن خطوة التحليل ستنجح دائماً؛ لفّها في إعادة محاولة (retry) ترسل نص الخطأ إلى النموذج مرة واحدة.
المزالق وكيفية إصلاحها
- حجج الأداة غير صحيحة. أحيانًا يرسل نموذج مفتوح الأوزان نصًا حيث حددت عددًا صحيحًا. تحقق من الحجج، وأرسل رسالة خطأ قصيرة من الأداة، واسمح للنموذج بتصحيح نفسه في الخطوة التالية.
- الحلقات غير المنضبطة. حدد دائمًا حدًا للعدد الأقصى للخطوات. سجّل كل خطوة أثناء التطوير.
- الوكلاء المتوازيون. المفتاح الكامل يسمح بـ 8 طلبات متزامنة. إذا كنت تشغل دفعة، اضبط
config={"max_concurrency": 4}علىllm.batchلترك هامش أمان. تجاوز الحد يعيد خطأ 429 مع الرمزconcurrency؛ تجاوز 300 طلب في الدقيقة يعيدrate_limited. - البث المتدفق مع الأدوات. يحتفظ الطلب المتدفق بخانة التزامن الخاصة به حتى ينتهي البث، لذا لا تبدأ استدعاءات جديدة من داخل معالج البث باستخدام مفتاح تجريبي.
- توسع السجل. يعيد إرسال قائمة الرسائل الكاملة في كل خطوة. نافذة السياق هي 100,000 رمز (token)، لكنك تدفع مقابل المدخلات في كل استدعاء.
تكلفة تشغيل الوكيل
خذ وكيلاً متواضعاً يقوم بـ 4 استدعاءات للنموذج لإنهاء مهمة، بموجّه متوسط 3,000 رمز و200 رمز مخرج لكل استدعاء. المدخلات: 4 × 3,000 = 12,000 رمز، أي 12,000 × $0.15 / 1,000,000 = $0.0018. المخرجات: 4 × 200 = 800 رمز، أي $0.0004. تكلفة تشغيل واحد هي $0.0022، لذا فإن 1,000 تشغيل تكلّف $2.20. رصيد التجربة بقيمة $0.50 يغطي حوالي 227 تشغيلاً من هذا النوع. الطلبات الفاشلة أو المرفوضة لا تُحسب، ولا ينتهي الرصيد. انظر الأسعار لمعرفة مبالغ الشحن والمكافآت. يقبل هذا الموقع العملات المشفرة فقط، USDT على TRC20 أو USDC على Base.
