Põhjalik juhend arendajatele TypeScripti kasutamiseks tugevate, skaleeritavate ja tüübikindlate rakenduste loomisel suurte keelemudelite (LLM) ja NLP abil. Õppige vältima käitusaja vigu ja omandage struktureeritud väljundid.
LLM-ide kasutamine TypeScriptiga: Ülim juhend tüübikindla NLP integreerimiseks
Suurte keelemudelite (LLM) ajastu on käes. Selliste pakkujate nagu OpenAI, Google, Anthropic API-sid ja avatud lähtekoodiga mudeleid integreeritakse rakendustesse hingematva kiirusega. Alates intelligentstest juturobotitest kuni keerukate andmeanalüüsivahenditeni muudavad LLM-id tarkvaras võimaliku piire. Kuid see uus piir seab arendajatele olulise väljakutse: hallata LLM-i väljundite ettearvamatut, tõenäosuslikku olemust rakenduse koodi deterministlikus maailmas.
Kui palute LLM-il genereerida teksti, tegelete mudeliga, mis toodab sisu statistiliste mustrite, mitte range loogika alusel. Kuigi saate seda suunata tagastama andmeid kindlas vormingus, näiteks JSON, pole mingit garantiid, et see iga kord täiuslikult vastab. See varieeruvus on käitusaja vigade, ootamatu rakenduse käitumise ja hoolduslike õudusunenägude peamine allikas. Siin muutub TypeScript, JavaScripti staatiliselt tüübitud ülikomplekt, mitte ainult kasulikuks tööriistaks, vaid ka oluliseks komponendiks tootmiskvaliteediga AI-toega rakenduste ehitamisel.
See põhjalik juhend juhatab teid läbi TypeScripti kasutamise miks ja kuidas, et tagada tüübikindlus teie LLM-i ja NLP integratsioonides. Uurime aluspõhimõtteid, praktilisi rakendusmustreid ja täiustatud strateegiaid, mis aitavad teil ehitada rakendusi, mis on vastupidavad, hooldatavad ja vastupidavad AI-le omase ettearvamatuse ees.
Miks TypeScript LLM-ide jaoks? Tüübikindluse nõue
Traditsioonilises API integratsioonis on teil sageli range leping – OpenAPI spetsifikatsioon või GraphQL skeem – mis määratleb täpselt andmete kuju, mida te saate. LLM API-d on erinevad. Teie "leping" on saadetav loomuliku keele küsimus ja selle tõlgendamine mudeli poolt võib erineda. See põhiline erinevus muudab tüübikindluse ülioluliseks.
LLM-i väljundite ettearvamatu olemus
Kujutage ette, et olete palunud LLM-il eraldada kasutaja andmed tekstiplokist ja tagastada JSON objekt. Te ootate midagi sellist:
{ "name": "John Doe", "email": "john.doe@example.com", "userId": 12345 }
Kuid mudeli hallutsinatsioonide, küsimuse valesti tõlgendamise või selle koolituse väikeste variatsioonide tõttu võite saada:
- Puuduva välja: 
{ "name": "John Doe", "email": "john.doe@example.com" } - Vale tüübiga välja: 
{ "name": "John Doe", "email": "john.doe@example.com", "userId": "12345-A" } - Täiendavad, ootamatud väljad: 
{ "name": "John Doe", "email": "john.doe@example.com", "userId": 12345, "notes": "Kasutaja tundub sõbralik." } - Täiesti valesti vormistatud string, mis pole isegi kehtiv JSON.
 
Tavalises JavaScriptis võib teie kood proovida juurde pääseda response.userId.toString(), mis viib veani TypeError: Cannot read properties of undefined, mis krahhib teie rakenduse või rikub teie andmed.
TypeScripti peamised eelised LLM-i kontekstis
TypeScript tegeleb nende väljakutsetega otse, pakkudes tugevat tüübisüsteemi, mis pakub mitmeid peamisi eeliseid:
- Kompileerimisaja veakontroll: TypeScripti staatiline analüüs tabab potentsiaalsed tüübipõhised vead arenduse ajal, ammu enne, kui teie kood tootmisse jõuab. See varajane tagasisideahel on hindamatu, kui andmeallikas on olemuselt ebausaldusväärne.
 - Intelligentne koodi lõpetamine (IntelliSense): Kui olete määratlenud LLM-i väljundi eeldatava kuju, saab teie IDE pakkuda täpset automaatset lõpetamist, vähendades kirjavigu ja muutes arenduse kiiremaks ja täpsemaks.
 - Ise dokumenteeriv kood: Tüübimääratlused on selged, masinloetavad dokumendid. Arendaja, nähes funktsiooni allkirja nagu 
function processUserData(data: UserProfile): Promise<void>, saab kohe aru andmelepingust ilma ulatuslikke kommentaare lugemata. - Turvalisem refaktoreerimine: Rakenduse arenedes peate vältimatult muutma andmestruktuure, mida LLM-ilt ootate. TypeScripti kompilaator juhendab teid, tuues esile iga teie koodibaasi osa, mida tuleb uue struktuuriga kohanemiseks värskendada, vältides regressioone.
 
Aluspõhimõtted: LLM-i sisendite ja väljundite tüüpimine
Tüübikindluse teekond algab selgete lepingute määratlemisega nii LLM-ile saadetavate andmete (küsimus) kui ka andmete, mida te ootate saavat (vastus), jaoks.Küsimuse tüüpimine
Kuigi lihtne küsimus võib olla string, hõlmavad keerukad interaktsioonid sageli struktureeritumaid sisendeid. Näiteks vestlusrakenduses haldate te sõnumite ajalugu, millest igaühel on kindel roll. Saate seda modelleerida TypeScripti liidestega:
            
interface ChatMessage {
  role: 'system' | 'user' | 'assistant';
  content: string;
}
interface ChatPrompt {
  model: string;
  messages: ChatMessage[];
  temperature?: number;
  max_tokens?: number;
}
            
          
        See lähenemisviis tagab, et pakute alati sõnumeid kehtiva rolliga ja et kogu küsimuse struktuur on õige. Liittüübi, näiteks 'system' | 'user' | 'assistant' kasutamine omaduse role jaoks hoiab ära lihtsad kirjavigu, nagu 'systen', mis põhjustavad käitusaja vigu.
LLM-i vastuse tüüpimine: põhiväljakutse
Vastuse tüüpimine on keerukam, kuid ka kriitilisem. Esimene samm on veenda LLM-i pakkuma struktureeritud vastust, tavaliselt küsides JSON-i. Teie küsimuse loomine on siin võtmetähtsusega.
Näiteks võite oma küsimuse lõpetada sellise juhisega nagu:
"Analüüsige järgmise kliendi tagasiside meeleolu. Vastake AINULT JSON objektiga järgmises vormingus: { \"sentiment\": \"Positive\", \"keywords\": [\"word1\", \"word2\"] }. Meeleolu võimalikud väärtused on 'Positive', 'Negative' või 'Neutral'."
Selle juhisega saate nüüd määratleda vastava TypeScripti liidese selle eeldatava struktuuri esindamiseks:
            
type Sentiment = 'Positive' | 'Negative' | 'Neutral';
interface SentimentAnalysisResponse {
  sentiment: Sentiment;
  keywords: string[];
}
            
          
        Nüüd saab mis tahes funktsiooni teie koodis, mis töötleb LLM-i väljundit, tüüpida ootama SentimentAnalysisResponse objekti. See loob teie rakenduses selge lepingu, kuid see ei lahenda kogu probleemi. LLM-i väljund on endiselt vaid string, mida te loodate olevat kehtiv JSON, mis vastab teie liidesele. Meil on vaja viisi selle valideerimiseks käitusajal.
Praktiline rakendamine: samm-sammult juhend Zodiga
TypeScripti staatilised tüübid on mõeldud arendusajaks. Lõhe ületamiseks ja tagamaks, et käitusajal saadud andmed vastavad teie tüüpidele, vajame käitusaja valideerimisteeki. Zod on uskumatult populaarne ja võimas TypeScripti-esimene skeemi deklaratsiooni- ja valideerimisteek, mis sobib ideaalselt selle ülesande jaoks.
Loome praktilise näite: süsteemi, mis eraldab struktureeritud andmed struktureerimata töökohataotluse e-kirjast.
1. samm: projekti seadistamine
Algatage uus Node.js projekt ja installige vajalikud sõltuvused:
npm init -y
npm install typescript ts-node zod openai
npx tsc --init
Veenduge, et teie tsconfig.json on õigesti konfigureeritud (nt määrake "module": "NodeNext" ja "moduleResolution": "NodeNext").
2. samm: andmelepingu määratlemine Zodi skeemiga
Lihtsalt TypeScripti liidese määratlemise asemel määratleme Zodi skeemi. Zod võimaldab meil TypeScripti tüüpi otse skeemist järeldada, andes meile nii käitusaja valideerimise kui ka staatilised tüübid ühest tõesuse allikast.
            
import { z } from 'zod';
// Define the schema for the extracted applicant data
const ApplicantSchema = z.object({
  fullName: z.string().describe("The full name of the applicant"),
  email: z.string().email("A valid email address for the applicant"),
  yearsOfExperience: z.number().min(0).describe("The total years of professional experience"),
  skills: z.array(z.string()).describe("A list of key skills mentioned"),
  suitabilityScore: z.number().min(1).max(10).describe("A score from 1 to 10 indicating suitability for the role"),
});
// Infer the TypeScript type from the schema
type Applicant = z.infer<typeof ApplicantSchema>;
// Now we have both a validator (ApplicantSchema) and a static type (Applicant)!
            
          
        3. samm: tüübikindla LLM API kliendi loomine
Nüüd loome funktsiooni, mis võtab e-kirja keha, saadab selle LLM-ile ja proovib vastust parseldada ja valideerida meie Zodi skeemi vastu.
            
import { OpenAI } from 'openai';
import { z } from 'zod';
import { ApplicantSchema } from './schemas'; // Assuming schema is in a separate file
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});
// A custom error class for when LLM output validation fails
class LLMValidationError extends Error {
  constructor(message: string, public rawOutput: string) {
    super(message);
    this.name = 'LLMValidationError';
  }
}
async function extractApplicantData(emailBody: string): Promise<Applicant> {
  const prompt = `
    Please extract the following information from the job application email below.
    Respond with ONLY a valid JSON object that conforms to this schema:
    {
      "fullName": "string",
      "email": "string (valid email format)",
      "yearsOfExperience": "number",
      "skills": ["string"],
      "suitabilityScore": "number (integer from 1 to 10)"
    }
    Email Content:
    ---
    ${emailBody}
    ---
  `;
  const response = await openai.chat.completions.create({
    model: 'gpt-4-turbo-preview',
    messages: [{ role: 'user', content: prompt }],
    response_format: { type: 'json_object' }, // Use model's JSON mode if available
  });
  const rawOutput = response.choices[0].message.content;
  if (!rawOutput) {
    throw new Error('Received an empty response from the LLM.');
  }
  try {
    const jsonData = JSON.parse(rawOutput);
    // This is the crucial runtime validation step!
    const validatedData = ApplicantSchema.parse(jsonData);
    return validatedData;
  } catch (error) {
    if (error instanceof z.ZodError) {
      console.error('Zod validation failed:', error.errors);
      // Throw a custom error with more context
      throw new LLMValidationError('LLM output did not match the expected schema.', rawOutput);
    } else if (error instanceof SyntaxError) {
      // JSON.parse failed
      throw new LLMValidationError('LLM output was not valid JSON.', rawOutput);
    } else {
      throw error; // Re-throw other unexpected errors
    }
  }
}
            
          
        Selles funktsioonis on rida ApplicantSchema.parse(jsonData) sild ettearvamatu käitusaja maailma ja meie tüübikindla rakenduse koodi vahel. Kui andmete kuju või tüübid on valed, viskab Zod üksikasjaliku vea, mille me kinni püüame. Kui see õnnestub, võime olla 100% kindlad, et objekt validatedData vastab täiuslikult meie tüübile Applicant. Sellest hetkest alates saab ülejäänud meie rakendus neid andmeid kasutada täieliku tüübikindluse ja enesekindlusega.
Täiustatud strateegiad ülima vastupidavuse tagamiseks
Valideerimise ebaõnnestumiste ja uuesti proovimiste käsitlemine
Mis juhtub, kui LLMValidationError visatakse? Lihtsalt krahhi tekitamine pole vastupidav lahendus. Siin on mõned strateegiad:
- Logimine: Logige alati sisse 
rawOutput, mille valideerimine ebaõnnestus. Need andmed on hindamatud teie küsimuste silumiseks ja mõistmiseks, miks LLM ei suuda nõudeid täita. - Automatiseeritud uuesti proovimine: Rakendage uuesti proovimise mehhanism. Püüdmise plokis saate teha teise kõne LLM-ile. Seekord lisage küsimusse algne valesti vormistatud väljund ja Zodi veateated, paludes mudelil oma eelmist vastust parandada.
 - Varu loogika: Mittekriitiliste rakenduste puhul võite pärast mõningaid uuesti proovimisi langeda vaikeseisundisse või käsitsi läbivaatamise järjekorda, kui valideerimine ebaõnnestub.
 
            
// Simplified retry logic example
async function extractWithRetry(emailBody: string, maxRetries = 2): Promise<Applicant> {
  let attempts = 0;
  let lastError: Error | null = null;
  while (attempts < maxRetries) {
    try {
      return await extractApplicantData(emailBody);
    } catch (error) {
      attempts++;
      lastError = error as Error;
      console.log(`Attempt ${attempts} failed. Retrying...`);
    }
  }
  throw new Error(`Failed to extract data after ${maxRetries} attempts. Last error: ${lastError?.message}`);
}
            
          
        Generics korduvkasutatavate, tüübikindlate LLM funktsioonide jaoks
Saate kiiresti aru, et kirjutate sarnast ekstraheerimisloogikat erinevate andmestruktuuride jaoks. See on TypeScripti genericsi täiuslik kasutusjuht. Saame luua kõrgema astme funktsiooni, mis genereerib tüübikindla parseri mis tahes Zodi skeemi jaoks.
            
async function createStructuredOutput<T extends z.ZodType>(
  content: string,
  schema: T,
  promptInstructions: string
): Promise<z.infer<T>> {
  const prompt = `${promptInstructions}\n\nContent to analyze:\n---\n${content}\n---\n`;
  // ... (OpenAI API call logic as before)
  const rawOutput = response.choices[0].message.content;
  
  // ... (Parsing and validation logic as before, but using the generic schema)
  const jsonData = JSON.parse(rawOutput!);
  const validatedData = schema.parse(jsonData);
  return validatedData;
}
// Usage:
const emailBody = "...";
const promptForApplicant = "Extract applicant data and respond with JSON...";
const applicantData = await createStructuredOutput(emailBody, ApplicantSchema, promptForApplicant);
// applicantData is fully typed as 'Applicant'
            
          
        See geneeriline funktsioon kapseldab LLM-i kutsumise, parseldamise ja valideerimise põhiloogika, muutes teie koodi oluliselt modulaarsemaks, korduvkasutatavamaks ja tüübikindlamaks.
Lisaks JSON-ile: tüübikindel tööriistade kasutamine ja funktsioonide kutsumine
Kaasaegsed LLM-id arenevad lihtsast teksti genereerimisest kaugemale mõtlemismootoriteks, mis saavad kasutada väliseid tööriistu. Sellised funktsioonid nagu OpenAI "Funktsioonide kutsumine" või Anthropic "Tööriistade kasutamine" võimaldavad teil kirjeldada oma rakenduse funktsioone LLM-ile. Seejärel saab LLM valida ühe neist funktsioonidest "kutsuda", genereerides JSON objekti, mis sisaldab funktsiooni nime ja argumente, mida sellele edastada.
TypeScript ja Zod sobivad selle paradigma jaoks erakordselt hästi.
Tööriistade määratluste ja käivitamise tüüpimine
Kujutage ette, et teil on e-kaubanduse juturoboti jaoks tööriistade komplekt:
checkInventory(productId: string)getOrderStatus(orderId: string)
Saate need tööriistad määratleda, kasutades nende argumentide jaoks Zodi skeeme:
            
const checkInventoryParams = z.object({ productId: z.string() });
const getOrderStatusParams = z.object({ orderId: z.string() });
const toolSchemas = {
  checkInventory: checkInventoryParams,
  getOrderStatus: getOrderStatusParams,
};
// We can create a discriminated union for all possible tool calls
const ToolCallSchema = z.discriminatedUnion('toolName', [
  z.object({ toolName: z.literal('checkInventory'), args: checkInventoryParams }),
  z.object({ toolName: z.literal('getOrderStatus'), args: getOrderStatusParams }),
]);
type ToolCall = z.infer<typeof ToolCallSchema>;
            
          
        Kui LLM vastab tööriistakutse taotlusega, saate seda parseldada, kasutades ToolCallSchema. See tagab, et toolName on teie toetatud ja et objektil args on selle konkreetse tööriista jaoks õige kuju. See hoiab ära teie rakenduse proovimise käivitada olematuid funktsioone või kutsuda olemasolevaid funktsioone valede argumentidega.
Teie tööriista käivitamise loogika saab seejärel kasutada tüübikindlat switch lauset või kaarti, et saata kõne õigele TypeScripti funktsioonile, olles kindel, et argumendid on kehtivad.
Globaalne perspektiiv ja parimad praktikad
Globaalsele vaatajaskonnale mõeldud LLM-toega rakenduste ehitamisel pakub tüübikindlus täiendavaid eeliseid:
- Lokaliseerimise käsitlemine: Kuigi LLM saab genereerida teksti paljudes keeltes, peaksid ekstraheeritud struktureeritud andmed jääma järjepidevaks. Tüübikindlus tagab, et kuupäevaväli on alati kehtiv ISO string, valuuta on alati arv ja eelmääratletud kategooria on alati üks lubatud enum väärtustest, olenemata lähtekeeleest.
 - API areng: LLM pakkujad värskendavad sageli oma mudeleid ja API-sid. Tugeva tüübisüsteemi olemasolu muudab nende muudatustega kohanemise oluliselt lihtsamaks. Kui väli on aegunud või uus lisatakse, näitab TypeScripti kompilaator teile kohe iga teie koodi kohta, mida on vaja värskendada.
 - Auditeerimine ja vastavus: Tundlike andmetega tegelevate rakenduste puhul on LLM-i väljundite sundimine rangesse, valideeritud skeemi auditeerimise jaoks ülioluline. See tagab, et mudel ei tagasta ootamatut või mittevastavat teavet, muutes kallutatuse või turvaaukude analüüsimise lihtsamaks.
 
Järeldus: ehitame AI tulevikku enesekindlalt
Suurte keelemudelite integreerimine rakendustesse avab võimaluste maailma, kuid tutvustab ka uut klassi väljakutseid, mis on juurdunud mudelite tõenäosuslikus olemuses. Selges JavaScriptis sarnaste dünaamiliste keelte kasutamine selles keskkonnas on nagu tormis navigeerimine ilma kompassita – see võib mõnda aega töötada, kuid teil on pidev oht sattuda ootamatusse ja ohtlikku kohta.
TypeScript, eriti kui see on ühendatud käitusaja valideerimisteegiga nagu Zod, pakub kompassi. See võimaldab teil määratleda selged, ranged lepingud AI kaootilise, paindliku maailma jaoks. Kasutades staatilist analüüsi, järeldatud tüüpe ja käitusaja skeemi valideerimist, saate ehitada rakendusi, mis pole mitte ainult võimsamad, vaid ka oluliselt usaldusväärsemad, hooldatavamad ja vastupidavamad.
Sild LLM-i tõenäosusliku väljundi ja teie koodi deterministliku loogika vahel peab olema tugevdatud. Tüübikindlus on see tugevdamine. Neid põhimõtteid omaks võttes ei kirjuta te mitte ainult paremat koodi; te projekteerite usaldust ja prognoositavust oma AI-toega süsteemide tuumikusse, võimaldades teil uuendusi teha kiiresti ja enesekindlalt.