तुम्हाला गोव्याच्या सहलीचे नियोजन करायचे आहे. तुमच्याकडे पाच दिवस, २५,००० रुपयांचे बजेट आणि समुद्रकिनारे व सीफूडची आवड आहे. सामान्यतः, याचा अर्थ असा होतो की दहा ब्राउझर टॅब उघडणे, जुने फोरम पोस्ट वाचणे आणि मॅन्युअली एक वेळापत्रक तयार करणे. त्याऐवजी, फक्त एक POST request पाठवून जेवणाचे पर्याय, उपक्रमांची यादी आणि बजेटचे अचूक विभाजन असलेले एक संरचित (structured) दैनंदिन नियोजन मिळवा, अशी कल्पना करा. हेच हे प्रोजेक्ट साध्य करते.

आम्ही Spring Boot आणि Azure OpenAI वापरून एक REST API तयार करू. हे API ठिकाण (destination), बजेट, कालावधी आणि आवडीनिवडी स्वीकारते. हे एक स्वच्छ JSON परत करते जे फ्रंटएंड किंवा मोबाईल ॲप लगेच रेंडर करू शकते. कोणतेही स्क्रॅपिंग नाही, कोणतेही हार्डकोडेड वेळापत्रक नाही. फक्त एक AI मॉडेल ज्याला ट्रॅव्हल प्लॅनर म्हणून काम करण्यास सांगितले आहे.

API काय परत करते

प्रतिसाद (response) हा केवळ Markdown मजकुराचा संच नाही ज्याला तुम्हाला regex द्वारे वेगळे करावे लागेल. हा दैनंदिन उपक्रम, जेवणाचे शिफारसी आणि बजेटचे विभाजन असलेला एक संरचित JSON ऑब्जेक्ट आहे. गोव्याच्या सहलीसाठी, तुम्हाला असा विभाग मिळू शकतो जो समुद्रकिनाऱ्यावरील शेकमध्ये नाश्त्यासाठी ५०० रुपये, सकाळी पालोलेममध्ये वेळ आणि संध्याकाळी एखाद्या विशिष्ट भागात सीफूड डिनरसाठी बजेट देईल. प्रत्येक दिवसासाठी वेळ स्लॉट्स, अंदाजित खर्च आणि "beach" किंवा "food" सारखे टॅग्स असतात. ही रचना महत्त्वाची आहे कारण आधुनिक ट्रॅव्हल ॲप्सना परिच्छेद (paragraphs) पार्स करायचे नसतात. त्यांना असे ऑब्जेक्ट्स हवे असतात जे ते RecyclerViews किंवा React components ला मॅप करू शकतील.

स्टॅक आणि तो का योग्य आहे

या प्रोजेक्टमध्ये 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.
  • तुमच्या रिक्वेस्ट आणि रिस्पॉन्स POJOs मधील अनावश्यक कोड (boilerplate) कमी करण्यासाठी Lombok.

Spring AI तुमच्या बिझनेस लॉजिक आणि LLM प्रोव्हायडरच्या मध्ये काम करते. हे स्थान मुद्दाम ठेवलेले आहे. यामुळे तुमचे @Service क्लासेस स्वच्छ आणि प्रोव्हायडर-अज्ञेयवादी (provider-agnostic) राहतात.

PromptTemplates सह प्रॉम्प्ट इंजिनिअरिंग

Java स्ट्रिंग्समध्ये प्रॉम्प्ट्स हार्डकोड करणे हा सॉफ्टवेअर न हाताळण्यायोग्य (unmaintainable) बनवण्याचा जलद मार्ग आहे. जर प्रॉडक्ट टीमने ठरवले की AI ने अधिक अनौपचारिक (casual) बोलावे किंवा ठराविक मर्यादेपेक्षा जास्त बजेटचे अंदाज नाकारले पाहिजेत, तर तुम्हाला तुमचा सर्व्हिस पुन्हा कंपाईल करण्याची गरज पडू नये.

Spring AI PromptTemplate प्रदान करते. तुम्ही प्रॉम्प्टचा साचा (skeleton) रिसोर्स फाईलमध्ये किंवा समर्पित टेम्पलेट स्ट्रिंगमध्ये साठवू शकता, ज्यामध्ये {destination}, {budget}, {days}, आणि {interests} सारख्या व्हेरिएबल्ससाठी प्लेसहोल्डर्स असतील. रनटाइमला, सर्व्हिस एक Prompt ऑब्जेक्ट तयार करते आणि वापरकर्त्याची मूल्ये त्यात इंजेक्ट करते.

सिस्टम मेसेज आणि युजर मेसेज वेगळे ठेवा. 'Persona' (व्यक्तिमत्व) परिभाषित करण्यासाठी सिस्टम मेसेज वापरा. उदाहरणार्थ, तुम्ही मॉडेलला सांगता की ते भारतीय ठिकाणांमध्ये तज्ञ असलेला, बजेटबाबत जागरूक असलेला आणि केवळ JSON परत देणारा (कोणत्याही markdown fences शिवाय) ट्रॅव्हल प्लॅनर आहे. विशिष्ट सहलीचा तपशील देण्यासाठी युजर मेसेज वापरा. यामुळे भविष्यात API कॉन्ट्रॅक्ट न बदलता तुम्ही 'Personas' चे A/B टेस्टिंग करू शकता.

सर्व्हिस लेयर: Azure OpenAI शी संवाद साधणे

@Service क्लासचे एकच काम आहे. तो प्रॉम्प्ट तयार करतो, मॉडेलला कॉल करतो, प्रतिसाद स्वच्छ करतो आणि रिझल्ट पार्स करतो.

Spring AI चा ChatClient किंवा ChatModel इंजेक्ट करा. येणाऱ्या रिक्वेस्ट व्हॅल्यूजसह PromptTemplate रेंडर करा आणि नंतर चॅट मेथड कॉल करा. प्रतिसाद String स्वरूपात येतो. येथेच अनेक ट्युटोरियल्स थांबतात आणि खरा प्रोडक्शन कोड सुरू होतो.

LLMs कधीकधी सभ्य प्रस्तावना (preambles) जोडतात. तुम्हाला असा प्रतिसाद मिळू शकतो जो "Here is your itinerary" ने सुरू होतो आणि त्यानंतर ट्रिपल बॅकटिक्समध्ये (triple backticks) गुंडाळलेला JSON देतो. जर तुम्ही Jackson वापरून ते थेट डीसेरियलाईझ करण्याचा प्रयत्न केला, तर तुमचे ॲप क्रॅश होईल. एक लहान हेल्पर मेथड जोडा जी रॉ स्ट्रिंग स्कॅन करेल, पहिला उघडणारा कंस (opening brace) आणि शेवटचा बंद होणारा कंस (closing brace) शोधेल आणि फक्त JSON पेलोड बाहेर काढेल. त्यानंतर काढलेल्या ब्लॉकची पडताळणी करा. ऑब्जेक्ट कंट्रोलरला परत करण्यापूर्वी आवश्यक फील्ड्स आहेत की नाही आणि संख्यात्मक मूल्ये (numeric values) योग्य आहेत की नाही हे तपासा.

हे 'defensive parsing' ऐच्छिक नाही. हे एक डेमो आणि एक विश्वसनीय API यांच्यातील सीमा आहे.

प्रगल्भ प्रणालीप्रमाणे त्रुटी हाताळणे (Handling Errors Like a Mature System)

एक्सटर्नल APIs फेल होऊ शकतात. Azure OpenAI रेट लिमिट एरर्स, ऑथेंटिकेशन फेल्युअर किंवा ट्रान्झिएंट 500 एरर्स परत करेल. जर तुम्ही या त्रुटी स्टॅक ट्रेस (stack traces) म्हणून वापरकर्त्याला दाखवल्या, तर तुम्ही तुमची विश्वासार्हता गमावाल.

अपवादांना (exceptions) जागतिक स्तरावर रोखण्यासाठी @RestControllerAdvice वापरा. Spring AI exceptions, HttpClientErrorException, आणि generic RuntimeExceptions ला सुसंगत एरर रिस्पॉन्समध्ये मॅप करा. एक स्पष्ट संदेश, रेट लिमिटसाठी 429 सारखा HTTP status, आणि क्लायंटला पुन्हा प्रयत्न करण्यासाठी किंवा समस्या लॉग करण्यासाठी पुरेसा तपशील असलेला JSON body परत करा. वापरकर्त्याला "Service temporarily busy. Please retry in 30 seconds," असे काहीतरी दिसले पाहिजे, Java क्लासच्या नावांनी भरलेला स्क्रीन नाही.

कधीही सीक्रेट्स हार्डकोड करू नका

तुमची Azure OpenAI API key Git मध्ये चेक केलेल्या application.properties मध्ये नसावी. ती एक्सटर्नलाईज (Externalize) करा. तुमच्या Spring कॉन्फिगरेशनमध्ये संदर्भित केलेल्या एन्व्हायरनमेंट व्हेरिएबल्सचा (environment variables) वापर करा, जसे की ${AZURE_OPENAI_KEY} आणि ${AZURE_OPENAI_ENDPOINT}. डेव्हलपमेंटसाठी स्थानिक .env फाईल ठेवा, ती .gitignore मध्ये जोडा आणि Spring Boot च्या relaxed binding द्वारे लोड करा. जर एखादी की लीक झाली, तर तुम्हाला तुमचा आर्टिफॅक्ट (artifact) पुन्हा तयार करण्याऐवजी ती एकाच ठिकाणी बदलता (rotate) येईल.

Swagger द्वारे टेस्टिंग

springdoc-openapi डिपेंडन्सी रनटाइमला Swagger UI एंडपॉइंट उपलब्ध करून देते. एकदा तुमचे ॲप्लिकेशन सुरू झाले की, ब्राउझरमध्ये /swagger-ui.html उघडा. तुम्ही गोवा उदाहरण थेट भरू शकता: destination "Goa", budget 25000, days 5, interests "beaches, food". 'execute' वर क्लिक करा आणि JSON itinerary तयार होताना पहा. यामुळे तुम्हाला प्रॉम्प्टमधील बदल तपासणे (validate), serialization पडताळणे आणि दोन्ही बाजूने युनिट टेस्ट लिहिण्यापूर्वी फ्रंटएंड डेव्हलपर्ससोबत लाईव्ह प्लेग्राउंड शेअर करणे शक्य होते.

कोड पुन्हा न लिहिता प्रोव्हायडर्स बदलणे

स्टार्टअप्स प्रोव्हायडर्स बदलतात. कदाचित Azure क्रेडिट्स संपले असतील, किंवा खर्च कमी करण्यासाठी तुम्हाला स्थानिक Ollama instance वर इन्फरन्स (inference) चालवायचा असेल. Spring AI मुळे ChatModel इंटरफेस ॲबस्ट्रॅक्ट (abstract) होत असल्याने, हा बदल अगदी सोपा (mechanical) आहे. Maven डिपेंडन्सी spring-ai-starter-model-azure-openai वरून दुसऱ्या स्टार्टरमध्ये बदला, तुमच्या प्रॉपर्टीज फाईलमध्ये नवीन एंडपॉइंट आणि की अपडेट करा आणि तुमच्या सर्व्हिस क्लासला तसाच ठेवा. तुमच्या मोबाईल ॲपला दिसणारा API कॉन्ट्रॅक्ट तसाच राहील.

ही पोर्टेबिलिटी (portability) या आर्किटेक्चरला खऱ्या उत्पादनांसाठी (real products) विशेषतः उपयुक्त बनवते. तुम्ही Azure शी लग्न करत नाही आहात. तुम्ही त्याचा वापर एका स्वच्छ Spring पाइपलाइनमध्ये जोडलेल्या इंजिनसारखा करत आहात.

मुख्य निष्कर्ष

AI मॉडेल हे तुमचे ॲप्लिकेशन नाही. ती एक बाह्य सेवा (external service) आहे जी अनपेक्षित मजकूर परत करते. एखाद्या पेमेंट गेटवे किंवा थर्ड-पार्टी वेदर API प्रमाणेच तिच्याशी कडकपणे वागा. तुमची क्रेडेंशियल्स (credentials) एक्सटर्नलाईज करा. प्रत्येक रिस्पॉन्स व्हॅलिडेट करा. पार्सिंग करण्यापूर्वी पेलोड (payload) स्वच्छ करा. एरर्स जागतिक स्तरावर (globally) हाताळा जेणेकरून तुमच्या वापरकर्त्यांना कधीही स्टॅक ट्रेस (stack trace) दिसणार नाही.

25,000 रुपयांच्या बजेटमध्ये गोवा इटिनररी (itinerary) तयार करण्याचे सर्जनशील काम AI ला करू द्या. तुम्ही प्लंबिंग (plumbing/infrastructure) हाताळा. जेव्हा हे दोन्ही वेगळे राहतात, तेव्हा तुम्हाला असे सिस्टम मिळते जे खरोखर कार्यान्वित (ships) होऊ शकते.

या लेखासाठी प्रेरणा देणारा मूळ वॉकथ्रू येथे पाहू शकता.

Spring AI आणि तत्सम प्रकल्पांवर चर्चा करण्यात रस आहे का? GyaanSetu learning community मध्ये सामील व्हा.