تصور کنید توسعهدهندهای هستید که میخواهد بدون تغییر دادن کل کد برنامه، مدل خود را از OpenAI به Z.AI تغییر دهد. Prism PHP با معرفی پشتیبانی از مدلهای GLM، این انتقال را به یک تغییر ساده در تنظیمات تبدیل کرده است. توسعهدهندگان لاراول اکنون میتوانند از مدلهای Z.AI با استفاده از همان API منعطفی (Fluent API) استفاده کنند که برای OpenAI، Anthropic، Gemini، Groq، Mistral، xAI و Perplexity به کار میبرند.
طبق مستندات این پروژه، هدف اصلی از این بهروزرسانی این است که توسعهدهندگان لاراول بتوانند با همان API منعطفی که برای مدلهای Gemini یا Claude استفاده میکنند، به قدرت مدلهای Z.AI دسترسی داشته باشند. این رویکرد برای جلوگیری از وابستگی به یک ارائهدهنده واحد است؛ مشابه آنچه در تلاش Routara برای متمرکز کردن مدیریت چندین ارائهدهنده LLM در یک نقطه اتصال واحد OpenAI مشاهده میکنیم تا انعطافپذیری سیستم افزایش یابد. همانطور که در تحلیل قبلی ما دربارهی اینکه شرکتهایی مانند Elastic InfoSec چگونه از حلقههای بهینهسازی برای کاهش هزینهی عاملهای هوشمند استفاده میکنند اشاره کردیم، زیرساختی که Prism فراهم میکند، دقیقاً همان لولهکشیست (Plumbing) که اجرای این عاملها را ممکن میسازد. برای توسعهدهندگان، چالش اصلی صرفاً فراخوانی یک API نیست، بلکه انجام این کار به گونهای است که هنگام جابجایی بین مدلهای مختلف، بدهی فنی (Technical Debt) ایجاد نشود.
این یکپارچهسازی نتیجهی یک درخواست مشارکت (Pull Request) جامع در گیتهاب است که از طریق prism-php/prism#794 ادغام شد. این فرآیند در ماه مارس ۲۰۲۴ به پایان رسید و امکان دسترسی به مدلهای GLM را فراهم آورد.
نقشه راه معماری
به گزارش تیم توسعه، این پیادهسازی از یک مدل ذهنی سختگیرانه برای بازبینی سریع و نگهداری بلندمدت پیروی میکند. Prism منطق برنامه را به سه لایه مجزا تقسیم کرده است تا مدیریت کد آسانتر شود:
- کلاسهای ارائهدهنده (Provider Classes): این کلاسها به عنوان نقطه ورودی عمل میکنند. آنها صرفاً مالک کلاینت HTTP هستند و هیچ منطق دیگری در آنها قرار ندارد.
- پردازندهها (Handlers): کلاسهای مجزایی هستند که به قابلیتهای خاص (مانند متن، خروجیهای ساختاریافته یا بردار معنایی/Embeddings) اختصاص یافتهاند. یک پردازنده، بدنه درخواست (Payload) را میسازد، آن را ارسال میکند و JSON خام دریافتی را به اشیاء مقدار Prism (Value Objects) تبدیل میکند.
- نقشهها (Maps): کلاسهای کمکی کوچک و بدون حالت (Stateless) هستند که مفاهیم Prism را به مفاهیم خاص هر ارائهدهنده ترجمه میکنند؛ برای مثال تبدیل پیامها، ابزارها، انتخاب ابزار (Tool Choice) و دلایل توقف (Finish Reasons).

جزئیات فنی یکپارچهسازی
افزودن پشتیبانی از Z.AI مستلزم نوشتن نزدیک به ۱.۵ هزار خط کد در ۲۸ فایل مختلف بود. برای اینکه سیستم بتواند ارائهدهنده Z را شناسایی و Resolve کند، سه ویرایش خاص در کد اعمال شده است:
۱. The Enum: افزودن case Z = 'z'; به Enum ارائهدهندهها برای شناسایی مدل.
۲. The Config: افزودن یک ورودی در فایل config/prism.php برای مقدار 'z' که شامل Z_URL (با مقدار پیشفرض https://api.z.ai/api/coding/paas/v4) و Z_API_KEY باشد.
۳. The Manager: افزودن یک متد کارخانهای (Factory Method) به نام createZProvider در کلاس PrismManager. مدیر (Manager) این متد را بر اساس قراردادهای تعریف شده Resolve میکند تا ارائهدهنده را متصل کند.
در کلاس Z که از کلاس انتزاعی Provider ارثبری میکند، از ویژگی #[SensitiveParameter] برای کلید API استفاده شده است تا اطمینان حاصل شود که این کلید هرگز در گزارشهای خطای سیستم (Stack Trace) نمایش داده نشود و لو نرود.
در طول فرآیند بازبینی (Review)، کلمه کلیدی final از تعریف کلاس حذف شد و متدهای client() و encoder به حالت protected تغییر یافتند. این یک تصمیم استراتژیک بود؛ زیرا توسعهدهندگان اغلب ارائهدهندهها را گسترش میدهند تا آنها را به گیتویها یا پروکسیهای سفارشی متصل کنند و بسته کردن کلاس (Sealing) بدون هیچ مزیت خاصی، این قابلیت را مسدود میکرد.
مدیریت ورودیهای چندوجهی
مدلهای Z.AI در نحوه مدیریت رسانهها با OpenAI تفاوت دارند. در حالی که بسیاری از ارائهدهندهها از پیوستهای عمومی (Generic Attachments) استفاده میکنند، Z.AI برای هر نوع رسانه به بخشهای محتوایی تایپشده و مجزا نیاز دارد. پیادهسازی فعلی از یک MessageMap استفاده میکند که ابتدا پرامپتهای سیستمی را ادغام کرده و سپس بر اساس کلاس پیام (User, Assistant, ToolResult یا System) آنها را توزیع میکند.
برای پیامهای کاربر (User Messages)، سیستم از یک DocumentMapper و یک Enum به نام DocumentType استفاده میکند تا سه مسیر مجزا را مدیریت کند:
- آدرسهای تصویر: از طریق
DocumentType::ImageUrlبه عنوانimage_urlنگاشت میشوند. - آدرسهای فایل: از طریق
DocumentType::FileUrlبه عنوانfile_urlنگاشت میشوند. - آدرسهای ویدیو: از طریق
DocumentType::VideoUrlبه عنوانvideo_urlنگاشت میشوند.
نکته حیاتی این است که متد validateMedia() بررسی میکند که ورودی حتماً یک URL باشد (isUrl()). این کار محدودیت را صریح میکند: این نقطه اتصال (Endpoint) فقط URL میپذیرد و نه دادههای Base64 (Blobs). اگر یک Blob ارسال شود، Prism بهجای اینکه اجازه دهد API یک خطای مبهم ۴۰۰ برگرداند، خودش یک استثنا (Exception) واضح پرتاب میکند.
فراخوانی ابزار و حلقههای عامل
یکپارچهسازی ابزارها بر دو نقشه متکی است: ToolMap برای تبدیل ابزارهای Prism به تعاریف تابع (Function Definitions)، و ToolChoiceMap برای مدیریت حالتهای اجباری. در ToolChoiceMap:
- اگر یک رشته (String) ارسال شود، به معنای درخواست برای یک ابزار خاص است.
- حالت
ToolChoice::Anyبه مقدارrequiredنگاشت میشود. - حالت
ToolChoice::Autoبه مقدارautoنگاشت میشود.
هر انتخاب دیگری بهشدت رد میشود تا از این اتفاق که درخواستها بهطور خاموش توسط API نادیده گرفته شوند، جلوگیری شود.
پردازنده متن (Text Handler) مدیریت حلقه ابزار چندمرحلهای را بر عهده دارد. این پردازنده درخواست را میفرستد و سپس بر اساس «دلیل پایان» (Finish Reason) شاخه میزند:
- FinishReason::ToolCalls: این حالت متد
handleToolCallsرا فعال میکند که ابزارها را اجرا کرده، یکToolResultMessageرا اضافه میکند، یک مرحله (Step) را ثبت کرده و سپس به صورت بازگشتی (Recurse) تکرار میشود. - FinishReason::Stop یا Length: این موارد متد
handleStopرا برای نهایی کردن پاسخ فعال میکنند. - دلایل ناشناخته: برای جلوگیری از شکستهای خاموش، یک
PrismExceptionپرتاب میکنند.
برای جلوگیری از اجرای بینهایت و خارج شدن عاملها از کنترل، این حلقه توسط یک سقف maxSteps که در درخواست تعریف شده، کنترل میشود. به عنوان یک اقدام دفاعی، اگر دلیل پایان نشاندهنده فراخوانی ابزار باشد اما آرایه همراه آن خالی باشد، پردازنده خطا میدهد تا پاسخهای بدساخت (Malformed) به عنوان پاسخهای خالی پذیرفته نشوند.
حل شکاف خروجیهای ساختاریافته
یکی از بزرگترین چالشهای طراحی، نبود پشتیبانی بومی Z.AI از طرحوارههای (Schema) سختگیرانه JSON بود. اگرچه نقطه اتصال کدنویسی از response_format: json_object پشتیبانی میکند، اما این تنها اعتبار کلی JSON را تضمین میکند و نه یک ساختار یا Schema خاص را.
برای حل این مسئله، توسعهدهنده یک StructuredMap پیادهسازی کرد که از MessageMap ارثبری میکند و طرحواره JSON مورد نیاز را به عنوان آخرین پیام سیستمی با استفاده از json_encode و گزینه JSON_PRETTY_PRINT اضافه میکند.
علاوه بر این، درخواست بهطور صریح مقدار ['type' => 'disabled'] را برای پارامتر «تفکر» (Thinking) ارسال میکند. این کار باعث میشود ردپاهای استدلالی (Reasoning Traces) مدل با محتوای JSON ترکیب نشوند، زیرا این یکی از رایجترین دلایل شکست در خروجیهای ساختاریافته است. با subclass کردن نقشه پیامها، درخواستهای ساختاریافته چندوجهی نیز بهطور خودکار به درستی عمل میکنند.
اعتبارسنجی با دادههای نمونه (Fixtures)
این یکپارچهسازی با استفاده از ۹ نمونه پاسخ HTTP ثبت شده (Fixtures) تأیید شده است. در این تستها از FixtureResponse::fakeResponseSequence برای بازپخش توالیهای شمارهگذاری شده در تستهای چندمرحلهای استفاده شده است. این روش اجازه میدهد تا مجموعه تستها بدون نیاز به ارسال درخواست واقعی به APIهای زنده اجرا شوند. این نمونهها موارد زیر را پوشش میدهند:
- پرامپتهای ساده و پرامپتهای سیستمی.
- فراخوانیهای موازی و اجباری ابزارها.
- خطاهای محدودیت نرخ (Rate Limiting 429).
- URLهای تصویر، فایل و ویدیو.
- اعتبارسنجی خروجیهای ساختاریافته.
یک گام حیاتی در این فرآیند، پاکسازی (Scrubbing) توکنها از دادههای نمونه پیش از Commit کردن بود. این فرآیند سختگیرانه تست و بازبینی از دسامبر تا مارس ۲۰۲۴ به طول انجامید و در نهایت با commitهای مربوط به اصلاح فرمت و بازسازی (Refactor) توسط نگهدارنده پروژه به پایان رسید.
راهنمای پیادهسازی
توسعهدهندگان اکنون میتوانند با تنظیم Z_API_KEY و استفاده از API منعطف، Z.AI را پیاده کنند. برای متن استاندارد:
Prism::text()->using('z', 'glm-4.6')->withPrompt('Write a short story about a robot learning to love')->asText();
و برای درخواستهای چندوجهی با استفاده از مدل glm-4.6v:
Prism::text()->using('z', 'glm-4.6v')->withMessages([new UserMessage('What is in this image?', additionalContent: [Image::fromUrl('https://example.com/image.png')])])->asText();
درس مهم برای کسانی که میخواهند ارائهدهندههای جدید اضافه کنند این است که ابتدا یک ارائهدهنده مشابه را مطالعه کنند و از کپی کردن نقشهها بدون تأیید avoided کنند؛ برای مثال، توسعهدهنده این پروژه در ابتدا بهاشتباه یک نقشه دلیل پایان متعلق به xAI را بازیافت کرده بود که با وجود کامپایل شدن، از نظر منطقی غلط بود. برای تکمیل درخواست مشارکت (PR)، ارائه مستندات کامل و یک صفحه ارائهدهنده در مسیر docs/providers/ الزامی است.
گام بعدی شما
- اگر از اکوسیستم Laravel استفاده میکنید، پکیج Prism را بهروزرسانی کرده و مدلهای GLM را برای کارهای کدنویسی تست کنید.
- برای کاهش وابستگی به یک ارائهدهنده واحد (Vendor Lock-in)، معماری لایهای Prism را در پروژههای خود پیاده کنید.
- در صورت نیاز به خروجیهای JSON دقیق از مدلهای Z.AI، از متدهای
StructuredMapدر Prism بهره ببرید.
اما مدیریت توکنها در مقیاس بالا چالش دیگری است؛ برای بهینهسازی مصرف توکن در عاملها، تحلیل ما دربارهی پروتکل MCP را بخوانید.




گفتگو