Tabelsets via de API¶
Het concept achter kennis staat in Kennis.
Een tabelset bevat de tabellen in je documenten -- elk werkblad van een werkmap, elk CSV- of
JSON-bestand, elke tabel in een Word-bestand, en met het sjabloon tables/llama-parse die van een
geparseerde PDF -- elk met zijn kolommen en zijn
rijen, zodat je ze op veld kunt bevragen. Een index beantwoordt "welke passages
lijken op deze vraag"; een graaf beantwoordt "wat hangt hiermee samen"; een
tabelset beantwoordt "welke rijen voldoen aan deze voorwaarden, en wat is hun totaal": de facturen
boven 10.000 euro van vorig kwartaal, de omzet per leverancier, het product met de laagste
voorraad.
Je sleutel heeft de capability knowledge nodig. Een tabelset heeft geen eigen capability.
Hetzelfde doe je met de muis in Tabelsets in de console.
Tabellen, kolommen en bronnen¶
| Wat het is | |
|---|---|
| Tabelset | selecteert een of meer collecties en bevat de tabellen die in hun bronnen staan. Het id begint met tset_ |
| Tabel | één tabel uit één bron: een werkblad, de inhoud van een CSV- of JSON-bestand, of een tabel in een document. Het id begint met tbl_ |
| Kolom | een veld van een tabel, met een type dat Core uit de waarden afleidt: boolean, integer, number, date of string |
| Rij | één regel van een tabel, als JSON-object van kolomnaam en waarde |
| Bron | het document waaruit een tabel is gelezen. Een tabel hoort bij precies één bron en verdwijnt als de bron verdwijnt |
Core schrijft de tabellen zelf, uit je documenten. Je kunt een rij niet met de hand toevoegen, wijzigen of verwijderen; wijzig het document en Core leest het opnieuw. Voor records die je applicatie zelf schrijft, gebruik je een datastore.
1. Een tabelset maken¶
curl -s -X POST "$CORE_URL/knowledge/table-sets" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{
"name": "financiele-tabellen",
"description": "Facturen, begrotingen en leverancierslijsten uit de map financiën.",
"collection_ids": [{"collection_id": "col_2Pf7...",
"metadata_filter": {"afdeling": "financien"}}],
"ingest_pipeline": {"template": "tables/documents"},
"labels": {"afdeling": "financien"}
}'
Het antwoord is 201 met de tabelset en een Location-header. De voorbeelden hieronder gebruiken
$TABLE_SET_ID voor het id.
| Veld | Wat het doet |
|---|---|
name |
uniek binnen de organisatie: kleine letters, cijfers, _ en -, beginnend met een kleine letter of een cijfer, hoogstens 64 tekens. Een policyregel noemt de tabelset bij deze naam, dus die verandert daarna nooit meer |
description |
wat de tabelset bevat, hoogstens 1.024 tekens |
collection_ids |
hoogstens 16 collecties, elk met een optioneel metadata_filter. Alleen bronnen waarvan de metadata het filter bevat, worden gelezen; zonder filter de hele collectie |
ingest_pipeline |
hoe tabellen worden gelezen: een ingebouwd sjabloon tables/... of een versie van een eigen pipeline (De pipeline kiezen). Laat het weg of stuur null om tables/documents te gebruiken |
labels |
de eigen indeling van je organisatie, als label en waarde; regels over Tabelsets lezen ze |
metadata |
een eigen JSON-object, hoogstens 16 KiB. Core bewaart het en geeft het terug, en leest het nooit |
GET /knowledge/table-sets geeft de tabelsets die je sleutel bereikt, GET
/knowledge/table-sets/{id} één tabelset. PATCH wijzigt description, labels, metadata,
collection_ids en ingest_pipeline; een veld dat je weglaat, blijft zoals het is. De naam kun je
niet wijzigen. DELETE verwijdert de tabelset met al zijn tabellen en hun rijen en geeft 204;
de collecties en hun bronnen blijven.
| Antwoord | Betekenis |
|---|---|
409 |
een andere tabelset in je organisatie heeft deze naam al |
403 |
geen regel in de policy van je sleutel staat een tabelset met deze naam toe |
422 op labels.<naam> |
het label staat niet in de vocabulaire van je organisatie |
422 op ingest_pipeline |
de pipeline kan geen tabelset vullen (De pipeline kiezen) |
2. Vullen uit bronnen¶
Een tabelset leest de bronnen van de collecties die hij selecteert. Bij het maken wordt nog niets
gelezen: staan er al bronnen in zijn collecties, dan draagt het antwoord reindex_required: true
en start je het lezen zelf (Opnieuw lezen). Een bron die je daarna toevoegt,
wordt automatisch gelezen.
De pipeline kiezen¶
Drie ingebouwde sjablonen vullen een tabelset. Geen ervan roept een model aan.
| Sjabloon | Wat het leest |
|---|---|
tables/structured |
de werkbladen van een werkmap, een CSV- of JSON-bestand, en een bron van het soort records |
tables/documents (de standaard) |
hetzelfde, plus de tabellen in een Word-bestand, een presentatie, een webpagina of Markdown |
tables/llama-parse |
hetzelfde, plus de tabellen van een PDF of een scan, gelezen door de parseerdienst van je installatie. Alleen beschikbaar als je installatie een parseerdienst heeft |
Een eigen pipeline mag alleen de stappen input, load, parse, llama_parse,
tabular_extract, table_store en output bevatten. Hij heeft minstens één tabular_extract
nodig en precies één table_store, die direct naar output leidt. Elke andere pipeline geeft
422 op ingest_pipeline. Een pipeline die een tabelset vult, kan geen index of graaf vullen, en
omgekeerd. Zie Pipelines voor het bewerken van stappen; de stap tabular_extract
kent sheets (welke werkbladen hij leest) en max_tables (hoogstens zoveel tabellen per bron,
standaard 50).
Hoe een tabel wordt gelezen¶
- Een werkblad is één tabel, met de eerste rij als kop. Een CSV- of JSON-bestand (een lijst platte objecten) is één tabel. In een Word-bestand, een presentatie, een webpagina of Markdown is elke tabel één tabel, genoemd naar de kop erboven.
- Elke tabel krijgt zijn eigen kolommen. Core geeft elke kolom het smalste type waar al zijn
waarden in passen:
boolean,integer,number,date, andersstring. Lege cellen tellen niet mee. Een kolom met zowel gehele als decimale getallen isnumber; elke andere mix isstring. - Een cel met alleen
n/a,na,-,null,noneof#n/ageldt als leeg, zodat één plaatshouder een kolom met bedragen niet in tekst verandert. - Het scheidingsteken van een CSV wordt herkend: komma, puntkomma, tab of verticale streep.
- Een getal met een decimale komma, zoals
1.234,56, blijft tekst. Schrijf getallen in een CSV met een decimale punt. Een werkmap bewaart echte getallen en heeft hier geen last van. - Een tabel bevat hoogstens 50.000 rijen.
Wanneer een bron wordt gelezen¶
Een nieuwe bron in een geselecteerde collectie wordt automatisch gelezen. Wijzig je de metadata van een bron, dan bepaalt Core opnieuw in welke tabelsets ze hoort: een tabelset die haar niet meer selecteert, verliest haar tabellen meteen.
GET .../sources geeft per bron de title, de status (queued, processing, ready of
failed), table_count en row_count, en bij een fout een vaste error_code. Voeg
?status=failed toe om alleen de mislukte te zien.
error_code |
Betekenis | Wat je doet |
|---|---|---|
parse_error |
een bestand dat niet te lezen is: een beschadigde werkmap, een CSV met dubbele of lege kolomnamen, een JSON-bestand dat geen array van platte objecten is | verbeter het bestand en upload het opnieuw |
too_many_tables |
het document bevat meer tabellen dan max_tables |
splits het document, of verhoog max_tables in een eigen pipeline |
sheet_not_found |
sheets van de pipeline noemt een werkblad dat de werkmap niet heeft |
verbeter sheets |
too_large |
een tabel van meer dan 50.000 rijen | splits het bestand |
too_large_for_pipeline |
een bestand groter dan de uploadlimiet, standaard 25 MiB; een tabelset leest een document in zijn geheel | splits het bestand |
table_set_full |
de tabelset zou meer dan 10.000 tabellen of 5.000.000 rijen bevatten | verdeel de collecties over meer tabelsets, of versmal een metadata_filter |
no_pipeline |
de pipelineversie die de tabelset noemt, is verwijderd | kies een andere pipeline en lees opnieuw |
Andere codes, zoals needs_parse, betekenen hetzelfde als bij een index
(Waarom indexeren mislukte).
Een bron zonder tabel, zoals een PDF met alleen lopende tekst, is ready met table_count: 0.
Een nieuwe versie van een document vervangt zijn tabellen. Verandert een bron, dan worden haar
tabellen opnieuw gelezen en in hun geheel vervangen: een kolom die verdween, is weg, en een kolom
waarvan de waarden van type veranderden, krijgt het nieuwe type. Een tabel op dezelfde plek met
dezelfde naam houdt zijn id, dus een bewaard table_id blijft geldig tot het werkblad een andere
naam of plek krijgt. Een vraag die een verdwenen kolom noemt, geeft 422 met de naam van die
kolom.
Wordt een bron verwijderd, dan verdwijnen haar tabellen en hun rijen mee, in elke tabelset.
Opnieuw lezen¶
curl -s -X POST "$CORE_URL/knowledge/table-sets/$TABLE_SET_ID/reindex" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"mode": "full"}'
full (de standaard) leest elke bron binnen bereik opnieuw en haalt de tabellen weg van bronnen
die de tabelset niet meer selecteert. failed probeert alleen de bronnen opnieuw waarvan het
laatste lezen mislukte. Het antwoord is 202 met een run; GET .../runs en GET
.../runs/{run_id} tonen de voortgang (status, sources_total, sources_done,
sources_failed). Loopt er al een run voor deze tabelset of staat er een in de wachtrij, dan is
het antwoord 409.
Lees opnieuw nadat je de tabelset maakte over collecties waar al bronnen in stonden, de pipeline
wijzigde, een collectie toevoegde of weghaalde, of een metadata_filter wijzigde. Elk antwoord met
de tabelset zegt reindex_required: true tot er een volledige run is afgerond.
3. Een tabel vinden en de rijen lezen¶
curl -s "$CORE_URL/knowledge/table-sets/$TABLE_SET_ID/tables?q=facturen" \
-H "Authorization: Bearer $CORE_KEY"
Elke tabel in de lijst heeft een id, een name (de naam van het werkblad, de kop boven de tabel,
of table-<n>), een origin (sheet, csv, json of markdown), de position in de bron,
columns met hun types, row_count, en de source_id en source_title waaruit hij is gelezen.
q houdt de tabellen over waarvan de naam of de titel van de bron de tekst bevat; source_id
houdt de tabellen van één bron over. GET .../tables/{table_id} geeft één tabel.
curl -s "$CORE_URL/knowledge/table-sets/$TABLE_SET_ID/tables/tbl_4Hq9.../rows?limit=100" \
-H "Authorization: Bearer $CORE_KEY"
GET .../rows geeft de rijen in de volgorde van het document, elk als {"id", "data"}, hoogstens
100 per pagina; geef next_cursor mee als cursor voor de volgende pagina.
GET /knowledge/collections/{id}/sources/{sid}/tables geeft de tabellen van één document in elke
tabelset die je sleutel bereikt, elk met zijn table_set_id, een pagina tegelijk: limit (1-100,
standaard 20), en de next_cursor van een pagina als cursor voor de volgende.
4. Een tabel bevragen¶
Een vraag is een formulier en geen querytekst. Core controleert hem tegen de kolommen van de tabel en voert hem uit met elke waarde als parameter.
curl -s -X POST "$CORE_URL/knowledge/table-sets/$TABLE_SET_ID/tables/tbl_4Hq9.../query" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{
"filters": [
{"field": "land", "op": "in", "value": ["NL", "BE"]},
{"field": "factuurdatum", "op": "gte", "value": "2026-07-01"}
],
"aggregate": {"fn": "sum", "field": "bedrag"},
"group_by": "leverancier",
"sort": {"field": "$value", "direction": "desc"},
"limit": 10
}'
Dit leest als: de tien leveranciers met het hoogste totaalbedrag gefactureerd in Nederland of België sinds juli.
| Onderdeel | Wat je invult |
|---|---|
filters |
hoogstens 20 voorwaarden, waaraan een rij allemaal moet voldoen. Elk is {"field", "op", "value"} |
aggregate |
een van count, sum, avg, min en max, met een field. count mag het veld weglaten en telt rijen; met een veld telt hij de rijen die voor dat veld een waarde hebben. sum en avg vragen een kolom van het type integer of number; min en max werken op elke kolom behalve een boolean |
group_by |
één kolom om de aggregatie op te groeperen. Vraagt een aggregate |
sort |
{"field", "direction"}, direction asc (de standaard) of desc. Zonder aggregatie een kolom. Met group_by de groepskolom of $value, de aggregatie. Een aggregatie zonder group_by geeft één rij en neemt geen sortering |
fields |
de kolommen die elke rij teruggeeft, hoogstens 50; alleen zonder aggregatie. Weggelaten: elke kolom |
limit |
rijen, of groepen, 1 tot 500; standaard 50 |
cursor |
de next_cursor van een eerder antwoord op dezelfde vraag, voor de volgende pagina; alleen zonder aggregatie |
op |
Past op |
|---|---|
eq, ne, lt, lte, gt, gte |
een vergelijking, in het type van de kolom. Niet ordenen op een boolean |
contains, starts_with, ends_with |
tekst, letterlijk en zonder onderscheid tussen hoofd- en kleine letters. Alleen tekstkolommen |
in |
een van de waarden in een lijst van hoogstens 100 |
exists |
true: de rij heeft een waarde voor de kolom; false: niet |
Zonder sort komen rijen in de volgorde van het document en groepen in de volgorde van de
groepskolom. Een sortering zet rijen zonder waarde achteraan, in beide richtingen.
Het antwoord, ingekort:
{
"columns": [{"name": "leverancier", "type": "string"}, {"name": "sum", "type": "number"}],
"items": [{"leverancier": "Acme", "sum": 48210.5}, {"leverancier": "Widget BV", "sum": 30115.0}],
"next_cursor": null,
"truncated": false
}
items bevat de rijen, elk met alleen de gevraagde kolommen, of de groepen, elk met de groepskolom
en de aggregatie onder de naam van zijn functie. Een vraag zonder aggregatie blader je door met
next_cursor; tussen pagina's mag je limit en fields wijzigen, verder niets. Een
gegroepeerd antwoord heeft geen pagina's: zijn er meer groepen dan limit, dan krijg je de eerste
limit en truncated: true. Stel dan een smallere vraag in plaats van limit te verhogen.
| Antwoord | Betekenis |
|---|---|
422 met een veld, zoals filters[1].value of sort.field |
de tabel heeft die kolom niet, de operator past niet bij het type van de kolom, de waarde is niet als dat type te lezen, of de onderdelen passen niet bij elkaar |
422 op cursor |
de cursor hoort bij een andere tabel, andere filters of een andere sortering |
422 met de vraag om een smallere vraag |
de vraag liep over zijn tijdslimiet, standaard vijf seconden; voeg een filter toe |
403 |
de policy van je sleutel vereist een agent; rijen lezen en bevragen werkt alleen binnen een beurt |
404 |
de tabelset of de tabel bestaat niet, of je sleutel of de persoon die het verzoek noemt, bereikt hem niet |
Een vraag via deze route wordt niet als gebruik gemeten.
5. Een model laten zoeken¶
Een model bereikt een tabelset via een zoektool. Kopieer het ingebouwde
sjabloon retrieval/table-set, vul het blok table_set in, en registreer de tool op de versie die
je terugkrijgt:
curl -s -X POST "$CORE_URL/knowledge/pipelines/from-template" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"template": "retrieval/table-set", "name": "financiele-tabellen-zoeken",
"sources": {"table_set": "tset_6Rk2..."}}'
curl -s -X POST "$CORE_URL/knowledge/retrieval-tools" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"name": "financiele_tabellen",
"description": "Beantwoordt vragen over facturen, begrotingen en leveranciers uit de financiële spreadsheets.",
"pipeline": {"pipeline_id": "pip_...", "version": 1}}'
Bied de tool aan als {"type": "tool", "name": "knowledge__financiele_tabellen"}. Het sjabloon
roept zelf geen model aan: het model in de beurt schrijft de vraag.
De tool biedt het model een argument table_query: een table_id, en dezelfde vraag die je
stuurt in Een tabel bevragen -- filters met elke operator, aggregate,
group_by, sort, fields en limit -- zonder cursor, want een toolaanroep geeft één pagina
antwoord. Een kopie van het sjabloon kan de stap tabular_query op de modus llm zetten en een
model noemen; dat model stelt dezelfde vraag dan zelf op.
- De beschrijving van de tool noemt geen tabel. De naam van een tabel, de titel van het
document en de kolommen zijn tekst die de auteur van het document schreef, dus de beschrijving
bevat alleen id's. Een aanroep zonder
table_querykrijgt als antwoord de tabellen waar de vraag het waarschijnlijkst over gaat, hoogstens 24, elk met id, naam, documenttitel en kolommen, gerangschikt op de woorden uit de vraag die erin staan. Het model leest die lijst en vraagt opnieuw met een id. - Bereikt de tool hoogstens 24 tabellen, dan is
table_ideen keuze uit hun id's; bereikt hij er meer, dan is het vrije tekst, gecontroleerd als de aanroep loopt. Zo blijft een tabelset met duizenden maandexports bruikbaar.
Het model krijgt de tabel terug, zijn kolommen, de gevonden rijen en de vraag die Core uitvoerde,
als gestructureerde gegevens. Rijen voorbij 32.768 tekens JSON vallen weg en het antwoord zegt
truncated, zodat het model zijn vraag versmalt in plaats van een gedeeltelijke som als een hele
te lezen. Een tabel die de tool niet bereikt en een vraag waar de kolommen niet bij passen, komen
bij het model terug als "Unknown table." of als ongeldige argumenten, zodat het zichzelf kan
verbeteren; de beurt mislukt er niet door. Een vraag die over zijn tijdslimiet loopt, standaard
vijf seconden, komt op dezelfde manier terug als te kostbaar, zodat het model een filter kan
toevoegen en opnieuw kan vragen.
Een tabelset kun je op metadata versmallen. Anders dan bij een graaf hoort een tabel bij één
bron, dus het metadata_filter en de filter_keys van een tool werken op een tabelset: ze worden
vergeleken met de metadata van de bron waaruit elke tabel is gelezen.
Combineer lopende tekst en tabellen in één tool met een eigen pipeline met een blok index en een
blok table_set, zodat één tool antwoordt uit zowel de tekst als de tabellen van dezelfde
documenten (Eigen zoekpipelines).
Elke tabelvraag in een beurt telt als één retrieval-eenheid (retrieval).
Wie welke tabellen bereikt¶
Welke tabelsets een sleutel bereikt, bepalen regels over Tabelsets (knowledge_table_set) in
de policy, op naam of op label, met de acties see, use, write
en delete. De standaardregels van een sleutel die zonder policy is geregistreerd, staan elke
tabelset toe. Een tabelset die geen regel toestaat,
ontbreekt in lijsten en geeft 404; er een maken onder een naam die geen regel toestaat, geeft
403. Een zoektool waarvan de pipeline een tabelset noemt die je sleutel niet bereikt, kun je niet
schrijven of vastzetten, en een verzoek dat hem aanbiedt, wordt geweigerd alsof de tool niet
bestaat.
Een policy van een sleutel of organisatie die is opgeslagen voordat er tabelsets bestonden, ook
de standaardregels die bij het registreren van een sleutel zijn uitgeschreven, heeft geen regel over
Tabelsets en bereikt er geen: opvragen en lezen geven 404 en maken geeft 403, tot je een
regel toevoegt die Tabelsets toestaat
(Een regel toevoegen of wijzigen).
Binnen een tabelset is een tabel zichtbaar precies wanneer zijn bron dat is. Een verzoek dat met
de header X-Core-Subject een persoon noemt (Bouwen voor personen),
ziet de tabellen van de eigen bronnen van die persoon, van gedeelde bronnen, en van bronnen die de
regels over Kennisbronnen en Opgeslagen bestanden op label toestaan. Dat geldt voor lijsten,
rijen, vragen en de toolaanroepen van een model. Een tabel uit de spreadsheet van een ander geeft
404.
Wat een tabelset niet doet¶
- Een vraag leest één tabel. Hij koppelt geen tabellen en voegt twaalf maandexports niet samen tot één tabel, ook niet als hun kolommen gelijk zijn.
- De kop is altijd de eerste rij. Een werkblad dat begint met een titelblok, of een kop over twee rijen heeft, wordt gelezen met de eerste rij als kolomnamen.
- Getallen met een decimale komma worden als tekst gelezen.
- Een model kan tijdens een beurt geen rijen schrijven, en via de API kan dat ook niet.
- Een tabelset kan niet in een publicatie.
- Een export van de organisatie bevat de tabelset en zijn collecties, niet zijn tabellen: een volledige herindexering op de nieuwe plek leest ze opnieuw.