استضافة 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 الحد الأدنى من الموارد للمكوّنات الثلاثة التي تنفّذ العمل.
The data behind this chart
[
{
"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 التي يحدّد Langfuse متطلباتها، بينما لا تزال Postgres وRedis وMinIO تحتاج إلى ذاكرة إضافية. يوصي دليل Docker Compose الخاص بالمشروع بجهاز مزوّد بـ4 نوى و16 GiB من الذاكرة ونحو 100 GiB من مساحة التخزين، وهو ما يتوافق مع هذه الحسابات بدلاً من إضافة هامش غير ضروري.
لا تحاول تشغيل هذا على خطة بسعة 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. تكفي 8 GiB لمطوّر واحد يرسل بضعة آلاف من عمليات التتبّع يومياً. أما 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.
ثبّت وسوم الصور قبل البدء
يستخدم الملف المرفق langfuse/langfuse:4 وlangfuse/langfuse-worker:4. هذه الوسوم تتغير. يشغّل Langfuse عمليات ترحيل Postgres وClickHouse تلقائياً عند بدء التشغيل. لذلك قد يتحول docker compose pull عادي بعد أشهر إلى ترحيل مخطط غير مخطط له على قاعدة بيانات لم تنشئ لها نسخة احتياطية في ذلك الصباح. ثبّت كليهما على إصدار واحد في docker-compose.override.yml. يدمج Compose هذا الملف فوق الملف المرفق، وبذلك لا يتعارض git pull لاحقاً مع تعديلاتك.
services:
langfuse-web:
image: docker.io/langfuse/langfuse:4.3.1
langfuse-worker:
image: docker.io/langfuse/langfuse-worker:4.3.1كان الإصدار 4.3.1 هو الإصدار الحالي من سلسلة 4.3 في August 2026، وقد صدر الإصدار 4.4.0 منذ ذلك الحين. تحقّق من صفحة إصدارات المشروع على GitHub، وثبّت الإصدار الحالي في يوم النشر، ثم غيّر هذا الرقم عن قصد عند الترقية. صور التخزين في الملف المرفق مثبتة مسبقاً على الإصدارات الرئيسية، وهي postgres:17 وclickhouse-server:25.12 وredis:7، وينبغي إخضاعها للمعاملة نفسها. لا تقتصر هذه القاعدة على Langfuse: يشغّل متتبّع تمارين openGym مستضافاً ذاتياً جزءاً من هذه الحزمة فقط، لكنه لا يزال يحتاج إلى وسم git محدد بالاسم، لأن أي تطبيق يرحّل قاعدة بياناته عند بدء التشغيل يمكن أن يحوّل سحباً عادياً إلى تغيير في المخطط.
شغّل الحزمة.
docker compose up -d
docker compose ps
docker compose logs -f langfuse-workerينفّذ الإقلاع الأول عمليات الترحيل، لذا انتظر دقيقة أو دقيقتين قبل اختبار الاستجابة. يجب أن يعرض docker compose ps ست خدمات بالحالة running. إذا أعاد العامل التشغيل في حلقة، فسيتضمن سجله السبب: يستخدم CLICKHOUSE_MIGRATION_URL البروتوكول الأصلي لـ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 تعمل، لأنه يتجاوز قاعدة البيانات عمداً حتى تواصل الخدمة تقديم الطلبات أثناء انقطاع Postgres مؤقتاً. أما صيغة failIfDatabaseUnavailable=true فهي المناسبة لربط أداة مراقبة بها، وتعيد 503 عندما يتعذر الوصول إلى قاعدة البيانات. يعيد /api/public/ready القيمة 200 بعد اكتمال عمليات الترحيل وعندما تصبح الحاوية مستعدة لقبول حركة الشبكة. كلا الفحصين عبارة عن فحصي HTTP عاديين، لذا يمكن لـصفحة حالة Uptime Kuma مراقبتهما وإبلاغك بتوقف الحزمة قبل أن يكتشف الوكلاء ذلك.
ضع TLS أمام الخدمة وأغلق المنافذ الإضافية
ينشر ملف Compose المرفق 3000:3000 لحاوية الويب و9090:9000 لـ MinIO. ويربط كلا المنفذين بكل واجهات الشبكة. على عنوان IP عام، يعني ذلك أن أي شخص يفحص المنفذ 3000 يصل إلى صفحة التسجيل، وأي شخص يفحص المنفذ 9090 يتصل بالحاوية التي تحتوي على المطالبات الأولية.
لا تكفي قاعدة جدار ناري وحدها لإغلاقهما. يكتب Docker قواعد DNAT الخاصة به في جدول nat، وتُقيَّم هذه القواعد قبل أن ترى قواعد filter في ufw الحزمة، لذلك يترك ufw deny 3000 المنفذ المنشور مفتوحاً. يتكرر هذا الأمر بما يكفي لتخصيص دليل له: لماذا تتجاوز المنافذ التي ينشرها Docker قواعد ufw. اربط المنفذ بواجهة loopback في ملف override بدلاً من ذلك.
services:
langfuse-web:
ports:
- "127.0.0.1:3000:3000"
environment:
NEXTAUTH_URL: https://langfuse.example.com
minio:
ports:
- "127.0.0.1:9090:9000"
- "127.0.0.1:9091:9001"يجب أن يكون NEXTAUTH_URL العنوان العام الدقيق، بما في ذلك scheme، لأن مسار تسجيل الدخول ينشئ عنوان callback من هذه القيمة. إذا تركته http://localhost:3000 خلف وكيل HTTPS، فسترسل دورة تسجيل الدخول المتصفح إلى وجهة لا يمكنه الوصول إليها.
وجّه الآن reverse proxy إلى 127.0.0.1:3000 ودعه يتولى الشهادة. عادةً ما يكون Traefik في مشروع Compose نفسه هو الخيار المعتاد، وتسميات التوجيه هي التسميات المشروحة في تشغيل عدة تطبيقات خلف reverse proxy واحد باستخدام Traefik. ينفذ Caddy المهمة نفسها في سطرين إذا كان Langfuse هو التطبيق الوحيد على الخادم. تحقّق باستخدام curl -sI https://langfuse.example.com/api/public/ready، ثم تأكد من جهاز ثانٍ من أن curl http://YOUR_IP:3000 ينتهي الآن بمهلة.
هناك ملاحظة مهمة بشأن MinIO. يقدّم Langfuse الوسائط المرفقة إلى المتصفح عبر عناوين URL موقعة مسبقاً تشير إلى نقطة نهاية S3 تلك. لذلك، إذا كنت تستخدم traces متعددة الوسائط تحتوي على صور أو صوت، فلن تُحمّل هذه المرفقات عندما يكون MinIO متاحاً عبر loopback فقط. اقرأ صفحة إعدادات تخزين الكائنات قبل وضعه خلف proxy، لأن نقطة النهاية المكتوبة في عنوان URL الموقّع مسبقاً يجب أن تطابق العنوان الذي تنشره. لا تتأثر traces النصية العادية.
أنشئ حسابك عند الزيارة الأولى، ثم اجعل الـinstance تحت إدارتك. اضبط LANGFUSE_ALLOWED_ORGANIZATION_CREATORS على عنوان بريدك الإلكتروني، حتى لا يتمكن شخص غريب يصل إلى الصفحة من إنشاء مؤسسة على خادمك. إذا كنت تشغّل بالفعل Authentik باعتباره موفّر الهوية الخاص بك، فإن Langfuse يقبل اتصال OIDC قياسياً، وبذلك تُنشأ الحسابات وتُحذف مع بقية تطبيقاتك بدلاً من تخزينها في قائمة كلمات مرور لا يعرفها إلا هذا الخادم.
أرسل أول trace
أنشئ مشروعاً في واجهة الويب، وانسخ مفتاحه العام ومفتاحه السري من إعدادات المشروع. يقرأ 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 بدلاً من خادمك، فالسبب هو أن عنوان URL الأساسي غير مضبوط، لأن القيمة الافتراضية تشير إلى النسخة المستضافة.
pip install langfuse opentelemetry-instrumentation-anthropic anthropicimport 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()يفتح مزخرف @observe عملية observation حول الدالة، ويلتقط الوسائط والقيمة المعادة، ويضعها ضمن observation النشطة مسبقاً. أما AnthropicInstrumentor فهو instrumentation الخاص بـOpenTelemetry لعميل Anthropic، ويحوّل كل استدعاء messages.create إلى generation يتضمن اسم النموذج، واستخدام الرموز، ووقت الاستجابة، من دون أي تغيير في موضع الاستدعاء.
تجري استدعاءان عملية التحقق نيابةً عنك. يعيد langfuse.auth_check() القيمة False عند استخدام مفاتيح غير صالحة أو عنوان URL أساسي خاطئ، وهذا أسرع من محاولة معرفة سبب فراغ لوحة المعلومات. يحظر langfuse.flush() التنفيذ حتى تُرسل الـspans الموضوعة في قائمة الانتظار، وتحتاج إليه العمليات قصيرة العمر لأن SDK يرسل البيانات على دفعات في الخلفية، بينما يأخذ السكربت الذي ينتهي فوراً دفعته غير المرسلة معه. إذا كان فريق كامل يرسل traces بدلاً من سكربت واحد، فاضبط هذه المتغيرات الثلاثة مرة واحدة في الـgateway الذي تمر عبره agents لدى الجميع، بدلاً من ضبطها في shell الخاص بكل شخص. بهذه الطريقة يحافظ نظام OneCLI مستضاف ذاتياً على instrumented runs لجميع الزملاء، بينما تبقى المفاتيح في مكان واحد.
لماذا يستمر ClickHouse في النمو؟
تُعدّ التتبعات البيانات الأسرع نمواً التي يستضيفها معظم الأشخاص ذاتياً. يكتب كل تشغيل للوكيل صفاً واحداً لكل خطوة، وتُخزَّن المدخلات والمخرجات بالكامل، لذلك ينتج وكيل كثير الرسائل وذو مطالبات طويلة بايتات يومية تفوق بكثير ما ينتجه التطبيق الذي يراقبه. إذا تُرك ClickHouse دون ضبط، فسيمتلئ القرص، وامتلاء القرص يوقف إدخال البيانات بدلاً من إبطائه.
ينمو هنا شيئان منفصلان، ولكل منهما إصلاح منفصل.
الأول هو بيانات التتبعات الخاصة بك، وإصلاحه هو إعداد الاحتفاظ. افتح إعدادات المشروع في واجهة الويب وحدد مدة الاحتفاظ بالبيانات بالأيام. يقبل Langfuse مدة لا تقل عن 3 أيام. تحدد مهمة ليلية التتبعات والمشاهدات والتقييمات ووسائط البيانات الأقدم من هذه المدة، ثم تحذفها من ClickHouse ومن تخزين الكائنات. تحتاج المهمة إلى صلاحية DeleteObject على الحاوية، وتملكها بالفعل بيانات اعتماد MinIO root الموجودة في ملف compose الافتراضي. الحذف نهائي، لذلك اضبط تصدير تخزين الكائنات أولاً إذا كنت تحتاج إلى سجل طويل الأمد. لا تكتب عبارات TTL يدوياً على جداول Langfuse نفسها: مهمة الاحتفاظ هي التي تُبقي ClickHouse والحاوية متزامنين، بينما تحذف TTL اليدوية أحد الجانبين فقط.
اختر المدة وفقاً لاستخدامك الفعلي. تحدث مراجعة التكلفة والجودة على بيانات عمرها أيام، لا أشهر. تُعد 30 يوماً نقطة بداية معقولة لفريق صغير، وتكفي 14 يوماً إذا كنت تفتح التتبّع فقط عند حدوث عطل.
الثاني هو جداول سجلات النظام الخاصة بـClickHouse، وهذا يفاجئ بعض المستخدمين، لأن القرص يستمر في الامتلاء بعد ضبط الاحتفاظ. يكتب ClickHouse الجداول trace_log وtext_log وopentelemetry_span_log وmetric_log وasynchronous_metric_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"/>
</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 للتوسّع هذه الطريقة بالتفصيل.
هناك جدول آخر تجدر معرفته. يتتبع 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 compose exec -T postgres pg_dump -U postgres postgres \
| gzip > langfuse-pg-$(date +%F).sql.gzيتطلب ClickHouse عناية أكبر، لأن نسخ دليل بيانات حي أثناء تشغيل عمليات الدمج لا ينتج نسخة احتياطية متسقة. النهج البسيط على خادم واحد هو إيقاف الحاوية وأرشفة الـvolume.
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استخدم اسم الـvolume الذي يطبعه docker volume ls، وليس الاسم المكتوب في YAML. يعرّف الملف langfuse_clickhouse_data، ويضيف Compose اسم المشروع كـprefix، لذلك ينتج عن استنساخ المشروع في دليل اسمه langfuse الاسم langfuse_langfuse_clickhouse_data. إذا أخطأت في ذلك، ينشئ docker run volume جديداً فارغاً من دون إصدار تحذير، وسيحتوي أرشيفك على لا شيء.
تكتب حاوية الويب كل حدث وارد إلى الـbucket قبل أن يعالجه الـworker، لذلك يعني إيقاف ClickHouse لفترة قصيرة غالباً أن الـworker سيعيد المحاولة بعد ذلك. نفّذ العملية خلال ساعة هادئة وأبقِ مدة الإيقاف قصيرة. في النسخ الأكثر انشغالاً، تكتب عبارة BACKUP DATABASE default TO S3(...) الخاصة بـClickHouse نسخة احتياطية متسقة من دون إيقاف الخادم. MinIO هو الجزء الثالث، ويغطيه mc mirror أو نسخ MinIO المتماثل إلى bucket خارج الخادم. أياً كان ما تنتجه، انقله خارج الخادم، فهذا هو الغرض من نسخ restic الاحتياطية المشفّرة على VPS.
لا يحتاج Redis إلى نسخة احتياطية. فهو يحتفظ بقائمة الانتظار والـcache، ولذلك يؤدي فقدانه إلى خسارة الأحداث الجاري معالجتها حالياً، ولا شيء أقدم منها.
ملاحظة الاتساق هذه حقيقية ويجب توضيحها. يُجرى تفريغ Postgres وClickHouse في لحظتين مختلفتين، لذلك قد تؤدي الاستعادة إلى وجود صف مشروع من دون تتبعات، أو تتبعات لمشروع لم يعد موجوداً. يتعامل Langfuse مع ذلك، لكن خذ النسختين الاحتياطيتين بفاصل زمني قصير وفي فترة منخفضة الاستخدام. ويُعدّ event bucket شبكة الأمان الفعلية، لأن Langfuse يحفظ كل حدث وارد فيه قبل معالجته.
استعد البيانات إلى scratch stack مرة واحدة على الأقل. بهذه الطريقة تكتشف الآن وجود اسم volume خاطئ، بدلاً من اكتشافه أثناء انقطاع الخدمة.
ما الذي يجب فحصه أولاً
هناك أربعة أمور تستحق المتابعة خلال الأسبوع الأول.
- التكلفة لكل 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 من مساحة التخزين. الحد الأدنى المنشور لمكوّنات Langfuse هو 8 GiB لـClickHouse و4 GiB لكل من حاويتي الويب والعامل، كما تحتاج Postgres وRedis وMinIO إلى ذاكرة إضافية. تكفي 8 GiB لتشغيل نسخة لمطوّر واحد. أما 2 GiB فلا تكفي: يوقف kernel عملية ClickHouse أثناء عمليات الدمج في الخلفية، ويعرض dmesg القيمة Out of memory: Killed process.
لماذا يمتلئ قرص ClickHouse باستمرار بعد ضبط مدة الاحتفاظ بالبيانات؟
يغطي إعداد الاحتفاظ بيانات Langfuse الخاصة فقط. يكتب ClickHouse بشكل منفصل جداول التشخيص trace_log وtext_log وopentelemetry_span_log وmetric_log وasynchronous_metric_log، ولا تأتي هذه الجداول مع TTL. نفّذ الاستعلام system.parts مع التجميع حسب الجدول لمعرفة الجدول الأكبر، ثم عطّل الجداول غير المستخدمة بإضافة إدخال remove="1" في ملف ضمن /etc/clickhouse-server/config.d/، وأعد تشغيل ClickHouse، واحذف الجداول الموجودة لتحرير المساحة المستخدمة مسبقاً.
ما الحد الأدنى لمدة الاحتفاظ بالبيانات في Langfuse؟
3 أيام. تُضبط مدة الاحتفاظ لكل مشروع من إعدادات المشروع أو عبر projects API، وتحذف مهمة ليلية الآثار والملاحظات والتقييمات وملفات الوسائط الأقدم من هذه المدة من ClickHouse ومن تخزين الكائنات. لا يمكن التراجع عن الحذف، لذلك اضبط تصدير تخزين الكائنات أولاً إذا كنت تحتاج إلى الاحتفاظ بسجل يتجاوز هذه المدة.
هل يجب أن أنشئ نسخة احتياطية من Postgres وClickHouse معاً؟
نعم، لأنهما يخزّنان بيانات مختلفة. يخزّن Postgres المستخدمين والمؤسسات والمشاريع ومفاتيح API، بينما يخزّن ClickHouse بيانات الآثار نفسها. تؤدي استعادة Postgres فقط إلى إنشاء نسخة يمكنك تسجيل الدخول إليها، لكنها ستكون خالية من البيانات. أنشئ نسخة احتياطية من حاوية MinIO أيضاً، لأنها تحتوي على الأحداث الأولية التي يحفظها Langfuse عند وصولها، وهي أقرب ما يكون إلى مصدر الحقيقة في هذه المنظومة.
هل يمكنني توجيه إعداد OpenTelemetry موجود إلى Langfuse مستضاف ذاتياً؟
نعم. يعتمد Langfuse v4 وحزم SDK الخاصة به للإصدار v4 على 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() قبل البحث عن لوحة معلومات مفقودة.