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

الوحدة 9 — قابليّة المراقبة وتسجيل القرارات

الوكيل صندوق أسود بطبعه: مسار مختلف كلّ تشغيل، حجم رسائل يتغيّر، أدوات تُستدعى بترتيب لا يُتنبَّأ به. قابليّة المراقبة (observability) هي ما يُحوّل هذا الصندوق إلى نظام مفهوم. سنبني ثلاث طبقات: أثر لكلّ تشغيل، مؤشّرات للتجميع، وإعادة تشغيل للتصحيح.

الأثر: البنية الأصلب

أثر واحد لكلّ تشغيل، بصيغة JSON، يحوي:

{
"id_execution": "exe_20260906_142301_abc",
"question": "هل يدعم Postgres 16 التقسيم المنطقيّ؟",
"debut": "2026-09-06T14:23:01Z",
"fin": "2026-09-06T14:23:19Z",
"duree_ms": 18320,
"statut": "succes",
"raison_arret": "reponse_naturelle",
"budget_utilise": {"input_tokens": 6432, "output_tokens": 812, "cout_usd": 0.087},
"etapes": [
{
"n": 1,
"type": "outil",
"nom": "recherche_web",
"arguments": {"requete": "Postgres 16 logical partitioning"},
"duree_ms": 830,
"resultat_len": 512,
"erreur": null
},
{"n": 2, "type": "outil", "nom": "lire_page", "arguments": {"url": "..."}, "...": "..."}
]
}

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

المؤشّرات: من الأثر إلى القرار

نجمع الآثار في تجميع يوميّ، ونعرض ثلاث لوحات:

  • معدّل النجاح (%): نسبة الأثر التي انتهت بجواب طبيعيّ (لا بحدّ تكرار، لا بميزانيّة، لا بخطأ).
  • الكلفة اليوميّة ($): مقسَّمة على النموذج (مخطِّط، منفِّذ، متحقِّق) وعلى الأداة.
  • زمن الاستجابة p95 (ms): إذا تجاوز خمس ثوانٍ باستمرار، الوكيل لم يعد قابلًا للاستخدام التفاعليّ.

تنبيهات: كلفة تتضاعف في يوم، زمن p95 يقفز 30 ٪، معدّل نجاح ينحدر تحت 80 ٪. كلّ تنبيه يُشير إلى استفسار عيّنة يُفتَح للتحقيق.

كلفة كلّ خطوة: قياس منفصل

الكلفة الإجماليّة لا تكفي: يجب تفكيكها. عمود «كلفة الأداة» في الأثر يُتيح:

  • ترتيب الأدوات من الأغلى إلى الأرخص. lire_page غالبًا الأغلى بسبب حجم الملاحظة.
  • كشف الأدوات التي تُستدعى كثيرًا بلا فائدة (خطوات ملاحظتها لا تُغيّر السلوك اللاحق).
  • تحسين الوصف بعد رؤية أنّ أداة تُختار خطأً في نصف الأحيان.

قياس على 100 تشغيل من وكيل البحث: recherche_web 0.008 دولار متوسّط، lire_page 0.031، base_interne 0.004، noter_fait 0.001. lire_page وحدها 47 ٪ من الكلفة. تحسين الوصف والاقتطاع خفّضها 35 ٪.

إعادة التشغيل للتصحيح

عند حادث، نريد إعادة تشغيل الوكيل من خطوة معيّنة. LangGraph يُقدِّم هذا عبر checkpointer: كلّ حالة عقدة تُخزَّن، ويمكن استئناف من أيّها. في تنفيذ يدويّ، نُخزِّن قائمة الرسائل بعد كلّ خطوة في قاعدة SQLite.

def checkpoint(exe_id, n_etape, messages):
db.execute("INSERT INTO checkpoints VALUES (?, ?, ?)",
(exe_id, n_etape, json.dumps(messages, ensure_ascii=False)))

def reprendre(exe_id, n_etape):
row = db.query_one("SELECT etat FROM checkpoints WHERE exe_id=? AND n=?", (exe_id, n_etape))
return json.loads(row["etat"])

فائدة عمليّة: مطوّر يرى في التاسعة صباحًا أنّ الوكيل فشل ليلة أمس عند الخطوة الرابعة بسبب أداة معطَّلة. الأداة أُصلحت. بدل تشغيل الوكيل من الصفر (كلفة كاملة، زمن كامل، ربّما نتيجة مختلفة)، يُعيد التشغيل من الخطوة الرابعة بحالة الأمس.

مقارنة مع LangGraph

كتبنا الوكيل ببايثون خام لفهم آليّته. LangGraph يوفّر:

  • رسم صريح يُعرَض بصريًّا (لوحة LangSmith).
  • حالة مُهيكلة بـTypedDict، لا قاموس رسائل حرّ.
  • نقاط استئناف جاهزة، لا SQL يدويّ.
  • interrupt_before جاهز لتأكيد بشريّ (الوحدة 7).

كلفة LangGraph: تعلّم إطار، تبعيّة إضافيّة، صياغة أقلّ شفافيّة للمبتدئ. القرار: تنفيذ يدويّ للفهم والنماذج البسيطة (خمس أدوات، خطّة خطّيّة)؛ LangGraph حين البنية تتعقّد (تفريع مشروط، توازٍ، نقاط استئناف كثيرة).

OpenTelemetry: التسجيل المفتوح

بدل الالتزام بلوحة مزوّد (LangSmith، Langfuse)، ننشئ آثارًا معياريّة بـOpenTelemetry:

from opentelemetry import trace
tracer = trace.get_tracer("agent-veille")

with tracer.start_as_current_span("agent.execution") as span:
span.set_attribute("question", question)
for etape in etapes:
with tracer.start_as_current_span(f"outil.{etape.nom}") as s:
s.set_attribute("arguments", json.dumps(etape.args))
resultat = executer(etape.nom, etape.args)
s.set_attribute("resultat.taille", len(str(resultat)))

الآثار تُوجَّه إلى أيّ نظام (Datadog، Grafana Tempo، Jaeger). فائدة: نفس بنية التسجيل مع بقيّة الخدمات، لا صندوق أدوات منفصل للذكاء الاصطناعيّ.

الخيط الأحمر: لوحة وكيل البحث

مساعد البحث الوثائقيّ الآن يعمل بلوحة تعرض:

  • 30 تشغيلًا في الأسبوع الماضي.
  • معدّل نجاح 87 ٪، ست حالات وصلت حدّ التكرار، حالة واحدة تجاوزت الميزانيّة.
  • كلفة أسبوعيّة 3.42 دولارًا، منها 46 ٪ على lire_page و29 ٪ على المخطِّط.
  • زمن استجابة p95: 21 ثانية، p50: 14 ثانية.

لوحة أسبوعيّة نصف ساعة: نراجع خمس آثار عشوائيّة، والحالات الفاشلة، والأسئلة التي شكاها المستخدمون. نُحدِّث كتالوج الأعطال (الوحدة 8) عند الحاجة.

الفخّ: التسجيل بعد الحادث

فرق كثيرة تُضيف التسجيل بعد حادث. النتيجة: ثلاث ساعات من العمل لإعادة بناء ما وقع، بلا يقين. قاعدة: التسجيل جزء من الوكيل، ليس ميزة إضافيّة. حتّى وكيل النمط الأوّل يجب أن يُسجّل خطواته منذ اليوم الأوّل.

فخّ آخر: تسجيل يحوي بيانات حسّاسة (كلمات مرور، بيانات شخصيّة). نُصفّي وسائط الأدوات قبل التسجيل: قائمة سوداء من مفاتيح («password», «token», «...»), قيمها تُستبدَل بـ***.

اجعل الأثر أوّل ما تكتب

قبل شيفرة الحلقة، قبل تعريف الأدوات، اكتب دالّة sauvegarder_trace(execution). هذا التسلسل يُغيّر شيئًا: كلّ خطوة تُصاغ لتُسجَّل. الوكلاء التي تُبنى بهذا الترتيب تُصحَّح في دقائق، لا ساعات.

الخلاصة

  • أثر كامل لكلّ تشغيل بصيغة JSON: الوسائط، والملاحظات، وسبب التوقّف، والكلفة.
  • ثلاث لوحات إلزاميّة: معدّل نجاح، كلفة يوميّة، زمن p95؛ مع تنبيهات على التغيّرات الحادّة.
  • إعادة التشغيل من نقطة استئناف يوفّر كلفة إعادة كاملة عند إصلاح أداة معطَّلة.
  • LangGraph يوفّر رسمًا صريحًا ونقاط استئناف جاهزة؛ يبرَّر حين البنية تتعقّد، لا للأعمال البسيطة.