هفته گذشته کاری گرفتم: ساخت یک نمونه اولیه پشتیبانی هوشمند برای تیمی که سیستم تیکت SaaS میسازد. باید ظرف یک هفته راه میافتاد و علاوه بر آن، کیفیت پاسخدهی چهار مدل GPT-4o، DeepSeek-V3، Qwen-Max و Doubao بهصورت جانبی مقایسه میشد. در ظاهر سخت به نظر نمیرسد، اما وقتی دستبهکار شدم فهمیدم که در یکپارچهسازی چندمدلی، همه مشکلات در جزئیات پنهاناند. این مقاله روند کار را ثبت میکند تا همکارانی که قصد مقایسه چندمدلی دارند، کمی در وقت صرفهجویی کنند.
مدیریت Key: ۵ پلتفرم، ۵ پنل مدیریت، اول حسابها را روشن کنید
اولین دردسر نوشتن کد نبود، مدیریت Key بود. چهار مدل از چهار پلتفرم بودند و بهعلاوه یک پلتفرم پشتیبان، پنج پنل و پنج کنسول با فرمت Key، روش مشاهده سهمیه و قوانین محدودیت نرخ متفاوت. برخی پلتفرمها Key را مستقیم و واضح نشان میدهند، بعضی دیگر باید ابتدا زیرحساب بسازید و سپس تخصیص دهید. در مرحله نمونه اولیه برای سرعت، همه Keyها را در یک فایل .env ریختم. نتیجه این شد که روز بعد با بالا رفتن حجم تست، Key یکی از سرویسدهندهها محدود شد و از پیام خطا اصلاً نمیشد فهمید مشکل کدام سرویسدهنده است.
بعداً یک لایه نگاشت تنظیمات اضافه کردم و به هر Key یک نام مستعار و برچسب کاربرد چسباندم و در لاگها فقط نام مستعار چاپ میشد. راه سادهتر این است که از یک پلتفرم تجمیعی AI API استفاده کنید تا یک Key همه مدلها را مدیریت کند. در مقایسه، token8341 را امتحان کردیم؛ دروازه مدل آن احراز هویت چند مدل بزرگ داخلی را یکجا جمع میکند و برای تغییر مدل فقط نام مدل در تنظیمات عوض میشود و Key دست نمیخورد. برای مرحله نمونه اولیه، نگهداشتن چهار مجموعه منطق احراز هویت کمتر دقیقاً همان چیزی است که یک هفته زمان را کافی میکند.
سازگاری SDK: رابط هر شرکت شکل متفاوتی دارد
مرحله نصب SDK نصف صبر آدم را از بین میبرد. اکوسیستم SDK سبک OpenAI بالغترین است و بسیاری از شرکتها ادعای سازگاری میکنند، اما وقتی واقعاً وصل میشوید میبینید نام پارامترها نمیخواند. مثلاً در بعضی پلتفرمها temperature همان temperature است، بعضی آن را با top_p قاطی میکنند و بعضی max_tokens را به max_output_tokens تغییر دادهاند. کلید جریان هم یکسان نیست؛ بعضی از stream=True استفاده میکنند و بعضی باید جداگانه stream_options بفرستند.
رویکرد من این بود که یک لایه آداپتور انتزاعی بسازم، به بیرون فقط یک تابع فراخوانی یکپارچه ارائه دهم و در داخل بر اساس سرویسدهنده شاخهبندی کنم. اینطور کد کسبوکار تفاوتها را حس نمیکند. اگر نمیخواهید این لایه را خودتان بنویسید، راهکار سازگار با OpenAI SDK خیلی کار را راحت میکند؛ با تغییر یک خط base_url میتوانید مدل را عوض کنید و پیچیدگی یکپارچهسازی چندمدلی مستقیماً از لایه کد به لایه تنظیمات منتقل میشود. در مرحله اعتبارسنجی نمونه اولیه، این معاوضه ارزشش را دارد.
خروجی جریانی: پیادهسازی پروتکل SSE در هر شرکت متفاوت است
پشتیبانی هوشمند باید جریانی باشد، وگرنه کاربر سه ثانیه منتظر میماند تا متن را ببیند و تجربه کاملاً خراب میشود. مشکل اینجاست که جزئیات پیادهسازی پروتکل SSE در هر شرکت متفاوت است. بعضی پلتفرمها در هر chunk ساختار کامل event را میفرستند، بعضی فقط فیلد data را push میکنند؛ نشانه پایان در بعضی [DONE] است و در بعضی فیلد finish_reason تنظیم میشود؛ بعضی هم در میانه بستههای heartbeat تزریق میکنند که هنگام تجزیه در فرانتاند بهراحتی با محتوا اشتباه گرفته میشوند.
ابتدا parser را بر اساس فرمت OpenAI نوشتم، اما با وصل کردن سرویسدهنده دوم به هم ریخت. راهحل این بود که یک میانافزار یکپارچه تجزیه SSE بنویسم و chunkهای همه شرکتها را به یک ساختار رویداد یکسان نرمالسازی کنم تا فرانتاند فقط همان یکی را بشناسد. اشتباهی که مرتکب شدم این بود: به «سازگاری کامل» نوشتهشده در مستندات اعتماد نکنید؛ حتماً بازگشت واقعی را با capture ببینید، چون مستندات و پیادهسازی اغلب با هم فاصله دارند.
مدیریت استثنا: وقتی یک سرویسدهنده timeout میدهد، چطور خودکار جایگزین کنیم
بعد از راه افتادن تست مقایسه، آزاردهندهترین چیز timeout یک سرویسدهنده بود. در یکی از تستهای فشار، پاسخ Qwen-Max ناگهان کند شد، کل زنجیره پشتیبانی قفل شد و فرانتاند مدام در حال چرخیدن بود. در مرحله نمونه اولیه مکانیزم تنزل نداشتیم و اگر یکی میافتاد همه میافتادند.
بعداً یک لایه مسیریابی مدل اضافه کردم، برای هر درخواست آستانه timeout گذاشتم، در صورت timeout بهطور خودکار به مدل پشتیبان سوییچ میشد و لاگ سوییچ هم ثبت میشد. نکته اینجاست که سوییچ نباید بدون منطق retry شود؛ باید تفکیک کنید که timeout شبکه است یا مسدودسازی بازبینی محتوا. اولی قابل سوییچ است، اما دومی را حتی اگر سوییچ کنید فایدهای ندارد. ارزش مسیریابی مدل بزرگ همینجاست: تبدیل دسترسپذیری از یک نقطه به چند نقطه. در پروژه ما با زمانبندی SiliconFlow اعتبارسنجی مشابهی انجام دادیم و مدل را بر اساس نوع وظیفه بهطور خودکار انتخاب کردیم؛ این زنجیره تنزل در timeout نسبتاً پایدار اجرا شد.
پایش هزینه: مصرف Token چطور جمعآوری شود
در طول یک هفته، غیرمنتظرهترین هزینه، Token بود. چهار مدل بهصورت موازی تست میشدند؛ حجم فراخوانی روزانه زیاد نبود، اما چون جمعآوری نداشتیم، هنگام تسویه پایان ماه فهمیدیم مصرف یکی از سرویسدهندهها سه برابر برآورد است. دلیلش این بود که در خروجی جریانی، فیلد usage بازگشتی بسیاری از پلتفرمها خالی است و باید خودتان بر اساس کاراکتر تخمین بزنید که دقیق نیست.
کاری که کردم این بود که در لایه دروازه بهصورت یکپارچه حسابداری کنم: هر فراخوانی نام مدل، Token ورودی و خروجی، زمان صرفشده و اینکه تنزل رخ داده یا نه را ثبت کند و در یک جدول ذخیره شود. در مدل پرداخت بهازای مصرف، این حساب باید خودتان روشن باشد و نمیتوانید کاملاً به پنل پلتفرم تکیه کنید. در مقایسه قیمت API هم توجه کنید که مدل با قیمت پایینتر اگر قوانین محاسبه Token خروجیاش پیچیده باشد، هزینه واقعی ممکن است بیشتر شود.
در یک جمله: هسته نمونه اولیه مقایسه چندمدلی این نیست که یک مدل را راه بیندازید، بلکه این است که چهار کار اتصال، جریان، تنزل و حسابداری را به یک لایه یکپارچه تبدیل کنید. اگر میخواهید درباره انتخاب دروازه مدل عمیقتر بدانید، میتوانید مطالب مرتبط با تجمیع API را بیشتر مطالعه کنید.
نویسنده: ژو مینگژِ
تاریخ انتشار: ۶ اکتبر ۲۰۲۶