Chat agents
Chat agents voor tandtechnische labs: 1.180 orders/week
Het is 14:47 in een tandtechnisch lab buiten Eindhoven. Net binnen in Outlook: een zirconia-order van een tandarts uit Tilburg. Dertien minuten tot de Roland DWX-52DCi de batch locked.

Het is 14:47 in een tandtechnisch lab vlak naast de A2, ten noorden van Eindhoven. De CAD-technicus van dienst werkt een zirconia-brug af in Exocad. Over dertien minuten locked de Roland DWX-52DCi vijfassige freesmachine de middagbatch. Wat om 15:00 in de queue staat, wordt gefreesd. Wat er niet staat, wacht vier uur.
Een order valt binnen in de gedeelde inbox. Tandarts uit Tilburg. Implantaatkroon op positie 36. Emergence-profile uit de hand geschetst op een gescand formulier. Het traceability-systeem op SQL Server 2016 wil een lotnummer gekoppeld aan het implantaatmerk van de patiënt, en dat merk staat niet in de mail. De klok staat nu op 14:48.
Dit is het moment waarop de chat agent zijn geld waard wordt.
Het lab, de cijfers en de 15:00-lock
Het lab is een fabrikant van 19 mensen die kronen, bruggen en custom abutments maakt voor ongeveer 340 actieve tandartsen in de Benelux. In een normale week gaan er 1.180 orders door de queue — drukker op maandag, rustiger op vrijdag, met af en toe een dinsdagpiek die nog nooit iemand goed heeft kunnen verklaren.
De productie loopt in twee freescycli per dag. De eerste batch start om 09:30, de tweede om 15:00. Zodra een batch is ingepland, ligt de joblist van de Roland vast en wordt de puck-carousel met de hand geladen. Een gemiste 15:00-batch is geen ramp, maar de gevolgen lopen op: het implantaat gaat een dag later de deur uit, de OK-slot van de tandarts komt in gevaar, en iemand moet een kliniek in Limburg bellen om sorry te zeggen.
Vóór de chat agent was het uur tussen 14:00 en 15:00 een stille paniek. Eén persoon triagete mail, één persoon zat aan de telefoon, één CAD-technicus probeerde de uitschieters te vangen voordat ze in de queue belandden. Alles wat onduidelijk was — en dat is veel — ging in een Word-document genaamd te_bespreken.docx en werd de volgende ochtend pas weer opgepakt.
De 11 jaar oude Exocad-library die niemand met pensioen wil sturen
Toen het lab in 2014 werd opgezet, hebben ze een Exocad-library gebouwd voor hun drie meest gebruikte implantaatsystemen. Ze hebben hem gekalibreerd op testcases. Ze hebben er interne SOP's omheen geschreven. De validatiepapieren zijn naar de aangemelde instantie gegaan. Die library is nu 11 jaar oud.
Hij is gepatcht, maar nooit vervangen. Elke vervangingspoging loopt tegen dezelfde muur op: de gevalideerde cases zijn tegen déze library gevalideerd. Hem eruit gooien betekent honderden case-templates opnieuw valideren, en dat betekent een kwart van de CAD-technicus-tijd kwijt aan revalidatie in plaats van frezen.
Dus de library blijft. De chat agent moet er gewoon mee leven. Concreet: de agent moet de case-template-namen van de library kunnen lezen, en die volgen een naamgevingsconventie die al twee hoofden CAD ouder is dan de huidige hoofd CAD:
SY-{system}-{position}-{abutment-rev}-{em-class}.dmeDe em-class is de emergence-profile-klasse — A, B, C of X. X betekent "niet-standaard, vraagt om een technicus-oog". De eerste taak van de agent: de binnenkomende order lezen, de em-class afleiden en bepalen of de order door kan naar de freesmachine of in de queue van de CAD-technicus moet landen.
Wat de chat agent eigenlijk doet
De chat agent is geen klantenservice-bot. Tandartsen chatten er niet mee. De agent leest orders uit drie kanalen — mail, het webportaal van het lab en een oude fax-naar-PDF-pipeline die nog altijd twaalf orders per week binnen krijgt — en bepaalt wat ermee moet gebeuren.
De beslisboom is saai, en dat is precies het punt:
def route(order):
if not order.implant_brand or order.implant_brand == "unknown":
return queue("disambiguate_brand", sla_minutes=10)
em_class = classify_emergence(order.scan, order.notes)
if em_class == "X":
return queue("cad_technicus", sla_minutes=45)
library_match = exocad_lookup(
system=order.implant_brand,
position=order.tooth_position,
em_class=em_class,
)
if not library_match:
return queue("cad_technicus", sla_minutes=45)
lot = mssql.fetch_lot(order.implant_brand, order.batch_hint)
if not lot:
return queue("traceability", sla_minutes=15)
return schedule_for_mill(library_match, lot, before="15:00")
Ongeveer 80% van de orders valt in één keer door naar schedule_for_mill. Zo'n 12% parkeert een paar minuten in disambiguate_brand of traceability, meestal lang genoeg voor een mens om naar een scan te kijken en één antwoord te typen. Zo'n 8% komt in de cad_technicus-queue terecht, en dat is precies waar die orders horen — bij dit type moet een mens naar de geometrie kijken voordat de freesmachine begint te snijden.
Die cijfers tellen omdat ze de menskant vormgeven. De CAD-technicus-shift plant nu rond een instroom van 8%, niet meer rond de constante laagwaardige onderbrekingen van "is deze oké?".
Lotnummers uit SQL Server 2016 trekken
De implantaat-traceability-database is een zelfgebouwde applicatie op SQL Server 2016, die inmiddels al een paar jaar uit de mainstream support is. Hij is ouder dan de huidige laptops van het bedrijf. Een REST API heeft hij niet, en die komt er ook nooit, want de applicatie als service ontsluiten betekent dat je hem opnieuw moet valideren tegen het kwaliteitssysteem van het lab.
De chat agent leest er rechtstreeks uit, read-only, via een replica die ongeveer 30 seconden achterloopt op de live database. De query is — bewust — saai:
SELECT TOP 1
lot_number,
expiry_date,
sterilisation_cycle
FROM implant_lots
WHERE brand_code = @brand_code
AND status = 'AVAILABLE'
AND expiry_date > DATEADD(day, 30, GETDATE())
ORDER BY received_date ASC;
FIFO op ontvangstdatum, met een buffer van 30 dagen op de expiry. Die buffer was 7 dagen. Hij werd 30 nadat een batch abutments was gefreesd op een lot dat drie weken later, tijdens transport naar een kliniek in Luxemburg, verliep — een spoedvervanging op kosten van het lab.
De agent schrijft niet naar de database. De lot-allocatie — de daadwerkelijke reservering — loopt via dezelfde interne applicatie die het lab sinds 2017 gebruikt, getriggerd door een klein servicelaagje dat de agent aanroept. We wilden de agent geen voorraadmutaties laten committen. Als de agent fout zit, willen we dat die fout in de queue zichtbaar wordt, niet in het lot-grootboek.
Zit je traceability-systeem in een gereguleerd kwaliteitssysteem, laat de agent er dan niet naar schrijven. Read-only toegang plus een dun commit-laagje houdt je validatiedossier intact en je auditor rustig.
Het emergence-profile-randgeval
Een emergence profile is de vorm van de kroon ter hoogte van de tandvleesrand. Krijg je dat goed, dan geneest het zachte weefsel netjes rond de restauratie. Krijg je dat mis, dan zit de patiënt binnen een jaar weer in de stoel met ontstoken tandvlees, en de CAD-technicus een klachtenformulier in te vullen.
In de meeste cases zit het profiel impliciet in de library — kies het juiste systeem, de juiste tandpositie en de juiste abutment-revisie, en de geometrie komt binnen tolerantie naar buiten. In ongeveer één op de twaalf cases heeft de tandarts óf een custom profiel op de scan getekend, óf een notitie geschreven als "iets meer concaaf onder mesiaal". Die orders moeten voor een mens komen.
De agent classificeert emergence in twee stappen. Eerst kijkt hij naar de gestructureerde velden die het portaal verzamelt — tandpositie, soft-tissue-diepte, gingivaal biotype als dat is ingevuld. Vallen die binnen standaardparameters, dan wijst hij voorlopig klasse A, B of C toe. Vervolgens scant hij de vrije notities en eventuele tekeningen op trefwoorden en vormen die op een custom profiel wijzen. Elk signaal van customisatie duwt de klasse naar X.
X is eenrichtingsverkeer. We classificeren nooit terug uit X. Twijfelt het model, dan gaat de order naar de mens, punt. Over een venster van zes maanden is de X-rate gestabiliseerd rond 8,4%. Het CAD-technicus-team vindt een licht ruisende queue prettiger dan een stille queue die af en toe om 16:30 een probleem in de schoot werpt.
14:47, één minuut later
Terug naar de order uit Tilburg. Om 14:48 heeft de agent:
- Het implantaatmerk afgeleid uit de metadata-header van de scan (de intraorale scanner van de tandarts stempelt die erin).
- Het merk gekruist met de lot-tabel in SQL Server en twee bruikbare lots gevonden, beide ruim voorbij de 30-daagse expiry-buffer.
- De vrije notitie gelezen, "vrije emergentie naar buccaal" eruit gepikt en de case naar em-class X geduwd.
De order staat in de cad_technicus-queue met een aaneengestikte contextkaart: scan, notitie, twee kandidaat-lots, het bijpassende Exocad-case-template en de geschiedenis van de tandarts (twee eerdere custom cases, beide netjes genezen). De technicus leest het in ongeveer 90 seconden, keurt de geometrie met een kleine aanpassing goed, en om 14:54 staat de order op de 15:00-frees.
Dit is de saaie succescase. Er zijn dagen waarop de agent in het laatste uur zes orders markeert en er één tot de 19:00-batch moet wachten. Dat is prima. Het doel van de agent was nooit om 100% door de 15:00-lock te persen. Het doel was om het uur tussen 14:00 en 15:00 te besteden aan beslissen wélke orders dat verdienen.
Wat we tijdens de bouw hebben geleerd
Uit dit project kwamen een paar dingen naar boven die we op de muur van elke chat-agent-build zouden schrijven.
De waarde van de agent zit niet in de antwoorden die hij geeft. Hij zit in de tijd die hij teruggeeft aan de mensen die anders aan het triagen waren. Het CAD-technicus-team van het lab was vroeger zo'n 90 minuten per dag kwijt aan triage-onderbrekingen. Dat is nu rond de 20 minuten. Die teruggewonnen tijd is de ROI; de agent zelf is het transportmiddel.
Legacy-systemen zijn niet de vijand. De Exocad-library en de SQL Server 2016-applicatie zijn allebei een decennium ouder dan de agent, en allebei prima. We hebben er dunne adapters omheen geschreven en ze behandeld als de bron van waarheid. Wie je vertelt dat je je stack moet moderniseren voordat je er een agent op kunt zetten, verkoopt je een moderniseringsproject, geen agent.
Confidence-drempels zijn een productbeslissing, geen modelbeslissing. De 8,4% X-rate is gekozen door het hoofd CAD, niet door ons. Hij heeft de queue op vier verschillende drempels bekeken en die gekozen waarbij zijn team zich geïnformeerd voelde in plaats van overspoeld. Wij stemmen het model af op zijn keuze, niet andersom.
Toen we deze chat agent bouwden voor het lab in Eindhoven, liepen we er steeds tegenaan dat elke "edge case" uiteindelijk een vingerafdruk had in óf de Exocad-library óf het SQL Server-schema. De winst zat in het zorgvuldig lezen van de legacy-stack, niet in het vervangen ervan. Heb je een vergelijkbare puzzel — een klein ops-team, een gereguleerde workflow, twee stukken software die ouder zijn dan je laatste aanwerving — dan is dat het soort AI-agents-werk waar we van houden.
Wil je weten of zo'n agent contact met jouw operatie zou overleven, doe dan vandaag dit: open de laatste 100 orders, mails of tickets die door je inbox zijn gekomen en label ze stuk voor stuk met de queue waar ze hadden moeten landen. Kun je minder dan vijf queues benoemen, dan heb je de routingtabel al. Het model is het makkelijke deel.
Kern
In een gereguleerde workflow is de taak van een chat agent niet antwoorden geven — het is de juiste order naar de juiste mens routeren voordat de volgende batch locked.
FAQ
Waarom hebben jullie de 11 jaar oude Exocad-library niet vervangen?
Daar zitten de gevalideerde case-templates van het lab in. Vervangen betekent opnieuw valideren via een aangemelde instantie, en dat kost meer CAD-technicus-tijd dan de library zelf.
Waarom direct SQL Server 2016 uitlezen in plaats van er een API voor te zetten?
De traceability-applicatie als service ontsluiten heropent de validatie onder het kwaliteitssysteem van het lab. Een read-only replica plus een dun commit-laagje houdt het papieren spoor intact.
Wat gebeurt er als de chat agent een emergence-profile verkeerd classificeert?
Hij classificeert alleen ooit verkeerd richting X, de queue voor menselijke review. Elk dubbelzinnig signaal duwt de order naar een CAD-technicus in plaats van naar de freesmachine. We classificeren nooit terug uit X.
Hoe lang duurde de bouw?
Ruim negen weken van kickoff tot productie-overgang, met daarna nog zes weken onder begeleid draaien voordat het lab de agent orders liet routeren zonder dat iemand bij elke case dubbelcheckte.
Wat doet de agent met orders die de 15:00-freeslock niet halen?
Die rollen door naar de 19:00-batch met een vlag waarop staat waarom ze de 15:00 niet hebben gehaald. De labplanner ziet de reden in de queue-kaart, niet ergens diep in een mailthread.