Agentic RL trainen met GPT OSS: wat er misgaat, hoe je het fixt, en waarom dat ertoe doet

Home
/
bloggen

Als je met AI agents werkt, herken je dit waarschijnlijk: de demo ziet er goed uit, maar zodra je het model echt laat handelen in stappen, met tools, lange context en echte feedback, begint het gedoe. Beloningen lopen niet op, de training klapt eruit, of je GPU’s lopen vol. In deze terugblik neem ik je mee langs een praktische engineering reis om agentic reinforcement learning mogelijk te maken op GPT OSS. Niet als theoretische oefening, maar als checklist vol valkuilen die je duur komen te staan als je ze te laat ziet.

Waarom agentic RL anders voelt dan gewone LLM training

Bij klassiek LLM finetunen leer je het model vooral om één antwoord te geven op één prompt. Dat werkt prima voor veel content en klantenservice scenario’s, zolang het gesprek niet te veel zijpaden heeft.

Agentic reinforcement learning doet iets anders. Je traint niet één antwoord, maar een beslisproces. Het model plant stappen, roept tools aan, kijkt naar de uitkomst, past zijn aanpak aan en gaat door. De beloning komt vaak pas later, wanneer een taak echt af is. Daardoor moet je credit geven over een hele keten van keuzes. Denk aan een agent die eerst de vraag herschrijft, dan een databron opzoekt, vervolgens een tool kiest, daarna een fout herstelt, en pas aan het einde het juiste resultaat neerzet.

In de praktijk is het een gesloten lus. De agent draait rollouts, je berekent rewards over die trajecten, je werkt de policy bij met iets als PPO of GRPO, en daarna laat je die bijgewerkte policy weer nieuwe rollouts verzamelen. Dat klinkt logisch, maar het maakt je systeem ook gevoelig voor kleine mismatches. In agentic RL worden kleine fouten groot, omdat ze zich over stappen opstapelen.

Waarom dit relevant is voor Nederlandse ondernemers en marketeers

Je hoeft geen researchlab te zijn om hiermee te maken te krijgen. Als je een agent bouwt voor leadkwalificatie, recruitment, productadvies of interne kennis, dan wil je dat hij niet alleen mooi praat, maar ook goed handelt. Dus niet alleen “een tekst genereren”, maar bijvoorbeeld informatie ophalen, filters aanscherpen, de juiste vervolgvraag stellen, en pas dan een beslissing nemen.

Mijn zorg, als ik het wat ouderlijk mag zeggen, is dat teams te snel aannemen dat een model dat goed scoort op benchmarks ook vanzelf goed te trainen is voor dit soort werk. Dat is zelden zo. De trainingstack, de inference engine en zelfs de chat template bepalen of je experimenten betrouwbaar zijn. Als je daar te laat achter komt, ben je weken verder en heb je vooral geleerd hoe je GPU’s heel duur kunt laten roken.

Waarom GPT OSS interessant is, en waarom dat nog niets bewijst

GPT OSS laat prestaties zien die in de buurt komen van modellen als o3 mini en o4 mini. Dat maakt het aantrekkelijk als backbone voor agenten, zeker als je open source wilt blijven.

Maar er zit een verschil tussen “goed kunnen antwoorden” en “goed te trainen zijn in een agentic RL setting”. Veel recente voorbeelden rond GPT OSS gaan over finetuning zonder tool calling. Het moment dat je een omgeving toevoegt, rollouts gaat verzamelen, en lange trajecten gaat optimaliseren, komen andere problemen naar boven.

In de experimenten waar ik je nu doorheen loop, is verl gebruikt als trainingsframework, omdat het breed wordt ingezet in de open source gemeenschap. De taken waren GSM8K voor wiskunde, ReTool voor tool use met een code compiler, en een verifieerbare instruction following taak. De focus lag op GPT OSS 20B, met een fix die ook werkt voor 120B. Qwen 2.5 32B is gebruikt als referentie om te zien hoe “normale” RL curves eruit horen te zien.

Eerste hobbel: Harmony template en het saaie, maar noodzakelijke werk

GPT OSS gebruikt de Harmony chat template. De eerste stap was daarom niet meteen “trainen”, maar zorgen dat het framework de nieuwe message format en de conversatieregels goed snapt. Als je tool calls, trajectory bouw en parsing hier niet kloppen, kun je de rest van je metrics eigenlijk weggooien.

ReTool is hier handig als testbed, omdat het gedrag zichtbaar is. Het model krijgt een rekenprobleem en mag een code tool gebruiken. Het model hoeft dus niet elk sommetje in zijn hoofd te doen, maar moet wel slim genoeg zijn om de tool goed aan te sturen, uitkomsten te lezen en correct te herstellen. Aan het einde komt een final answer en daar hangt de reward aan.

In de eerste runs ging het mis. KL divergences en entropie liepen op, rewards bleven achter en de gradient norm begon te exploderen. Dat zijn signalen die je serieus moet nemen. Niet omdat “de training lastig is”, maar omdat er bijna altijd een concrete mismatch onder zit.

PPO hoort on policy te zijn, maar dat was het niet

Voor stabiliteit is on policy PPO vaak een veilige keuze. De kern is simpel: als je data verzamelt met de huidige policy, dan moet de importance sampling ratio precies 1 zijn. Wiskundig is dat:

ratio = π(a|s) / π_old(a|s)

Bij pure on policy training geldt π(a|s) = π_old(a|s), dus ratio = 1. Als die ratio afwijkt, ga je clippen alsof je off policy data hebt, en dan update je op een manier die niet past bij wat je net hebt uitgerold.

In ReTool training bleek de clip waarde niet nul, terwijl het on policy zou moeten zijn. De oorzaak zat in een mismatch tussen twee log probabilities. Je had een log_prob voor de “huidige” policy en een oldlogprob die je dacht terug te halen voor dezelfde state action. In een normale dense model setup is dat vaak al gevoelig, maar bij Mixture of Experts wordt het extra tricky.

De echte oorzaak: twee forward passes plus MoE routing

In oudere versies van verl werd voor hetzelfde state action paar twee keer een forward pass gedaan. Eén keer om log_prob te berekenen, en één keer om oldlogprob op te halen.

Bij MoE kiest een gating netwerk welke experts worden gebruikt. Door kleine floating point verschillen of expliciete stochastiek kan die routing net anders uitvallen tussen die twee passes. Dan krijg je precies het probleem dat je niet wilt:

log(π(a|s)) ≠ log(π_old(a|s))

En dus ratio ≠ 1, terwijl je dacht dat je on policy trainde. Het gevolg is dat PPO gaat clippen en je training zich gedraagt alsof je stiekem off policy aan het werk bent. Je ziet dat terug als instabiliteit en rare metric trends.

De fix: oude log prob vervangen wanneer je zeker on policy bent

De oplossing was eigenlijk nuchter. Als je weet dat je on policy traint, bijvoorbeeld omdat je minibatch gelijk is aan de global batch, dan dwing je die ratio terug naar 1 door oldlogprob gelijk te zetten aan de nieuwe log_prob, maar dan zonder gradient.

In codevorm komt het neer op:

Als on_policy waar is, zet je oldlogprob = log_prob.detach(). Anders gebruik je de opgeslagen oldlogprob uit je inputs.

Door detach te gebruiken voorkom je dat gradients door de referentiewaarde lopen. Zo maak je PPO weer echt on policy, en haal je een bron van valse clipping weg. Dit omzeilt de niet deterministische routing die je bij MoE anders blijft achtervolgen.

Na deze ingreep zakte de importance sampling clip ratio naar nul. Mooi, maar we waren er nog niet. De gradient norm explodeerde nog steeds en rewards wilden nauwelijks stijgen.

Als inference en training niet hetzelfde rekenen, train je jezelf gek

Om het probleem te isoleren is de setup teruggebracht naar GSM8K. Dat is een single step taak zonder tool use, dus veel minder bewegende delen. Toch bleef de instabiliteit bestaan. Dat wijst op iets fundamentelers dan “agentic complexiteit”.

Een plausibele verklaring is training en inference mismatch. Inference engines zoals vLLM en SGLang zijn gebouwd voor snelheid. Training onder FSDP is gebouwd voor stabiliteit en precisie. Als die twee paden andere kernels gebruiken of net anders rekenen, dan kan je rollout policy feitelijk anders zijn dan je training policy. Dan doe je in de praktijk off policy updates, zelfs als je denkt dat je on policy zit.

Rollout correction, dus sequence level importance sampling, hielp om de training te stabiliseren. De gradient norm bleef netjes. Maar de reward ging nog steeds langzaam omhoog. Dat is precies het soort signaal waar ik altijd even mijn hand op de schouder leg en zeg: stop met doorduwen, zoek waar de mismatch zit.

Waarom de aandacht lagen verdacht werden

Er kwam nog een aanwijzing bij. Toen de attention layers tijdens training werden bevroren, ging de reward bijna net zo hard omhoog als zonder bevriezen. Dat suggereert dat het leren vooral in de MoE lagen zat, en dat attention minder bijdroeg dan je verwacht.

Daarnaast was er een duidelijke mismatch in token level probabilities tussen de inference engine, die een Triton kernel gebruikte met support voor attention sinks, en de trainingstack onder FSDP, die op FlashAttention v2 draaide. Die mismatch zie je terug als een grote log perplexity afwijking.

Samen wijst dat naar één plek: de attention implementatie, en specifiek iets wat GPT OSS gebruikt en wat niet overal ondersteund wordt, namelijk attention sinks.

Attention sinks uitgelegd zonder mystiek

Attention sinks in GPT OSS zijn leerbare scalars, één per attention head. Je kunt ze zien als een virtuele plek waar attention naartoe mag gaan, zodat niet alle massa verplicht op content tokens terechtkomt. Dit helpt met stabiliteit, onder andere bij streaming en bij sliding window setups.

In standaard attention heb je scores = QKᵀ / √d, daarna probs = softmax(scores), en output = probs @ V.

Met sinks gebeurt er iets subtiels. Je berekent dezelfde scores, maar je plakt er per head ook een sink score aan vast in de softmax. Dus de softmax normaliseert over content tokens plus die sink. De content probabilities gebruik je wel voor de output, maar de sink component laat je vallen. De sink beïnvloedt dus de verdeling, maar draagt niet direct bij aan output.

Dat is precies waarom je kernel support nodig hebt. Als één stack dit wel doet en de andere niet, train je op een andere functie dan je tijdens rollout gebruikt.

Wat er misging in FlashAttention, en waarom dat je training breekt

Er waren meerdere problemen tegelijk.

Eén: verl had in fsdp_worker FlashAttention v2 hard gecodeerd, en die ondersteunt attention sinks niet.

Twee: zelfs wanneer FlashAttention v3 aan stond, ontbrak ondersteuning voor de backward pass van de sink parameters. Dat betekent dat je sinks in de praktijk niet goed leert, of dat de gradient niet klopt.

Drie: de forward pass met sinks was nog niet gemerged in de originele FlashAttention v3 repository. Daarom is de forward pass geleend uit de vLLM FlashAttention fork, uit pull request 75, en is daarna een backward pass toegevoegd om sink gradients te kunnen berekenen.

Als je hier doorheen leest en denkt “dit is wel heel laag niveau”, dan heb je gelijk. Maar dit is precies het punt: agentic RL is meedogenloos. Je kunt de mooiste reward shaping hebben, maar als je kernels niet hetzelfde rekenen, dan train je op drijfzand.

De wiskunde achter de sink, en wat er in de backward gebeurt

Voor een content token j in rij i wordt het attention gewicht:

Pᵢⱼ = exp(Sᵢⱼ) / (Σⱼ’ exp(Sᵢⱼ’) + exp(S_h))

waar Sᵢⱼ = Qᵢ Kⱼᵀ / √d en S_h de leerbare sink parameter is voor head h.

De sink probability zelf is:

Pᵢ,h = exp(S_h) / (Σⱼ’ exp(Sᵢⱼ’) + exp(S_h))

Belangrijk detail: die sink probability wordt wel uitgerekend, maar niet gebruikt in de output. Daardoor geldt voor de gradient van de loss L naar de sink score in de output richting dat ∂L/∂Sᵢ,h = 0.

De volledige backward vorm werd in het onderzoek uitgeschreven, en door die nulterm krijg je een vereenvoudigde gradient voor de sink parameter:

∂L/∂S_h = − Σᵢ Pᵢ,h ( Σⱼ Pᵢⱼ ∂L/∂Sᵢⱼ )

Die berekening is toegevoegd in de FlashAttention v3 implementatie, zodat de sink parameters ook echt mee leren.

Wat er daarna gebeurde: stabiele training en sneller leren

Na de sink fix in FlashAttention v3 veranderde het beeld direct. GPT OSS 20B leerde zichtbaar sneller op verschillende RL taken.

Op GSM8K ging de reward curve duidelijk steiler omhoog dan zonder de fix. Bij de verifieerbare instruction following taak zag je dat runs zonder fix konden instorten, terwijl de gefixte variant gestaag bleef verbeteren. En op ReTool, dus het multi turn tool use scenario, verdween de gradient exploding en ging de validatie score omhoog.

Wat ik hier belangrijk aan vind is niet alleen “het werkt”, maar vooral waarom. Je haalt een training en inference mismatch weg die je hele loop onbetrouwbaar maakt. Pas dan zie je of je reward ontwerp, je prompts, je rollout settings en je modelkeuze echt goed zijn.

Geheugenproblemen: waarom FSDP ineens alles vol trekt bij MoE

Stabiliteit is één ding, maar je wilt dit ook kunnen draaien zonder steeds out of memory te gaan. Tijdens training van GPT OSS 20B in bf16 op 16 H200 nodes, met een response length van 16k en een prompt van 8k, traden herhaaldelijk OOM fouten op. Voor een 20B MoE model voelt dat tegenstrijdig.

De oorzaak zat in de manier waarop verl log probabilities onder FSDP berekende. Daar werd de inference forward path geactiveerd, onder andere doordat de module in eval werd gezet. In de Hugging Face implementatie betekent die inference path dat hidden states voor alle experts worden gedupliceerd en in één grote batched matrix multiplication worden verwerkt. Dat materialiseert gigantische tensors in GPU geheugen.

De training forward path doet hetzelfde werk, maar met een for loop per expert en daarna combineren. Dat is trager, maar gebruikt minder geheugen.

Door de implementatie te patchen zodat de memory vriendelijkere route wordt gebruikt, verdween een groot deel van de OOM ellende. Niet glamorous, wel het verschil tussen een experiment dat draait en een experiment dat stilstaat.

Sequence parallelisme: lange context zonder je cluster te slopen

Agentic RL vraagt om lange context. Elke stap voegt observaties en tool outputs toe, en dat groeit door. Onder FSDP shard je parameters, gradients en optimizer state over alle GPU’s, maar je rollout data blijft vaak gerepliceerd. Elke GPU ziet dus de hele geschiedenis en dat vreet activatie geheugen.

Sequence parallelisme, ook wel context parallelisme genoemd, pakt dat anders aan. In plaats van alles op één GPU te laten landen, verdeel je de sequence over devices. Zo daalt de piek in activatie geheugen per GPU.

Belangrijk detail: bij attention moet je tokens van dezelfde sequence bij elkaar hebben om attention goed te kunnen berekenen. Daarom wordt rond de attention laag een all to all communicatie gedaan. Voor en na die laag worden tokens herverdeeld, vaak met splits op head niveau. Buiten attention lagen is die extra synchronisatie niet nodig, omdat andere lagen geen inter position dependencies hebben.

In deze aanpak worden tokens uit meerdere sequences ook slim gepackt door padding weg te halen, en position ids houden bij wat bij welke sequence hoort. Dat werkt goed samen met FlashAttention’s variable length ondersteuning. De implementatie is sink aware gemaakt, zodat attention sinks ook in deze setting consistent werken.

Wat ik hier als ondernemer uit meeneem

Als je agentic RL serieus neemt, koop je niet alleen een model. Je koopt een hele keten van aannames. Chat templates, tool parsing, kernels, training en inference paden, distributed settings, alles moet op elkaar passen.

De drie lessen die ik je wil meegeven zijn eenvoudig, maar je moet ze wel echt naleven.

Eerst: vertrouw niet blind op “on policy” labels. Kijk naar je importance sampling ratio en clip stats. Als die niet kloppen, ben je niet bezig met finetunen maar met gokken.

Dan: zorg dat training en inference dezelfde berekening doen, zeker rond attention. Een klein verschil in kernels kan je beloningssignaal al vervormen.

Tot slot: plan geheugen en schaalbaarheid vanaf dag één. Lange context en MoE zijn prachtig, maar alleen als je compute en memory gedrag voorspelbaar zijn.

Voor wie zelf agents wil bouwen in marketing en e commerce betekent dit: begin klein, meet obsessief, en beschouw instabiliteit als een bug tot het tegendeel bewezen is. Dat bespaart je geld, tijd en vooral frustratie.

Dankwoord en bronnen

In het oorspronkelijke werk werd expliciet dank uitgesproken aan Deepak Agarwal, Bee Chung Chen, Animesh Singh, Gungor Polatkan, Balaji Krishnapuram en Jitendra Agarwal voor ondersteuning.

Wie verder wil lezen, kan deze referenties opzoeken. Feng et al over ReTool, Xiao et al over attention sinks, en een verdieping over RL collapse door training en inference mismatch. Dat laatste onderwerp is minder sexy, maar als je hiermee werkt is het vaak de echte oorzaak van gedoe.

Neem contact op

Eerlijkheid staat voorop in mijn werk. Daarom zeg ik direct: ik ben niet de juiste partner voor jou als. Ik help je om jouw merk te transformeren van een fluistering naar een brul die niemand kan negeren.

Ik ben niet gebouwd om mee te doen, ik ben ontworpen om te domineren.

Contact Us