چیتشیت
عرضه یک برنامه عملیاتی LLM
Shipping a Production LLM App
پایپلاین درخواست، تلاش مجدد، زنجیرههای جایگزین، استریمهای SSE، ازسرگیری، محدودیتهای دمو و یک درگاه جایگزین.
فهرست مطالب
وظایف درس
کار و کد| وظیفه | کد |
|---|---|
| نصب استک سرور | pip install fastapi uvicorn |
| اجرای برنامه با 4 ورکر | uvicorn production_app:app --host 0.0.0.0 --port 8000 --workers 4 |
| انتخاب مدل اصلی | LLM_MODEL=gpt-4o |
| راهاندازی سرور دمو | python3 code/main.py --serve |
| الزامی کردن توکن دسترسی | export DEMO_ACCESS_TOKEN=<token> |
| ارسال یک پیشبینی | curl -s -X POST http://127.0.0.1:8000/api/predict -H 'Content-Type: application/json' -d '{"text": "ok"}' |
| مشاهده استریم رویدادها | curl -N -X POST http://127.0.0.1:8000/api/stream -H 'Content-Type: application/json' -d '{"text": "<text>"}' |
| اجرای آزمونهای دمو | python3 -m unittest discover -s code/tests -v |
پایپلاین درخواست
معماریتلاش مجدد با عقبگرد
کدpython
async def call_llm_with_retry(prompt, model, max_retries=3):
for attempt in range(max_retries + 1):
try:
... # یک فراخوانی ارائهدهنده
except (ConnectionError, TimeoutError):
if attempt < max_retries:
backoff = min(2 ** attempt + random.uniform(0, 1), 10) # سقف 10 ثانیه
await asyncio.sleep(backoff)
else:
raise
raise ConnectionError(f"All {max_retries} retries exhausted for {model.value}")
# call_with_fallback walks FALLBACK_CHAIN, preferred model first.
مدیریت خطاها
عیبیابیکد 429 از API
- علت
- محدودیت نرخ درخواست
- راهحل
- تلاش مجدد با عقبگرد. قرار دادن درخواست در صف
کد 500 از API
- علت
- خطای سرور
- راهحل
- 3 بار تلاش مجدد. سوئیچ به مدل جایگزین
تایماوت API بیش از 30 ثانیه
- علت
- ارائهدهنده کند
- راهحل
- یک بار تلاش مجدد. پرامپت کوتاهتر، مدل کوچکتر
خروجی ناقص یا ناهماهنگ
- علت
- شکست مدل
- راهحل
- تلاش مجدد همراه با درج خطا در پرامپت
مسدود شدن توسط گاردریل
- علت
- ورودی ناامن
- راهحل
- بدون تلاش مجدد. توضیح علت مسدودسازی
از کار افتادن پایگاه داده برداری
- علت
- خطای برنامه
- راهحل
- رد شدن از کانتکست RAG
از کار افتادن کش
- علت
- خطای برنامه
- راهحل
- فراخوانی مستقیم LLM
شکست تمام مدلها
- علت
- اتمام زنجیره
- راهحل
- پاسخ کششده، سپس عذرخواهی ثابت
پیشفرضهای درون کد
پیشفرضها| تنظیم | پیشفرض | نکته |
|---|---|---|
similarity_threshold | 0.92 | امتیاز اصابت کش معنایی |
ttl_seconds | 3600 | طول عمر ورودی کش |
max_entries | 10000 | اندازه کش |
max_retries | 3 | به ازای هر مدل در زنجیره |
MAX_CHARS | 500 | محدودیت متن دمو |
MAX_BODY_BYTES | 4096 | بدنه بزرگتر خطای 413 میدهد |
BUCKET_CAPACITY | 5 | شارژ مجدد 0.5 توکن در ثانیه |
DAILY_CALL_CAP | 2000 | بیشتر از سقف خطای 503 میدهد |
کدهای خطای دمو
دستورها| دستور | عملکرد |
|---|---|
413 body_too_large | بدنه بیش از 4,096 بایت |
408 request_timeout | توقف بدنه پیش از Content-Length |
415 unsupported_media_type | نوع محتوا JSON نیست |
400 bad_json | بدنه یک شیء JSON نیست |
422 text_too_long | متن بیش از 500 کاراکتر |
422 control_characters | متن دارای کاراکترهای کنترلی است |
429 rate_limited | سطل توکن خالی است. هدر Retry-After را بخوانید |
503 usage_cap_reached | سقف روزانه پر شد. هدر Retry-After را بخوانید |
فرمت ارتباطی SSE
کدbash
event: token # نوع محتوا text/event-stream
data: {"index":0,"text":"Label:"}
# یک خط خالی پایان هر رویداد را مشخص میکند
event: token
data: {"index":1,"text":" positive."}
event: done
data: {"label":"positive","score":1.0,"tokens":11}
ازسرگیری یک پاسخ استریمشده
گامبهگام- SSEDecoderبایتهای چندتکهی UTF-8 را به رویدادهای کامل متصل میکند.
- applyنیازمند شناسههای مثبت و پیوسته در حافظه است. وجود فاصله خطای
noncontiguous answer event IDمیدهد. - applyشناسه تکراری با همان داده بیاثر است. دادهی متفاوت خطا میدهد.
- applyنوع داده
text،citationیاcompleteاست. ارجاعات نیازمند http یا https هستند. - connectقطعی ارتباط پیش از
completeوضعیت راinterruptedمیکند. - connectفراخوانی بعدی
Last-Event-IDرا ارسال کرده و فقط رویدادهای بعدی را میگیرد. - traceمقدار
schemaVersion: 1را همراه رویدادهای پذیرفتهشده مینویسد. متدreplayوضعیت را بازسازی میکند.
سیاست طرح پشتیبان درگاه
مقایسه| عامل | کد 2xx | کد 429 یا 5xx | سایر وضعیتها | لغو درخواست |
|---|---|---|---|---|
ClassifyStatus | success | retry | terminal | ندارد |
| ارائهدهنده بعدی | خیر | بله | خیر | خیر |
| وضعیت مسیر | completed | در پایان exhausted | terminal-error | cancelled |
| پاسخ پروکسی | بدنه دریافتی از بالادست | در پایان 502 | 502 | 504 |
فایل ارائهدهندگان درگاه
کدbash
{"providers": [
{"name": "primary", "url": "http://127.0.0.1:11434/v1/chat/completions"},
{"name": "backup", "url": "http://127.0.0.1:11435/v1/chat/completions",
"key_env": "ORCHARD_BACKUP_KEY"}], # نام متغیر محیطی، نه کلید اصلی
"max_attempts": 2, # 1 تا 16، یک بازدید به ازای هر ارائهدهنده
"max_response_bytes": 65536, # 1 بایت تا 16 مبیبایت
"timeout_ms": 4000} # 1 تا 5000، کل مسیر
وظایف پروژه
کار و کد| وظیفه | کد |
|---|---|
| ساخت یک پوشه کاری | python3 scripts/project_test.py <project> --init /tmp/<project>-work |
| نمرهدهی به یک مرحله | python3 scripts/project_test.py <project> --stage 1 --path /tmp/<project>-work --strict |
| استریم دمو، قطعی و سپس ازسرگیری | cd projects/streaming-answer-recovery/solution && python3 demo.py |
| خواندن اندپوینت SSE خودتان | node --experimental-strip-types cli.ts <url> /tmp/trace.json |
| دموی درگاه، خطای 503 سپس پشتیبان | cd projects/llm-gateway-with-fallbacks/solution && go run . |
| اجرای پروکسی درگاه | go run . --config /tmp/providers.json --listen 127.0.0.1:8088 |
| فراخوانی پروکسی | curl --fail-with-body http://127.0.0.1:8088/v1/chat/completions --data-binary @fixtures/request.json |
اهداف و چکلیست
نکتهها- تاخیر P50 زیر 2 ثانیه. تاخیر P99 زیر 10 ثانیه.
- نرخ اصابت کش بالای 30%. نرخ مسدودسازی گاردریل زیر 5%.
- هزینه هر درخواست زیر 0.01 دلار.
- محدودیت نرخ درخواست هر کاربر بین 10 تا 50 درخواست در دقیقه.
- تست بار با 100 کاربر همزمان پیش از راهاندازی.
پرسش و پاسخ
پرسش و پاسخ- چرا POST و نه
EventSource؟ - آبجکت
EventSourceفقط GET ارسال میکند. متد GET ورودی را در لاگهای URL ذخیره میکند. - شکست مدل پس از وضعیت 200؟
- وضعیت ارسال شده است. یک رویداد
errorدر استریم بفرستید. - در بدنه خطا چه مواردی قرار دارد؟
- مقادیر
code،message،hintوrequest_id.