Ombi lilifanikiwa. Jibu lilikuwa JSON halali. SDK ilibaki kimya. Na bado, programu ilianguka.

Hii ni hadithi ya kinachotokea unapoichukulia kubadilisha mtoa huduma wa LLM kama mabadiliko ya usanidi (configuration) badala ya kamari ya kimuundo. Unabandika base URL mpya, unabadilisha API key, na unaacha mwili wa ombi (request body) uwe ule ule kwa sababu hati (docs) zinaahidi mwisho (endpoint) unaoendana na OpenAI. Kwa prompt ya msingi ya "hello world", inafanya kazi. Unasherehekea. Kisha trafiki halisi inapoingia, mianya inafunguka.

Njozi ya Uendano wa Kiunganishi (The Illusion of Wire Compatibility)

Uendano katika tabaka la HTTP ni mdogo. Kodi ya hali ya 200 (200 status code) na mwili wa JSON vinamaanisha kuwa seva imepokea ujumbe wako. Haimaanishi kuwa seva inafikiri sawa na ile ya awali. Miisho (endpoints) inayooendana na OpenAI inashirikiana umbo la ombi, lakini hazishirikiani mkataba wa kitabia (behavioral contract). Watoa huduma wawili wanaweza kupokea payloads zinazofanana kabisa na kurudisha majibu ambayo yanatofautiana kwa njia ndogo lakini zenye madhara.

Code yako inafanya dhana fulani. Unadhani message.content ni string kwa sababu ilikuwa hivyo hapo awali. Unadhani mwito wa zana (tool call) unakuja na JSON safi inayoweza kuchambuliwa. Unadhani finish_reason inaashiria kile unachofikiri inaashiria. Dhana hizi hazionekani mpaka zinapokuwa hatari.

Fikiria hitilafu iliyoanzisha yote:

const text = response.choices[0].message.content.trim();

Mstari huu unaonekana usiokuwa na hatari. Ulifanya kazi kwa wiki kadhaa. Kisha mtoa huduma mpya alirudisha mwito wa zana (tool call). Katika wakati huo, message.content haikuwa string tupu. Ilikuwa null. Payload halisi ilikuwa ndani ya message.tool_calls, lakini mchambuzi (parser) ulikuwa tayari umeendelea, ukijaribu kutumia .trim() kwenye kitu kisichopo. API haikutupa kosa. Tabaka la mtandao halikulong'enya. Mchambuzi wako mwenyewe aliua ombi hilo.

Mahali Ambapo Watoa Huduma Wanatofautiana Kimyakimya

Tofauti hizo hazitangaziwi kwenye changelogs. Zimejificha kwenye pembe za object ya jibu, zikisubiri matukio ya kipekee (edge cases).

Uandishi wa mwito wa zana (Tool-call formatting). Mtoa huduma mmoja hutuma argument za zana kama object ya JSON iliyothibitishwa tayari. Mwingine huzituma kama string iliyokwepeshwa (escaped string) ndani ya field. Mwingine anaweza kugawanya mwito mrefu wa zana katika vipande vingi vya streaming deltas, hali inayokulazimu kuhifadhi vipande (buffer chunks) kabla hata huwezi kuona ikiwa muundo ni halali. Ikiwa programu yako inatarajia blob moja inayoweza kuchambuliwa, itashindwa.

Sababu za kumaliza (Finish reasons). OpenAI hutumia string maalum kama "stop", "length", "tool_calls", na "content_filter". Mtoa huduma anayeelewana na OpenAI anaweza kurudisha "end_turn" au pengine kuacha field hiyo kabisa wakati modeli inapofikia kikomo cha token. Ikiwa mantiki yako ya kujaribu tena (retry) au mbadala (fallback) inasubiri "length" ili kugundua ukataji wa maandishi, itabaki bila kufanya kazi wakati mtumiaji anapoona jibu lililokatwa nusu.

Sehemu za matumizi (Usage fields). Baadhi ya watoa huduma huondoa idadi ya token kutoka kwenye majibu ya streaming ili kupunguza ucheleweshaji (latency). Wengine huongeza matumizi kwenye kipande cha mwisho tu, au huyaacha kabisa kwenye simu zisizo za streaming. Ikiwa unatoza wateja kwa kila token na code yako ya uhasibu inatarajia usage.total_tokens kuwepo katika kila object ya jibu, mfumo wako wa malipo utarekodi sifuri kimyakimya.

Tabia ya kusambaza (Streaming behavior). Server-sent events zinapaswa kuwa za kawaida, lakini watoa huduma husafisha buffer kwa masafa tofauti. Mipaka ya matukio (event boundaries) inatofautiana. Mtoa huduma mmoja huishia stream kwa ishara ya [DONE]. Mwingine hukata muunganisho vizuri bila ishara yoyote. Ikiwa client yako inasubiri ishara maalum ya kufunga, itakwama.

Makosa na muda uliopitiliza (Errors and timeouts). Kikomo cha kasi (rate limit) kinaweza kuja kama 429 yenye kichwa cha retry-after kutoka kwa mtoa huduma mmoja, na kama 502 isiyoeleweka kutoka kwa mwingine. Baadhi ya watoa huduma hukubali ombi kisha wanakaa kimya kwa dakika mbili kabla ya muda wa mtandao kuisha. OpenAI SDK haitatafsiri haya kiotomatiki kuwa aina za exception ambazo logs zako zinatarajia.

Uchambuzi wa Kinga kwa Maumbo Yasiyotabirika

Suluhisho si kuamini schema. Suluhisho ni kuchukulia kila jibu kama mshukiwa.

Usidhani content ni string. Ikague kabla ya kuigusa.

const content = response.choices?.[0]?.message?.content;
const text = typeof content === "string" ? content.trim() : "";

Usidhani argument za zana ni JSON halali. Modeli inapendekeza kitendo. Code yako lazima iamue ikiwa pendekezo hilo ni salama vya kutosha kutekelezwa. Zungusha (wrap) kila uchambuzi wa argument ya zana kwenye try-catch. Ikiwa JSON.parse itatupa kosa, chukulia mwito wa zana kama takataka isiyo na mpangilio na uelekeze kwenye msimamizi wa makosa (failure handler). Mabano yaliyotengenezwa (hallucinated bracket) au alama ya nukuu iliyokosekana haipaswi kamwe kuibuka kama kosa lisilodhibitiwa (unhandled exception).

Ikiwa tool_calls ipo lakini content haipo, programu yako inapaswa kutambua mabadiliko ya hali (state transition). Mtumiaji hakupata jibu la chat. Mfumo ulipata agizo la kazi. Hizi ni njia mbili tofauti, na router yako inapaswa kujua tofauti hiyo kabla ya kujaribu ubadilishaji wa string.

Majaribio ya Kitabia Kabla ya Kuweka (Behavioral Tests Before You Deploy)

Kutuma ujumbe kwenye endpoint kwa kutumia "hi" kunathibitisha kuwa mtandao unafanya kazi. Lakini hakuthibitishi chochote kuhusu programu yako.

Kabla ya kuelekeza trafiki ya uzalishaji (production traffic), endesha seti ya majaribio ya kitabia (behavioral test suite) iliyolengwa dhidi ya mtoa huduma mpya:

  • Jibu la maandishi la kawaida. Hakikisha kuwa content ipo, ni string, na inaweza kupitishwa kwenye mfumo wako wa kusafisha data (sanitization pipeline) bila makosa ya kubadilisha aina (casting errors).
  • Wito wa zana uliolazimishwa (Forced tool call). Set tool_choice kuwa required. Thibitisha kuwa mtoa huduma anaizingatia, na kagua ikiwa content inakuja kama null, string tupu, au key iliyokosekana. Kila hali kati ya hizo inahitaji msimamizi (handler) wake binafsi.
  • Mizigo ya zana iliyoharibika (Malformed tool arguments). Ingiza hali ambapo modeli inarudisha JSON iliyovunjika ndani ya mizigo ya zana (tool arguments). Hakikisha kuwa mchanganuzi (parser) wako unazikataa kwa utaratibu badala ya kusababisha mfanyakazi (worker) kusimama ghafla.
  • Jibu lililo karibu na kikomo cha token. Sukuma dirisha la muktadha (context window). Kagua finish_reason. Ikiwa mtoa huduma anarudisha kitu kisichotarajiwa wakati ukataji (truncation) unapotokea, mantiki yako ya muhtasari au kujaribu tena (retry logic) lazima ijue jinsi ya kuitikia.

Haya ni majaribio ya muunganisho (integration tests), siyo majaribio ya vipande (unit tests). Yanachunguza uhusiano halisi kati ya kodi yako na tabia ya mtoa huduma. Yapite kabla ya kutangaza uhamiaji (migration) kuwa umekamilika.

Jenga Mkataba wa Ndani

Tofauti za watoa huduma zinapaswa kuishia kwenye mpaka wa mtandao wako. Usiziache zivuje hadi kwenye mantiki ya biashara (business logic).

Tengeneza tabaka la usawazishaji (normalization layer) ambalo linachukua jibu ghafi la SDK na kutoa kitu (object) ambacho programu yako inamiliki kikweli. Linganisha (map) upekee wa mtoa huduma fulani katika muundo thabiti wa ndani. Ikiwa Mtoa Huduma A anarudisha mizigo ya zana kama string na Mtoa Huduma B anarudisha objects, mtafsiri (mapper) wako unazifanya zote kuwa muundo wako wa ToolRequest. Ikiwa matumizi (usage) hayapo, mtafsiri wako ama unayakadiria au unaashiria pengo hilo, lakini hauruhusu undefined ivuje kwenye moduli zako za kufuatilia gharama.

Ikiwa finish_reason si wa kawaida, ubadilishe kuwa enum yako ya hali za mwisho: COMPLETE, TRUNCATED, TOOL_CALL, FILTERED. Programu yako inapaswa kuamua cha kufanya kulingana na dhana hizi safi (clean abstractions), na si kwa kunusa string ghafi kutoka kwa seva ya upande wa tatu.

Tabaka hili linageuza kubadilisha watoa huduma kutoka mchezo wa "whack-a-mole" kuwa mabadiliko ya faili moja tu. Unandika upya mtafsiri (mapper), unarudia majaribio ya kitabia, na unaendelea. Programu yako inabaki bila kuguswa.

Upgradiaji wa Utegemezi, si Marekebisho ya Usanidi

Kubadilisha watoa huduma wa LLM si kama kubadilisha endpoint za CDN. Ni karibu zaidi na kubadilisha kanzi data (database) yako kutoka PostgreSQL kwenda MySQL. Hungepata kamwe dhana kwamba string ile ile ya muunganisho (connection string) inamaanisha tabia sawa ya hoja (query behavior). Ungetathmini mantiki ya kufunga (locking semantics), njia za uhamiaji (migration paths), na upekee wa uwekaji index (indexing quirks). LLM zinastahili heshima hiyo hiyo. Ni mifumo ya uwezekano (probabilistic systems) inayojifanya kuwa API za kawaida, na majibu yao hubeba dhana kuhusu uundaji (formatting), ukataji (truncation), na mtiririko wa udhibiti (control flow) ambao unaweza kuvunja programu yako bila kutoa hata kosa moja la mtandao.

Hitilafu haikuwa kwenye muunganisho. Ilikuwa kwenye dhana kwamba utangamano (compatibility) unamaanisha usawa. Haiwezi kuwa hivyo. Thibitisha umbo. Jaribu mipaka. Miliki mkataba.


Source: The Bug Only Happened After I Switched LLM Providers

Community: GyaanSetu AI on Telegram