SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor

استضافة Langfuse ذاتياً لتتبّع وكلاء الذكاء الاصطناعي

شغّل Langfuse على VPS تملكه: تعرّف إلى الحد الأدنى الحقيقي للموارد، وثبّت وسوم الصور، واضبط TLS واحتفاظ ClickHouse والنسخ الاحتياطية قبل امتلاء القرص.

لماذا تراقب وكيل الذكاء الاصطناعي أصلاً

تستضيف Langfuse ذاتياً لمعرفة ما فعله وكيلك فعلياً أثناء إحدى عمليات التشغيل. Langfuse أداة مفتوحة المصدر لمراقبة LLM (نموذج اللغة الكبير). تسجل كل prompt، وكل استجابة من النموذج، وكل استدعاء لأداة، وكل token، ثم تجمعها ضمن trace واحد يمكنك فتحه وقراءته. وعند تشغيلها على VPS تملكه، لا تغادر تلك الـprompts خادماً تتحكم فيه.

السبب واضح. لا يمكنك إصلاح مشكلة في التكلفة أو الجودة إذا كنت لا تراها. تُظهر لك فاتورة المزوّد أن تكلفة يوم الثلاثاء كانت أربعة أضعاف تكلفة يوم الاثنين. أما trace فيُظهر لك أي عملية تشغيل للوكيل سببت ذلك، وأي prompt نما إلى 40,000 token، وأي حلقة retry تكررت تسع مرات قبل أن تتوقف. تمنحك الفاتورة الرقم. ويمنحك trace الشيفرة التي أنتجته.

تُستخدم ثلاثة مصطلحات في هذا الدليل. trace هو عملية تشغيل واحدة للوكيل من البداية إلى النهاية. وobservation هي خطوة واحدة داخل تلك العملية: span للشيفرة العادية، وgeneration لاستدعاء نموذج. أما score فهو رقم مرتبط بـtrace، مصدره مراجعة بشرية أو أداة تقييم آلية. يستخدم Langfuse معيار OpenTelemetry (OTel)، وهو معيار محايد تجاه المورّد لتتبّع الأنظمة الموزعة، لذلك يمكن للتجهيزات التي لديك مسبقاً توجيه البيانات إليه.

ما الذي يشغّله الاستضافة الذاتية لـLangfuse فعلياً

Langfuse v4 ليس حاوية واحدة. يتكوّن من حاويتي تطبيق وأربع خدمات تخزين، وتعمل الخدمات الست كلها على خادم VPS واحد.

  • langfuse-web يقدّم واجهة الويب وواجهة ingestion API.
  • langfuse-worker يفرّغ قائمة الانتظار في الخلفية. يحلّل دفعات ingestion، ويحسب التكلفة، وينفّذ مهمة الاحتفاظ الليلية.
  • يحتفظ Postgres بالبيانات المعاملاتية، مثل المستخدمين والمؤسسات والمشاريع ومفاتيح API وprompts.
  • يحتفظ ClickHouse ببيانات التتبّع نفسها، أي observations وscores. وهو مخزن أعمدة مصمم للاستعلامات التحليلية، لذلك تستجيب لوحة المعلومات بسرعة حتى عند التعامل مع أكثر من مئة مليون صف.
  • يوفّر Redis قائمة الانتظار وذاكرة التخزين المؤقت بين web وworker.
  • يوفّر MinIO تخزين كائنات متوافقاً مع S3 على الخادم نفسه. ويحتفظ بكل event خام وارد، إضافة إلى أي وسائط ترفقها.

تنشر Langfuse الحد الأدنى من الموارد لكل مكوّن تشغّله. يوضّح المخطط المكوّنات الثلاثة التي تنفّذ العمل.

رسم بيانيLangfuse published minimum resources per component
البيانات وراء هذا الرسم البياني
[
  {
    "label": "ClickHouse",
    "cpu_cores": 2,
    "memory_gib": 8
  },
  {
    "label": "Langfuse web",
    "cpu_cores": 2,
    "memory_gib": 4
  },
  {
    "label": "Langfuse worker",
    "cpu_cores": 2,
    "memory_gib": 4
  }
]

يحتاج ClickHouse وحده إلى 8 GiB من الذاكرة. وتحتاج حاوية web وحاوية worker إلى 4 GiB لكل منهما. هذه هي الحدود الدنيا المنشورة للمكوّنات 3 الظاهرة في المخطط. وتذكر الصفحة نفسها أن Postgres يحتاج إلى 4 GiB، وأن Redis أو Valkey يحتاج إلى 1.5 GiB، وأن MinIO يحتاج إلى 4 GiB، ولذلك يبلغ الحد الأدنى للمكوّنات الستة مجتمعة 25.5 GiB. يوصي دليل Docker Compose الخاص بالمشروع بما لا يقل عن 4 أنوية و16 GiB من الذاكرة ونحو 100 GiB من التخزين لجهاز واحد. وهذا أقل من مجموع الحدود الدنيا للمكوّنات، لذلك اعتبر 16 GiB الحد الأدنى الذي يحتاج إليه خادم Compose مشترك، وليس مساحة احتياطية.

لا تحاول تشغيل ذلك على خطة بسعة 2 GiB. يبدأ ClickHouse، ويقبل عمليات الكتابة لبعض الوقت، ثم يتوقف أثناء عملية merge في الخلفية، لأن merge يحمّل أجزاء كبيرة من جدول إلى الذاكرة. سترى docker compose ps يعرض حاوية clickhouse بالحالة restarting، وستحمل dmesg سطراً مثل Out of memory: Killed process 1234 (clickhouse-serv)، وستُرجع كل لوحات Langfuse الحالة 500. وعند وجود ضغط أخف، يرفض ClickHouse الاستعلام بدلاً من ذلك ويسجّل DB::Exception: Memory limit (total) exceeded. خطّط على أساس 16 GiB. وإذا كان الخادم VPS نفسه سيشغّل أي شيء آخر، فاحجز موارده بصورة مستقلة، لأن حزمة خفيفة نسبياً مثل مساحة عمل AFFiNE مستضافة ذاتياً تحتاج إلى GiB خاصة بها، ولن يعيد ClickHouse أي موارد.

نشر Langfuse باستخدام Docker Compose

استنسخ المستودع. توجد الحزمة، وربط الخدمات، وبيئة التشغيل الافتراضية كلها في ملف docker-compose.yml.

git clone https://github.com/langfuse/langfuse.git
cd langfuse

كل قيمة يجب تغييرها مميّزة بالعلامة # CHANGEME في ذلك الملف. أنشئ أسرار التطبيقات الثلاثة أولاً.

openssl rand -base64 32   # NEXTAUTH_SECRET
openssl rand -base64 32   # SALT
openssl rand -hex 32      # ENCRYPTION_KEY

يجب أن يكون ENCRYPTION_KEY بطول 256 بت، مكتوباً على شكل 64 محرفاً سداسياً عشرياً. وهذا ما يطبعه openssl rand -hex 32 تحديداً. يُستخدم لتشفير القيم الحساسة أثناء التخزين، بما في ذلك مفاتيح موفّري LLM التي تخزنها في المثيل. إذا غيّرته بعد وجود البيانات، فلن يعود من الممكن فك تشفير تلك الصفوف. لذلك اعتبره قيمة دائمة منذ الإقلاع الأول. يُستخدم SALT لتجزئة مفاتيح Langfuse API، لذا يؤدي تغييره إلى إبطال كل مفتاح تستخدمه وكلاؤك حالياً.

بعد ذلك اضبط POSTGRES_PASSWORD وCLICKHOUSE_PASSWORD وREDIS_AUTH وMINIO_ROOT_PASSWORD. تظهر كلمة مرور MinIO في أربعة مواضع: أولاً بصفتها MINIO_ROOT_PASSWORD، ثم مرة أخرى بصفتها LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY وLANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY وLANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY. إذا أغفلت أحدها، فسيرفض MinIO ذلك العميل مع SignatureDoesNotMatch. ويظهر هذا الخطأ في سجل العامل، بينما تبدو واجهة الويب سليمة. إن الاحتفاظ بهذه القيم في ملف env بدلاً من ملف Compose المتتبَّع هو النمط المشروح في ملفات env والأسرار في Docker Compose.

ثبّت وسوم الصور قبل البدء

يستخدم الملف المرفق docker.langfuse.com/langfuse/langfuse:4 وdocker.langfuse.com/langfuse/langfuse-worker:4. هذه الوسوم تتغير. يشغّل Langfuse عمليات ترحيل Postgres وClickHouse تلقائياً عند البدء. لذلك يتحول تنفيذ docker compose pull اعتيادي بعد أشهر إلى ترحيل مخطط غير مخطط له على قاعدة بيانات لم تنشئ لها نسخة احتياطية في ذلك الصباح. ثبّت كليهما على إصدار واحد في ملف docker-compose.override.yml. يدمج Compose هذا الملف فوق الملف المرفق، ولذلك لا يتعارض git pull لاحقاً مع تعديلاتك. تُنشر الصور نفسها على Docker Hub، ومنه يسحبها ملف التجاوز أدناه.

services:
  langfuse-web:
    image: docker.io/langfuse/langfuse:4.72.0
  langfuse-worker:
    image: docker.io/langfuse/langfuse-worker:4.72.0

كان الإصدار 4.3.1 هو إصدار 4.3 الحالي حتى August 2026. تسارعت الإصدارات منذ ذلك الحين، وكان v4.56.0 أحدث إصدار على GitHub حتى October 11, 2026. يستخدم ملف التجاوز أعلاه v4.72.0، وهو أحدث إصدار على GitHub حتى October 31, 2026. راجع صفحة إصدارات المشروع على GitHub، وثبّت الإصدار الحالي في يوم النشر، ثم غيّر هذا الرقم عن قصد. تستحق بعض صور التخزين في الملف المرفق المعاملة نفسها، لأن وسومها تتغير أيضاً. يتبع postgres:17، وهو القيمة الافتراضية للمتغير POSTGRES_VERSION، وredis:7 إصداراً رئيسياً. يستخدم الملف المرفق الآن وسم إصدار دقيقاً لـdocker.io/clickhouse/clickhouse-server، وبذلك يثبّت إصدار ClickHouse محدداً. لا تقتصر هذه القاعدة على Langfuse: يستخدم متتبّع تمارين openGym مستضافاً ذاتياً جزءاً من هذه الحزمة، ومع ذلك يحتاج إلى وسم git محدد، لأن أي برنامج يرحّل قاعدة بياناته عند البدء قد يحوّل عملية سحب اعتيادية إلى تغيير في المخطط.

شغّله.

docker compose up -d
docker compose ps
docker compose logs -f langfuse-worker

يشغّل الإقلاع الأول عمليات الترحيل، لذلك انتظر دقيقتين أو ثلاث دقائق قبل اختبار الاستجابة. يسجّل حاوي الويب Ready بعد أن يصبح جاهزاً. يجب أن يعرض docker compose ps ست خدمات بحالة running. إذا أعيد تشغيل حاوية في حلقة، فسيوضح سجلها السبب. تُطبَّق عمليات ترحيل ClickHouse عند بدء حاوي الويب، لذلك يظهر CLICKHOUSE_MIGRATION_URL غير الصحيح في docker compose logs langfuse-web، وليس في سجل العامل الذي تراقبه. يستخدم ذلك المتغير البروتوكول الأصلي لـClickHouse على المنفذ 9000، وليس منفذ HTTP 8123. ويؤدي توجيهه إلى 8123 إلى فشل عمليات ترحيل حاوي الويب عند بدء التشغيل. يشغّل العامل عمليات الترحيل الأبطأ في الخلفية، ولذلك يظهر تقريرها في سجله.

تحقق من الصحة من الخادم نفسه.

curl -s "http://localhost:3000/api/public/health?failIfDatabaseUnavailable=true"
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/api/public/ready

يثبت استدعاء /api/public/health العادي أن عملية API تعمل فقط، لأنه لا يتحقق من قاعدة البيانات افتراضياً. أما صيغة failIfDatabaseUnavailable=true فهي المناسبة لربطها بأداة مراقبة، وتعيد 503 عندما يتعذر الوصول إلى قاعدة البيانات. تعيد /api/public/ready الرمز 200 عندما تكون حاوية الويب جاهزة لاستقبال حركة الشبكة، وتعيد 500 بعد إصدار أمر إيقافها. كلاهما فحص HTTP عادي، لذلك يستطيع صفحة حالة Uptime Kuma مراقبتهما وإبلاغك بتوقف الحزمة قبل أن يكتشف وكلاؤك ذلك.

ضع TLS أمام الخدمة وأغلق المنافذ الإضافية

ينشر ملف Compose المرفق 3000:3000 لحاوية الويب و9090:9000 لـMinIO. ويرتبط كلاهما بكل الواجهات. عند استخدام عنوان IP عاماً، يعني ذلك أن أي شخص يفحص المنفذ 3000 سيصل إلى صفحة التسجيل، وأي شخص يفحص المنفذ 9090 سيتصل بالحاوية التي تحتوي على مطالباتك الأولية.

لا تكفي قاعدة جدار ناري وحدها لإغلاقهما. يكتب Docker قواعد DNAT الخاصة به في جدول nat، وتُقيَّم هذه القواعد قبل أن ترى قواعد التصفية في ufw الحزمة أصلاً، لذلك تُبقي ufw deny 3000 المنفذ المنشور مفتوحاً. هذه مشكلة شائعة بما يكفي لتخصيص دليل لها: لماذا تتجاوز المنافذ التي ينشرها Docker قواعد ufw. اربط المنافذ بواجهة loopback في ملف التجاوز بدلاً من ذلك. استخدم !override مع Docker Compose 2.24.4 أو إصدار أحدث لاستبدال المنافذ المنشورة؛ إذ تدمج القائمة العادية الارتباطات الأصلية.

services:
  langfuse-web:
    ports: !override
      - "127.0.0.1:3000:3000"
    environment:
      NEXTAUTH_URL: https://langfuse.example.com
  minio:
    ports: !override
      - "127.0.0.1:9090:9000"
      - "127.0.0.1:9091:9001"

يجب أن تكون NEXTAUTH_URL العنوان العام الدقيق، بما في ذلك المخطط، لأن تدفق تسجيل الدخول ينشئ عنوان URL لإعادة الاستدعاء من هذه القيمة. إذا أبقيتها على http://localhost:3000 خلف وكيل HTTPS، فستوجّه دورة تسجيل الدخول المتصفح إلى مكان لا يمكنه الوصول إليه.

وجّه الآن reverse proxy إلى 127.0.0.1:3000 ودعه يتولى الشهادة. عند استخدام Traefik في مشروع Compose نفسه، وجّه الطلبات إلى langfuse-web:3000 عبر شبكة Compose باستخدام الملصقات الموضحة في تشغيل عدة تطبيقات خلف reverse proxy واحد من Traefik. ينفذ Caddy المهمة نفسها في سطرين إذا كان Langfuse هو التطبيق الوحيد على الخادم. تحقّق باستخدام curl -sI https://langfuse.example.com/api/public/ready، ثم تأكد من جهاز ثانٍ من أن curl http://YOUR_IP:3000 لا يمكنه الاتصال.

هناك ملاحظة مهمة بشأن MinIO. يرسل Langfuse عناوين URL موقعة مسبقاً تشير إلى نقطة نهاية S3 هذه، سواء إلى حزم SDK التي ترفع الوسائط أو إلى المتصفح الذي يحمّلها. لذلك، إذا كنت تستخدم آثاراً متعددة الوسائط تتضمن صوراً أو ملفات صوتية، فلن يتيح MinIO المرتبط بواجهة loopback فقط رفع تلك المرفقات من أجهزة أخرى أو تحميلها في متصفحك. اقرأ صفحة إعداد تخزين الكائنات قبل وضعه خلف proxy، لأن نقطة النهاية المكتوبة في عنوان URL الموقّع مسبقاً، LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT، يجب أن تطابق ما تنشره. ولا تتأثر الآثار النصية العادية بذلك.

أنشئ حسابك عند الزيارة الأولى، ثم احرص على بقاء المثيل تحت سيطرتك. عيّن AUTH_DISABLE_SIGNUP=true بعد إنشاء حسابك، حتى لا يتمكن أي شخص يصل إلى الصفحة من التسجيل. يتضمن Langfuse أيضاً LANGFUSE_ALLOWED_ORGANIZATION_CREATORS، الذي يحدّد من يمكنه إنشاء المؤسسات، لكنه ميزة في Enterprise Edition ولا يعمل إلا عند توفير مفتاح ترخيص. إذا كنت تشغّل Authentik كموفر هوية خاص بك، فإن Langfuse يقبل اتصال OIDC قياسياً، ولذلك تُدار الحسابات مع بقية تطبيقاتك بدلاً من تخزينها في قائمة كلمات مرور لا يعرفها إلا هذا الخادم.

أرسل أول trace

أنشئ مشروعاً في واجهة الويب، وانسخ public key وsecret key من إعدادات المشروع. يقرأ Python SDK ثلاثة متغيرات بيئية.

export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
export LANGFUSE_BASE_URL="https://langfuse.example.com"

LANGFUSE_BASE_URL هو اسم المتغير في SDK v4، الذي صدر في March 2026. تستخدم الشيفرة والأدلة الأقدم LANGFUSE_HOST. إذا وصلت traces إلى Langfuse Cloud بدلاً من خادمك، فالسبب هو أن عنوان base URL غير مضبوط، لأن القيمة الافتراضية تشير إلى النسخة المستضافة.

يتطلب Python SDK v5 الحالي Python 3.11 أو إصداراً أحدث، ويتطلب أيضاً Langfuse v4.59.0 أو إصداراً أحدث في بيئة self-hosted. يفي الخادم المثبت بالإصدار المحدد أعلاه بهذا الشرط.

pip install langfuse opentelemetry-instrumentation-anthropic anthropic
import os
from anthropic import Anthropic
from langfuse import get_client, observe
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor

AnthropicInstrumentor().instrument()
langfuse = get_client()
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

@observe(as_type="tool")
def lookup_order(order_id: str) -> str:
    return f"order {order_id}: shipped"

@observe()
def handle_request(question: str) -> str:
    context = lookup_order("A-1042")
    message = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=512,
        messages=[{"role": "user", "content": f"{context}\n\n{question}"}],
    )
    return message.content[0].text

if __name__ == "__main__":
    assert langfuse.auth_check()
    print(handle_request("Where is my order?"))
    langfuse.flush()

يفتح decorator @observe observation حول الدالة، ويلتقط وسيطاتها وقيمتها المعادة، ويضعها داخل observation النشط حالياً. أما AnthropicInstrumentor فهو instrumentation الخاص بـ OpenTelemetry لعميل Anthropic، ويحوّل كل استدعاء messages.create إلى generation يتضمن اسم النموذج، واستخدام الرموز، ومدة الاستجابة، من دون تغيير موضع الاستدعاء.

تتولى عمليتان التحقق نيابة عنك. تعيد langfuse.auth_check() القيمة False عند استخدام مفاتيح غير صحيحة أو base URL خاطئ، وهذا أسرع من محاولة معرفة سبب فراغ لوحة المعلومات. أما langfuse.flush() فتنتظر حتى تُرسل spans الموجودة في قائمة الانتظار. تحتاج إليها العمليات القصيرة، لأن SDK يجمع البيانات في الخلفية، والسكريبت الذي ينتهي فوراً يأخذ معه الدفعة التي لم تُرسل. إذا كان فريق كامل يرسل traces بدلاً من سكريبت واحد، فاضبط هذه المتغيرات الثلاثة مرة واحدة في الـgateway الذي تمر عبره agents لدى الجميع، بدلاً من ضبطها في shell الخاص بكل شخص. بهذه الطريقة يحافظ نظام OneCLI مستضاف ذاتياً على instrumented runs الخاصة بكل زميل، بينما تبقى المفاتيح في مكان واحد.

لماذا يستمر ClickHouse في النمو؟

تُعدّ آثار التنفيذ من أسرع أنواع البيانات نمواً في معظم عمليات الاستضافة الذاتية. يكتب كل تشغيل للوكيل صفاً واحداً لكل خطوة، وتُخزَّن المدخلات والمخرجات كاملةً، لذلك ينتج الوكيل كثير الكلام ذي المطالبات الطويلة بايتات يومية أكثر بكثير من التطبيق الذي يراقبه. إذا تُرك الأمر دون معالجة، سيمتلئ القرص بـClickHouse، وامتلاء القرص يوقف الإدخال بدلاً من إبطائه.

هناك شيئان منفصلان ينموان هنا، ولكل منهما إصلاح مختلف.

الأول هو بيانات الآثار الخاصة بك، وإصلاحه هو إعداد الاحتفاظ. في النسخة المستضافة ذاتياً، يُعدّ الاحتفاظ بالبيانات ميزة في Enterprise Edition ويتطلب مفتاح ترخيص. من دونه، يحتفظ Langfuse بالآثار والمشاهدات والدرجات والوسائط إلى أجل غير محدد، لذلك يجب أن تهيّئ مساحة القرص وفقاً لذلك، وأن تعتمد على تنبيه القرص في نهاية هذا القسم. عند توفر الترخيص، يفتح مالكو المشاريع والمسؤولون إعدادات المشروع ويحددون مدة الاحتفاظ بالبيانات بالأيام. يقبل Langfuse مدة دنيا قدرها 3 أيام. ثم تحدد مهمة ليلية الآثار والمشاهدات والدرجات ومواد الوسائط الأقدم من تلك المدة وتحذفها من ClickHouse ومن تخزين الكائنات، باستثناء الوسائط المرتبطة بعناصر مجموعات البيانات المحفوظة. تحتاج المهمة إلى صلاحية DeleteObject على الحاوية، وهي متوفرة مسبقاً لبيانات اعتماد root الخاصة بـMinIO في ملف compose الافتراضي. الحذف نهائي، لذلك اضبط تصدير تخزين الكائنات أولاً إذا كنت تحتاج إلى الاحتفاظ بسجل طويل الأمد. لا تكتب عبارات TTL يدوياً على جداول Langfuse الخاصة: مهمة الاحتفاظ هي التي تبقي ClickHouse والحاوية متزامنين، بينما يحذف TTL اليدوي أحد الجانبين فقط.

اختر المدة وفقاً لاستخدامك الفعلي. تحدث مراجعة التكلفة والجودة على بيانات عمرها أيام، لا أشهر. مدة 30 يوماً بداية مناسبة لفريق صغير، و14 يوماً تكفي إذا كنت تفتح الأثر فقط عند حدوث عطل.

الثاني هو جداول سجلات النظام الخاصة بـClickHouse، وهذا الأمر يفاجئ كثيراً من المستخدمين لأن القرص يستمر في النمو بعد ضبط الاحتفاظ. يكتب ClickHouse الجداول trace_log وtext_log وopentelemetry_span_log وmetric_log وasynchronous_metric_log وlatency_log لأغراض التشخيص الخاصة به، وتأتي هذه الجداول من دون TTL، ولا يقرأها Langfuse مطلقاً. اعرف أولاً أين استُهلكت مساحة القرص فعلياً.

SELECT table, formatReadableSize(size) AS size, rows FROM (
    SELECT table, database, sum(bytes) AS size, sum(rows) AS rows
    FROM system.parts
    WHERE active
    GROUP BY table, database
    ORDER BY size DESC
)

شغّله باستخدام docker compose exec clickhouse clickhouse-client --password "$CLICKHOUSE_PASSWORD". إذا كانت جداول النظام قريبة من أعلى القائمة، عطّلها باستخدام طبقة إعداد، لأن ClickHouse يدمج كل ملف في /etc/clickhouse-server/config.d/ فوق إعداده الرئيسي عند بدء التشغيل.

<clickhouse>
    <trace_log remove="1"/>
    <text_log remove="1"/>
    <opentelemetry_span_log remove="1"/>
    <asynchronous_metric_log remove="1"/>
    <metric_log remove="1"/>
    <latency_log remove="1"/>
</clickhouse>

ثبّتها ثم أعد تشغيل ClickHouse.

services:
  clickhouse:
    volumes:
      - ./clickhouse-config.d/system-logs.xml:/etc/clickhouse-server/config.d/system-logs.xml:ro

يوقف ذلك عمليات الكتابة الجديدة. تبقى الصفوف الموجودة على القرص، لذلك استعد المساحة صراحةً باستخدام DROP TABLE IF EXISTS system.trace_log، وطبّق الأمر نفسه على كل جدول أزلته. إذا كنت تفضّل الاحتفاظ ببيانات التشخيص، فالبديل هو ضبط TTL صارم لكل جدول بدلاً من remove="1"، كما توضّح وثائق توسيع Langfuse بالتفصيل. لا تنطبق توجيهات TTL هذه إلا عند إنشاء جدول النظام للمرة الأولى، لذلك تشرح الوثائق نفسها أيضاً كيفية إضافتها إلى الجداول في تثبيت موجود.

هناك جدول آخر من المفيد معرفته. يتتبع blob_storage_file_log ملفات الأحداث التي رُفعت إلى الحاوية. إذا ضبطت أيضاً سياسة دورة حياة على الحاوية، فاضبط للجدول TTL مطابقاً حتى لا يختلفا بمرور الوقت.

ALTER TABLE blob_storage_file_log MODIFY TTL created_at + INTERVAL 30 DAY DELETE;

ضع أيضاً تنبيهاً بسيطاً باستخدام df -h على قرص البيانات. لا تنمو الآثار بوتيرة منتظمة. بل تنمو في اليوم الذي تطلق فيه وكيلاً جديداً، ولا ينبغي أن تكون أول علامة على ذلك هي فشل الإدخال.

النسخ الاحتياطي لـ Postgres وClickHouse

يتكوّن النسخ الاحتياطي لـ Langfuse من ثلاثة أجزاء. يحتوي Postgres على المستخدمين والمؤسسات والمشروعات ومفاتيح API. ويحتوي ClickHouse على آثار الاستخدام. أما MinIO فيحتوي على الأحداث الخام. إذا استعدت Postgres فقط، فستحصل على تسجيل دخول يعمل من دون أي سجل سابق. وإذا استعدت ClickHouse فقط، فستحصل على سجل لا يستطيع أحد تسجيل الدخول لعرضه. هذا الفصل ليس خاصاً بـ Langfuse؛ إذ يتخذ مكتب دعم Chatwoot المستضاف ذاتياً البنية نفسها، حيث تؤدي استعادة تفريغ Postgres أُخذ من دون دليل uploads إلى استعادة محادثات اختفت مرفقاتها بالكامل.

Postgres هو pg_dump عادي، وهو الأسلوب المستخدم في وثائق النسخ الاحتياطي لـ Langfuse عند تثبيته عبر Docker.

docker compose exec -T postgres pg_dump -U postgres postgres \
  | gzip > langfuse-pg-$(date +%F).sql.gz

يتطلب ClickHouse عناية أكبر، لأن نسخ دليل بيانات حي أثناء تشغيل عمليات الدمج لا ينتج نسخة احتياطية متسقة. الأسلوب البسيط على خادم واحد هو إيقاف الحاوية وأرشفة وحدة التخزين.

docker compose stop clickhouse
docker volume ls | grep clickhouse
docker run --rm -v langfuse_langfuse_clickhouse_data:/data -v "$PWD":/backup alpine \
  tar czf /backup/langfuse-ch-$(date +%F).tar.gz -C /data .
docker compose start clickhouse

استخدم اسم وحدة التخزين الذي يطبعه docker volume ls، وليس الاسم المكتوب في YAML. يعرّف الملف langfuse_clickhouse_data، ويضيف Compose اسم المشروع كبادئة، لذلك ينتج عن النسخ إلى دليل يسمى langfuse الاسم langfuse_langfuse_clickhouse_data. إذا أخطأت في ذلك، فإن docker run ينشئ وحدة تخزين جديدة وفارغة من دون إصدار تحذير، وسيحتوي الأرشيف على لا شيء.

يرفع Langfuse بيانات الأحداث الخام إلى الحاوية في كل طلب إدخال، ويعيد قراءتها لإعادة محاولة مهام الإدخال الفاشلة. لذلك يعني إيقاف ClickHouse لفترة قصيرة غالباً أن العامل سيعيد المحاولة بعد ذلك. نفّذ العملية خلال ساعة هادئة واجعلها قصيرة. في الحالات التي تشهد نشاطاً أكبر، تكتب عبارة BACKUP DATABASE default TO S3(...) الخاصة بـ ClickHouse نسخة احتياطية متسقة من دون إيقاف الخادم. أما MinIO فهو الجزء الثالث، وتغطيه mc mirror أو ميزة النسخ المتماثل في MinIO إلى حاوية خارج الخادم. أياً كان ما تنتجه، انقله خارج الخادم؛ فهذا هو الغرض من نسخ restic الاحتياطية المشفّرة على VPS.

يحتفظ Redis بقائمة الانتظار وذاكرة التخزين المؤقت. ويؤدي فقدانه إلى إسقاط المهام الموجودة في قائمة الانتظار، حتى عندما تبقى ملفات الأحداث الخام في الحاوية.

التحفظ المتعلق بالاتساق حقيقي ويستحق التوضيح مباشرة. يُنشأ تفريغ Postgres وتفريغ ClickHouse في وقتين مختلفين، لذلك قد تترك الاستعادة صف مشروع بلا آثار، أو آثاراً مرتبطة بمشروع لم يعد موجوداً. يتعامل Langfuse مع ذلك، لكن خذ التفريغين في وقتين متقاربين وخلال فترة منخفضة الحركة. وتبقى حاوية الأحداث شبكة الأمان الفعلية، لأن Langfuse يرفع بيانات الأحداث الخام إليها في كل طلب إدخال.

استعد البيانات إلى بيئة اختبار مؤقتة مرة واحدة على الأقل. بهذه الطريقة تكتشف الآن وجود اسم وحدة تخزين خاطئ، بدلاً من اكتشافه أثناء انقطاع الخدمة.

ما الذي يجب فحصه أولاً

هناك أربعة أمور تستحق المتابعة خلال الأسبوع الأول.

  • التكلفة لكل trace. يحسب Langfuse التكلفة من اسم النموذج واستخدام الرموز، لذا رتّب traces حسب التكلفة واقرأ أغلاها من البداية إلى النهاية. غالباً ما يكون السبب prompt ازداد حجمه: مستند كامل أُلصق في السياق، أو سجل محادثة لا يختصره أحد. عندما يصبح ذلك واضحاً، يتحول التحكم في تكلفة وكيل AI عليك إلى مهمة هندسية بدلاً من التخمين.
  • تقسيم استخدام الرموز بين الإدخال والإخراج. تكون رموز الإدخال كثيرة ورخيصة، بينما تكون رموز الإخراج قليلة ومكلفة، وتكون تكلفة الإدخال المخزّن مؤقتاً أقل من ذلك أيضاً. يشرح كيفية احتساب استخدام الرموز في Claude Code هذه المحاسبة بالتفصيل نفسه، وهي تنطبق على أي وكيل تكتبه بنفسك.
  • النسب المئوية للتأخير. يخفي الوسيط المشكلة. تقع حالات انتهاء المهلة عند p95 وp99، وداخل حلقة الوكيل تتضاعف مدة استدعاء أداة بطيء عند p95 بعدد التكرارات.
  • استدعاءات الأدوات الفاشلة. صفِّ observations حسب المستوى ERROR. الأداة التي تفشل في 5% من الحالات قد لا تظهر في معدل النجاح الإجمالي، لكنها تكون واضحة جداً في traces، حيث تراقب النموذج وهو يعيد المحاولة ثم يستهلك الرموز للتحايل على الفشل.

حدّد فترة الاحتفاظ، واختر لوحة المعلومات التي ستفحصها أسبوعياً في اليوم نفسه الذي تنشر فيه. أداة observability لا يفتحها أحد هي قاعدة بيانات تملأ القرص.

FAQ

ما مقدار الذاكرة التي يحتاج إليها Langfuse مستضاف ذاتياً؟

خطّط لاستخدام 4 أنوية CPU و16 GiB من الذاكرة، فهذا ما يوصي به دليل Langfuse Docker Compose لآلة افتراضية واحدة، بالإضافة إلى نحو 100 GiB من مساحة التخزين. الحد الأدنى المنشور لمكوّن ClickHouse هو 8 GiB، ولكل من حاويتي web وworker 4 GiB. وتذكر الصفحة نفسها 4 GiB لـPostgres، و1.5 GiB لـRedis، و4 GiB لـMinIO. وبذلك يبلغ مجموع المكوّنات الستة 25.5 GiB، أي أكثر من توصية Compose البالغة 16 GiB.

لماذا تمتلئ مساحة قرص ClickHouse باستمرار بعد ضبط مدة الاحتفاظ بالبيانات؟

يغطي إعداد الاحتفاظ بيانات Langfuse الخاصة فقط. يكتب ClickHouse بشكل منفصل جداول التشخيص trace_log وtext_log وopentelemetry_span_log وmetric_log وasynchronous_metric_log وlatency_log، ولا تحتوي هذه الجداول على TTL افتراضياً. نفّذ الاستعلام system.parts مع التجميع حسب الجدول لمعرفة الجدول الأكبر، ثم عطّل الجداول غير المستخدمة بإضافة إدخال remove="1" إلى ملف ضمن /etc/clickhouse-server/config.d/، وأعد تشغيل ClickHouse، ثم احذف الجداول الحالية لتحرير المساحة المستخدمة مسبقاً.

ما الحد الأدنى لمدة الاحتفاظ بالبيانات في Langfuse؟

ثلاثة أيام. تُضبط مدة الاحتفاظ لكل مشروع من إعدادات المشروع، أو عبر Organization Management API. وتحذف مهمة ليلية الآثار والمشاهدات والدرجات وأصول الوسائط الأقدم من هذه المدة من ClickHouse ومن تخزين الكائنات، باستثناء الوسائط المرتبطة بعناصر مجموعات البيانات المحفوظة. في النسخة المستضافة ذاتياً، هذه الميزة متاحة في Enterprise Edition وتتطلب license key. من دونه، تُحتفظ بالبيانات إلى أجل غير محدد. لا يمكن التراجع عن الحذف، لذلك اضبط تصدير البيانات إلى blob storage أولاً إذا كنت تحتاج إلى الاحتفاظ بالسجل لما بعد هذه المدة.

هل يجب إجراء نسخ احتياطي لكل من Postgres وClickHouse؟

نعم، لأن كلّاً منهما يخزن أنواعاً مختلفة من البيانات. يخزن Postgres المستخدمين والمؤسسات والمشاريع وAPI keys، بينما يخزن ClickHouse بيانات الآثار نفسها. تؤدي استعادة Postgres فقط إلى تشغيل instance يمكنك تسجيل الدخول إليه، لكنه سيكون خالياً من البيانات. أجرِ نسخاً احتياطية من bucket الخاص بـMinIO أيضاً، لأنه يخزن الأحداث الخام التي يحتفظ بها Langfuse عند وصولها، وهو أقرب مكوّن إلى مصدر الحقيقة في هذه البنية.

هل يمكنني توجيه إعداد OpenTelemetry الحالي إلى Langfuse مستضاف ذاتياً؟

نعم. يعتمد Langfuse v4 وSDKs الحالية الخاصة به على OpenTelemetry، كما تصدّر instrumentations الخاصة بـAnthropic وOpenAI البيانات إليه مباشرة. في Python، شغّل pip install langfuse opentelemetry-instrumentation-anthropic، واستدعِ AnthropicInstrumentor().instrument() مرة واحدة عند بدء التشغيل، واضبط LANGFUSE_PUBLIC_KEY وLANGFUSE_SECRET_KEY وLANGFUSE_BASE_URL على المضيف الخاص بك. تحقّق باستخدام langfuse.auth_check() قبل البحث عن لوحة معلومات مفقودة.

هل كان هذا المقال مفيدًا؟