আপনি গোয়া ভ্রমণের পরিকল্পনা করতে চান। আপনার কাছে পাঁচ দিন, ২৫,০০০ টাকার বাজেট এবং সমুদ্র সৈকত ও সামুদ্রিক খাবার পছন্দ করার একটি স্পষ্ট রুচি আছে। সাধারণত, এর মানে হলো দশটি ব্রাউজার ট্যাব খোলা, পুরনো ফোরাম পোস্ট পড়া এবং ম্যানুয়ালি একটি ভ্রমণসূচী তৈরি করা। পরিবর্তে, কল্পনা করুন একটি মাত্র POST রিকোয়েস্ট পাঠিয়ে খাবারের পরামর্শ, কার্যকলাপের তালিকা এবং একটি সঠিক বাজেট বিভাজন সহ একটি সুবিন্যস্ত দিনভিত্তিক পরিকল্পনা ফিরে পাওয়া। এই প্রজেক্টটি ঠিক সেটিই প্রদান করে।
আমরা Spring Boot এবং Azure OpenAI ব্যবহার করে একটি REST API তৈরি করব। এই API গন্তব্য, বাজেট, সময়কাল এবং আগ্রহ গ্রহণ করে। এটি একটি পরিষ্কার JSON রিটার্ন করে যা একটি ফ্রন্টএন্ড বা মোবাইল অ্যাপ তাৎক্ষণিকভাবে রেন্ডার করতে পারে। কোনো স্ক্র্যাপিং নয়, কোনো হার্ডকোডেড ভ্রমণসূচী নয়। শুধু একটি AI মডেল যা ট্রাভেল প্ল্যানার হিসেবে কাজ করার জন্য প্রম্পট করা হয়েছে।
API কী রিটার্ন করে
রেসপন্সটি কোনো মার্কডাউন টেক্সটের ব্লক নয় যা আপনাকে regex দিয়ে আলাদা করতে হবে। এটি একটি সুবিন্যস্ত JSON অবজেক্ট যাতে প্রতিদিনের কার্যকলাপ, খাবারের সুপারিশ এবং বাজেটের বিভাজন থাকে। গোয়া ভ্রমণের জন্য, আপনি হয়তো প্রথম দিনের একটি অংশ পেতে পারেন যেখানে একটি বিচ শ্যাকে প্রাতঃরাশের জন্য ৫০০ টাকা, সকালে পালেলেম (Palolem) ভ্রমণ এবং সন্ধ্যায় একটি নির্দিষ্ট এলাকার সামুদ্রিক খাবারের ডিনারের জন্য বরাদ্দ থাকবে। প্রতিটি দিনের জন্য টাইম স্লট, আনুমানিক খরচ এবং "beach" বা "food"-এর মতো ট্যাগ থাকবে। এই গঠনটি গুরুত্বপূর্ণ কারণ আধুনিক ট্রাভেল অ্যাপগুলো অনুচ্ছেদ (paragraphs) পার্স করতে চায় না। তারা এমন অবজেক্ট চায় যা তারা RecyclerView বা React কম্পোনেন্টে ম্যাপ করতে পারে।
টেক স্ট্যাক এবং কেন এটি উপযুক্ত
এই প্রজেক্টে Spring AI সহ Spring Boot 3.5 ব্যবহার করা হয়েছে। Spring AI হলো সবচেয়ে গুরুত্বপূর্ণ অংশ। এটি একটি ইউনিফাইড ChatModel অ্যাবস্ট্রাকশন প্রদান করে যাতে আপনাকে Azure OpenAI-এর জন্য সরাসরি HTTP ক্লায়েন্ট লিখতে না হয়। আপনি শুধু ডিপেন্ডেন্সি এবং প্রপার্টি পরিবর্তন করবেন, সার্ভিস কোড নয়।
আপনার বিল্ড ফাইলে চারটি ডিপেন্ডেন্সি প্রয়োজন:
- REST লেয়ারের জন্য
spring-boot-starter-web। - Spring AI-এর ইন্টারফেসের মাধ্যমে LLM-এর সাথে সংযোগ করার জন্য
spring-ai-starter-model-azure-openai। - স্বয়ংক্রিয় Swagger ডকুমেন্টেশনের জন্য
springdoc-openapi। - আপনার রিকোয়েস্ট এবং রেসপন্স POJO-র বয়েলারপ্লেট কোড কমানোর জন্য
Lombok।
Spring AI আপনার বিজনেস লজিক এবং LLM প্রোভাইডারের মাঝে অবস্থান করে। এই অবস্থানটি উদ্দেশ্যপ্রণোদিত। এটি আপনার @Service ক্লাসগুলোকে পরিষ্কার এবং প্রোভাইডার-অ্যাগনস্টিক (provider-agnostic) রাখে।
PromptTemplates-এর মাধ্যমে প্রম্পট ইঞ্জিনিয়ারিং
জাভা স্ট্রিং-এর ভেতরে প্রম্পট হার্ডকোড করা একটি রক্ষণাবেক্ষণ অযোগ্য সফটওয়্যার তৈরির দ্রুততম উপায়। যদি প্রোডাক্ট টিম সিদ্ধান্ত নেয় যে AI-কে আরও ক্যাজুয়াল শোনা উচিত বা একটি নির্দিষ্ট সীমার উপরে বাজেটের অনুমান প্রত্যাখ্যান করা উচিত, তবে আপনার সার্ভিসটি পুনরায় কম্পাইল করার প্রয়োজন হওয়া উচিত নয়।
Spring AI PromptTemplate প্রদান করে। আপনি প্রম্পটের কাঠামোটি একটি রিসোর্স ফাইলে বা একটি ডেডিকেটেড টেমপ্লেট স্ট্রিং-এ সংরক্ষণ করতে পারেন, যেখানে {destination}, {budget}, {days}, এবং {interests}-এর মতো ভেরিয়েবলের জন্য প্লেসহোল্ডার থাকবে। রানটাইমে, সার্ভিসটি একটি Prompt অবজেক্ট তৈরি করে এবং ব্যবহারকারীর মানগুলো ইনজেক্ট করে।
সিস্টেম মেসেজ এবং ইউজার মেসেজ আলাদা রাখুন। পারসোনা (persona) সংজ্ঞায়িত করতে সিস্টেম মেসেজ ব্যবহার করুন। উদাহরণস্বরূপ, আপনি মডেলটিকে বলবেন যে এটি ভারতীয় গন্তব্যগুলিতে বিশেষজ্ঞ একজন ট্রাভেল প্ল্যানার, বাজেট সচেতন এবং কোনো মার্কডাউন ফেন্স ছাড়াই শুধুমাত্র JSON রিটার্ন করার ব্যাপারে কঠোর। নির্দিষ্ট ভ্রমণের বিবরণ পাস করতে ইউজার মেসেজ ব্যবহার করুন। এই বিভাজনটি পরে API কন্ট্রাক্ট পরিবর্তন না করেই পারসোনাগুলোর A/B টেস্ট করতে সাহায্য করে।
সার্ভিস লেয়ার: Azure OpenAI-এর সাথে কথা বলা
@Service ক্লাসের একটি মাত্র কাজ আছে। এটি প্রম্পট তৈরি করে, মডেল কল করে, রেসপন্স পরিষ্কার করে এবং ফলাফল পার্স করে।
Spring AI-এর ChatClient বা ChatModel ইনজেক্ট করুন। আসা রিকোয়েস্ট ভ্যালু দিয়ে PromptTemplate রেন্ডার করুন, তারপর চ্যাট মেথড কল করুন। রেসপন্সটি একটি String হিসেবে আসে। এখানেই অনেক টিউটোরিয়াল শেষ হয় এবং আসল প্রোডাকশন কোড শুরু হয়।
LLM মাঝে মাঝে ভদ্রতা প্রদর্শনের জন্য কিছু ভূমিকা (preambles) যোগ করে। আপনি এমন একটি রেসপন্স পেতে পারেন যা "Here is your itinerary" দিয়ে শুরু হয় এবং তারপর ট্রিপল ব্যাকটিকের (triple backticks) মধ্যে JSON প্রদান করে। আপনি যদি সরাসরি Jackson দিয়ে এটি ডেসিরিয়ালাইজ (deserialize) করার চেষ্টা করেন, তবে আপনার অ্যাপ ক্র্যাশ করবে। একটি ছোট হেল্পার মেথড যোগ করুন যা র-স্ট্রিংটি স্ক্যান করবে, প্রথম ওপেনিং কার্লি ব্রেস { এবং শেষ ক্লোজিং কার্লি ব্রেস } খুঁজে বের করবে এবং শুধুমাত্র JSON পেলোডটি এক্সট্রাক্ট করবে। তারপর এক্সট্রাক্ট করা ব্লকটি ভ্যালিডেট করুন। কন্ট্রোলারে অবজেক্টটি রিটার্ন করার আগে নিশ্চিত করুন যে প্রয়োজনীয় ফিল্ডগুলো আছে এবং সংখ্যাসূচক মানগুলো সঠিক।
এই ডিফেন্সিভ পার্সিং (defensive parsing) ঐচ্ছিক নয়। এটি একটি ডেমো এবং একটি নির্ভরযোগ্য API-এর মধ্যে সীমানা নির্ধারণ করে।
একটি পরিপক্ক সিস্টেমের মতো ত্রুটি (Error) হ্যান্ডেল করা
এক্সটার্নাল API ব্যর্থ হতে পারে। Azure OpenAI রেট লিমিট এরর, অথেন্টিকেশন ফেইলর বা ট্রানজিয়েন্ট 500 এরর রিটার্ন করতে পারে। আপনি যদি এগুলোকে স্ট্যাক ট্রেস (stack traces) হিসেবে ব্যবহারকারীর কাছে পৌঁছে দেন, তবে আপনি বিশ্বাসযোগ্যতা হারাবেন।
গ্লোবালভাবে এক্সেপশন (exceptions) ইন্টারসেপ্ট করতে @RestControllerAdvice ব্যবহার করুন। Spring AI এক্সেপশন, HttpClientErrorException, এবং সাধারণ RuntimeException-গুলোকে একটি সুসংগত এরর রেসপন্সে (error response) ম্যাপ করুন। একটি পরিষ্কার মেসেজ, রেট লিমিটের জন্য 429-এর মতো একটি HTTP স্ট্যাটাস এবং ক্লায়েন্টের পুনরায় চেষ্টা করার বা সমস্যাটি লগ করার জন্য যথেষ্ট তথ্যসহ একটি JSON বডি রিটার্ন করুন। ব্যবহারকারীকে "Service temporarily busy. Please retry in 30 seconds," এর মতো কিছু দেখা উচিত, জাভা ক্লাস নেম দিয়ে ভরা কোনো স্ক্রিন নয়।
কখনোই সিক্রেট হার্ডকোড করবেন না
আপনার Azure OpenAI API কী Git-এ চেক-ইন করা application.properties-এ রাখা উচিত নয়। এটিকে এক্সটার্নাল বা বাহ্যিক করুন। আপনার Spring কনফিগারেশনে রেফারেন্স করা এনভায়রনমেন্ট ভেরিয়েবল ব্যবহার করুন, যেমন ${AZURE_OPENAI_KEY} এবং ${AZURE_OPENAI_ENDPOINT}। ডেভেলপমেন্টের জন্য একটি লোকাল .env ফাইল রাখুন, এটিকে .gitignore-এ যোগ করুন এবং Spring Boot-এর relaxed binding-এর মাধ্যমে লোড করুন। যদি কোনো কী লিক (leak) হয়, তবে আপনার আর্টিফ্যাক্ট (artifact) পুনরায় তৈরি করার পরিবর্তে আপনি কেবল এক জায়গাতেই সেটি পরিবর্তন (rotate) করতে পারবেন।
Swagger-এর মাধ্যমে টেস্টিং
springdoc-openapi ডিপেন্ডেন্সিটি রানটাইমে একটি Swagger UI এন্ডপয়েন্ট প্রকাশ করে। আপনার অ্যাপ্লিকেশন চালু হওয়ার পর, ব্রাউজারে /swagger-ui.html ওপেন করুন। আপনি সরাসরি Goa-এর উদাহরণটি পূরণ করতে পারেন: destination হিসেবে "Goa," budget হিসেবে 25000, days হিসেবে 5, এবং interests হিসেবে "beaches, food" দিন। execute বাটনে ক্লিক করুন এবং JSON itinerary আসা দেখুন। এটি আপনাকে প্রম্পট পরিবর্তন যাচাই করতে, সিরিয়ালাইজেশন (serialization) পরীক্ষা করতে এবং উভয় পক্ষ ইউনিট টেস্ট লেখার আগেই ফ্রন্টএন্ড ডেভেলপারদের সাথে একটি লাইভ প্লেগ্রাউন্ড শেয়ার করতে সাহায্য করে।
কোড পুনরায় না লিখে প্রোভাইডার পরিবর্তন করা
স্টার্টআপগুলো প্রায়ই প্রোভাইডার পরিবর্তন করে। হতে পারে Azure ক্রেডিটের মেয়াদ শেষ হয়ে গেছে, অথবা খরচ কমাতে আপনি একটি লোকাল Ollama ইনস্ট্যান্সের বিরুদ্ধে ইনফারেন্স (inference) চালাতে চান। যেহেতু Spring AI ChatModel ইন্টারফেসটিকে অ্যাবস্ট্রাক্ট করে রাখে, তাই এই পরিবর্তনটি অত্যন্ত সহজ। Maven ডিপেন্ডেন্সি spring-ai-starter-model-azure-openai থেকে অন্য কোনো স্টার্টারে পরিবর্তন করুন, নতুন এন্ডপয়েন্ট এবং কী দিয়ে আপনার প্রপার্টিজ ফাইল আপডেট করুন, এবং আপনার সার্ভিস ক্লাসটি অপরিবর্তিত রাখুন। আপনার মোবাইল অ্যাপ যে API কন্ট্রাক্ট দেখে তা একই থাকবে।
এই পোর্টেবিলিটি এই আর্কিটেকচারটিকে বাস্তব পণ্যের জন্য বিশেষভাবে উপযোগী করে তোলে। আপনি Azure-এর সাথে আবদ্ধ হয়ে যাচ্ছেন না; আপনি এটিকে একটি পরিচ্ছন্ন Spring পাইপলাইনে যুক্ত একটি ইঞ্জিন হিসেবে ব্যবহার করছেন।
আসল শিক্ষা
একটি AI মডেল আপনার অ্যাপ্লিকেশন নয়। এটি একটি বাহ্যিক পরিষেবা যা অনির্দেশ্য টেক্সট প্রদান করে। এটিকে একটি পেমেন্ট গেটওয়ে বা থার্ড-পার্টি ওয়েদার API-এর মতো একই কঠোরতার সাথে বিবেচনা করুন। আপনার ক্রেডেনশিয়ালগুলো (credentials) এক্সটার্নাল করুন। প্রতিটি রেসপন্স যাচাই করুন। পার্স (parse) করার আগে পেলোড (payload) ক্লিন করুন। এররগুলো গ্লোবালভাবে হ্যান্ডেল করুন যাতে আপনার ব্যবহারকারীরা কখনোই স্ট্যাক ট্রেস (stack trace) না দেখেন।
২৫,০০০ টাকার বাজেটে একটি Goa itinerary তৈরির সৃজনশীল কাজ AI-কে করতে দিন। আপনি কেবল সিস্টেমের মূল কাঠামো বা 'plumbing' সামলান। যখন এই দুটি আলাদা থাকে, তখন আপনি এমন একটি সিস্টেম পান যা প্রকৃতপক্ষে সফলভাবে কাজ করে।
এই নিবন্ধটির অনুপ্রেরণা হিসেবে কাজ করা মূল ওয়াকথ্রু (walkthrough) এখানে পাওয়া যাবে এখানে।
Spring AI এবং এই ধরণের প্রজেক্ট নিয়ে আলোচনা করতে আগ্রহী? GyaanSetu লার্নিং কমিউনিটিতে যোগ দিন।
