SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-13

آموزش نصب و میزبانی Langfuse روی سرور شخصی

راهنمای کامل خودمیزبانی Langfuse روی VPS. تنظیم دقیق ClickHouse برای جلوگیری از پر شدن دیسک، استفاده از تگ‌های ثابت Docker، پیکربندی TLS و روش‌های بک‌آپ‌گیری مطمئن.

چرا باید یک عامل هوش مصنوعی را ردیابی (Trace) کرد

شما Langfuse را به‌صورت self-host اجرا می‌کنید تا ببینید عامل (agent) شما در یک اجرا واقعاً چه کاری انجام داده است. Langfuse یک ابزار متن‌باز برای مشاهده‌پذیری (observability) مدل‌های زبانی بزرگ (LLM) است. این ابزار هر prompt، هر پاسخ مدل، هر فراخوانی ابزار و هر token را ثبت کرده و آن‌ها را در یک trace واحد دسته‌بندی می‌کند تا بتوانید آن را باز کرده و مطالعه کنید. اجرای آن روی VPS شخصی به این معناست که آن promptها هرگز از سروری که تحت کنترل شماست خارج نمی‌شوند.

دلیل اهمیت این موضوع روشن است. شما نمی‌توانید مشکل هزینه یا کیفیت را که قادر به دیدن آن نیستید، برطرف کنید. صورت‌حساب ارائه‌دهنده به شما می‌گوید که هزینهٔ روز سه‌شنبه چهار برابر روز دوشنبه بوده است. اما یک trace به شما می‌گوید کدام اجرای عامل باعث این هزینه شده، کدام prompt به 40,000 توکن رسیده و کدام حلقهٔ تلاش مجدد (retry loop) نه بار اجرا شده و سپس متوقف شده است. صورت‌حساب فقط عدد را به شما می‌دهد، اما trace کدی را که آن عدد را تولید کرده است، نشان می‌دهد.

در این راهنما از سه اصطلاح استفاده شده است. یک trace به معنای یک اجرای کامل و سرتاسری از عامل شماست. یک observation یک گام در داخل آن اجراست: یک span برای کدهای معمولی و یک generation برای فراخوانی یک مدل. یک score عددی است که به یک trace اختصاص داده می‌شود و از طریق بازبینی انسانی یا یک ارزیاب خودکار به‌دست می‌آید. Langfuse از OpenTelemetry (OTel) پشتیبانی می‌کند که استاندارد خنثی و مستقل از فروشنده برای ردیابی توزیع‌شده است؛ بنابراین ابزارهای اندازه‌گیری (instrumentation) که هم‌اکنون در اختیار دارید، می‌توانند به آن متصل شوند.

اجزای تشکیل‌دهنده Langfuse در حالت self-hosting

نسخه Langfuse v4 تنها یک کانتینر نیست. این سرویس شامل دو کانتینر اپلیکیشن و چهار سرویس ذخیره‌سازی است که در یک VPS، هر شش مورد روی سرور شما اجرا می‌شوند.

  • langfuse-web رابط کاربری وب و API دریافت داده (ingestion API) را ارائه می‌دهد.
  • langfuse-worker صف را در پس‌زمینه تخلیه می‌کند. این بخش دسته‌های داده ورودی را پردازش کرده، هزینه‌ها را محاسبه می‌کند و وظایف نگهداری شبانه (retention job) را اجرا می‌نماید.
  • Postgres داده‌های تراکنشی مانند کاربران، سازمان‌ها، پروژه‌ها، کلیدهای API و پرامپت‌ها را ذخیره می‌کند.
  • ClickHouse داده‌های trace، شامل مشاهدات و امتیازها را نگهداری می‌کند. این یک پایگاه‌داده ستونی (column store) است که برای کوئری‌های تحلیلی ساخته شده؛ به همین دلیل است که داشبوردها حتی با وجود صدها میلیون ردیف داده، با سرعت پاسخ می‌دهند.
  • Redis به عنوان صف و حافظه کش بین بخش وب و worker عمل می‌کند.
  • MinIO فضای ذخیره‌سازی شیء (object storage) سازگار با S3 را روی سرور فراهم می‌کند. این بخش تمام رویدادهای خام ورودی و هر نوع رسانه‌ای که پیوست می‌کنید را نگه می‌دارد.

Langfuse حداقل منابع مورد نیاز برای سه مؤلفه‌ای که بار پردازشی دارند را منتشر کرده است.

ChartLangfuse published minimum resources per component
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 گیگابایت حافظه رم نیاز دارد. کانتینر وب و کانتینر worker هر کدام به 4 گیگابایت حافظه نیاز دارند. این‌ها حداقل‌های اعلام‌شده برای 3 مؤلفه‌ای هستند که Langfuse برای آن‌ها تعیین اندازه کرده است؛ علاوه بر این، Postgres، Redis و MinIO نیز به حافظه رم اضافی نیاز دارند. راهنمای Docker Compose خود پروژه، استفاده از ماشینی با 4 هسته پردازشی، 16 گیگابایت رم و حدود 100 گیگابایت فضای ذخیره‌سازی را توصیه می‌کند که با این محاسبات همخوانی دارد و نه صرفاً یک عدد اغراق‌آمیز.

این کار را روی پلن‌های 2 گیگابایتی امتحان نکنید. ClickHouse بالا می‌آید و برای مدتی داده‌ها را می‌پذیرد، اما هنگام انجام عملیات ادغام در پس‌زمینه (background 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 گیگابایت رم برای یک توسعه‌دهنده که روزانه چند هزار trace ارسال می‌کند قابل استفاده است، اما برای برنامه‌ریزی بلندمدت، 16 گیگابایت عدد مناسب است.

استقرار Langfuse با Docker Compose

مخزن را کلون کنید. استک، اتصالات و محیط پیش‌فرض همگی در docker-compose.yml آن قرار دارند.

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

هر مقداری که باید تغییر دهید در آن فایل با # CHANGEME مشخص شده است. ابتدا سه secret برنامه را تولید کنید.

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 چاپ می‌کند. این مقدار مقادیر حساس را در حالت استراحت (at rest) رمزنگاری می‌کند، از جمله کلیدهای ارائه‌دهنده LLM که در instance ذخیره می‌کنید. تغییر آن پس از ایجاد داده‌ها باعث می‌شود آن ردیف‌ها دیگر قابل رمزگشایی نباشند، بنابراین از همان اولین بوت آن را دائمی در نظر بگیرید. مقدار SALT برای هش کردن کلیدهای API در Langfuse استفاده می‌شود، بنابراین تغییر آن باعث ابطال تمام کلیدهایی می‌شود که agentهای شما در حال حاضر از آن‌ها استفاده می‌کنند.

سپس 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 رد می‌کند که در لاگ worker ثبت می‌شود، در حالی که رابط وب همچنان سالم به نظر می‌رسد. نگهداری این مقادیر در یک فایل env به جای فایل compose ردیابی‌شده، الگویی است که در فایل‌های env و secret در Docker Compose پوشش داده شده است.

قبل از شروع، تگ‌های image را ثابت (Pin) کنید

فایل ارائه‌شده از langfuse/langfuse:4 و langfuse/langfuse-worker:4 استفاده می‌کند. این تگ‌ها تغییر می‌کنند. Langfuse مهاجرت‌های Postgres و ClickHouse خود را به‌طور خودکار در هنگام شروع اجرا می‌کند، بنابراین یک docker compose pull معمولی در ماه‌های بعد، به یک مهاجرت schema برنامه‌ریزی‌نشده روی دیتابیسی تبدیل می‌شود که همان روز صبح از آن بک‌آپ نگرفته‌اید. هر دو را روی یک release در یک 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 تا اوت 2026 بود (نسخه 4.4.0 از آن زمان منتشر شده است). صفحه GitHub releases پروژه را بررسی کنید، هر نسخه‌ای که در روز استقرار فعلی است را ثابت کنید و سپس آن عدد را به‌صورت آگاهانه تغییر دهید. imageهای ذخیره‌سازی در فایل ارائه‌شده قبلاً روی نسخه‌های اصلی postgres:17، clickhouse-server:25.12 و redis:7 ثابت شده‌اند و آن‌ها نیز مستحق همین رفتار هستند.

آن را بالا بیاورید.

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

اولین بوت مهاجرت‌ها را اجرا می‌کند، بنابراین یک یا دو دقیقه صبر کنید تا سرویس‌ها پاسخ دهند. docker compose ps باید شش سرویس را در وضعیت running فهرست کند. اگر worker در یک حلقه ری‌استارت می‌شود، لاگ آن دلیل را نشان می‌دهد: CLICKHOUSE_MIGRATION_URL از پروتکل بومی ClickHouse روی پورت 9000 استفاده می‌کند، نه پورت HTTP 8123، و اشاره دادن آن به 8123 باعث شکست در آنجا می‌شود در حالی که container وب همچنان خوب به نظر می‌رسد.

سلامت سرویس را از خود سرور بررسی کنید.

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 برمی‌گرداند و container ترافیک را می‌پذیرد. هر دو بررسی‌های معمولی HTTP هستند، بنابراین یک صفحه وضعیت Uptime Kuma می‌تواند آن‌ها را زیر نظر بگیرد و قبل از اینکه agentهای شما متوجه شوند، به شما اطلاع دهد که استک از دسترس خارج شده است.

قرار دادن TLS در لایه جلو و بستن پورت‌های اضافی

فایل compose ارائه‌شده، پورت 3000:3000 را برای container وب و 9090:9000 را برای MinIO منتشر می‌کند. هر دو روی تمام اینترفیس‌ها bind می‌شوند. روی یک IP عمومی، این یعنی هر کسی که پورت 3000 را اسکن کند به صفحه ثبت‌نام شما می‌رسد و هر کسی که پورت 9090 را اسکن کند، مستقیماً با bucket حاوی promptهای خام شما در ارتباط خواهد بود.

قوانین فایروال به‌تنهایی این پورت‌ها را نمی‌بندند. Docker قوانین DNAT خود را در جدول nat می‌نویسد و این قوانین پیش از آنکه فیلترهای ufw بسته‌ها را ببینند ارزیابی می‌شوند؛ بنابراین ufw deny 3000 پورت منتشرشده را باز نگه می‌دارد. این موضوع آن‌قدر رایج است که راهنمای اختصاصی خود را دارد: چرا پورت‌های منتشرشده توسط Docker از ufw عبور می‌کنند. در فایل override خود، bind را فقط روی loopback تنظیم کنید.

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 باشد، زیرا فرآیند ورود (login flow)، آدرس callback خود را بر اساس این مقدار می‌سازد. اگر آن را پشت یک proxy با HTTPS به صورت http://localhost:3000 رها کنید، رفت‌وبرگشت ورود، مرورگر را به جایی می‌فرستد که قابل دسترسی نیست.

حالا یک reverse proxy را به 127.0.0.1:3000 هدایت کنید و اجازه دهید گواهی TLS را مدیریت کند. Traefik در همان پروژه Compose انتخاب معمول است و برچسب‌های مسیریابی (routing labels) همان‌هایی هستند که در اجرای چندین برنامه پشت یک reverse proxy از نوع Traefik پوشش داده شده‌اند. Caddy نیز اگر Langfuse تنها سرویس روی سرور باشد، همین کار را در دو خط انجام می‌دهد. با curl -sI https://langfuse.example.com/api/public/ready بررسی کنید و سپس از یک دستگاه دیگر مطمئن شوید که curl http://YOUR_IP:3000 اکنون با خطای timeout مواجه می‌شود.

یک نکته در مورد MinIO وجود دارد. Langfuse رسانه‌های پیوست‌شده را از طریق URLهای presigned که به آن endpoint S3 اشاره دارند به مرورگر شما ارائه می‌دهد؛ بنابراین اگر از traceهای چندرسانه‌ای (multi-modal) حاوی تصویر یا صوت استفاده می‌کنید، MinIO که فقط روی loopback باشد باعث می‌شود آن پیوست‌ها بارگذاری نشوند. پیش از proxy کردن آن، صفحه پیکربندی blob storage را مطالعه کنید، زیرا endpoint نوشته‌شده در URLهای presigned باید با آنچه منتشر می‌کنید مطابقت داشته باشد. traceهای متنی ساده تحت تأثیر قرار نمی‌گیرند.

در اولین بازدید، حساب کاربری خود را بسازید و سپس دسترسی به instance را محدود کنید. مقدار LANGFUSE_ALLOWED_ORGANIZATION_CREATORS را روی آدرس ایمیل خود تنظیم کنید تا غریبه‌ای که به صفحه می‌رسد، نتواند در سرور شما سازمان (organisation) ایجاد کند. اگر در حال حاضر از Authentik به عنوان identity provider خود استفاده می‌کنید، Langfuse از اتصال استاندارد OIDC پشتیبانی می‌کند؛ بنابراین حساب‌های کاربری همراه با سایر برنامه‌های شما مدیریت می‌شوند و دیگر نیازی نیست در لیست رمز عبوری که فقط این سرور از آن اطلاع دارد، باقی بمانند.

ارسال اولین trace

یک پروژه در رابط کاربری وب ایجاد کنید و کلیدهای عمومی و محرمانه آن را از تنظیمات پروژه کپی کنید. SDK پایتون سه متغیر محیطی را می‌خواند.

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

LANGFUSE_BASE_URL نام متغیر در نسخه 4 از SDK است که در مارس 2026 منتشر شد. کدهای قدیمی‌تر و راهنماهای پیشین از LANGFUSE_HOST استفاده می‌کنند. اگر traceهای شما به‌جای سرور خودتان در Langfuse Cloud قرار می‌گیرند، دلیل آن تنظیم‌نشدن base URL است، زیرا مقدار پیش‌فرض به نمونه میزبانی‌شده (hosted instance) اشاره دارد.

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()

دکوراتور @observe یک observation پیرامون تابع باز می‌کند، آرگومان‌ها و مقدار بازگشتی آن را ثبت کرده و آن را در زیرمجموعه هر observation که در حال حاضر فعال است، قرار می‌دهد. AnthropicInstrumentor ابزار OpenTelemetry برای کلاینت Anthropic است و هر فراخوانی messages.create را به یک generation تبدیل می‌کند که شامل نام مدل، میزان مصرف توکن و تأخیر (latency) است، بدون اینکه تغییری در محل فراخوانی ایجاد شود.

دو فراخوانی، بررسی‌های لازم را برای شما انجام می‌دهند. langfuse.auth_check() در صورت اشتباه بودن کلیدها یا نادرست بودن base URL مقدار False را برمی‌گرداند که سریع‌تر از این است که بخواهید بدانید چرا داشبورد خالی است. langfuse.flush() تا زمانی که spanهای در صف قرار گرفته ارسال شوند، منتظر می‌ماند؛ فرآیندهای کوتاه‌مدت به این دستور نیاز دارند، زیرا SDK عملیات را در پس‌زمینه دسته‌بندی (batch) می‌کند و اسکریپتی که بلافاصله خارج می‌شود، دسته ارسال‌نشده خود را نیز همراهش از بین می‌برد.

چرا حجم ClickHouse مدام افزایش می‌یابد؟

ردیابی‌ها (Traces) سریع‌ترین داده‌های در حال رشد هستند که اکثر افراد به‌صورت self-host میزبانی می‌کنند. هر اجرای agent یک ردیف در هر مرحله می‌نویسد و ورودی‌ها و خروجی‌ها به‌طور کامل ذخیره می‌شوند؛ بنابراین یک agent پرحرف با promptهای طولانی، بایت‌های بسیار بیشتری در روز نسبت به برنامه‌ای که آن را نظارت می‌کند، تولید می‌نماید. اگر به حال خود رها شود، ClickHouse دیسک را پر می‌کند و پر شدن کامل دیسک، به‌جای کند کردن، باعث توقف کامل ingestion می‌شود.

دو مورد مجزا در اینجا رشد می‌کنند و به دو راهکار مجزا نیاز دارند.

مورد اول داده‌های ردیابی خود شماست و راهکار آن تنظیم retention است. تنظیمات پروژه را در رابط کاربری وب باز کنید و یک دوره نگهداری داده به روز تعیین کنید. Langfuse حداقل 3 روز را می‌پذیرد. سپس یک job شبانه، ردیابی‌ها، مشاهدات، امتیازات و دارایی‌های رسانه‌ای قدیمی‌تر از آن بازه را انتخاب کرده و آن‌ها را از ClickHouse و blob storage حذف می‌کند. این job به مجوز DeleteObject روی bucket نیاز دارد که اعتبارنامه‌های root در MinIO در فایل compose پیش‌فرض، از قبل آن را دارند. حذف دائمی است، بنابراین اگر به تاریخچه بلندمدت نیاز دارید، ابتدا یک export از blob storage پیکربندی کنید. بندهای TTL را به‌صورت دستی روی جداول خود Langfuse ننویسید: job نگهداری همان چیزی است که ClickHouse و bucket را هماهنگ نگه می‌دارد و TTL دستی فقط یک سمت را پاک می‌کند.

بازه زمانی را بر اساس آنچه واقعاً استفاده می‌کنید انتخاب کنید. بررسی هزینه و کیفیت روی داده‌های چند روزه انجام می‌شود، نه داده‌های چند ماهه. 30 روز شروع معقولی برای یک تیم کوچک است و 14 روز کافی است اگر فقط زمانی که مشکلی پیش می‌آید، ردیابی را باز می‌کنید.

مورد دوم جداول لاگ سیستم خود ClickHouse است و این مورد افراد را غافلگیر می‌کند، زیرا پس از پیکربندی retention، دیسک همچنان به رشد خود ادامه می‌دهد. 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" اجرا کنید. اگر جداول سیستم در صدر لیست قرار دارند، آن‌ها را با یک config overlay غیرفعال کنید، زیرا 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>

آن را mount کرده و ClickHouse را restart کنید.

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 فایل‌های رویداد آپلود شده در bucket شما را ردیابی می‌کند. اگر یک سیاست چرخه حیات (lifecycle policy) روی bucket تنظیم کرده‌اید، به این جدول نیز یک TTL مشابه بدهید تا این دو از هم فاصله نگیرند.

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

یک هشدار ساده df -h نیز روی دیسک داده قرار دهید. ردیابی‌ها به‌طور یکنواخت رشد نمی‌کنند. آن‌ها در روزی که یک agent جدید عرضه می‌کنید رشد می‌کنند و اولین نشانه آن نباید شکست در ingestion باشد.

پشتیبان‌گیری از Postgres و ClickHouse

پشتیبان‌گیری از Langfuse شامل سه بخش است. Postgres داده‌های کاربران، سازمان‌ها، پروژه‌ها و کلیدهای API را نگه می‌دارد. ClickHouse داده‌های traces را ذخیره می‌کند و MinIO رویدادهای خام را در خود جای می‌دهد. اگر فقط Postgres را بازیابی کنید، ورود به سیستم کار می‌کند اما تاریخچه‌ای وجود نخواهد داشت. اگر فقط ClickHouse را بازیابی کنید، تاریخچه وجود دارد اما کسی نمی‌تواند برای مشاهده آن وارد سیستم شود.

پایگاه‌داده Postgres یک pg_dump ساده است که روش توصیه‌شده در مستندات پشتیبان‌گیری Langfuse نیز همین است.

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

پشتیبان‌گیری از ClickHouse به دقت بیشتری نیاز دارد، زیرا کپی کردن دایرکتوری داده‌ها در حین اجرای عملیات ادغام (merges)، منجر به یک نسخه پشتیبان منسجم نمی‌شود. رویکرد ساده در یک سرور واحد، متوقف کردن container و آرشیو کردن 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 نام پروژه را به ابتدای آن اضافه می‌کند؛ بنابراین اگر یک clone در دایرکتوری با نام langfuse ایجاد کنید، نام نهایی langfuse_langfuse_clickhouse_data خواهد بود. اگر در این مورد اشتباه کنید، docker run بدون هیچ هشداری یک volume جدید و خالی ایجاد می‌کند و آرشیو شما حاوی هیچ داده‌ای نخواهد بود.

container وب، هر رویداد ورودی را پیش از پردازش توسط worker در bucket می‌نویسد، بنابراین توقف کوتاه ClickHouse معمولاً باعث می‌شود worker پس از آن عملیات را دوباره تلاش کند. این کار را در ساعات کم‌ترافیک انجام دهید و زمان توقف را کوتاه نگه دارید. برای نمونه‌های پرکاربردتر، دستور BACKUP DATABASE default TO S3(...) در خود ClickHouse یک نسخه پشتیبان منسجم بدون نیاز به متوقف کردن سرور ایجاد می‌کند. MinIO بخش سوم است و استفاده از mc mirror یا replication در MinIO به یک bucket خارج از سرور، این بخش را پوشش می‌دهد. هر خروجی که تهیه می‌کنید را از سرور خارج کنید؛ این همان کاری است که پشتیبان‌های رمزنگاری‌شده restic روی یک VPS برای آن انجام می‌شوند.

Redis نیازی به پشتیبان‌گیری ندارد. این سرویس صف و کش را نگه می‌دارد، بنابراین از دست دادن آن فقط منجر به از دست رفتن رویدادهای در حال پردازش می‌شود و داده‌های قدیمی‌تر آسیب نمی‌بینند.

هشدار مربوط به انسجام داده‌ها واقعی است و باید به‌صراحت بیان شود. Postgres و ClickHouse در لحظات متفاوتی dump می‌شوند، بنابراین بازیابی ممکن است باعث شود یک ردیف پروژه بدون trace باقی بماند یا traceهایی متعلق به پروژه‌ای که دیگر وجود ندارد، باقی بمانند. Langfuse این وضعیت را تحمل می‌کند، اما سعی کنید هر دو dump را در بازه زمانی نزدیک به هم و در ساعات کم‌ترافیک تهیه کنید. bucket رویدادها در واقع شبکه ایمنی اصلی شماست، زیرا Langfuse هر رویداد ورودی را پیش از پردازش در آنجا ذخیره می‌کند.

حداقل یک بار بازیابی را در یک محیط آزمایشی (scratch stack) تست کنید. این تنها راهی است که متوجه می‌شوید نام volume اشتباه است؛ بهتر است این موضوع را الان بفهمید تا در زمان بروز قطعی واقعی.

نخستین مواردی که باید بررسی کنید

چهار مورد زیر در هفتهٔ اول اهمیت ویژه‌ای دارند.

  • هزینه به ازای هر trace. ابزار Langfuse هزینه را بر اساس نام مدل و تعداد توکن‌های مصرفی محاسبه می‌کند؛ بنابراین traceها را بر اساس هزینه مرتب کرده و پرهزینه‌ترین آن‌ها را از ابتدا تا انتها بررسی کنید. پاسخ معمولاً در یک prompt نهفته است که بیش از حد بزرگ شده است: سندی کامل که در context کپی شده یا تاریخچهٔ گفتگویی که هیچ‌کس آن را کوتاه نمی‌کند. هنگامی که این موضوع را مشاهده کردید، کنترل هزینه‌های یک عامل هوش مصنوعی از یک حدس‌وگمان به یک وظیفهٔ مهندسی تبدیل می‌شود.
  • تفکیک مصرف توکن بر اساس ورودی و خروجی. توکن‌های ورودی تعداد زیادی دارند و ارزان هستند، توکن‌های خروجی تعداد کمی دارند و گران‌اند، و توکن‌های ورودیِ کش‌شده (cached) حتی ارزان‌تر هستند. همین حساب‌وکتاب در نحوه محاسبه مصرف توکن در Claude Code تشریح شده است و برای هر عاملی که خودتان می‌نویسید نیز صدق می‌کند.
  • صدک‌های تأخیر (Latency percentiles). مقدار میانه (median) مشکل را پنهان می‌کند. p95 و p99 نقاطی هستند که timeoutها در آنجا رخ می‌دهند و در یک حلقهٔ عامل (agent loop)، یک فراخوانی ابزار کند در p95، در تعداد تکرارها ضرب می‌شود.
  • فراخوانی‌های ناموفق ابزار. مشاهدات را بر اساس سطح ERROR فیلتر کنید. ابزاری که 5 درصد مواقع شکست می‌خورد، در نرخ موفقیت کلی دیده نمی‌شود اما در traceها کاملاً مشهود است؛ جایی که می‌بینید مدل تلاش می‌کند دوباره سعی کند و سپس توکن‌های زیادی را برای دور زدن مشکل هدر می‌دهد.

بازهٔ نگهداری (retention window) را تنظیم کنید و داشبوردی را انتخاب کنید که هر هفته در همان روزی که deploy می‌کنید، آن را بررسی خواهید کرد. ابزار مشاهده‌پذیری (observability) که هیچ‌کس آن را باز نمی‌کند، صرفاً پایگاه‌داده‌ای است که دیسک را پر می‌کند.

FAQ

Langfuse در حالت self-hosted به چه مقدار حافظه نیاز دارد؟

برای یک ماشین مجازی، 4 هسته CPU و 16 گیگابایت حافظه در نظر بگیرید که مطابق با توصیه راهنمای Docker Compose برای Langfuse است؛ به علاوه حدود 100 گیگابایت فضای ذخیره‌سازی. حداقل‌های اعلام‌شده برای هر مؤلفه شامل 8 گیگابایت برای ClickHouse و 4 گیگابایت برای هر یک از کانتینرهای web و worker است؛ همچنین Postgres، Redis و MinIO نیز به حافظه جداگانه نیاز دارند. با 8 گیگابایت حافظه می‌توان یک نمونه برای توسعه‌دهنده اجرا کرد. با 2 گیگابایت حافظه این کار ممکن نیست: ClickHouse در حین mergeهای پس‌زمینه توسط هسته سیستم‌عامل (kernel) متوقف (kill) می‌شود و dmesg مقدار Out of memory: Killed process را نشان می‌دهد.

چرا با وجود تنظیم دوره نگهداری داده (retention)، دیسک ClickHouse همچنان پر می‌شود؟

تنظیمات retention فقط داده‌های خود 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 را restart کنید و برای بازیابی فضای اشغال‌شده، جداول موجود را حذف (drop) کنید.

حداقل دوره نگهداری داده در Langfuse چقدر است؟

سه روز. دوره نگهداری برای هر پروژه در تنظیمات پروژه یا از طریق API پروژه‌ها تعیین می‌شود و یک job شبانه، traceها، observationها، scoreها و دارایی‌های رسانه‌ای قدیمی‌تر از این بازه را از ClickHouse و blob storage پاک می‌کند. حذف داده‌ها غیرقابل بازگشت است، بنابراین اگر به تاریخچه فراتر از این بازه نیاز دارید، ابتدا یک export از blob storage پیکربندی کنید.

آیا باید از هر دو دیتابیس Postgres و ClickHouse نسخه پشتیبان تهیه کنم؟

بله، زیرا داده‌های متفاوتی را ذخیره می‌کنند. Postgres شامل کاربران، سازمان‌ها، پروژه‌ها و کلیدهای API است و ClickHouse داده‌های مربوط به traceها را نگه می‌دارد. بازیابی فقط Postgres منجر به نمونه‌ای می‌شود که می‌توانید به آن وارد شوید، اما هیچ داده‌ای در آن وجود ندارد. از bucket مربوط به MinIO نیز نسخه پشتیبان تهیه کنید، زیرا رویدادهای خام دریافتی را نگه می‌دارد که نزدیک‌ترین منبع به حقیقت (source of truth) در این پشته نرم‌افزاری است.

آیا می‌توانم تنظیمات فعلی OpenTelemetry را به سمت Langfuse خود-میزبانی‌شده هدایت کنم؟

بله. Langfuse نسخه v4 و SDKهای آن بر پایه OpenTelemetry ساخته شده‌اند و ابزارهای instrumentation مربوط به Anthropic و OpenAI مستقیماً به آن export می‌کنند. در پایتون، دستور pip install langfuse opentelemetry-instrumentation-anthropic را اجرا کنید، AnthropicInstrumentor().instrument() را یک‌بار در زمان شروع برنامه فراخوانی کنید و LANGFUSE_PUBLIC_KEY، LANGFUSE_SECRET_KEY و LANGFUSE_BASE_URL را روی host خود تنظیم کنید. پیش از جستجو برای داشبوردی که نمایش داده نمی‌شود، اتصال را با langfuse.auth_check() تأیید کنید.