എന്റെ MCP സെർവർ വെറുതെ പ്രവർത്തിക്കാതെ പോകാറുണ്ടായിരുന്നു. ക്രാഷ് ഡംപ് ഇല്ല. ലോഗുകളിൽ സ്റ്റാക്ക് ട്രാസ് ഇല്ല. ക്ലയന്റുകൾ പരാതികളില്ലാതെ കണക്ട് ചെയ്യും, എന്നാൽ ഏതാനും മണിക്കൂറുകൾക്ക് ശേഷം എല്ലാം നിശബ്ദമാകും. റിക്വസ്റ്റുകൾ അപ്രത്യക്ഷമാകും, മറുവശത്തുള്ള AI ഏജന്റിന് ശൂന്യമായ വിവരങ്ങൾ മാത്രമേ ലഭിക്കൂ.
Model Context Protocol (MCP) ഇക്കോസിസ്റ്റത്തിൽ ഇത് വളരെ സാധാരണമായ ഒരു പ്രശ്നമാണ്. AI ഏജന്റുകൾ എങ്ങനെയാണ് എക്സ്റ്റേണൽ ടൂളുകൾ കണ്ടെത്തുന്നത് എന്നും അവയെ എങ്ങനെ വിളിക്കണം എന്നും ഈ പ്രോട്ടോക്കോൾ നിർവചിക്കുന്നു, എന്നാൽ പിശകുകൾ (errors) നിങ്ങൾ തന്നെ കൈകാര്യം ചെയ്യണം എന്നാണ് ഇതിന്റെ സ്പെസിഫിക്കേഷൻ അനുമാനിക്കുന്നത്. മിക്ക ട്യൂട്ടോറിയലുകളും സ്റ്റാർട്ടർ ഇംപ്ലിമെന്റേഷനുകളും ഈ ഭാഗം ഒഴിവാക്കുന്നു. അവ 'ഹാപ്പി പാത്ത്' (happy path) ആണ് ശ്രദ്ധ കേന്ദ്രീകരിക്കുന്നത്: ഒരു ഫംഗ്ഷൻ അനോട്ടേറ്റ് ചെയ്യുക, അത് സെർവറിലൂടെ എക്സ്പോസ് ചെയ്യുക, ഒരു ക്ലീൻ റിസൾട്ട് നൽകുക. നിങ്ങളുടെ എക്സ്റ്റേണൽ API-യിൽ ഒരു നെറ്റ്വർക്ക് തടസ്സം ഉണ്ടാകുമ്പോഴോ, അല്ലെങ്കിൽ മോഡൽ ഒരു പാരാമീറ്റർ പേര് തെറ്റായി പറഞ്ഞ് (hallucinate) തെറ്റായ ഇൻപുട്ട് അയക്കുമ്പോഴോ എന്താണ് സംഭവിക്കുന്നതെന്ന് അവ അപൂർവ്വമായി മാത്രമേ കാണിക്കാറുള്ളൂ. ഇതിന്റെ ഫലം, ആരോഗ്യത്തോടെ ഇരിക്കുന്നതായി തോന്നുമെങ്കിലും യഥാർത്ഥത്തിൽ മണിക്കൂറുകളായി പ്രവർത്തിക്കാത്ത ഒരു ബ്രൈറ്റിൽ (brittle) സെർവർ ആണ്.
ശൂന്യമായ മറുപടികൾ ക്രാഷുകളേക്കാൾ മോശമാകുന്നത് എന്തുകൊണ്ട്?
ഒരു MCP ടൂൾ ഹാൻഡ്ലറിൽ അൺഹാൻഡിൽഡ് എക്സെപ്ഷൻ (unhandled exception) സംഭവിക്കുമ്പോൾ, ട്രാൻസ്പോർട്ട് ലെയർ പലപ്പോഴും അത് വിഴുങ്ങുന്നു. സെർവർ പ്രോസസ്സ് സജീവമായിരിക്കും, സോക്കറ്റ് തുറന്നിരിക്കും, എന്നാൽ ക്ലയന്റിന് ലഭിക്കുന്നത് ശൂന്യമായ മറുപടിയായിരിക്കും. ഇത് ഒരു വലിയ ക്രാഷിനേക്കാൾ അപകടകരമാണ്, കാരണം നിങ്ങളുടെ മോണിറ്ററിംഗ് അത് ശ്രദ്ധിച്ചേക്കില്ല. പ്രോസസ്സ് ഇപ്പോഴും പ്രവർത്തിക്കുന്നുണ്ട്. പോർട്ട് ഇപ്പോഴും ലിസണിംഗ് ആണ്. എന്നിട്ടും ഓരോ ടൂൾ കോളും ഒന്നും നൽകുന്നില്ല.
AI മോഡൽ നിശബ്ദതയെ ഒരു പരാജയമായി കാണുന്നില്ല. പകരം, ഡാറ്റ ഒന്നും ലഭിക്കാത്ത ഒരു വിജയകരമായ കോൾ ആയിട്ടാണ് അത് നിശബ്ദതയെ വ്യാഖ്യാനിക്കുന്നത്. ആ ശൂന്യമായ മറുപടി, വിവരങ്ങൾ സ്വയം നിർമ്മിക്കാൻ (improvise) മോഡലിനെ പ്രേരിപ്പിക്കുന്നു. വിടവ് നികത്താൻ അത് വസ്തുതകൾ കെട്ടിച്ചമയ്ക്കാൻ (hallucinating facts) തുടങ്ങുന്നു, അല്ലെങ്കിൽ ഒരേ തെറ്റായ കോൾ തന്നെ വീണ്ടും വീണ്ടും ചെയ്യുന്ന ഒരു ലൂപ്പിൽ അകപ്പെടുന്നു. താൽക്കാലികമായ നെറ്റ്വർക്ക് ടൈമൗട്ട് അല്ലെങ്കിൽ തെറ്റായ ടൂൾ ആർഗ്യുമെന്റ് പോലുള്ള ചെറിയ പ്രശ്നങ്ങൾ പോലും ഇത്തരത്തിലുള്ള പെരുമാറ്റത്തിന് കാരണമാകാൻ അനുവദിക്കരുത്.
The Wrapper Pattern: പ്രതിരോധത്തിന്റെ മൂന്ന് നിരകൾ
ഓരോ ടൂൾ ഹാൻഡ്ലറിനെയും ഒരു ചെറിയ എറർ-റിക്കവറി ലെയറിൽ (error-recovery layer) ഉൾപ്പെടുത്തിക്കൊണ്ട് ഞാൻ ഇത് പരിഹരിച്ചു. ഈ Wrapper എല്ലാ സാധ്യമായ പരാജയങ്ങളെയും മുൻകൂട്ടി കാണാൻ ശ്രമിക്കുന്നില്ല. പകരം, അവയെ തരംതിരിക്കുകയും അതിനനുസരിച്ച് പ്രതികരിക്കുകയും ചെയ്യുന്നു.
ConnectionError and TimeoutError
നിങ്ങളുടെ സെർവർ ഒരു എക്സ്റ്റേണൽ API-യുമായി സംസാരിക്കുമ്പോൾ നെറ്റ്വർക്ക് തടസ്സപ്പെടുകയാണെങ്കിൽ ഇവ ഉണ്ടാകുന്നു. ഇതിനുള്ള സ്വാഭാവിക പരിഹാരം മുഴുവൻ MCP സെർവർ പ്രോസസ്സും റീസ്റ്റാർട്ട് ചെയ്യുക എന്നതാണ്. എന്നാൽ അത് ചെയ്യരുത്. റീബൂട്ട് ചെയ്യുന്നത് സജീവമായ ക്ലയന്റ് കണക്ഷനുകൾ വിച്ഛേദിക്കാനും, മെമ്മറിയിലുള്ള സ്റ്റേറ്റ് (in-memory state) കളയാനും, മുഴുവൻ റീ-ഇനീഷ്യലൈസേഷനും കാരണമാകും. പകരം, കണക്ഷൻ പരാജയം കണ്ടെത്തുകയും നിങ്ങളുടെ ടൂൾ ഉപയോഗിക്കുന്ന ട്രാൻസ്പോർട്ട് ലെയറോ HTTP ക്ലയന്റോ മാത്രം വീണ്ടും കണക്ട് ചെയ്യുകയും ചെയ്യുക. സെർവർ സജീവമായിരിക്കും, അടുത്ത റിക്വസ്റ്റിനായി ഉടൻ തന്നെ തയ്യാറാകുകയും ചെയ്യും.
ValueError
AI ക്ലയന്റ് തെറ്റായ ആർഗ്യുമെന്റുകൾ (malformed arguments) അയക്കുമ്പോഴാണ് ഇത് സംഭവിക്കുന്നത്. ഒരുപക്ഷേ മോഡൽ ഒരു പാരാമീറ്റർ സ്വയം നിർമ്മിച്ചതാകാം, ഒരു ഇന്റീജറിന് പകരം സ്ട്രിംഗ് നൽകിയതാകാം, അല്ലെങ്കിൽ ആവശ്യമായ ഒരു ഫീൽഡ് മറന്നുപോയതാകാം. ഇത് അൺഹാൻഡിൽഡ് ആയി വിട്ടാൽ, ക്ലയന്റിന് ഒന്നുകിൽ ഒരു ക്രാഷ് അല്ലെങ്കിൽ ശൂന്യമായ മറുപടി ലഭിക്കും. ഇത് Wrapper-നുള്ളിൽ തന്നെ പിടിച്ചെടുക്കുക (catch), തുടർന്ന് എന്താണ് സംഭവിച്ചതെന്ന് മോഡലിന് കൃത്യമായി പറഞ്ഞുതരുന്ന വ്യക്തമായ സന്ദേശം നൽകുക. ഏത് പാരാമീറ്റർ പരാജയപ്പെട്ടു എന്നും എന്താണ് പ്രതീക്ഷിച്ചതെന്നും വിശദീകരിക്കുക. മിക്ക ആധുനിക AI മോഡലുകളും ആ സന്ദേശം വായിച്ച് അടുത്ത തവണ തന്നെ സ്വയം തിരുത്തുന്നു. അവ്യക്തമായ ഒരു എറർ ചിന്താ പ്രക്രിയ (reasoning cycle) പാഴാക്കുന്നു. കൃത്യമായ ഒരു എറർ പ്രശ്നം ഉടൻ തന്നെ പരിഹരിക്കുന്നു.
General Exceptions
ഒരു സേഫ്റ്റി നെറ്റ് (safety net) സൂക്ഷിക്കുക. മുകളിൽ പറഞ്ഞ വിഭാഗങ്ങളിൽ പെടാത്ത ഒരു എറർ സംഭവിക്കുകയാണെങ്കിൽ, അതിന്റെ വിവരങ്ങൾ നിങ്ങൾക്കായി ലോഗ് ചെയ്യുകയും ക്ലയന്റിന് ഒരു ക്ലീൻ, ജനറിക് ഫെയിലർ റെസ്പോൺസ് നൽകുകയും ചെയ്യുക. ഇത് ഒരു ചെറിയ പ്രശ്നം കാരണം എല്ലാവരുടെയും സെഷൻ തകരാതിരിക്കാൻ സഹായിക്കുന്നു. സെർവർ നിലനിൽക്കും, എന്തോ പരാജയപ്പെട്ടു എന്ന സിഗ്നൽ ക്ലയന്റിന് ലഭിക്കും, കൂടാതെ പിന്നീട് ഡീബഗ് (debug) ചെയ്യാൻ ആവശ്യമായ വിവരങ്ങൾ നിങ്ങളുടെ ലോഗുകളിൽ ഉണ്ടാകുകയും ചെയ്യും.
The isError Flag Is Non-Negotiable
നിങ്ങളുടെ പരിഹാരം ഫലപ്രദമാണോ എന്ന് തീരുമാനിക്കുന്നത് ഈ കാര്യമാണ്. MCP റെസ്പോൺസുകളിൽ ഒരു isError ബൂളിയൻ (boolean) ഫീൽഡ് ഉണ്ട്. ഒരു എക്സെപ്ഷൻ സംഭവിക്കുകയും നിങ്ങൾ isError എന്നത് true എന്ന് സെറ്റ് ചെയ്യാതെ ഒരു എറർ മെസ്സേജ് നൽകുകയും ചെയ്താൽ, ക്ലയന്റ് ആ എറർ ടെക്സ്റ്റിനെ ഒരു വിജയകരമായ ടൂൾ റിസൾട്ട് ആയിട്ടാണ് കണക്കാക്കുന്നത്.
നിങ്ങളുടെ എക്സ്റ്റേണൽ API ഒരു റേറ്റ് ലിമിറ്റിൽ (rate limit) എത്തിയെന്ന് സങ്കൽപ്പിക്കുക. നിങ്ങൾ ആ എക്സെപ്ഷൻ പിടിച്ചെടുക്കുകയും "API rate limit exceeded" എന്ന സ്ട്രിംഗ് നൽകുകയും എന്നാൽ isError എന്നത് false ആയി തന്നെ നിലനിർത്തുകയും ചെയ്യുന്നു. ക്ലയന്റ് ആ സ്ട്രിംഗിനെ യഥാർത്ഥ ടൂൾ ഔട്ട്പുട്ട് ആണെന്ന രീതിയിൽ മോഡലിന്റെ കോൺടെക്സ്റ്റ് വിൻഡോയിലേക്ക് (context window) നൽകുന്നു. തുടർന്ന് മോഡൽ ആ ടെക്സ്റ്റിനെ ഒരു ഡാറ്റയായി കണ്ട് വിശകലനം ചെയ്യാൻ ശ്രമിക്കുന്നു. അത് ആ എറർ ഒരു സമ്മറിയിൽ ഉൾപ്പെടുത്തിയേക്കാം, അല്ലെങ്കിൽ അതിലും മോശമായി, ആ എറർ ടെക്സ്റ്റിനും മറ്റ് വസ്തുതകൾക്കും ഇടയിൽ ബന്ധങ്ങൾ കെട്ടിച്ചമച്ചേക്കാം (hallucinate). നിങ്ങൾ ഒരു താൽക്കാലികമായ സാങ്കേതിക തടസ്സം തെറ്റായ വിവരങ്ങൾ നൽകുന്ന ഒരു സ്രോതസ്സായി മാറ്റിയിരിക്കുന്നു.
ഒരു എറർ പേലോഡ് (error payload) തിരികെ നൽകുമ്പോൾ എപ്പോഴും isError എന്നത് true എന്ന് സെറ്റ് ചെയ്യുക. ഇത് ടൂൾ കോൾ പരാജയപ്പെട്ടുവെന്ന വ്യക്തമായ സൂചന ക്ലയന്റിന് നൽകുന്നു, ഇത് വീണ്ടും ശ്രമിക്കണോ, കൂടുതൽ വ്യക്തത ചോദിക്കണോ, അതോ മറ്റൊരു ടൂൾ പരീക്ഷിക്കണോ എന്ന് തീരുമാനിക്കാൻ മോഡലിനെ സഹായിക്കുന്നു.
എന്ത് പിടിക്കണം (catch), എന്ത് അവസാനിപ്പിക്കണം (kill) എന്ന് തിരിച്ചറിയുക
എല്ലാം വിഴുങ്ങുന്ന രീതിയിലുള്ള ഒരു ബൈൻഡ് try-catch ഉപയോഗിച്ച് നിങ്ങളുടെ മുഴുവൻ സെർവറിനെയും പൊതിയരുത്. ചില പിശകുകൾ സെർവർ ഉടൻ തന്നെ നിർത്തണമെന്നാണ് അർത്ഥമാക്കുന്നത്. സ്റ്റാർട്ടപ്പ് സമയത്ത് ആവശ്യമായ ഒരു എൻവയോൺമെന്റ് വേരിയബിൾ (environment variable) ഇല്ലെങ്കിലോ, അല്ലെങ്കിൽ നിങ്ങളുടെ കോൺഫിഗറേഷൻ ഫയൽ കേടുപാടുകൾ സംഭവിച്ചതാണെങ്കിലോ, റിക്വസ്റ്റ് തലത്തിലുള്ള പിശക് പിടിക്കൽ (request-level catching) കൊണ്ട് ഒരു പ്രയോജനവുമില്ല. ഇത്തരം മാരകമായ പിശകുകൾക്കായി (fatal errors) ഒരു പ്രത്യേക എക്സെപ്ഷൻ ക്ലാസ് (exception class) നിർമ്മിക്കുകയും അവ പ്രോസസ്സ് ക്രാഷ് ചെയ്യാൻ അനുവദിക്കുകയും ചെയ്യുക.
നിയമം ലളിതമാണ്. പിശക് താൽക്കാലികമാണെങ്കിലോ അല്ലെങ്കിൽ ഒരു റിക്വസ്റ്റിൽ മാത്രം പരിമിതമാണെങ്കിലോ, അത് പിടിച്ചെടുത്ത് (catch) തിരിച്ചുപിടിക്കുക (recover). പിശക് കാരണം തുടർന്നുള്ള എല്ലാ റിക്വസ്റ്റുകളും പരാജയപ്പെടുമെന്ന് ഉറപ്പാണെങ്കിൽ, സെർവർ തകരാറിലാകാൻ അനുവദിക്കുക. തകരാറിലായ അവസ്ഥയിൽ ദിവസങ്ങളോളം പതറിക്കൊണ്ട് പ്രവർത്തിക്കുന്ന ഒരു സെർവറിനേക്കാൾ എത്രയോ നല്ലതാണ് സ്റ്റാർട്ടപ്പിൽ തന്നെ വേഗത്തിൽ പരാജയപ്പെടുന്നത്.
ആവശ്യം വരുന്നതിന് മുൻപേ Observability ചേർക്കുക
ഒരിക്കൽ നിങ്ങൾ Wrapper തയ്യാറാക്കി കഴിഞ്ഞാൽ, അതിനോടൊപ്പം സ്ട്രക്ചേർഡ് ലോഗിംഗും (structured logging) ഉപയോഗിക്കുക. ഓരോ ടൂൾ കോളിന്റെയും ഫലം JSON ഫോർമാറ്റിൽ ലോഗ് ചെയ്യുക. ടൂളിന്റെ പേര്, റ〉 (raw arguments), ലേറ്റൻസി (latency), അത് വിജയിച്ചോ, പരാജയപ്പെട്ടോ അതോ വീണ്ടും ശ്രമിച്ചോ (retried) എന്നിവ ഉൾപ്പെടുത്തുക.
ഈ രീതി പെട്ടെന്ന് ഫലം നൽകും. പിശകുകൾ വർദ്ധിക്കുന്നത് ശ്രദ്ധയിൽപ്പെട്ടാൽ, ടൂൾ അനുസരിച്ച് ഫിൽട്ടർ ചെയ്ത് മിനിറ്റുകൾക്കുള്ളിൽ പാറ്റേണുകൾ കണ്ടെത്താൻ നിങ്ങൾക്ക് കഴിയും. ഒരുപക്ഷേ ഒരു പ്രത്യേക എക്സ്റ്റേണൽ API എല്ലാ ദിവസവും ഒരേ സമയത്ത് ടൈമൗട്ട് (timeout) കാണിക്കുന്നുണ്ടാകാം, ഇത് നിങ്ങൾ അറിയാത്ത ഒരു ഷെഡ്യൂൾഡ് മെയിന്റനൻസ് വിൻഡോയെ സൂചിപ്പിക്കുന്നുണ്ടാകാം. അല്ലെങ്കിൽ ഒരു ടൂളിന് നിരന്തരം തെറ്റായ ആർഗ്യുമെന്റുകൾ (malformed arguments) ലഭിക്കുന്നുണ്ടാകാം, ഇത് പ്രോംപ്റ്റ് എഞ്ചിനീയറിംഗിലെ ഒരു പോരായ്മയെ വെളിപ്പെടുത്തുന്നുണ്ടാകാം. സ്റ്റാക്ക് ട്രാസുകളിൽ (stack traces) കുടുങ്ങിക്കിടക്കുന്ന പ്ലെയിൻ ടെക്സ്റ്റ് ലോഗുകൾ ഈ അന്വേഷണത്തെ പ്രയാസകരമാക്കുന്നു. സ്ട്രക്ചേർഡ് JSON ഇത് വളരെ എളുപ്പമാക്കുന്നു.
പ്രൊഡക്ഷൻ ഫലം
കഴിഞ്ഞ മൂന്നാഴ്ചയായി ഞാൻ രണ്ട് പ്രൊഡക്ഷൻ MCP സെർവറുകളിൽ ഈ Wrapper പാറ്റേൺ ഉപയോഗിക്കുന്നുണ്ട്. ആ കാലയളവിൽ ഒരു സൈലന്റ് ഫെയിലിയറും (silent failure) ഞാൻ കണ്ടിട്ടില്ല. Wrapper ചേർക്കുന്നതിന് മുമ്പ്, ശരാശരി ഓരോ ദിവസവും ഒരു കാരണം വ്യക്തമല്ലാത്ത പരാജയം സംഭവിക്കുമായിരുന്നു. ഈ രീതി സങ്കീർണ്ണമല്ല, പക്ഷേ അതിന്റെ സ്വാധീനം വളരെ വലുതാണ്, കാരണം ഇത് പരിഹരിക്കാവുന്ന ചെറിയ പ്രശ്നങ്ങളിൽ നിന്നും യഥാർത്ഥ പ്രശ്നങ്ങളിൽ നിന്നും വ്യത്യാസം തിരിച്ചറിയാൻ സഹായിക്കുന്നു.
ക്രാഷുകളേക്കാൾ കൂടുതൽ നഷ്ടമുണ്ടാക്കുന്നത് സൈലന്റ് ഫെയിലിയറുകളാണ്. ഒരു ക്രാഷ് നിങ്ങളുടെ അലേർട്ടിംഗ് സിസ്റ്റത്തെ (alerting system) ഉണർത്തും. എന്നാൽ സൈലന്റ് ഫെയിലിയർ വിശ്വാസം തകർക്കുന്നു. ഒരു ദിവസം നിങ്ങളുടെ AI ഏജന്റ് ഉപയോഗപ്രദമായ ടൂൾ ഡാറ്റ നൽകുന്നു, എന്നാൽ അടുത്ത ദിവസം സെർവർ മണിക്കൂറുകൾക്ക് മുമ്പ് മറുപടി നൽകുന്നത് നിർത്തിയതിനാൽ അത് വെറുതെ കാര്യങ്ങൾ ഉണ്ടാക്കി പറയുന്ന അവസ്ഥയുണ്ടാകാം. Wrapper പാറ്റേൺ ആ വിടവ് നികത്തുന്നു. ഇത് ചെറിയ തടസ്സങ്ങൾക്കിടയിലും നിങ്ങളുടെ സെർവർ പ്രവർത്തിപ്പിക്കുന്നു, മോഡലിന് അതിന്റെ തെറ്റുകൾ തിരുത്താൻ ആവശ്യമായ കോൺടെക്സ്റ്റ് നൽകുന്നു, കൂടാതെ മാരകമായ എന്തെങ്കിലും സംഭവിക്കുമ്പോൾ അത് ഉടൻ തന്നെ നിങ്ങളെ അറിയിക്കുന്നുവെന്ന് ഉറപ്പാക്കുന്നു.
നിങ്ങൾ ഇന്ന് MCP ടൂളുകൾ നിർമ്മിക്കുകയാണെങ്കിൽ, Wrapper-ഉം isError ഫ്ലാഗും ഉപയോഗിച്ച് തുടങ്ങുക. ബാക്കിയുള്ളവ വെറും ക്ലീനപ്പ് ജോലികൾ മാത്രമാണ്.
