15 geselecteerde LLM en generatieve AI interviewvragen, gegroepeerd per senioriteitsniveau. Gebruik ze om basisprincipes, praktische afwegingen en redeneringen op seniorniveau voor productieomgevingen door te nemen.
1Leg subword-tokenisatie uit en waarom het de voorkeur heeft boven tokenisatie op woordniveau of karakterniveau in moderne taalmodellen.
Subword-tokenisatie is een hybride tekstsegmentatiebenadering die tekst opsplitst in morfologische brokken van variabele lengte of frequentiegebaseerde substrings (zoals 'on', 'door', 'breek', 'baar') in plaats van hele woorden of individuele karakters. Algoritmen zoals Byte-Pair Encoding (BPE), WordPiece en Unigram LM leren een vocabulair van vaste grootte uit een trainingscorpus, waarbij frequente woorden intact blijven als enkele tokens, terwijl zeldzame of onbekende woorden worden ontleed in bekende subword-eenheden. Subword-tokenisatie heeft de voorkeur in moderne taalmodellen omdat het een balans vindt tussen vocabulairegrootte, sequentielengte en robuustheid tegen onbekende woorden (Out-Of-Vocabulary, OOV). Zuivere tokenisatie op woordniveau vereist een buitensporig groot vocabulair (wat leidt tot enorme embedding-matrices) en lijdt nog steeds aan OOV-tokens die worden toegewezen aan generieke '[UNK]'-tokens. Zuivere tokenisatie op karakterniveau elimineert daarentegen OOV-problemen, maar levert zeer lange sequenties op die de computationele complexiteit in aandachtsmechanismen (die kwadratisch schalen met sequentielengte) drastisch verhogen en de semantische dichtheid per token verdunnen. Subword-tokenisatie bereikt een optimale afweging door sequentielengtes beheersbaar te houden, vocabulairegroottes praktisch te maken (doorgaans 32k tot 128k tokens) en OOV-percentages op nul te houden (vooral in combinatie met byte-niveau fallbacks).
2Leg het verschil uit tussen statische woordembeddings, contextuele embeddings en verborgen toestanden van transformers.
Statische woordembeddings, contextuele embeddings en verborgen toestanden van transformers representeren progressieve ontwikkelingen in hoe tekstrepresentaties betekenis en syntactische context vastleggen.
1. **Statische woordembeddings** (bijv. Word2Vec, GloVe, FastText) wijzen een enkele, vaste vector toe aan elk vocabulairetoken, ongeacht de zinscontext. In dit paradigma hebben polyseme woorden zoals 'bank' (rivieroever versus financiële bank) of 'appel' (vrucht versus technologiebedrijf) identieke vectorrepresentaties in alle contexten, vertrouwend op een statische opzoektabel.
2. **Contextuele embeddings** (bijv. vroege ELMo, BERT token-representaties, of zins-embeddings van Bi-Encoders) produceren representaties waarbij de vector voor een token een dynamische functie is van de omringende context. In BERT of ELMo ontvangt 'bank' in 'rivieroever' een compleet andere embedding-vector dan 'bank' in 'geld storten bij de bank'.
3. **Verborgen toestanden van transformers** verwijzen naar de intermediaire vectorrepresentaties die worden geproduceerd in elke individuele laag van een transformernetwerk tijdens een forward-pass. Gegeven input token-embeddings in laag 0, past elke opeenvolgende transformerlaag self-attention en feed-forward transformaties toe, wat een sequentie van verborgen toestand-vectoren h_l in laag l oplevert. Hoewel de verborgen toestanden van de laatste laag fungeren als contextuele embeddings op hoog niveau, vangen lagere en middelste verborgen toestanden laag-niveau syntactische, lexicale en structurele kenmerken op. Zo omvatten verborgen toestanden van transformers het volledige verticale continuüm van laag-voor-laag representaties binnen het netwerk.
import torch
from transformers import AutoTokenizer, AutoModel
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
model = AutoModel.from_pretrained('bert-base-uncased', output_hidden_states=True)
text1 = 'River bank'
text2 = 'Bank deposit'
inputs1 = tokenizer(text1, return_tensors='pt')
inputs2 = tokenizer(text2, return_tensors='pt')
with torch.no_grad():
out1 = model(**inputs1)
out2 = model(**inputs2)
# Layer 0 (Input token embeddings - static lookup before self-attention)
static_bank_1 = out1.hidden_states[0][0, 2] # 'bank'
# Layer 12 (Final contextual hidden state after all attention layers)
contextual_bank_1 = out1.hidden_states[12][0, 2]
contextual_bank_2 = out2.hidden_states[12][0, 1]
print('Cosine similarity of bank in different contexts (Layer 12):',
torch.cosine_similarity(contextual_bank_1, contextual_bank_2, dim=0).item())
3Leg uit wat een `embedding space` vertegenwoordigt en hoe cosinusgelijkenis wordt geïnterpreteerd voor tekst-embeddings.
Een `embedding space` is een continue, hoogdimensionale vectorruimte `R^d` waarin discrete tekstuele entiteiten (woorden, zinnen of documenten) worden afgebeeld, zodat semantische, syntactische of relationele gelijkenissen overeenkomen met geometrische nabijheid en directionele relaties. In deze ruimte weerspiegelen afstanden en hoeken de semantische gerelateerdheid. `Cosinusgelijkenis` meet de cosinus van de hoek `theta` tussen twee vectoren `u` en `v`, berekend als: `Cosine Similarity(u, v) = (u . v) / (||u|| ||v||)`
`Cosinusgelijkenis` wordt in tekst-embeddings als volgt geïnterpreteerd:
- Bereik & Oriëntatie: Het produceert een scalaire waarde die doorgaans is begrensd in `[-1, 1]` (of `[0, 1]` voor niet-negatieve embeddings). Een waarde dicht bij `1.0` geeft aan dat de twee vectoren vrijwel in dezelfde richting wijzen, wat een hoge semantische gelijkenis of thematische afstemming weerspiegelt. Een waarde nabij `0.0` impliceert orthogonaliteit (semantische onafhankelijkheid of niet-gerelateerdheid), en negatieve waarden duiden op tegengestelde oriëntaties.
- Magnitudonafhankelijkheid: In tegenstelling tot `Euclidische afstand` (`L2`-afstand) of `inproduct`, normaliseert `cosinusgelijkenis` voor vectorlengte. In tekst-embeddings kan de vectormagnitude soms correleren met sequencelengte, `token` frequentie of term-specificiteit. Door zich puur te richten op directionele uitlijning, isoleert `cosinusgelijkenis` de semantische oriëntatie van verschillen in vectormagnitude.
4Leg het verschil uit tussen token-embeddings, positionele embeddings en segment- of type-embeddings in transformer-inputs.
In transformer-inputs (met name BERT-achtige architecturen) wordt de input-representatie voor elk token doorgaans gevormd door de elementsgewijze optelling van drie afzonderlijke embedding-vectoren:
1. Token-embeddings: Deze zetten discrete token-ID's van het vocabulaire om in dichte vectoren die de kern semantische en lexicale identiteit van de tokens vertegenwoordigen.
2. Positionele embeddings: Deze voegen informatie over de token-volgorde en opeenvolgende index toe aan de representatie, ter compensatie van het feit dat self-attention inherent permutatie-invariant is.
3. Segment- (of Token Type-) embeddings: Deze onderscheiden verschillende tekstsegmenten of zinnen die in één enkele inputreeks zijn verpakt (zoals zin A versus zin B in gepaarde classificatie- of vraag-antwoord taken).
De combinatie van deze embeddings levert een enkele dichte input-tensor op die de token-betekenis, positie en volgorde-groepering codeert voordat deze wordt doorgegeven aan de eerste transformer-laag.
import torch
import torch.nn as nn
vocab_size, max_seq_len, num_segments, d_model = 30522, 512, 2, 768
tok_embed = nn.Embedding(vocab_size, d_model)
pos_embed = nn.Embedding(max_seq_len, d_model)
seg_embed = nn.Embedding(num_segments, d_model)
input_ids = torch.tensor([[101, 7592, 102, 2023, 102]]) # Token IDs
type_ids = torch.tensor([[0, 0, 0, 1, 1 ]]) # Segment IDs (Sentence A vs B)
positions = torch.arange(input_ids.size(1)).unsqueeze(0) # Indices: [0, 1, 2, 3, 4]
# Final representation is the element-wise sum
input_rep = tok_embed(input_ids) + pos_embed(positions) + seg_embed(type_ids)
print(input_rep.shape)
5Leg attentie uit als een mechanisme voor het relateren van tokens in een sequentie, inclusief queries, keys, values en multi-head attentie.
Attentie is een mechanisme dat tokens in een sequentie in staat stelt om dynamisch informatie te routeren en de relevantie van alle andere tokens af te wegen op basis van contextuele matching. Lineaire projecties converteren de input van elk token naar drie vectoren: - Query (Q): Vertegenwoordigt welke informatie het huidige token zoekt. - Key (K): Vertegenwoordigt welke attributen of inhoud een token aanbiedt om te matchen met queries. - Value (V): Bevat de feitelijke informatiepayload die geaggregeerd moet worden. In geschaalde dot-product attentie worden attentiescores berekend door Queries en Keys ($Q K^T$) te vermenigvuldigen, geschaald met $\frac{1}{\sqrt{d_k}}$ om gradient vanishing over grote dimensies te voorkomen, en genormaliseerd met een softmax-functie. De uiteindelijke output is de gewogen som van Values: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ Multi-Head Attentie (MHA) projecteert $Q$, $K$ en $V$ parallel in meerdere onafhankelijke representatieve deelruimten (heads). Dit stelt het model in staat om gelijktijdig aandacht te besteden aan verschillende soorten relaties (bijv. syntactische structuur, coreferentie, langeafstandsafhankelijkheden) over posities. De outputs van de heads worden geconcateneerd en lineair teruggeprojecteerd naar de modeldimensie.
6Leg uit hoe aandachtsmaskering (attention masking) verschilt voor causale decoders versus bidirectionele encoders en welk gedrag het mogelijk maakt of voorkomt.
Aandachtsmaskering regelt welke tokens 'aandacht mogen besteden' aan welke andere tokens door aandachtslogits (scores vóór softmax) op $-\infty$ in te stellen voor niet-toegestane paren, zodat hun aandachtsgewicht na softmax strikt 0 is. 1. Causale decoders (bijv. GPT, LLaMA): Gebruiken een lager-driehoekig causaal (autoregressief) masker. Een token op positie $i$ kan alleen aandacht besteden aan posities $j \le i$. Dit voorkomt aandacht voor toekomstige tokens, maakt autoregressieve token-voor-token-generatie tijdens inferentie mogelijk en voorkomt 'label leakage' van toekomstige tokens tijdens parallelle training. 2. Bidirectionele encoders (bijv. BERT): Gebruiken geen causaal masker; elk token kan aandacht besteden aan alle voorgaande en toekomstige tokens binnen de sequentie. Ze gebruiken padding-maskers om te voorkomen dat geldige tokens aandacht besteden aan lege `[PAD]` tokens in gebatchte sequenties. Bidirectionele aandacht produceert rijke, alomvattende contextuele representaties die ideaal zijn voor begripsgerichte taken, maar voorkomt directe single-pass autoregressieve tekstgeneratie.
import torch
import torch.nn.functional as F
scores = torch.randn(3, 3)
# Create upper-triangular mask for future positions
causal_mask = torch.triu(torch.ones(3, 3, dtype=torch.bool), diagonal=1)
# Mask future positions with -inf before softmax
masked_scores = scores.masked_fill(causal_mask, float('-inf'))
atten_weights = F.softmax(masked_scores, dim=-1)
print(atten_weights)
7Leg het verschil uit tussen encoder-only, decoder-only en encoder-decoder transformer-architecturen voor taaltaken.
De drie primaire transformer-architecturen verschillen fundamenteel in hun aandacht-maskeringspatronen en beoogde operationele doelen:
1. **Encoder-Only** (bijv. BERT, RoBERTa): Gebruikt bidirectionele zelf-aandacht waarbij elk token tegelijkertijd aandacht kan schenken aan alle andere tokens in de sequentie. Het produceert rijke contextuele representaties voor een hele invoersequentie, waardoor het ideaal is voor classificatie, extractieve QA (Question Answering) en feature-representatie. Het kan niet van nature autoregressieve tekst genereren.
2. **Decoder-Only** (bijv. GPT-3, Llama, Mistral): Gebruikt causale (unidirectionele) zelf-aandacht, waarbij token $i$ alleen aandacht kan schenken aan tokens op posities $j \le i$. Het wordt autoregressief getraind met behulp van 'next-token prediction' en dient als de standaardarchitectuur voor generatieve taalmodellen, codegeneratie en open-ended conversatie.
3. **Encoder-Decoder** (bijv. T5, BART): Combineert een bidirectionele encoder met een autoregressieve causale decoder. Naast causale zelf-aandacht over gegenereerde tokens, gebruikt de decoder kruis-aandachtlagen die de uitvoerrepresentaties van de encoder bevragen. Deze architectuur is specifiek gebouwd voor 'sequence-to-sequence' transformatietaken zoals vertaling en samenvatting.
8Leg uit hoe de keuzes van tokenizers op byte-niveau, Unicode-bewust en meertalig de modelkwaliteit, kosten en eerlijkheid tussen talen beïnvloeden.
Ontwerpkeuzes voor tokenizers – zoals segmentaties op byte-niveau versus Unicode-bewuste segmentaties en toewijzingen van meertalige vocabulaires – hebben een directe invloed op de downstream modelkwaliteit, de inferentie-/trainingskosten en de linguïstische eerlijkheid. Wat betreft kosten en eerlijkheid, tokenizers die voornamelijk zijn getraind op Engelse of Latijnse-schrift corpora, wijzen de meeste vocabulaire-items toe aan Engelse woorden en morfemen. Bijgevolg bereikt Engels een hoge compressie (bijvoorbeeld ~1,3 tokens per woord), terwijl niet-Latijnse schriften (bijvoorbeeld Arabisch, Devanagari, Thai, Chinees) of talen met weinig bronnen vaak worden gefragmenteerd in meerdere subwoorden of ruwe UTF-8 bytes (vaak 3 tot 6 tokens per woord). Dit verschil wordt vaak de 'tokenbelasting' of 'vruchtbaarheidsverschil' genoemd: niet-Engelse gebruikers betalen aanzienlijk meer per eenheid van semantische inhoud in API-facturering, verbruiken contextvensterlimieten veel sneller en ervaren een hogere latentie. Wat betreft kwaliteit, tokenizers op byte-niveau (zoals Byte-level BPE in GPT-2/GPT-4 of SentencePiece met byte-fallback in LLaMA) voorkomen volledig crashes door ongeziene tekens en out-of-vocabulary (UNK) fouten, omdat elke geldige UTF-8 string kan worden ontbonden in byte-tokens. Overmatige byte-fragmentatie vermindert echter de representatiekwaliteit, omdat de transformer lagen moet besteden aan het recombineren van bytefragmenten tot semantische concepten voordat het redeneren op hoog niveau kan uitvoeren. Het vergroten van de meertalige vocabulairegrootte (bijvoorbeeld uitbreiding van 32k naar 128k+ tokens) brengt de vruchtbaarheidsverschillen in evenwicht en verbetert de downstream taakprestaties in diverse talen, ten koste van een matig grotere input/output embedding-laag.
9Welke tokenisatieartefacten verschijnen er bij numeriek redeneren, codegeneratie of zeldzame Unicode-tekst, en hoe kunnen gespecialiseerde tokenizers deze verminderen?
Tokenisatieartefacten treden op wanneer subwoord-tokenizers gestructureerde, numerieke of zeldzame tekst inconsistent partitioneren, waardoor het model de onderliggende semantische of syntactische regelmaat niet kan herkennen. Belangrijke artefacten zijn: 1. Artefacten bij numeriek redeneren: Standaard BPE (Byte Pair Encoding) tokenizers die zijn getraind op algemene tekst splitsen getallen in willekeurige segmentlengtes op basis van frequentie (bijv. '12345' kan tokeniseren als ['12', '345'] terwijl '12346' tokeniseert als ['123', '46']). Deze inconsistente groepering doorbreekt de plaats-waarde-uitlijning (eenheden, tienen, honderden) en belemmert rekenkundig redeneren. 2. Artefacten bij codegeneratie: Inspringen (leidende spaties/tabs) en operatoren met meerdere tekens (bijv. '==', '!=', '->') worden vaak onregelmatig gesplitst over spatie-teken-grenzen, wat leidt tot inspringfouten, opgeblazen tokentellingen in diep ingesprongen code en syntactische corruptie. 3. Artefacten bij zeldzame Unicode- en emoji-tekens: Meerbyte UTF-8 (Unicode Transformation Format - 8-bit) sequenties (zoals complexe emoji's met zero-width joiners of zeldzame scripts) worden gesplitst in ruwe byte-tokens die geen individuele semantische betekenis dragen, wat leidt tot gehallucineerde tekens of corrupte glyph-rendering bij generatie. Gespecialiseerde tokenizers verminderen deze artefacten met behulp van op maat gemaakte pre-tokenisatieregels en vocabulairebeperkingen: - Cijfersplitsing: Het afdwingen van tokenisatie per enkel cijfer (bijv. regex (regular expression) splitsing van elk cijfer `0-9` in een eigen token) zorgt voor een uniforme plaats-waarde-representatie voor wiskundig redeneren. - Speciale witruimte-/inspring-tokens: Het toevoegen van expliciete tokens voor inspringen met meerdere spaties (bijv. 2, 4, 8 spaties) en het bewaren van programmeertaal-keywords/operatoren. - Regex pre-tokenisatie / byte-niveau fallbacks: Het gebruik van regex-splitters (zoals GPT-4/tiktoken regexes) die leestekens, letters en cijfers in strikte categorieën scheiden voordat BPE-samenvoegingen worden berekend, waardoor categorie-overschrijdende samenvoegingen worden voorkomen (bijv. voorkomen dat 'a=10' wordt samengevoegd tot één token).
import tiktoken
# tiktoken cl100k_base (GPT-4 / ChatGPT) enforces digit and whitespace handling
enc = tiktoken.get_encoding('cl100k_base')
num1 = '12345'
num2 = '12346'
code_indent = ' def foo():'
print('Tokens num1:', [enc.decode([t]) for t in enc.encode(num1)])
print('Tokens num2:', [enc.decode([t]) for t in enc.encode(num2)])
print('Tokens code:', [enc.decode([t]) for t in enc.encode(code_indent)])
10Bespreek de afwegingen bij aandacht (attention) voor lange contexten, inclusief kwadratische volledige aandacht, sliding-window aandacht, sparse of globale aandacht, KV-cache kosten en aandachtverdunning.
Het schalen van aandacht naar lange contextvensters brengt afwegingen met zich mee op het gebied van rekenkracht, geheugengebruik en modelgetrouwheid: 1. Kwadratische Volledige Aandacht versus Sliding-Window / Sparse Aandacht: Standaard volledige aandacht schaalt kwadratisch ($O(N^2)$) in rekenkracht en activatiegeheugen met sequencelengte $N$. Sliding-window (lokale) aandacht beperkt de aandacht tot een vaste buurt $W$, waardoor de complexiteit wordt gereduceerd tot $O(N \cdot W)$, maar vereist meerdere lagen om informatie over verre tokens te propageren. Sparse of globale aandachtspatronen combineren lokale vensters met geselecteerde globale anker-tokens om $O(N)$ schaling te behouden terwijl communicatie over lange afstand mogelijk wordt. 2. KV-Cache Geheugenkosten: Tijdens autoregressieve generatie worden sleutels en waarden voor alle voorgaande tokens gecachet om redundante berekeningen te voorkomen. KV-cache geheugen schaalt lineair met de sequencelengte ($O(B \cdot L \cdot H_{KV} \cdot D \cdot N)$). Voor zeer lange contexten (32k–128k+ tokens) verbruikt de KV-cache tientallen gigabytes aan GPU (Graphics Processing Unit) VRAM (Video Random Access Memory) per batch, wat een knelpunt vormt voor de maximale batchgrootte en geheugenbandbreedte. 3. Aandachtverdunning (Lost-in-the-Middle): Naarmate de context groeit, sommeert de noemer van softmax over tienduizenden tokens, waardoor de waarschijnlijkheidsmassa dun wordt verspreid over irrelevante context. Deze entropieverhoging verdunt de scherpte van de aandacht, wat de capaciteit van het model om specifieke informatie die in het midden van lange prompts is ingebed betrouwbaar te herinneren, vermindert.
11Vergelijk MHA, MQA en GQA en leg uit hoe ze de KV-cache geheugen en decodeerdoorvoer beïnvloeden.
Multi-Head Attention (MHA), Multi-Query Attention (MQA) en Grouped-Query Attention (GQA) verschillen in de manier waarop Key ($K$) en Value ($V$) heads worden gedeeld over Query ($Q$) heads:
1. **Multi-Head Attention (MHA)**: Heeft een gelijk aantal $Q$-, $K$- en $V$-heads ($H_Q = H_{KV}$, een verhouding van 1:1). Elke query-head richt zich op zijn eigen onafhankelijke key/value-representaties. Hoewel het expressief is, vereist het het cachen van afzonderlijke KV-matrices voor elke head.
2. **Multi-Query Attention (MQA)**: Gebruikt meerdere $Q$-heads ($H$), maar slechts 1 gedeelde $K$-head en 1 gedeelde $V$-head (een verhouding van $H:1$). Dit vermindert de grootte van de KV-cache met een factor $H$, maar kan leiden tot een licht kwaliteitsverlies of trainingsinstabiliteit.
3. **Grouped-Query Attention (GQA)**: Groepeert $Q$-heads in $G$ partities, waarbij elke groep één enkele $K$- en $V$-head deelt (bijv. 8 $Q$-heads per KV-head). GQA biedt een optimale afweging, waarbij vrijwel de gehele modelleringskwaliteit van MHA wordt hersteld, terwijl de geheugenvoordelen van MQA behouden blijven.
**Impact op KV-cache & Decodeerdoorvoer:**
Autoregressieve token-generatie (decoderen) is beperkt door geheugenbandbreedte, omdat de GPU de gehele KV-cache van High-Bandwidth Memory (HBM) naar on-chip SRAM moet overdragen voor elke afzonderlijk gegenereerde token. Door het aantal KV-heads te verminderen met $H/G$ (bijv. $4\times$ tot $8\times$ in GQA, of $32\times+$ in MQA):
* De geheugenvoetafdruk van de KV-cache wordt proportioneel verminderd, waardoor veel grotere serving batch-groottes mogelijk zijn in GPU VRAM.
* Het HBM-geheugenleesverkeer per token daalt aanzienlijk, waardoor de decodeerdoorvoer van tokens drastisch toeneemt.
# Model with 32 Query Heads
num_q_heads = 32
mha_kv_heads = 32 # 1:1 ratio
gqa_kv_heads = 8 # 4:1 ratio (4 query heads per KV head)
mqa_kv_heads = 1 # 32:1 ratio (1 shared KV head)
print(f"KV Cache Size Relative to MHA:")
print(f"MHA: {mha_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"GQA: {gqa_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"MQA: {mqa_kv_heads / mha_kv_heads * 100:.1f}%")
12Leg uit hoe FlashAttention de exacte aandachtsberekening versnelt zonder de aandachtsoutputs te veranderen.
FlashAttention versnelt de aandachtsberekening door het algoritme I/O-bewust te maken — het minimaliseren van het lees- en schrijfgeheugenverkeer tussen het langzame GPU High Bandwidth Memory (HBM) en het snelle on-chip SRAM, in plaats van te proberen het totale rekenkundige FLOP-aantal te verminderen. Standaard aandachtsberekening materialiseert intermediaire N x N aandachtsscore- en waarschijnlijkheidsmatrices in HBM, wat een belangrijke geheugenbandbreedte-bottleneck veroorzaakt. FlashAttention overkomt dit door drie belangrijke mechanismen: 1. Tegelverdeling: Het verdeelt de Query-, Key- en Value-matrices in blokken die volledig passen binnen GPU on-chip SRAM. 2. Online Softmax: Het berekent softmax incrementeel over blokken door lopende maxima en normalisatorsommen bij te houden, en gedeeltelijke outputs bij te werken zonder de volledige gematerialiseerde N x N matrix in het geheugen nodig te hebben. 3. Exacte Herberekening: Tijdens de backward pass leest het geen opgeslagen intermediaire aandachtsmatrices uit HBM; in plaats daarvan herberekent het deze on-the-fly in SRAM vanuit de opgeslagen lopende statistieken. Omdat er geen benaderingen, low-rank factorisaties of token-dropping heuristieken worden gebruikt, is de output mathematisch exact tot op numerieke precisie van drijvende komma, terwijl de HBM-geheugenvoetafdruk wordt verminderd van O(N^2) naar O(N).
13Ontwerp deterministische agentic workflows met behulp van planners, toestandsmachines, DAG's, getypeerde tussentoestand, begrensde herkansingen en verificatie van toolresultaten, in plaats van open-eind agentlussen.
Open-eind agentlussen (bijv. onbeperkte autonome ReAct-lussen) in productie lijden vaak aan niet-deterministische vertakkingen, oneindige lussen, buitensporig tokenverbruik en state drift. Een deterministische agentic workflow vervangt vrije-vorm lussen door gestructureerde, observeerbare controlestroom: 1. Toestandsmachines en DAG's: De controlestroom wordt gedefinieerd als een expliciete Directed Acyclic Graph (DAG) of eindige toestandsmachine (bijv. LangGraph, Temporal, AWS Step Functions). Node-overgangen zijn afhankelijk van expliciete condities en getypeerde uitkomsten in plaats van open-eind modelbeslissingen. 2. Getypeerde Tussentoestand: State die gedeeld wordt over nodes wordt gemodelleerd met strikte schema's (bijv. Pydantic-modellen of dataclasses). Nodes voeren gevalideerde lees- en schrijfbewerkingen uit, wat schema drift of onjuist gevormde state voorkomt. 3. Planners: Gestructureerde planners genereren vooraf een beperkt plan (bijv. een geordende lijst van enum-gesteunde stappen) of kiezen uit een beperkte set van geldige state-overgangen in plaats van vrijelijk volgende acties te bepalen zonder beperkingen. 4. Verificatie van Toolresultaten: Uitvoer die door tools wordt geretourneerd, wordt deterministisch gevalideerd tegen schema's en bedrijfsregels voordat de state wordt bijgewerkt of doorgegeven aan downstream Large Language Model (LLM)-stappen. 5. Begrensde Herkansingen & Fallbacks: Elke stap handhaaft expliciete herkansingsbudgetten, exponentiële backoffs, timeouts en fallback-overgangen (bijv. escaleren naar menselijke beoordeling of het activeren van veilige onthouding) om beëindiging te garanderen.
from pydantic import BaseModel
from typing import Optional, Literal
class WorkflowState(BaseModel):
user_query: str
extracted_id: Optional[str] = None
verification_status: Literal["PENDING", "VERIFIED", "FAILED"] = "PENDING"
retry_count: int = 0
max_retries: int = 3
def execute_validation_node(state: WorkflowState) -> WorkflowState:
if state.retry_count >= state.max_retries:
state.verification_status = "FAILED"
return state
try:
result = call_verification_service(state.extracted_id)
state.verification_status = "VERIFIED" if result.is_valid else "FAILED"
except Exception:
state.retry_count += 1
return state
14Ontwerp een betrouwbaarheids- en onthoudingsbeleid voor een LLM (Large Language Model)-assistent die vragen beantwoordt binnen gereguleerde domeinen.
In gereguleerde domeinen (zoals gezondheidszorg, bankwezen, juridische sector en compliance) brengen onjuiste antwoorden regelgevende sancties, juridische aansprakelijkheid en veiligheidsrisico's met zich mee. Een robuust betrouwbaarheids- en onthoudingsbeleid combineert meersignaal gekalibreerde betrouwbaarheidsscores, gelaagde responsdrempels en deterministische escalatieworkflows:
1. **Meersignaal Betrouwbaarheidskalibratie:** Ruwe LLM-logprobs zijn vaak onjuist gekalibreerd bij `out-of-domain` query's. De betrouwbaarheidsscore moet meerdere onafhankelijke signalen synthetiseren:
* **Retrieval Grounding Score:** Semantische gelijkenis en `re-ranking`-betrouwbaarheid van opgehaalde bewijsfragmenten.
* **Claim-Level Entailment (NLI (Natural Language Inference)):** `Natural Language Inference`-modellen die verifiëren dat elke geëxtraheerde bewering wordt geïmpliceerd door de opgehaalde broncontext.
* **Semantische Entropie / Zelfconsistentie:** Meten van semantische consistentie over meerdere gesamplede generaties.
* **Model Token Logprobs:** Minimale en gemiddelde `logprobs` op belangrijke benoemde entiteiten en feitelijke tokens.
2. **Gelaagd Onthoudingsbeleid:**
* **Hoge Betrouwbaarheid (Score >= Hoge Drempel):** Direct het gegenereerde antwoord presenteren met inline citaten.
* **Gemiddelde Betrouwbaarheid / Ambigu (Lage Drempel <= Score < Hoge Drempel):** Een conservatief antwoord geven met expliciete voorbehouden, disclaimers, of de gebruiker om verhelderende details vragen.
* **Lage Betrouwbaarheid / Buiten Bereik (Score < Lage Drempel):** Harde onthouding met een gestandaardiseerd weigeringsbericht.
3. **Escalatie en Controleerbaarheid van Naleving:**
* **Deterministische Escalatie:** Onthoudingen of kritieke afwijkingen worden automatisch doorgestuurd naar `human-in-the-loop` (HITL)-wachtrijen of `agent ticketing systems` met volledige context.
* **Audit Trail & Herkomst:** Volledige telemetrie — inclusief `prompt hashes`, opgehaalde document-ID's, individuele betrouwbaarheidscomponentenscores en uiteindelijke routeringsbeslissingen — moet worden gelogd voor controleerbaarheid door regelgevende instanties.
15Ontwerp een modelrouteringsstrategie die kiest tussen kleine, middelgrote en grote modellen op basis van verzoekcomplexiteit, kosten, risico en kwaliteitseisen.
Een productie modelrouteringsarchitectuur stuurt inkomende verzoeken naar kleine (bijv. 1B–8B SLM's (Small Language Models)), middelgrote (bijv. 14B–70B modellen) en grote (bijv. grensverleggende of grote MoE (Mixture of Experts) modellen) lagen door een balans te vinden tussen complexiteit, latentie, risico en rekentijd kosten. De routeringsworkflow combineert over het algemeen statische regels, voorspellende routering en dynamische terugvalcascades:
1. **Deterministische/Statische Beleidspoorten:** Filter verzoeken op klantniveau, harde latentie `SLA's` (Service Level Agreements), regelgevings-/domeinrisico (bijv. medische diagnose of juridische opstelling direct naar topmodel-lagen geleid), of eenvoudige taken op basis van regels (bijv. basis `regex`/opmaak naar kleine modellen).
2. **Voorspellende Complexiteitsroutering:** Een snelle, lichtgewicht classifier (zoals een `embedding similarity lookup`, een `cross-encoder`, of een kleine SLM-router) scoort de complexiteit van verzoeken, redeneerdiepte en domeinambiguïteit om de meest kosteneffectieve laag vooraf te selecteren.
3. **Dynamische Uitvoering & Escalatiecascades:** Stuur de `prompt` eerst naar een kleiner model en evalueer de uitvoerbetrouwbaarheid (via `token logprobs`/entropie, geldigheid van gestructureerd schema, of `guardrail`-controles). Als de betrouwbaarheid onder de drempel ligt of validatie mislukt, escaleert de router naar een middelgroot of groot model. Belangrijke systeemafwegingen omvatten de latentie-overhead van de router versus rekenbesparingen, terugval timeout-budgetten bij verkeerspieken, en continue evaluatie (bijv. `shadow evaluation` om de drift in uitvoerkwaliteit over lagen heen te volgen).
class DynamicModelRouter:
def __init__(self, small_client, medium_client, large_client, classifier, guardrail):
self.small = small_client
self.medium = medium_client
self.large = large_client
self.classifier = classifier
self.guardrail = guardrail
async def route_and_execute(self, request):
# 1. Deterministic Risk Gate
if request.risk_level == "high" or request.domain in ["legal", "medical_compliance"]:
return await self.large.generate(request.prompt)
# 2. Predictive Complexity Classifier
complexity = self.classifier.predict_complexity(request.prompt) # 0.0 to 1.0
if complexity < 0.35:
response = await self.small.generate(request.prompt)
if self.guardrail.is_acceptable(response):
return response
return await self.medium.generate(request.prompt) # Fallback
if complexity < 0.75:
response = await self.medium.generate(request.prompt)
if self.guardrail.is_acceptable(response):
return response
return await self.large.generate(request.prompt) # Fallback
# 3. High complexity frontier execution
return await self.large.generate(request.prompt)