Vind schema-eigenschappen die meer dan één vraag zouden kunnen stellen — bekijk de concurrerende lezingen naast elkaar en leg elke eigenschap vast op één betekenis voordat de data wordt verzameld.
Entity Enricher behandelt LLM's als bevraagbare kennisbanken, en een eigenschapsnaam is de vraag die je stelt. Laat die naam meerdere lezingen toe, dan kiest elk model er stilletjes één — zo komt size bij een bedrijf van het ene model terug als personeelsaantal, van het andere als omzetcijfer en van een derde als vloeroppervlak. De modellen waren het niet oneens over een feit. Ze beantwoordden verschillende vragen, en je kolom bevat nu een mengeling van antwoorden die geen enkele afnemer verderop nog uit elkaar kan houden.
Door de betekenis vast te leggen wordt een verrijking vergelijkbaar tussen modellen en stabiel in de tijd. Het ruimt ook alles stroomafwaarts op: multi-modelfusie ziet geen conflicten meer die in werkelijkheid twee vragen zijn, en benchmark-vergelijkingen straffen modellen niet langer af omdat ze je schema anders lezen dan de referentie deed.
Een feit dat gewoon in de loop van de tijd verandert, is geen ambiguïteit. Een schema is een duurzaam contract, dus een eenvoudigweg ceo genoemde eigenschap betekent “de CEO op het moment van verrijking”, en het schema volgend jaar opnieuw uitvoeren hoort de nieuwe op te leveren. De controle stelt nooit voor om een datum in een naam vast te leggen — dat zou elke toekomstige run breken.
De hele controle is één vraag, gesteld over elke eigenschap: als je haar naam in de context van het bovenliggende object leest, hoeveel verschillende dingen kan ze dan vragen? Het aantal is het oordeel.
| Interpretaties | Oordeel | Wat het betekent |
|---|---|---|
| Precies één | Wissen | Elk model zoekt hetzelfde op. Geen label, niets te herstellen. |
| Twee of meer | Dubbelzinnig | Elk model kiest zijn eigen lezing, dus de kolom vermengt stilzwijgend antwoorden op verschillende vragen. De controle benoemt de concurrerende lezingen en stelt een formulering voor die er één overhoudt. |
| Geen | Niet toewijsbaar | De naam duidt niets aan wat het bovenliggende object heeft, dus het model kan geen waarde opzoeken — het verzint er een. De genoemde interpretaties zijn degene die de analyse heeft overwogen en verworpen, en de oplossing is hernoemen of verwijderen: geen enkele beschrijving kan een entiteit een eigenschap geven die ze niet heeft. |
Een eigenschap die precies één ding benoemt, kan alsnog worden gemarkeerd als de waarde niet is afgebakend — de lezer weet wat er gevraagd wordt, maar niet in welke termen het antwoord terugkomt. Dit zijn de terugkerende patronen:
| Subgeval | Voorbeeld | Wat openstaat |
|---|---|---|
| Onduidelijke verwijzing | Companysize | De naam wijst naar meerdere verschillende feiten die het bovenliggende object echt heeft — personeelsbestand, omzet, vloeroppervlak. Niets in de naam maakt die keuze. |
| Onduidelijke maat of eenheid | Companyannual_revenue | Eén feit, maar geen valuta, geen periode en geen bruto/netto-kader — een plausibel antwoord kan er drie ordes van grootte naast zitten en toch "correct" zijn. |
| Onduidelijke schaal of richting | Supplierrisk_score | Geen vermeld bereik en geen vermelde polariteit: 0–10 of 0–100, en is een hoog getal veiliger of riskanter? Twee modellen kunnen elkaars omgekeerde opleveren. |
| Onduidelijke reikwijdte of afbakening | Companyemployees | Welke subset, welk aggregatieniveau, wiens perspectief — de hele groep of deze vestiging, personeelsaantal of fte's, externen wel of niet meegeteld. |
| Niet toewijsbaar | Authorrelease_year | Een auteur heeft geen verschijningsjaar — zijn boeken wel. Het model kan dit niet opzoeken en verzint dus iets. Hernoem de eigenschap naar iets wat de bovenliggende entiteit wél bezit, of verplaats haar naar het object dat het wel heeft. |
Tekstuele eigenschappen — description, summary, notes, bio — worden nooit gemarkeerd. Hun formulering verschilt uiteraard per model, maar de gestelde vraag is volstrekt duidelijk, en dat is het enige waarover deze controle oordeelt. Dubbelzinnigheid gaat over de vraag, nooit over hoezeer de antwoorden op elkaar lijken.
Een oordeel op zichzelf (“dit is onduidelijk”) laat je raden wat de analyzer voor ogen had. Daarom bevat elke bevinding haar interpretations: twee tot vier korte, onderscheiden lezingen die de eigenschap toelaat, de meest waarschijnlijke eerst. Die lijst ís de bevinding — kan de analyzer geen twee lezingen benoemen, dan wordt de bevinding als ruis geschrapt in plaats van aan je getoond.
annual_revenue op een BedrijfDaarnaast krijg je een voorgestelde beschrijving die er precies één overhoudt — hier: “totale groepsomzet in USD over het laatst afgesloten boekjaar, bruto vóór retouren”. Het toepassen ervan kost niets: de beschrijving bereikt het verrijkende model net zo goed als de naam, maar de eigenschap behoudt haar naam, dus er verschuift geen enkel datacontract. Als de naam zelf misleidt, bevat de bevinding ook voorgestelde namen.
De interpretaties uitgeschreven zien, beslecht een eigenschap meestal sneller dan welke uitleg ook: je herkent degene die je bedoelde, en de rest is wat je stilzwijgend hebt binnengekregen.
Nadat een sample is gegenereerd, beoordeelt de analyzer de namen van de eigenschappen en levert hij een ambiguïteitsrapport op. Ondubbelzinnige hernoemingen worden automatisch toegepast op door AI bedachte sleutels (nooit op velden die je zelf hebt benoemd), zodat het sample dat je nakijkt al beter leest. Ook markeert de analyzer te specialistische eigenschappen — kenmerken die uit het voorbeeldexemplaar zijn binnengeslopen en alleen bij een subtype passen (de medailles van een atleet op een algemene Person) — en stelt hij een nauwer entiteitstype voor. De controle op identiteitsafbakening draait hier als aparte aanroep, direct daarna: die legt de vorm van gerelateerde items vast voordat jij het sample nakijkt. Samples die op bijgevoegde documenten zijn gebaseerd, worden overgeslagen — hun waarden komen uit het brondocument, niet uit het geheugen van het model.
Zodra een gegenereerd schema is opgeslagen, annoteert een naverwerking elke eigenschap met het dubbelzinnigheidsoordeel en stelt ze hernoemingen voor bij de eigenschappen die nog openstaan. Relatieplekken zijn dan al geannoteerd — de generatie beoordeelt hun afbakening zelf, als een van haar eigen stappen — dus de naverwerking dekt alleen eigenschapsnamen. Deze stap is best-effort: mislukt ze, dan blijft de generatie zelf onaangetast.
De knop Opnieuw controleren voert beide controles uit — eigenschapsnamen en relatieplekken — als twee parallelle aanroepen. Dit is de enige plek die een herschreven beschrijving voorstelt in plaats van een hernoeming. Ze analyseert alleen wat nog geen annotatie heeft, en schakelt over op een volledige heranalyse zodra alles geannoteerd is.
Voorbeeld-JSON die je plakt om een schema aan te maken, kan stateless worden geanalyseerd — je krijgt een rapport van dubbelzinnige en niet-toewijsbare eigenschapsnamen, en van gerelateerde items die entiteitsfeiten met koppelingsfeiten vermengen, zonder dat er iets wordt gewijzigd.
Dezelfde bevinding stelt op de ene plek een hernoeming voor en op de andere een beschrijving, en de reden daarvoor is het weten waard. Op het moment van generatie bestaat de beschrijving nog niet los — ze wordt vanuit de naam geschreven en kan de dubbelzinnigheid dus alleen herhalen. De naam is het enige wat je kunt repareren, en er hangt nog niets van af. Daarom stellen zowel de voorbeeldgeneratie als de naverwerking na schemageneratie een hernoeming voor.
Zodra het schema live is, verplaatst het hernoemen van een eigenschap kolommen, breekt het queries en verandert het de sleutels van gesynchroniseerde tabellen, terwijl een scherpere beschrijving het model net zo direct bereikt en verder niets verandert. De regel is dus simpel: zolang niets van het schema afhangt, hernoem je; zodra het live is, leg je de beschrijving vast — en houd je hernoemen achter de hand voor de gevallen waarin de naam zelf het probleem is.
De controle is uitsluitend adviserend. Niets analyseert je opgeslagen schema's op de achtergrond: ze draait bij generatie en wanneer je op Opnieuw controleren drukt. Ze blokkeert nooit de generatie, wijst nooit een verrijking af, en haar annotaties worden uit elke prompt gehaald die naar de verrijkende modellen gaat — ze informeert jou, niet de AI.
Gemarkeerde eigenschappen tonen een “ambiguous”-label in de Workflow Editor: oranje als de lezingen grotendeels overlappen en alleen randgevallen verschillen, rood als de concurrerende lezingen wezenlijk andere data zouden opleveren. Eigenschappen die duidelijk worden bevonden, krijgen geen label. Beweeg je muis over het label om de notitie van de analyzer te zien, de concurrerende lezingen die hij vond en de voorgestelde beschrijving of namen — zo staan het oordeel en de oplossing in dezelfde tooltip.
Een oordeel geldt voor de naam én de beschrijving van een eigenschap samen — een eigenschap hernoemen of haar beschrijving aanpassen laat de annotatie dus vervallen. De editor markeert zulke eigenschappen als verouderd en biedt een hercontrole aan, die alleen analyseert wat ontbreekt. Dat is precies wat je wilt na het toepassen van een voorgestelde correctie: de hercontrole bevestigt of de nieuwe formulering echt één enkele lezing vastlegt.
Identiteitsafbakening is een tweede controle, die als eigen modelaanroep naast de ambiguïteitscontrole draait en samen daarmee wordt gerapporteerd. Ze bekijkt elke relatieplek — de items van een gerelateerde array en geneste objecten: als daar feiten over de gerelateerde entiteit zelf (haar naam, haar land) worden gemengd met feiten over de koppeling (een rol die voor deze ouder geldt, een aanduiding per ouder), delen beide één identiteit — en overschrijven nieuwe verrijkingen de koppelingsfeiten over ouders heen. Zulke plekken krijgen een amberkleurige chip “gemengde feiten” waarvan de tooltip de aanbevolen structuur toont: de eigen velden van de entiteit genest in een subobject, de koppelingsvelden op het item zelf. Draagt het item al zo'n subobject, dan is de correctie kleiner — de verkeerd geplaatste velden verhuizen naar het subobject dat er al is.
De splitsing wordt toegepast tijdens de samplegeneratie, voordat je de sample goedkeurt: de vorm wordt op de eerste sample vastgelegd, elk geherstructureerd punt staat in de generatiewaarschuwingen, en de overige samples van de batch worden tegen die vastgelegde vorm gegenereerd. Schema's die uit een verse sample worden gegenereerd, komen er dus normaal gesproken schoon uit. Samples die op bijgevoegde documenten zijn gebaseerd, blijven zoals hun bronnen ze impliceerden en krijgen in plaats daarvan de chip.
Schemageneratie herstructureert de sample die je hebt goedgekeurd nooit zelf — ze beoordeelt dezelfde punten en rapporteert wat ze vindt. Bij een bestaand of handgeschreven schema zit de correctie dus in de chip: die biedt een splitsing met één klik die de sample herstructureert en het schema daaruit opnieuw genereert. De structuur is het contract, dus die verander je door opnieuw te genereren vanuit een nieuwe sample, nooit door ter plekke te patchen. Een punt dat je niet splitst, blijft gewoon werken — het houdt alleen één gedeelde identiteit, en de chip. De chip verdwijnt vanzelf zodra de veldenset van het item verandert.
De controle kan per schema worden uitgeschakeld via het overloopmenu van de Workflow Editor. Uitgeschakeld wordt de naverwerking na generatie overgeslagen, worden de chips, de knop Opnieuw controleren en verouderde waarschuwingen verborgen, en antwoorden de analyse-endpoints met een ambiguity_check_disabled-fout. Bestaande annotaties blijven behouden (alleen verborgen), en als je de controle weer inschakelt op een schema dat nooit is geanalyseerd, draait ze automatisch.
Elk gegenereerd schema begint met de controle aan, ook schema's die zijn gegenereerd op basis van bijgevoegde documenten. Ambiguïteit is een eigenschap van hoe het schema is geformuleerd, niet van waar de waarden van één run vandaan kwamen: het document heeft die waarden eenmalig vastgelegd, terwijl het schema daarna hergebruikt wordt voor entiteiten die het nooit heeft gedekt. Wat het document wél verandert, is de stap sample — de eigenschapsnamen daarvan komen uit het vocabulaire van het brondocument zelf, dus ze worden nooit in code hernoemd; het schema dat eruit wordt opgebouwd draagt in plaats daarvan de controle.
“De jaaromzet van het bedrijf” voegt geen informatie toe die de naam niet al bevatte, dus behandelt de analyzer zo'n beschrijving alsof die er niet is en beoordeelt hij alleen de naam. Een beschrijving verdient haar plek door de eenheid, de periode, de schaal of de afbakening te benoemen.
Notities en interpretaties van de analyzer staan in je interfacetaal — een Franse gebruiker ziet Franse lezingen, een Japanse gebruiker Japanse. Voorgestelde eigenschapsnamen blijven Engels, in lijn met de naamgevingsconventies van schema's.
Elke analyse is een echte (goedkope) modelaanroep — twee ervan, parallel uitgevoerd, wanneer er ook relatielocaties afgebakend moeten worden. Elke aanroep wordt als eigen prompt op het record vastgelegd, onder het type ambiguity_analysis, en net als elk ander AI-gebruik van je credits afgeschreven. Incrementele hercontroles betalen alleen voor de eigenschappen en locaties die daadwerkelijk zijn geanalyseerd.
Voorbeeld- en schemageneratie krijgen zelf de instructie om per eigenschap één ding te benoemen en beschrijvingen te schrijven die de eenheid, de schaal en de afbakening vermelden — en, bij discutabele lijsten, een bovengrens in de beschrijving in plaats van een aantal dat in de naam wordt geperst. De meeste schema's komen er dus schoon uit en de analyse hoeft alleen de achterblijvers op te vangen.
De controle is programmatisch beschikbaar:
| Symptoom | Beschrijving |
|---|---|
POST /api/schema/analyze-sample | Analyseer geplakte sample-JSON — beide controles parallel achter één verzoek, stateless rapport, er wordt niets gewijzigd |
POST /api/schema/saved/{id}/analyze | Analyseer een opgeslagen schema en schrijf de annotaties van beide controles — standaard incrementeel, force=true analyseert alles opnieuw |
POST /api/schema/scoping-split | Pas één "gemengde feiten"-splitsing toe op een sampleset — deterministisch, gratis, niets wordt opgeslagen; voer de teruggegeven samples terug in de schemageneratie |
analyze_sample | MCP-tool — hetzelfde stateless samplerapport, beide controles, vanuit Claude of elke MCP-client |
analyze_schema | MCP-tool — annoteer een opgeslagen schema; combineer met update_schema om een voorgestelde beschrijving of hernoeming toe te passen |
Bevindingen komen terug met een kind (ambiguous of unmappable), een level, een notitie, de lijst interpretations en de voorgestelde correctie. Op een opgeslagen schema worden ze bij elke eigenschap opgeslagen als ambiguity; sample-generatie geeft ze terug onder ambiguity_report.
Zie de API-referentie en de MCP Server-gids voor authenticatie en de volledige toolcatalogus.