معماری
مسیر داغ و تأخیر
مسیر اصلی، مسیر پیام بازدیدکننده تا اولین توکن است. دارای یک قرارداد سخت ۱ ثانیه p95 است — فراتر از آن، تنش "آیا این چیز زنده است؟" شکسته میشود. هر چیزی که نیازی به اجرا در مسیر اصلی ندارد، از آن خارج میشود.
بودجه تأخیر
تجزیه هدف برای اولین توکن در p95:
| مرحله | هدف p95 |
|---|---|
| دریافت HTTP + احراز هویت | ۳۰ میلیثانیه |
| بررسی مسیر کوتاه گزینشی | ۵ میلیثانیه |
| جستجوی هوشمند | ۱۲۰ میلیثانیه |
| جستجوی هوشمند (ANN) | ۸۰ میلیثانیه |
| رتبهبندی مجدد | ۱۲۰ میلیثانیه |
| مونتاژ راهنما | ۱۰ میلیثانیه |
| زمان هوش مصنوعی تا اولین توکن | ۵۰۰ میلیثانیه |
| مجموع | ~۸۶۵ میلیثانیه |
فضای باقیمانده برای بقیه کمتر از ۱۵۰ میلیثانیه است. هر چیزی فراتر از اولین توکن بهصورت تدریجی پخش میشود — زمان پاسخ کامل با نرخ توکن محدود میشود، نه با بودجه.
قوانین سخت
این موارد توسط بازبینی کد و تستها اعمال میشوند:
- هیچ نوشتهای در پایگاه داده در مسیر اصلی وجود ندارد. ماندگاری پس از پایان جریان بهصورت ناهمگام انجام میشود.
- هیچ وبهوک همزمانی وجود ندارد. وبهوکهای خروجی بهعنوان وظایف صف ارسال میشوند.
- هیچ تلاش مجددی وجود ندارد. اگر یک سرویسدهنده در میانه جریان با خطا مواجه شود، کاربر یک خطای خوشفرم میبیند و ویجت بهطور خودکار در سمت کلاینت تا ۳ بار تلاش مجدد میکند. سرور حلقه نمیزند.
- هیچ پرسوجوی N+1 وجود ندارد. همه خوانشها بهصورت دستهای انجام میشوند. تاریخچه اخیر از Redis (
conv:{id}:history) میآید، نه از Postgres. - یک فراخوانی هوش مصنوعی در هر نوبت. بدون استدلال چند مرحلهای دستیار فروش که به چندین فراخوانی مدل منشعب شود.
- نوشتههای مسیر کوتاه پایگاه داده در زیر
emit('done')انجام میشوند. مسیرهای کوتاه تحویل انسانی، انتظار انسانی و قصد انسانی قبلاً پیامMessageبازدیدکننده را ذخیره میکردند وconversation.attributionرا قبل از رسیدن رویداد بسته شدن SSE به کلاینت، مهر میزدند. حسابرسی ۲۰۲۶-۰۵-۳۰ این موارد را از طریقpersistShortCircuitVisitorTurn()به زیرemit('done')منتقل کرد تا جریان بلافاصله بسته شود و نوشتهها پس از آن اجرا شوند. مهرescalation_offered_atکه توسط مسیر اصلی هوش مصنوعی منتشر میشود، به همان شکاف پس از انتشار موکول میشود — حلقه ابزار نوبت بعدی، ردیف را دوباره از پایگاه داده میخواند، بنابراین تأخیر، معنای درگاهبندی ابزار را تغییر نمیدهد.
چه چیزی از مسیر اصلی خارج است
همه موارد زیر پس از اتمام جریان ارسال میشوند. هیچکدام بازدیدکننده را مسدود نمیکند:
- PersistTurnJob — ذخیره پیامهای کاربر + دستیار فروش.
- IncrementUsageJob — افزایش شمارنده مکالمات ماهانه فضای کاری.
- DetectGapJob — خوشهبندی سوالات با اطمینان پایین برای گزارش شکافها.
- DispatchWebhookJob — ارسال به نقاط پایانی مشتریان مشترک (سمت فرآیند خودکار؛ وبهوک جذب مشتری زمانی که بازدیدکننده فرم مشتری را ارسال میکند، بهصورت درونخطی از طریق
SignedDispatcherفعال میشود). - AutoIndexPageVisit — سرویس همزمانی که در زمان
/initفعال میشود و یکCrawlPageJobرا برای آدرس بازدیدشده در صورت فعال بودن پردازش خودکار، در صف قرار میدهد. یک وظیفه مسیر اصلی نیست، اما دانستن محل اجرای پردازش خودکار مفید است.
کش
دو کش، مسیر اصلی را سریع نگه میدارند:
- کش بازیابی — Redis،
rag:retrieve:{agentId}:{hash(query|currentPageUrl)}، ۳۰ دقیقه TTL. همان سوال در همان صفحه، کش را میزند. زمانی که منابع تغییر میکنند، باطل میشود. - کش تاریخچه مکالمه — Redis،
conv:{convId}:history، ۲ ساعت TTL، محدود به ۱۲ پیام (۶ نوبت). در هر نوبت از این بهجای Postgres خوانده میشود.
مکانیک پخش جریانی
SSE بسیار ساده است — HTTP زنده نگهدارنده، به ازای هر توکن data: {...}\n\n بنویسید، فلاش کنید. ویجت از طریق EventSource (یا fetch + reader برای مرورگرهای قدیمیتر که EventSource روی POST ندارند) میخواند.
نکته مهم این است که پاسخ SSE قبل از شروع هر کار RAG ساخته میشود. ما بلافاصله پس از دریافت درخواست، نوشتن هدرها را شروع میکنیم تا هر پراکسی جلوی ما (Cloudflare، متعادلکننده بار) زودتر به پخش جریانی متعهد شود. تا زمانی که توکنها برسند، اتصال قبلاً باز است.
محل قرارگیری اسپنها
اسپنهای OpenTelemetry هر مرحله را میپوشانند:
widget.message.receiverag.curated.matchrag.embedrag.vector.searchrag.rerankrag.prompt.assemblerag.llm.first_tokenrag.llm.streamrag.persist.async
Honeycomb / Grafana p95 هر کدام را نشان میدهد. وقتی بودجه شکسته میشود، نقشه حرارتی اسپن معمولاً مستقیماً به متخلف اشاره میکند.
حالتهای شکست
| شکست | رفتار |
|---|---|
| سرویسدهنده هوش مصنوعی 5xx در میانه جریان | جریان یک رویداد error منتشر میکند. ویجت تا ۳ بار بهطور خودکار تلاش مجدد میکند. |
| ذخیرهساز جستجوی هوشمند در دسترس نیست | مسیر سوال را بدون زمینه برمیگرداند. اطمینان ۰.۳ است → پرچم اطمینان پایین → پاسخ "نمیدانم". |
| زمان فراخوانی جستجوی هوشمند به پایان میرسد | همان — بدون زمینه ادامه دهید، اطمینان پایین را علامتگذاری کنید. |
| سهمیه فراتر رفته | در /init گرفته میشود، هرگز به پیامها نمیرسد. ۴۲۹ بازگردانده میشود. |
اصل: بازدیدکننده همیشه یک پاسخ دریافت میکند، حتی اگر "مطمئن نیستم" باشد. دستیار فروش مجاز به نادانی است؛ اجازه شکست بیصدا را ندارد.