با تکامل برنامههای مدل زبانی بزرگ (LLM) از رباتهای ساده پرسش و پاسخ به سیستمهای پیچیده تولید تقویتشده با بازیابی چندعاملی (RAG)، قابلیت مشاهده (Observability) به یک گلوگاه حیاتی تبدیل شده است. در یک تنظیم RAG تکعاملی، یک درخواست به صورت خطی جریان مییابد: پرسش، بازیابی، تولید. با این حال، در معماریهای چندعاملی، درخواستها به زیرعاملهای موازی شاخه میشوند که هر کدام وظایف متمایزی مانند تجزیه اسناد، جستجوی معنایی یا تأیید حقایق را انجام میدهند. این پیچیدگی منجر به تکهتکه شدن ردیابی میشود، جایی که یک درخواست کاربر واحد در دهها اسپن (Span) نامرتبط پراکنده میشود و عیبیابی را تقریباً غیرممکن میسازد.
لاگگیری سنتی به دلیل فقدان زمینه (Context) کافی نیست. اینجاست که OpenTelemetry (OTel) درخشش میکند. با پیادهسازی الگوهای ابزارگذاری سفارشی، میتوانید این ردیابیهای تکهتکه را دوباره به هم متصل کنید و دیدی یکپارچه از زیرساخت هوش مصنوعی خود ارائه دهید. در این پست، بررسی میکنیم که چگونه میتوان این الگوها را با استفاده از پایتون ساختاردهی کرد.
چالش جریانهای کاری هوش مصنوعی توزیعشده
سیستم پشتیبانی مشتری را با سه عامل تصور کنید: عامل تریاژ (Triage Agent)، عامل بازیابی دانش و مولد پاسخ. وقتی کاربر یک پرسش را ارسال میکند، عامل تریاژ قصد را تعیین کرده و وظایف را واگذار میکند. اگر قصد فنی باشد، عامل دانش یک پایگاه داده برداری را جستجو میکند؛ اگر مربوط به صورتحساب باشد، یک پایگاه داده SQL را پرسوجو میکند.
بدون همبستگی مناسب، اسپنهای تولید شده توسط این عوامل به صورت جزایر جداگانه در داشبورد مانیتورینگ عملکرد برنامه (APM) شما ظاهر میشوند. شما نمیتوانید ببینید که تأخیر بالا در مولد پاسخ ناشی از زمانبر شدن (Timeout) در جستجوی برداری عامل دانش بوده است. برای حل این مشکل، باید زمینه را به صراحت در مرزهای عاملها منتشر کنیم.
پیادهسازی ابزارگذاری سفارشی با OpenTelemetry
استراتژی اصلی شامل ایجاد یک بستهبندی سفارشی است که زمینه اجرای هر عامل را ضبط میکند. ما از opentelemetry-api برای مدیریت دستی اسپنها استفاده میکنیم تا اطمینان حاصل کنیم که تماسهای تودرتو در منطق داخلی یک عامل، زیر یک اسپن والد واحد گروهبندی میشوند. این موضوع به ویژه زمانی مفید است که عاملهای شما از چندین کتابخانه (مانند LangChain، LlamaIndex یا کلاینتهای HTTP سفارشی) استفاده میکنند که ممکن است به خوبی به صورت خودکار ابزارگذاری نشوند.
در زیر یک مثال عملی از یک دکوراتور سفارشی آورده شده است که روش اجرای یک عامل را در بر میگیرد و ساختار ردیابی یکسانی را تضمین میکند:
import functools
from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode
tracer = trace.get_tracer(__name__)
def agent_span(agent_name: str):
"""
دکوراتوری که روش یک عامل را در یک اسپن OpenTelemetry اختصاصی در بر میگیرد.
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
with tracer.start_as_current_span(f"agent.{agent_name}") as span:
try:
# تنظیم ویژگیها برای فیلتر کردن بهتر در ابزارهای APM
span.set_attribute("agent.name", agent_name)
span.set_attribute("args", str(args)[:100]) # پاکسازی ورودیها
# اجرای منطق عامل
result = func(*args, **kwargs)
# علامتگذاری موفقیت
span.set_status(Status(StatusCode.OK))
return result
except Exception as e:
# علامتگذاری شکست و ثبت خطا
span.set_status(Status(StatusCode.ERROR, str(e)))
span.record_exception(e)
raise
return wrapper
return decorator
# مثال استفاده
class KnowledgeRetrievalAgent:
@agent_span("knowledge_retrieval")
def search_docs(self, query: str):
# شبیهسازی جستجوی گرانقیمت در پایگاه داده برداری
return {"doc_id": "123", "content": "پاسخ 42 است."}
در این الگو، دکوراتور @agent_span به عنوان یک ظرف برای تمام عملیات داخلی انجام شده توسط آن عامل خاص عمل میکند. وقتی چندین عامل را به هم زنجیر میکنید، اسپنهای فرزند (از منطق داخلی) به طور خودکار درون اسپن عامل قرار میگیرند و دیدی سلسلهمراتبی به جای لیستی تخت از رویدادها ایجاد میکنند.
همبستگی اسپنها در مرزهای سرویس
برای سیستمهای چندعاملی که به عنوان میکروسرویسها مستقر میشوند، انتشار زمینه حیاتی است. باید اطمینان حاصل کنید که TraceId و SpanId از طریق هدرها (مثلاً هدرهای HTTP در REST APIها یا هدرهای پیام در Kafka) ارسال میشوند. ابزارهای انتشار زمینه OpenTelemetry این کار را به صورت خودکار انجام میدهند اگر ابزارگذاری کلاینت یا سرور HTTP شما فعال باشد. با این حال، برای بروکرهای پیام سفارشی یا ناوگانهای رویداد داخلی، ممکن است نیاز به تزریق دستی حامل زمینه داشته باشید:
from opentelemetry.propagate import inject
def send_message_to_agent(queue, message, agent_name):
# تزریق زمینه ردیابی به متادیتای پیام
headers = {}
inject(headers)
queue.publish({
"agent": agent_name,
"payload": message,
"trace_headers": headers
})
نتیجهگیری
حل تکهتکه شدن ردیابی در سیستمهای RAG چندعاملی تنها درباره جمعآوری دادههای بیشتر نیست؛ بلکه درباره ساختاردهی هوشمندانه آن دادهها است. با اتخاذ الگوهای ابزارگذاری سفارشی با OpenTelemetry، توسعهدهندگان میتوانند دید دقیقتری از تعاملات عامل کسب کنند، گلوگاهها را در جریانهای کاری موازی شناسایی کنند و در نهایت برنامههای هوش مصنوعی قابلاعتمادتری بسازند. با افزایش پیچیدگی این سیستمها، قابلیت مشاهده از یک ویژگی «خوب است که داشته باشیم» به ستون فقرات تعالی عملیاتی تبدیل خواهد شد.