تزریق هوش مصنوعی به برنامه‌های جاوا چگونه است؟

تزریق هوش مصنوعی به برنامه‌های جاوا چگونه است؟

نکات کلیدی

  • به‌عنوان یک توسعه‌دهنده جاوا، نیازی نیست برای شروعِ نوشتن برنامه‌هایی که هوش مصنوعی در آن‌ها تزریق شده، زبان دیگری یاد بگیرید.
  • توسعه‌دهندگان جاوا می‌توانند از پروژه متن‌باز LangChain4j برای مدیریت تعاملات بین برنامه‌های جاوا و مدل‌های زبانی بزرگ (LLMها) استفاده کنند؛ مثل ذخیره‌سازی و مدیریت «حافظه گفتگو» تا درخواست‌ها به LLM کارآمد، متمرکز و کم‌هزینه‌تر بماند.
  • استفاده از LangChain4j همراه با Quarkus تعامل با LLMها را ساده‌تر می‌کند و علاوه بر آن از «لذت توسعه‌دهندگی» Quarkus با حالت توسعه، رابط توسعه و یکپارچه‌سازی آسان ابزارهای مشاهده‌پذیری هم بهره می‌برید.
  • جاوا امتحانش را پس داده و یک اکوسیستم قدرتمند و آماده سازمانی دارد (به عملکرد و امنیت فکر کنید) که کمک می‌کند در نوشتن و اجرای برنامه‌های آماده تولیدِ مبتنی بر هوش مصنوعی در جاوا موفق شوید.
  • برای شروع یادگیری مفاهیم پایه نوشتن برنامه‌های تزریق‌شده با هوش مصنوعی در جاوا با LangChain4j و Quarkus اقدام کنید. خودتان امتحانش کنید؛ با ساختن یک چت‌بات ساده جلوتر از منحنیِ حوزه هوش مصنوعی که با سرعت در حال تحول است حرکت کنید.

هوش مصنوعی به‌طور فزاینده‌ای فراگیر می‌شود. به‌عنوان یک توسعه‌دهنده جاوای سازمانی ممکن است از خودتان بپرسید هوش مصنوعی چه ارزشی می‌تواند به برنامه‌های کسب‌وکار شما اضافه کند، جاوا چه ابزارهایی فراهم می‌کند تا این کار را ساده انجام دهید، و چه مهارت‌ها و دانشی لازم است یاد بگیرید. در این مقاله، دانش و مهارت‌های پایه‌ای را در اختیار شما می‌گذاریم که برای شروعِ بررسی قابلیت‌های هوش مصنوعی و ساخت برنامه‌های جاوای سازمانیِ هوشمند و پاسخ‌گو لازم دارید.

وقتی در این مقاله از هوش مصنوعی حرف می‌زنیم، منظورمان گرفتن پاسخ از یک مدل زبانی بزرگ (LLM) بر اساس درخواستی است که برنامه جاوا به LLM می‌فرستد. در مثال این مقاله، یک چت‌بات ساده می‌سازیم که مشتری‌ها از آن توصیه‌های مقصدهای گردشگریِ سیاره‌ای می‌خواهند و سپس با آن یک سفینه فضایی برای بازدید از آن‌ها رزرو می‌کنند. نشان می‌دهیم چطور با استفاده از چارچوب‌های جاوا مثل LangChain4j همراه با Quarkus می‌توان به‌شکل کارآمد با LLMها تعامل کرد و برنامه‌هایی رضایت‌بخش برای کاربران نهایی ساخت.

سلام (هوش مصنوعی) دنیا: گرفتن پاسخ از یک LLM به یک پرامپت

اولین نسخه از برنامه اجاره سفینه فضایی ما یک چت‌بات می‌سازد که با مشتری‌ها با زبان طبیعی تعامل می‌کند. باید به هر سؤال مشتری درباره سیاره‌هایی که می‌خواهد در منظومه شمسی به آن‌ها سفر کند پاسخ بدهد. برای کد کامل برنامه، پوشه spaceship rental step-01 را در مخزن GitHub ببینید.

تزریق هوش مصنوعی به برنامه‌های جاوا چگونه است؟

چت‌بات سؤال‌های مشتری را به برنامه می‌فرستد و برنامه با LLM تعامل می‌کند تا به پردازش سؤال‌های زبان طبیعی کمک کند و به مشتری پاسخ بدهد.

برای بخش‌های مربوط به هوش مصنوعیِ برنامه، فقط دو فایل می‌سازیم:

  • یک سرویس هوش مصنوعی به نام CustomerSupportAgent.java که یک پرامپت می‌سازد و به LLM درباره سیاره‌های منظومه شمسی اطلاعات می‌دهد و به LLM دستور می‌دهد به سؤال‌های مشتری‌ها پاسخ بدهد.
  • یک نقطه پایانی WebSocket به نام ChatWebSocket.java که پیام‌های کاربر را از چت‌بات دریافت می‌کند.

سرویس‌های هوش مصنوعی «اینترفیس‌های جاوا» هستند که یک لایه انتزاع فراهم می‌کنند. وقتی از LangChain4j استفاده می‌کنید، این اینترفیس‌ها تعامل با LLM را ساده‌تر می‌کنند. سرویس‌های هوش مصنوعی یک نقطه یکپارچه‌سازی هستند، پس در یک برنامه واقعی باید امنیت، مشاهده‌پذیری و تحمل خطای اتصال‌ها و تعامل‌ها با LLM را در نظر بگیرید. علاوه بر مدیریت جزئیات اتصال به LLM (که جداگانه در فایل پیکربندی application.properties ذخیره می‌شود)، یک سرویس هوش مصنوعی پرامپت‌ها را می‌سازد و حافظه گفتگو را برای درخواست‌هایی که به LLM می‌فرستد مدیریت می‌کند.

پرامپت از دو بخش اطلاعاتی در سرویس هوش مصنوعی ساخته می‌شود: «پیام سیستم» و «پیام کاربر». پیام‌های سیستم معمولاً توسط توسعه‌دهندگان برای دادن اطلاعات زمینه‌ای و دستورالعمل‌ها به LLM استفاده می‌شوند تا درخواست را چطور مدیریت کند، و اغلب شامل مثال‌هایی هستند که می‌خواهید LLM هنگام تولید پاسخ از آن‌ها پیروی کند. پیام‌های کاربر درخواست‌های کاربرِ برنامه را به LLM می‌دهند.

اینترفیس CustomerSupportAgent به‌عنوان سرویس هوش مصنوعی در برنامه ثبت می‌شود. این اینترفیس پیام‌هایی را تعریف می‌کند که برای ساخت پرامپت استفاده می‌شوند و پرامپت را به LLM می‌فرستد:

تزریق هوش مصنوعی به برنامه‌های جاوا چگونه است؟

بیایید ببینیم این کد چه کار می‌کند. annotationِ @SessionScoped نشست را برای مدت اتصال سرویس وب نگه می‌دارد و حافظه گفتگو را برای مدت مکالمه حفظ می‌کند. annotationِ @RegisterAIService یک اینترفیس را به‌عنوان سرویس هوش مصنوعی ثبت می‌کند. LangChain4j به‌صورت خودکار این اینترفیس را پیاده‌سازی می‌کند. annotationِ @SystemMessage به LLM می‌گوید هنگام پاسخ دادن به پرامپت چطور رفتار کند.

وقتی کاربر نهایی در چت‌بات یک پیام تایپ می‌کند، نقطه پایانی WebSocket آن پیام را به متد chat() در سرویس هوش مصنوعی ارسال می‌کند. در اینترفیس سرویس هوش مصنوعی ما هیچ annotationِ @UserMessage مشخص نشده است، پس پیاده‌سازی سرویس هوش مصنوعی به‌صورت خودکار یک پیام کاربر با مقدار پارامتر متد chat() می‌سازد (در اینجا پارامتر userMessage). سرویس هوش مصنوعی پیام کاربر را به پیام سیستم اضافه می‌کند تا یک پرامپت بسازد که به LLM می‌فرستد، سپس پاسخ LLM را در رابط چت‌بات نمایش می‌دهد.

توجه کنید که برای خوانایی، اطلاعات سیاره‌ها در یک کلاس جداگانه به نام PlanetInfo قرار داده شده است. به‌جای آن می‌توانید اطلاعات سیاره‌ها را مستقیم داخل پیام سیستم بگذارید.

کلاس ChatWebSocket یک نقطه پایانی WebSocket تعریف می‌کند تا رابط کاربری چت‌باتِ برنامه بتواند با آن تعامل کند:

تزریق هوش مصنوعی به برنامه‌های جاوا چگونه است؟

اینترفیس CustomerSupportAgent از «تزریق سازنده» استفاده می‌کند تا به‌صورت خودکار یک ارجاع به سرویس هوش مصنوعی دریافت کند. وقتی کاربر نهایی در چت‌بات پیامی تایپ می‌کند، متد onTextMessage() پیام را به متد chat() سرویس هوش مصنوعی می‌فرستد.

برای مثال، اگر کاربر بپرسد: «اگر بخواهم آتشفشان ببینم، کدام سیاره برای بازدید خوب است؟»، برنامه با یک پیشنهاد و دلیل این‌که چرا کاربر ممکن است از آنجا خوشش بیاید (به‌عنوان طرفدار آتشفشان‌ها) پاسخ می‌دهد:

ایجاد یک توهم از حافظه

وقتی مکالمه‌تان با چت‌بات ادامه پیدا می‌کند، ممکن است این‌طور به نظر برسد که انگار از پیام‌های قبلی ردوبدل‌شده آگاه است، یعنی زمینه مکالمه شما را می‌داند. وقتی با یک نفر دیگر حرف می‌زنید، بدیهی است که او یادش می‌ماند شما (و خودش) دفعه قبل چه گفتید. اما درخواست‌ها به LLM بدون حالت هستند، پس هر پاسخ فقط بر اساس اطلاعاتی تولید می‌شود که داخل پرامپتِ همان درخواست وجود دارد.

برای حفظ زمینه در یک مکالمه، سرویس هوش مصنوعی از طریق LangChain4j از «حافظه گفتگو» استفاده می‌کند تا پیام‌های قبلی کاربر و پاسخ‌های چت‌بات را ذخیره کند. به‌طور پیش‌فرض، افزونه Quarkus LangChain4j گفتگو را در حافظه ذخیره می‌کند و سرویس هوش مصنوعی حافظه گفتگو را مدیریت می‌کند (برای مثال، حذف‌کردن یا خلاصه‌کردن قدیمی‌ترین پیام‌ها) تا در محدوده محدودیت‌های حافظه باقی بماند. خود LangChain4j به‌تنهایی از شما می‌خواهد ابتدا یک فراهم‌کننده حافظه پیکربندی کنید، اما هنگام استفاده از افزونه Quarkus LangChain4j این کار لازم نیست. این کار برای کاربران نهایی یک «توهم عملی از حافظه» ایجاد می‌کند و تجربه کاربری را بهتر می‌کند تا بتوانند پیام‌های پیگیری را بدون تکرار همه چیزهایی که قبلاً گفته‌اند وارد کنند. تجربه چت‌بات همچنین با «جریان‌دهی» پاسخ‌های LLM می‌تواند بهتر شود.

جریان‌دهی پاسخ‌ها برای تجربه کاربری پاسخ‌گوتر

ممکن است متوجه شوید پاسخ‌ها به پنجره پیام چت زمان می‌برند تا تولید شوند و بعد یک‌باره ظاهر می‌شوند. برای بهترکردن حس پاسخ‌گویی چت‌بات، می‌توانیم کد را طوری تغییر دهیم که هر توکنِ پاسخ را همان لحظه‌ای که تولید می‌شود برگرداند. به این رویکرد «جریان‌دهی» گفته می‌شود و اجازه می‌دهد کاربر قبل از آماده‌شدن کل پاسخ، شروع به خواندنِ پاسخِ جزئی کند. برای کد کامل برنامه، پوشه spaceship rental step-02 را در GitHub ببینید.

تغییر برنامه برای جریان‌دهی پاسخ چت‌بات ساده است. اول، اینترفیس CustomerSupportAgent را به‌روزرسانی می‌کنیم تا یک متد اضافه شود که یک نمونه از اینترفیس SmallRye Mutiny Multi<String> را برگرداند:

تزریق هوش مصنوعی به برنامه‌های جاوا چگونه است؟

انتقال annotationِ @SystemMessage به اینترفیس باعث می‌شود لازم نباشد این annotation روی تک‌تک متدهای اینترفیس قرار بگیرد. متد streamChat() پاسخ LLM را توکن به توکن به پنجره چت برمی‌گرداند (به‌جای این‌که صبر کند تا کل پاسخ آماده شود و بعد یک‌جا نمایش دهد).

همچنین باید متد جدید streamChat() را از یک نقطه پایانی WebSocket صدا بزنیم. برای حفظ قابلیت‌های «بچ» و «جریان»، یک کلاس جدید به نام ChatWebSocketStream می‌سازیم که نقطه پایانی WebSocket با مسیر /chat/stream را ارائه می‌دهد:

تزریق هوش مصنوعی به برنامه‌های جاوا چگونه است؟

فراخوانی customerSupportAgent.streamChat() سرویس هوش مصنوعی را اجرا می‌کند تا پیام کاربر را به LLM بفرستد.

وقتی جریان‌دهی فعال باشد، هر توکن (هر کلمه، یا بخشی از کلمه) که توسط LLM تولید می‌شود، فوراً به رابط چت برگردانده می‌شود.

تولید خروجی‌های ساخت‌یافته از داده‌های بدون ساختار

تا اینجا خروجی‌های LLM برای کاربر نهاییِ برنامه در نظر گرفته شده‌اند. اما اگر بخواهیم خروجی LLM مستقیماً توسط خود برنامه استفاده شود چه؟ وقتی LLM به یک درخواست پاسخ می‌دهد، سرویس هوش مصنوعی که تعامل با LLM را میانجی‌گری می‌کند می‌تواند «خروجی‌های ساخت‌یافته» برگرداند؛ یعنی قالب‌هایی ساخت‌یافته‌تر از String، مثل POJOها، فهرست POJOها و نوع‌های بومی.

برگرداندن خروجی‌های ساخت‌یافته یکپارچه‌سازی خروجی LLM با کد جاوا را به‌شدت ساده می‌کند، چون مجبور می‌کند خروجی‌ای که برنامه از سرویس هوش مصنوعی می‌گیرد با شِمای از پیش تعریف‌شده شیء جاوای شما نگاشت شود. بیایید کاربرد خروجی‌های ساخت‌یافته را نشان بدهیم: کمک به کاربر برای انتخاب یک سفینه از ناوگان ما که نیازهایش را برآورده می‌کند. برای کد کامل برنامه، پوشه spaceship rental step-03 را در GitHub ببینید.

کار را با ساختن یک رکورد ساده به نام Spaceship شروع می‌کنیم تا اطلاعات هر سفینه را نگه داریم:

برای نمایش پرسش کاربر درباره سفینه‌های ناوگان، یک رکورد SpaceshipQuery می‌سازیم که بر اساس اطلاعاتی است که کاربر در گفتگو داده است:

کلاس Fleet چند شیء Spaceship را مقداردهی می‌کند و راهی فراهم می‌کند تا سفینه‌هایی که با نیاز کاربر جور نیستند فیلتر شوند.

سپس اینترفیس CustomerSupportAgent را به‌روزرسانی می‌کنیم تا پیام کاربر (متن بدون ساختار) را بگیرد و یک خروجی ساخت‌یافته در قالب رکورد SpaceshipQuery تولید کند. برای انجام این کار، فقط لازم است نوع بازگشتی متد جدید extractSpaceshipAttributes() در سرویس هوش مصنوعی را SpaceshipQuery قرار بدهیم:

SpaceshipQuery extractSpaceshipAttributes(String userMessage);

در پشت صحنه، LangChain4j به‌صورت خودکار یک درخواست به LLM تولید می‌کند که شامل یک نمایش شِمای JSON از پاسخِ موردنظر است. LangChain4j پاسخِ JSON-قالبِ LLM را deserialize می‌کند و از آن برای بازگرداندن رکورد SpaceshipQuery مطابق درخواست استفاده می‌کند.

همچنین باید بدانیم آیا ورودی کاربر درباره یکی از سفینه‌های ماست یا درباره موضوع دیگری. این فیلتر با یک درخواست خروجی ساخت‌یافته ساده‌تر انجام می‌شود که یک مقدار بولی برمی‌گرداند:

آخرین اضافه‌کردن ما به اینترفیس CustomerSupportAgent امکان می‌دهد عاملِ پشتیبانی بر اساس ناوگان و درخواست کاربر یک پیشنهاد سفینه ارائه کند، هم با جریان‌دهی و هم بدون آن:

گام آخر این است که کلاس‌های ChatWebSocket و ChatWebSocketStream را به‌روزرسانی کنیم تا اول بررسی کنند آیا پرسش کاربر درباره سفینه‌های ناوگان ما هست یا نه. اگر بود، عامل پشتیبانی مشتری با استخراج اطلاعات از پیام کاربر یک رکورد SpaceshipQuery می‌سازد و سپس با پیشنهاد سفینه‌های سازگار از ناوگان، پاسخ می‌دهد. کد به‌روزشده برای هر دو کلاس مشابه است، پس فقط کلاس ChatWebSocket اینجا نشان داده می‌شود:

با این کار، یک برنامه چت‌بات جاواییِ تزریق‌شده با هوش مصنوعی را کامل کرده‌ایم که توصیه‌های گردشگری سیاره‌ای و اجاره سفینه فضایی ارائه می‌دهد.

بیشتر درباره این مفاهیم هوش مصنوعی

در طول این مقاله مفاهیم مختلفی از هوش مصنوعی را مطرح کردیم. اگر می‌خواهید درباره هرکدام بیشتر بدانید، اینجا یک توضیح سریع آمده است.

مدل‌های زبانی بزرگ (LLMها)

وقتی در این مقاله از هوش مصنوعی حرف می‌زنیم، معمولاً منظورمان گرفتن پاسخ از یک مدل زبانی بزرگ است. LLMها مدل‌های یادگیری ماشین هستند که آموزش می‌بینند تا بر اساس یک توالی ورودی، یک توالی خروجی تولید کنند (اغلب ورودی و خروجی متنی است، اما برخی LLMهای چندرسانه‌ای می‌توانند با تصویر، صدا یا ویدئو هم کار کنند). LLMها می‌توانند کارهای بسیار متنوعی انجام دهند؛ مثل خلاصه‌کردن یک سند، ترجمه بین زبان‌ها، استخراج واقعیت‌ها، نوشتن کد و غیره. این کارِ تولید محتوای جدید از روی ورودی همان چیزی است که به آن «هوش مصنوعی مولد» گفته می‌شود. شما می‌توانید بنا به نیاز، چنین قابلیت‌هایی را به برنامه خود تزریق کنید.

درخواست‌دادن از LLMها: پرامپت‌ها، حافظه گفتگو و توکن‌ها

نحوه درخواست شما از یک LLM نه‌تنها پاسخ دریافتی را تحت تأثیر می‌گذارد، بلکه تجربه کاربر نهایی و هزینه اجرای برنامه را هم تغییر می‌دهد.

پرامپت‌ها

فرستادن یک درخواست به LLM، چه از داخل کد برنامه و چه به‌عنوان کاربر نهایی در یک رابط گفتگو، شامل نوشتن یک پرامپت است. پرامپت همان اطلاعاتی است (معمولاً، اما نه همیشه، متن) که LLM به آن پاسخ می‌دهد. اگر ارتباط با یک LLM را مثل ارتباط با یک انسان در نظر بگیرید، مهم است که درخواست‌تان را چطور بیان می‌کنید تا طرف مقابل (یا LLM) دقیقاً بفهمد چه می‌خواهید. مثلاً بهتر است قبل از پرسیدن یک اطلاعات مشخص، زمینه را بدهید و اطلاعات بی‌ربط زیادی که شنونده را گیج می‌کند وارد نکنید.

حافظه گفتگو

برخلاف وقتی با یک انسان حرف می‌زنید، LLMها بدون حالت هستند و درخواست قبلی را به خاطر نمی‌سپارند. پس هر چیزی که می‌خواهید LLM در نظر بگیرد باید در درخواست شما باشد: پرامپت، درخواست‌ها و پاسخ‌های قبلی (حافظه گفتگو) و هر ابزاری که برای کمک به پاسخ‌دادن در اختیارش می‌گذارید. اما دادن اطلاعات بیش از حد در پرامپت می‌تواند درخواست را پیچیده کند و هزینه‌بر هم باشد.

توکن‌ها

LLMها کلمات پرامپت شما را به یک توالی از توکن‌ها تبدیل می‌کنند. بیشتر LLMهای میزبانی‌شده هزینه را بر اساس تعداد توکن‌های درخواست و پاسخ محاسبه می‌کنند. یک توکن می‌تواند یک کلمه کامل یا بخشی از یک کلمه باشد. مثلاً واژه «unbelievable» معمولاً به چند توکن تقسیم می‌شود: «un»، «bel» و «ievable». هرچه توکن‌های بیشتری در درخواست بگنجانید، مخصوصاً وقتی کل حافظه گفتگو را وارد می‌کنید، هزینه بالقوه اجرای برنامه بیشتر می‌شود.

قرار دادن کل حافظه گفتگو در یک درخواست می‌تواند درخواست‌ها را هم گران و هم کم‌شفاف‌تر کند. درخواست‌ها به LLMها محدودیت طول دارند، پس مدیریت حافظه گفتگو و مقدار اطلاعاتی که در درخواست می‌آید مهم است. این کار با چارچوب‌های جاوا مثل LangChain4j همراه با Quarkus که در نمونه این مقاله استفاده کردیم، بسیار راحت‌تر می‌شود.

چارچوب‌های LangChain4j و Quarkus

LangChain4j یک چارچوب متن‌باز جاواست که تعاملات بین برنامه‌های جاوا و LLMها را مدیریت می‌کند. برای مثال، LangChain4j از طریق مفهوم «سرویس‌های هوش مصنوعی» حافظه گفتگو را ذخیره و به مدیریت آن کمک می‌کند تا درخواست‌های شما به LLM کارآمد، متمرکز و کم‌هزینه‌تر باشند.

Quarkus یک چارچوب مدرن، بومی ابر و متن‌باز جاواست که برای بهره‌وری توسعه‌دهنده، اجرای کانتینری، و شروع سریع با مصرف حافظه کم بهینه شده است. افزونه‌های LangChain4j برای Quarkus پیکربندی اتصال و تعامل با LLMها را در برنامه‌های جاواییِ تزریق‌شده با هوش مصنوعی ساده می‌کنند.

پروژه LangChain4j می‌تواند با چارچوب‌های دیگرِ برنامه‌های جاوا هم استفاده شود، از جمله Open Liberty، Spring Boot و Micronaut. همچنین MicroProfile و Jakarta EE هم در حال همکاری با LangChain4j هستند تا یک مدل برنامه‌نویسی مبتنی بر استانداردهای باز برای توسعه برنامه‌های هوش مصنوعی ارائه دهند.

برنامه نمونه

می‌توانید برنامه نمونه کامل را که در طول این مقاله نشان دادیم در GitHub پیدا کنید. این برنامه با جاوا نوشته شده و روی Quarkus اجرا می‌شود و از افزونه‌های Quarkus LangChain4j استفاده می‌کند.

نتیجه‌گیری

تزریق هوش مصنوعی به برنامه‌های جاوا قابلیت‌های برنامه و تجربه کاربر نهایی را بهبود می‌دهد. با کمک چارچوب‌هایی مثل Quarkus و LangChain4j که تعامل با LLMها را ساده می‌کنند، توسعه‌دهندگان جاوا می‌توانند به‌راحتی هوش مصنوعی را به برنامه‌های کسب‌وکار تزریق کنند.

نوشتن برنامه‌های تزریق‌شده با هوش مصنوعی در جاوا یعنی شما در اکوسیستم قدرتمند و آماده سازمانیِ جاوا کار می‌کنید؛ اکوسیستمی که نه‌تنها تعامل با مدل‌های هوش مصنوعی را ساده می‌کند، بلکه باعث می‌شود برنامه‌ها به‌راحتی از نیازهای ضروری سازمانی مثل عملکرد، امنیت، مشاهده‌پذیری و آزمون‌پذیری هم بهره‌مند شوند.

حوزه هوش مصنوعی با سرعت در حال تحول است. با تسلط بر مفاهیم و فناوری‌های این مقاله، می‌توانید جلوتر از منحنی حرکت کنید و بررسی کنید هوش مصنوعی چطور می‌تواند به شما کمک کند برنامه‌های جاواییِ هوشمند و جذاب بسازید. کد کامل برنامه نمونه را در کنار مستندات Quarkus همراه با LangChain4j امتحان و آزمایش کنید.

چگونه می‌توان از مدل‌های زبانی بزرگ (LLMها) برای به‌دست‌آوردن طیفی متنوع از دیدگاه‌ها استفاده کرد؟
معماری MVP در عصر هوش مصنوعی چگونه است؟

دیدگاهتان را بنویسید

سبد خرید
علاقه‌مندی‌ها
مشاهدات اخیر
دسته بندی ها