നിങ്ങൾക്ക് ഗോവയിലേക്ക് ഒരു യാത്ര പ്ലാൻ ചെയ്യണമെന്നുണ്ടെന്ന് കരുതുക. നിങ്ങളുടെ പക്കൽ അഞ്ച് ദിവസങ്ങളുണ്ട്, 25,000 രൂപ ബജറ്റുണ്ട്, കൂടാതെ ബീച്ചുകളും സീഫുഡും (seafood) ആണ് നിങ്ങളുടെ താൽപ്പര്യവും. സാധാരണയായി, ഇതിനായി പത്ത് ബ്രൗസർ ടാബുകൾ തുറക്കേണ്ടി വരും, പഴയ ഫോറം പോസ്റ്റുകൾ വായിക്കേണ്ടി വരും, കൂടാതെ ഒരു ഇറ്റിനററി (itinerary) സ്വയം തയ്യാറാക്കേണ്ടി വരും. അതിനുപകരം, ഒരു സിംഗിൾ POST റിക്വസ്റ്റ് അയച്ചാൽ ഭക്ഷണ നിർദ്ദേശങ്ങൾ, പ്രവർത്തനങ്ങളുടെ പട്ടിക, കൃത്യമായ ബജറ്റ് വിഭജനം എന്നിവയടങ്ങിയ ഒരു സ്ട്രക്ചർഡ് ഡേ-ബൈ-ഡേ പ്ലാൻ തിരികെ ലഭിക്കുന്നത് സങ്കൽപ്പിക്കുക. ഈ പ്രോജക്റ്റ് നൽകുന്നത് അതാണ്.

ഞങ്ങൾ Spring Boot-ഉം Azure OpenAI-ഉം ഉപയോഗിച്ച് ഒരു REST API നിർമ്മിക്കും. ഈ API ലക്ഷ്യസ്ഥാനം (destination), ബജറ്റ്, കാലാവധി, താൽപ്പര്യങ്ങൾ എന്നിവ സ്വീകരിക്കുന്നു. ഒരു ഫ്രണ്ട്‌എൻഡ് അല്ലെങ്കിൽ മൊബൈൽ ആപ്പിന് ഉടൻ തന്നെ റെൻഡർ ചെയ്യാൻ കഴിയുന്ന ക്ലീൻ JSON ആണ് ഇത് തിരികെ നൽകുന്നത്. സ്ക്രാപ്പിംഗോ ഹാർഡ്കോഡ് ചെയ്ത ഇറ്റിനററികളോ ഇല്ല. ഒരു ട്രാവൽ പ്ലാനറായി പ്രവർത്തിക്കാൻ പ്രോംപ്റ്റ് ചെയ്ത ഒരു AI മോഡൽ മാത്രം.

എന്താണ് ഈ API തിരികെ നൽകുന്നത്

മറുപടി എന്നത് നിങ്ങൾ regex ഉപയോഗിച്ച് വേർതിരിച്ചെടുക്കേണ്ട ഒരു മാർക്ക്ഡൗൺ ടെക്സ്റ്റ് ബ്ലോക്ക് അല്ല. ഇത് ദിവസേനയുള്ള പ്രവർത്തനങ്ങൾ, ഭക്ഷണ നിർദ്ദേശങ്ങൾ, ബജറ്റ് വിഭജനം എന്നിവ അടങ്ങിയ ഒരു സ്ട്രക്ചർഡ് JSON ഒബ്‌ജക്റ്റാണ്. ഒരു ഗോവ യാത്രയ്ക്ക്, ഒരു ബീച്ച് ഷാക്കിൽ പ്രഭാതഭക്ഷണത്തിനായി 500 രൂപ, രാവിലെ പാലോലെമിൽ (Palolem), വൈകുന്നേരം ഒരു പ്രത്യേക പ്രദേശത്ത് സീഫുഡ് ഡിന്നർ എന്നിവ ഉൾപ്പെടുത്തിക്കൊണ്ടുള്ള ഒന്നാം ദിവസത്തെ വിവരങ്ങൾ നിങ്ങൾക്ക് ലഭിച്ചേക്കാം. ഓരോ ദിവസവും ടൈം സ്ലോട്ടുകൾ, ഏകദേശ ചിലവ്, "beach" അല്ലെങ്കിൽ "food" പോലുള്ള ടാഗുകൾ എന്നിവ ഉൾക്കൊള്ളുന്നു. ഈ സ്ട്രക്ചർ പ്രധാനമാണ്, കാരണം ആധുനിക ട്രാവൽ ആപ്പുകൾക്ക് പാരഗ്രാഫുകൾ പാഴ്സ് (parse) ചെയ്യേണ്ടതില്ല. അവയ്ക്ക് RecyclerView-കളിലേക്കോ React കമ്പോണന്റുകളിലേക്കോ മാപ്പ് ചെയ്യാൻ കഴിയുന്ന ഒബ്‌ജക്റ്റുകളാണ് വേണ്ടത്.

സ്റ്റാക്കും അത് എന്തുകൊണ്ട് അനുയോജ്യമാകുന്നു

ഈ പ്രോജക്റ്റിൽ Spring AI-യോടൊപ്പം Spring Boot 3.5 ആണ് ഉപയോഗിക്കുന്നത്. Spring AI ആണ് ഇതിലെ നിർണ്ണായക ഘടകം. Azure OpenAI-ക്ക് വേണ്ടി നേരിട്ട് HTTP ക്ലയന്റുകൾ എഴുതേണ്ടതില്ല എന്ന രീതിയിൽ ഇത് ഒരു ഏകീകൃത ChatModel അബ്‌സ്‌ട്രാക്ഷൻ നൽകുന്നു. നിങ്ങൾ സർവീസ് കോഡല്ല, മറിച്ച് डिपൻഡൻസികളും പ്രോപ്പർട്ടികളും ആണ് മാറ്റുന്നത്.

നിങ്ങളുടെ ബിൽഡ് ഫയലിൽ നാല് डिपൻഡൻസികൾ ആവശ്യമാണ്:

  • spring-boot-starter-web REST ലെയറിനായി.
  • spring-ai-starter-model-azure-openai Spring AI-യുടെ ഇന്റർഫേസ് വഴി LLM-മായി ബന്ധിപ്പിക്കാൻ.
  • springdoc-openapi ഓട്ടോമാറ്റിക് Swagger ഡോക്യുമെന്റേഷനായി.
  • Lombok നിങ്ങളുടെ റിക്വസ്റ്റ്, റെസ്പോൺസ് POJO-കളിലെ ബോയിലർപ്ലേറ്റ് (boilerplate) കുറയ്ക്കാൻ.

Spring AI നിങ്ങളുടെ ബിസിനസ് ലോജിക്കും LLM പ്രൊവൈഡറും തമ്മിൽ പ്രവർത്തിക്കുന്നു. ഈ രീതിയിൽ ക്രമീകരിച്ചിരിക്കുന്നത് ബോധപൂർവ്വമാണ്. ഇത് നിങ്ങളുടെ @Service ക്ലാസുകളെ ക്ലീൻ ആയും പ്രൊവൈഡർ-അജ്ഞാതമായും (provider-agnostic) നിലനിർത്തുന്നു.

PromptTemplates ഉപയോഗിച്ചുള്ള പ്രോംപ്റ്റ് എഞ്ചിനീയറിംഗ്

Java സ്ട്രിംഗുകൾക്കുള്ളിൽ പ്രോംപ്റ്റുകൾ ഹാർഡ്കോഡ് ചെയ്യുന്നത് പരിപാലിക്കാൻ പ്രയാസമുള്ള സോഫ്റ്റ്‌വെയർ നിർമ്മിക്കാനുള്ള എളുപ്പവഴിയാണ്. പ്രോഡക്റ്റ് ടീം 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-യ്ക്കും ഇടയിലുള്ള അതിർവരമ്പാണ്.

പക്വതയുള്ള ഒരു സിസ്റ്റം പോലെ പിശകുകൾ കൈകാര്യം ചെയ്യുക

പുറത്തുള്ള API-കൾ പരാജയപ്പെട്ടേക്കാം. Azure OpenAI റേറ്റ് ലിമിറ്റ് എററുകൾ, ഓതന്റിക്കേഷൻ പരാജയങ്ങൾ അല്ലെങ്കിൽ താൽക്കാലികമായ 500 എററുകൾ എന്നിവ നൽകിയേക്കാം. ഇവ സ്റ്റാക്ക് ട്രാസുകളായി (stack traces) ഉപയോക്താവിലേക്ക് എത്തിച്ചാൽ നിങ്ങളുടെ വിശ്വാസ്യത നഷ്ടപ്പെടും.

ആഗോളതലത്തിൽ എക്സെപ്ഷനുകൾ (exceptions) കൈകാര്യം ചെയ്യാൻ @RestControllerAdvice ഉപയോഗിക്കുക. Spring AI എക്സെപ്ഷനുകൾ, HttpClientErrorException, കൂടാതെ ജനറിക്കായ RuntimeException എന്നിവയെ ഒരേപോലെയുള്ള എറർ റെസ്‌പോൺസുകളിലേക്ക് മാപ്പ് ചെയ്യുക. ഒരു വ്യക്തമായ സന്ദേശവും, റേറ്റ് ലിമിറ്റുകൾക്കായി (rate limits) 429 പോലുള്ള ഒരു HTTP സ്റ്റാറ്റസും, ക്ലയന്റിന് പ്രശ്നം വീണ്ടും ശ്രമിക്കാനോ (retry) ലോഗ് ചെയ്യാനോ ആവശ്യമായ വിവരങ്ങളും അടങ്ങിയ ഒരു JSON ബോഡി തിരികെ നൽകുക. ഉപയോക്താവിന് ജാവ ക്ലാസ് പേരുകൾ നിറഞ്ഞ ഒരു സ്ക്രീനിന് പകരം "Service temporarily busy. Please retry in 30 seconds" എന്ന രീതിയിലുള്ള സന്ദേശം ലഭിക്കണം.

രഹസ്യ വിവരങ്ങൾ (Secrets) ഒരിക്കലും കോഡിൽ നേരിട്ട് ഉൾപ്പെടുത്തരുത്

നിങ്ങളുടെ Azure OpenAI API കീ Git-ലേക്ക് ചെക്ക്-ഇൻ ചെയ്യുന്ന application.properties-ൽ ഉൾപ്പെടുത്താൻ പാടില്ല. അവ പുറത്തേക്ക് മാറ്റുക (Externalize). നിങ്ങളുടെ Spring കോൺഫിഗറേഷനിൽ ${AZURE_OPENAI_KEY}, ${AZURE_OPENAI_ENDPOINT} എന്നിങ്ങനെയുള്ള എൻവയോൺമെന്റ് വേരിയബിളുകൾ ഉപയോഗിക്കുക. ഡെവലപ്‌മെന്റിനായി ഒരു ലോക്കൽ .env ഫയൽ സൂക്ഷിക്കുക, അത് .gitignore-ൽ ചേർക്കുക, കൂടാതെ Spring Boot-ന്റെ relaxed binding വഴി അത് ലോഡ് ചെയ്യുക. ഒരു കീ ചോർന്നാൽ, ആർട്ടിഫാക്റ്റ് വീണ്ടും ബിൽഡ് ചെയ്യുന്നതിന് പകരം ഒരിടത്ത് മാത്രം അത് മാറ്റിയാൽ മതിയാകും.

Swagger ഉപയോഗിച്ചുള്ള ടെസ്റ്റിംഗ്

springdoc-openapi ഡിപ്പൻഡൻസി റൺടൈമിൽ ഒരു Swagger UI എൻഡ്‌പോയിന്റ് ലഭ്യമാക്കുന്നു. നിങ്ങളുടെ ആപ്ലിക്കേഷൻ സ്റ്റാർട്ട് ചെയ്തുകഴിഞ്ഞാൽ, ബ്രൗസറിൽ /swagger-ui.html തുറക്കുക. നിങ്ങൾക്ക് ഗോവ (Goa) ഉദാഹരണം നേരിട്ട് പൂരിപ്പിക്കാം: destination ആയി "Goa," budget ആയി 25000, days ആയി 5, interests ആയി "beaches, food" എന്നിവ നൽകുക. 'execute' ബട്ടൺ അമർത്തി JSON ഇറ്റിനററി (itinerary) വരുന്നത് കാണാം. ഇത് പ്രോംപ്റ്റ് മാറ്റങ്ങൾ പരിശോധിക്കാനും, സീരിയലൈസേഷൻ (serialization) ഉറപ്പാക്കാനും, ഫ്രണ്ട്‌എൻഡ് ഡെവലപ്പർമാർക്ക് ഒരു ലൈവ് പ്ലേഗ്രൗണ്ട് പങ്കിടാനും സഹായിക്കുന്നു.

കോഡ് മാറ്റാതെ തന്നെ പ്രൊവൈഡർമാരെ മാറ്റാം

സ്റ്റാർട്ടപ്പുകൾ പലപ്പോഴും പ്രൊവൈഡർമാരെ മാറ്റാറുണ്ട്. ഒരുപക്ഷേ Azure ക്രെഡിറ്റുകൾ തീർന്നേക്കാം, അല്ലെങ്കിൽ ചിലവ് കുറയ്ക്കാൻ ഒരു ലോക്കൽ Ollama ഇൻസ്റ്റൻസിനെ ഉപയോഗിച്ച് ഇൻഫറൻസ് നടത്താൻ നിങ്ങൾ ആഗ്രഹിച്ചേക്കാം. Spring AI, ChatModel ഇന്റർഫേസിനെ അബ്‌സ്‌ട്രാക്റ്റ് (abstract) ചെയ്യുന്നതുകൊണ്ട്, ഈ മാറ്റം വളരെ എളുപ്പമാണ്. Maven ഡിപ്പൻഡൻസി spring-ai-starter-model-azure-openai-ൽ നിന്ന് മറ്റൊരു സ്റ്റാർട്ടറിലേക്ക് മാറ്റുക, പുതിയ എൻഡ്‌പോയിന്റും കീയും ഉപയോഗിച്ച് നിങ്ങളുടെ പ്രോപ്പർട്ടി ഫയൽ അപ്‌ഡേറ്റ് ചെയ്യുക, നിങ്ങളുടെ സർവീസ് ക്ലാസിൽ മാറ്റമൊന്നും വരുത്തേണ്ടതില്ല. നിങ്ങളുടെ മൊബൈൽ ആപ്പ് കാണുന്ന API കോൺട്രാക്ട് മാറ്റമില്ലാതെ തുടരുന്നു.

ഈ പോർട്ടബിലിറ്റി (portability) ഈ ആർക്കിടെക്ചറിനെ യഥാർത്ഥ ഉൽപ്പന്നങ്ങൾക്ക് ഏറെ ഉപയോഗപ്രദമാക്കുന്നു. നിങ്ങൾ Azure-മായി മാത്രം ബന്ധിക്കപ്പെട്ടിരിക്കുകയല്ല. ഒരു ക്ലീൻ Spring പൈപ്പ്‌ലൈനിൽ ഘടിപ്പിച്ചിരിക്കുന്ന ഒരു എൻജിൻ മാത്രമായാണ് നിങ്ങൾ അതിനെ ഉപയോഗിക്കുന്നത്.

പ്രധാനമായും ശ്രദ്ധിക്കേണ്ട കാര്യങ്ങൾ

ഒരു AI മോഡൽ എന്നത് നിങ്ങളുടെ ആപ്ലിക്കേഷൻ അല്ല. അത് പ്രവചനാതീതമായ ടെക്സ്റ്റ് നൽകുന്ന ഒരു എക്സ്റ്റേണൽ സർവീസ് ആണ്. ഒരു പേയ്‌മെന്റ് ഗേറ്റ്‌വേയോ തേർഡ് പാർട്ടി വെതർ API-യോ കൈകാര്യം ചെയ്യുന്ന അതേ ഗൗരവത്തോടെ ഇതിനെയും കാണുക. നിങ്ങളുടെ ക്രെഡൻഷ്യലുകൾ പുറത്തേക്ക് മാറ്റുക. ഓരോ റെസ്‌പോൺസും പരിശോധിക്കുക. പാഴ്‌സ് ചെയ്യുന്നതിന് മുമ്പ് പേലോഡ് (payload) ക്ലീൻ ചെയ്യുക. എററുകൾ ആഗോളതലത്തിൽ കൈകാര്യം ചെയ്യുക, അങ്ങനെ ഉപയോക്താക്കൾക്ക് ഒരിക്കലും ഒരു സ്റ്റാക്ക് ട്രാസ് (stack trace) കാണേണ്ടി വരില്ല.

25,000 രൂപ ബജറ്റിൽ ഒരു ഗോവ ട്രിപ്പ് പ്ലാൻ ചെയ്യുന്ന ക്രിയേറ്റീവ് ജോലി AI-ക്ക് വിട്ടേക്കുക. നിങ്ങൾ അതിന്റെ അടിസ്ഥാന ഘടന (plumbing) കൈകാര്യം ചെയ്യുക. ഇവ രണ്ടും വേറിട്ടുനിൽക്കുമ്പോൾ, നിങ്ങൾക്ക് മികച്ച രീതിയിൽ പ്രവർത്തിക്കുന്ന ഒരു സിസ്റ്റം ലഭിക്കുന്നു.

ഈ ലേഖനത്തിന് പ്രചോദനമായ യഥാർത്ഥ വിവരങ്ങൾ ഇവിടെ കാണാം.

Spring AI യെക്കുറിച്ചും സമാനമായ പ്രോജക്റ്റുകളെക്കുറിച്ചും ചർച്ച ചെയ്യാൻ താൽപ്പര്യമുണ്ടോ? GyaanSetu learning community-ൽ ചേരുക.