Ga naar inhoud

Kennis via de API

Het concept achter kennis staat in Kennis.

Op deze pagina maak je met de API documenten van je organisatie doorzoekbaar, van collectie tot zoektool: je slaat bronnen op in een collectie, maakt een index over die collectie en doorzoekt de index direct of biedt haar aan een model aan als tool. De stappen staan in de volgorde waarin je ze uitvoert.

Je sleutel heeft de capability knowledge nodig voor deze endpoints. Een bestand uploaden vraagt in sommige collecties ook files (stap 1). Grafen hebben een eigen pagina: Grafen via de API.

De console gebruiken

Je kunt collecties, indexen, zoektools en pipelines ook beheren in het onderdeel Kennis van de console. De API en de console gebruiken dezelfde resources. Een beheerder kan bijvoorbeeld documenten bijhouden in de console terwijl je applicatie ze via de API doorzoekt.

Collecties, indexen, pipelines en tools

Wat het is
Collectie waar bronnen staan. Een bron hoort bij precies één collectie en verhuist niet.
Bron één document, tabel of geüpload bestand.
Index kiest een of meer collecties en bepaalt hoe hun bronnen worden gesplitst en ingebed. Dit is wat je doorzoekt.
Zoektool een benoemde zoekopdracht die het model kan aanroepen. Ze draait één versie van een zoekpipeline en bereikt de indexen, collecties en grafen die die versie noemt.

Het beleid van je sleutel kan beperken welke collecties en indexen die sleutel bereikt, op naam. Wat het beleid niet noemt, bestaat niet voor die sleutel: lijsten laten het weg, een aanvraag met het id antwoordt 404, en dat geldt ook voor alles wat eraan hangt -- de bronnen van een collectie, de statistieken, runs, zoekopdrachten en geheugenrecords van een index. Een collectie of index aanmaken of hernoemen naar een naam die het beleid niet noemt, geeft 403; er wordt dan niets opgeslagen. Een zoektool is daarop geen uitzondering: het beleid moet de tool toestaan én elke index, collectie en graaf die haar pipeline noemt (Welke regels gelden). Zie sleutels.

1. Een collectie en bron maken

curl -s -X POST "$CORE_URL/knowledge/collections" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "handboek", "description": "Intern handboek"}'

curl -s -X POST "$CORE_URL/knowledge/collections/col_2Pf7.../sources" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"kind": "text", "title": "Declaratiebeleid",
         "text": "Declaraties dien je binnen 30 dagen in...",
         "metadata": {"afdeling": "finance"}}'

Een bestand gaat er in één aanroep in. Post het bestand zelf naar .../sources/upload in plaats van het eerst te uploaden en daarna het id te noemen:

curl -s -X POST "$CORE_URL/knowledge/collections/col_2Pf7.../sources/upload" \
    -H "Authorization: Bearer $CORE_KEY" \
    -F "file=@handboek.pdf" \
    -F 'metadata={"afdeling": "finance"}'

title (standaard de bestandsnaam), retention en metadata -- een JSON-object met stringwaarden, als string, omdat een multipartformulier zelf geen typen kent -- zijn optionele velden ernaast. De bytes worden één keer opgeslagen, waar uploaden naar /files en daarna een file-bron maken van het id ze twee keer opslaat en een upload achterlaat.

Wat de upload wordt, volgt de collectie. In de eigen collectie van een sleutel en in de collectie uploads van je organisatie is het een bestand: je sleutel heeft de capability files nodig, anders geeft de upload 403, en de naam moet er een zijn die het bestandsbeleid van je sleutel noemt (Wat een bestands-id bereikt). In elke andere collectie is het een kennisdocument, zonder controle op de naam. Een title of bestandsnaam met een stuurteken geeft 422.

Een groot bestand gaat in delen. Een bestand dat groter is dan één upload mag zijn, of dat een weggevallen verbinding moet overleven, gaat naar binnen via een uploadsessie die eindigt als bron. Je sleutel heeft de capability knowledge nodig, en de bestanden van je organisatie moeten in een object store staan:

curl -s -X POST "$CORE_URL/uploads" \
    -H "Authorization: Bearer $CORE_KEY" \
    -H "Content-Type: application/json" \
    -d '{"filename": "handbook.pdf", "media_type": "application/pdf", "size_bytes": 157286400,
         "target": {"kind": "source", "collection_id": "col_2Pf7...",
                    "title": "Handbook", "metadata": {"department": "finance"}}}'

De sessie wordt geweigerd om wat de upload in één aanroep weigert, voordat er een deel is verstuurd: een mediatype dat geen pijplijn leest (422), een collectie die je sleutel niet bereikt (404), een gekoppelde kopie (409). Stuur de delen, rond de sessie af en vraag haar op zoals Een groot bestand in delen uploaden laat zien; is ze completed, dan draagt ze de source_id. Een bron boven de gewone uploadlimiet wordt opgeslagen en is te downloaden; een index leest haar toch in delen als ze tekst, een PDF, Word, PowerPoint of Excel is tot 100 MiB (Een groot bestand wordt in delen geïndexeerd), en elk ander bestand van die grootte komt in geen enkele index.

De bronroutes van een collectie tonen een sleutel alleen de bronnen waarvoor ze de capability heeft: zonder artifacts staat er geen artefact tussen de bronnen van een collectie, en zonder files geen upload.

Kies een van deze brontypen voor de JSON-body:

kind Verzoek Goed voor
text title, text (het hele verzoek hoogstens 1 MiB; voor meer tekst upload je een bestand) proza dat je al als string hebt
file file_id van een bestand of artefact dat je al hebt kopiëren van bytes die deze organisatie al heeft naar deze collectie; tekst, markdown, HTML, PDF, Word, PowerPoint, Excel, CSV, JSON en een afbeelding (PNG, JPEG, GIF, BMP, WebP), iets anders is 422
records title, records (tot 50.000 platte JSON-objecten) een tabel die je per veld wilt bevragen

Een file-bron kopieert de bytes naar een eigen blob, dus het verwijderen van het origineel of van de kopie laat de ander ongemoeid. Een artefact kopiëren is het downloaden ervan: dat antwoordt 403 als geen regel je sleutel toestaat artefacten te downloaden. En een collectienaam mag niet met key_ beginnen: dat is de naam van de collectie die Core per API-sleutel bijhoudt.

Word-bestanden, presentaties, webpagina's en PDF's worden gelezen door de pipeline die de index draait. De standaardpipeline leest ze alle vier, dus je kiest pas zelf een pipeline als je iets anders wilt dan de standaard. Een ouder Word-bestand (.doc, niet .docx) is de uitzondering: alleen een pipeline met de stap llama_parse leest het, zoals de sjablonen ingest/llama-parse en ingest/rag-with-tables, en elke andere pipeline laat het mislukken met needs_parse. Een spreadsheet komt juist als tabellen binnen: elk werkblad wordt rijen die je per veld kunt bevragen, net als een CSV.

Voeg metadata toe om bronnen te filteren bij indexeren en zoeken.

De bronnenlijst filteren. GET .../sources neemt folder, q (titel bevat, hoofdletterongevoelig) en het herhaalbare status (ingestiestatus van een van de indexen die de bron heeft opgenomen). folder matcht hele padsegmenten van de folder-metadata, dus beleid geeft beleid en beleid/2026 en nooit beleid-oud; met recursive=false krijg je alleen de eigen bronnen van de map. Een lege folder is de hoofdmap: met recursive=false geeft die de bronnen zonder map. Bijvoorbeeld:

curl -s "$CORE_URL/knowledge/collections/col_2Pf7.../sources?folder=beleid&status=ready&status=failed" \
    -H "Authorization: Bearer $CORE_KEY"

Een bron downloaden. GET .../sources/{source_id}/content geeft de bytes die Core voor een bron bewaart, als bijlage met de titel van de bron als naam: het bestand dat is geüpload, de tekst van een text-bron, of de records van een records-bron als JSON-array.

curl -s "$CORE_URL/knowledge/collections/col_2Pf7.../sources/src_9Qd2.../content" \
    -H "Authorization: Bearer $CORE_KEY" -o handboek.pdf

Een document later wijzigen

De titel, de metadata en de bewaarkeuze van een bron wijzig je later met een PATCH. metadata vervangt de hele map, net als bij een collectie, dus stuur elke sleutel mee die je wilt houden — ook folder en de sleutels die een connector schreef:

curl -s -X PATCH "$CORE_URL/knowledge/collections/col_2Pf7.../sources/src_9Qd2..." \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"title": "Declaratiebeleid 2026",
         "metadata": {"afdeling": "finance", "folder": "beleid/2026"}}'

Core bewaart het folder-pad genormaliseerd (/beleid//2026/ wordt beleid/2026), en een .- of ..-segment is een 422.

Een nieuwe map is geen veldwijziging maar een routeringsmoment: elke index die deze collectie kiest, wordt opnieuw langs haar eigen filter gelegd. Past de bron nu wel, dan wordt zij daar alsnog verwerkt; past zij niet meer, dan verdwijnt zij daar. Blijft zij passen, dan worden alleen de labels op haar fragmenten bijgewerkt en hoeft er niets opnieuw verwerkt te worden. De soort, de vorm, de bytes en de collectie van een bron liggen vast en staan niet op deze body.

retention zegt of het bewaarvenster van je organisatie dit document omvat: window laat het verwijderen zodra het ouder is dan het venster dat je beheerder instelde, keep houdt het tot iets het verwijdert. Een collectie die je zelf maakt staat standaard op keep en bepaalt met retention_default wat de documenten erin krijgen; de eigen collectie van je sleutel -- waar je uploads via /files en de bestanden van je tools terechtkomen -- staat op window. Eén document mag altijd afwijken van zijn collectie:

curl -s -X PATCH "$CORE_URL/knowledge/collections/col_2Pf7.../sources/src_9Qd2..." \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"retention": "keep"}'

DELETE .../sources/{source_id} legt een document in de prullenbak: het is meteen weg uit elke zoekopdracht en lezing, en zijn bytes blijven 30 dagen bewaard, tenzij je operator een ander aantal instelde. De eigen lijst van de collectie toont haar prullenbak, en terugzetten zet een document terug en leest het opnieuw in:

curl -s "$CORE_URL/knowledge/collections/col_2Pf7.../sources?trashed=true" \
    -H "Authorization: Bearer $CORE_KEY"
curl -s -X POST "$CORE_URL/knowledge/collections/col_2Pf7.../sources/src_9Qd2.../restore" \
    -H "Authorization: Bearer $CORE_KEY"

Een document in de prullenbak draagt deleted_at en purge_at. Een tweede DELETE verwijdert het definitief. Een gesynchroniseerd document dat je verwijdert, blijft in de prullenbak, ook als zijn bron het wijzigt: de nieuwe versie vervangt het daar onder een nieuw id, dus zoek het op in de lijst van de prullenbak voor je het terugzet. Een sleutel die ook files heeft, bereikt dezelfde prullenbak via /files (Een bestand verwijderen, terugzetten of definitief weggooien).

Een collectie synchroniseren met een systeem dat je hebt

In plaats van elke bron zelf aan te maken, geef je een collectie een connector en haalt Core de documenten op. Elk document wordt een gewone bron van die collectie, dus alles hierboven — indexen, zoeken, tools — werkt precies zoals bij een bron die je zelf hebt gepost.

Een collectie draagt één connector, en de soort ligt na schrijven vast: website (gecrawld via Firecrawl), sharepoint, gdrive, s3 en zendesk.

Firecrawl draait in de Verenigde Staten. Beperkt je sleutel waar aanroepen mogen draaien (policy.settings.regions_allow) tot plaatsen zonder us, dan mislukken de synchronisatie en de test van een website-connector met region_denied en wordt er niets verstuurd; noem vendor:firecrawl in policy.settings.region_exceptions om het toe te staan (beperkt je organisatie ook de regio's zonder us toe te laten, dan moet haar beleid hem ook noemen). Hetzelfde geldt voor de stap llama_parse, waarvan de dienst in de EU draait tenzij je operator een andere regio instelde: een sleutel die elders is vastgelegd, laat de bron mislukken met region_denied, tenzij hij vendor:llamacloud noemt. Een connector of bron die iemand in de console heeft ingesteld, en een zoekopdracht daar, volgen op dezelfde manier de regio's en uitzonderingen van je organisatie (Aanroepen binnen je regio's houden).

curl -s -X PUT "$CORE_URL/knowledge/collections/col_2Pf7.../connector" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"config": {"kind": "sharepoint",
                    "site_url": "https://contoso.sharepoint.com/sites/Docs",
                    "tenant_id": "8f1c...", "client_id": "b3d2...",
                    "client_secret": "<het applicatiegeheim>",
                    "exclude_path_regex": "^/Archief/"},
         "schedule_minutes": 1440}'

PUT schrijft de hele connector, dus een geheim dat je weglaat is een geheim dat je wist. PATCH wijzigt een deel: stuur de instellingen die je wilt en laat het geheim weg om het opgeslagen geheim te behouden, of stuur het mee om het te vervangen. Het leesmodel heeft helemaal geen veld voor een credential — has_credentials zegt alleen of er een is opgeslagen — en kind kun je niet met een PATCH wijzigen; vervang de connector met een PUT.

include_path_regex en exclude_path_regex filteren wat wordt meegenomen, en moeten expressies zijn die RE2 accepteert: een backreference of een lookaround geeft 422 met het veld erbij. Afbeeldingen, video, archieven en andere binaire bestanden worden bovenop jouw regel altijd overgeslagen. schedule_minutes is het aantal minuten tussen geplande synchronisaties, of null voor alleen handmatig.

In plaats van schedule_minutes kan een connector ook schedule_cron dragen: een expressie van vijf velden -- minuut, uur, dag-van-de-maand, maand, dag-van-de-week -- gelezen in schedule_timezone (een IANA-zone, standaard UTC). De twee sluiten elkaar uit: beide zetten geeft 422 met beide velden erbij.

curl -s -X PATCH "$CORE_URL/knowledge/collections/col_2Pf7.../connector" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"schedule_minutes": null,
         "schedule_cron": "0 6 * * 1-5", "schedule_timezone": "Europe/Amsterdam"}'
curl -s -X POST "$CORE_URL/knowledge/collections/col_2Pf7.../connector/test" \
    -H "Authorization: Bearer $CORE_KEY"

curl -s -X POST "$CORE_URL/knowledge/collections/col_2Pf7.../connector/sync" \
    -H "Authorization: Bearer $CORE_KEY"

curl -s "$CORE_URL/knowledge/collections/col_2Pf7.../connector/runs" \
    -H "Authorization: Bearer $CORE_KEY"

test benadert het systeem en haalt hoogstens één item op, op het requestpad, en slaat niets op. sync antwoordt 202 met een run in de wachtrij, of 409 als er al een loopt; POST .../connector/sync/cancel stopt de lopende run tussen twee items. Elke run draagt discovered, added, updated, removed, skipped en failed, bijgewerkt tijdens de run, en een vaste error_code als zij mislukte — een van auth_failed, not_found, rate_limited, egress_denied, too_many_items, provider_error, cancelled, deadline_exceeded, region_denied of tenant_unavailable (de organisatie, of een organisatie erboven, is opgeschort). Het eigen foutbericht van de leverancier wordt nooit teruggegeven. Een document dat een index toch niet kan inlezen wordt niet opgeslagen: een document groter dan wat de index leest (standaard 100 MiB voor tekst en JSON, en 25 MiB voor elk ander type, HTML-pagina's en afbeeldingen inbegrepen), of dan de eigen synchronisatiegrens van de installatie (standaard 50 MiB), telt mee in skipped, nooit in failed.

De connector verwijderen (DELETE .../connector) laat de bronnen die zij heeft aangemaakt in de collectie staan. Een connector die je daarna op die collectie instelt neemt ze over: wat zij ook aanbiedt werkt zij bij, wat zij niet aanbiedt verwijdert zij. Bronnen die je zelf hebt aangemaakt dragen geen connector-uri, dus een synchronisatie verwijdert ze nooit.

De console koppelt en ontkoppelt niet los van de collectie: daar kies je de bron wanneer je de collectie aanmaakt, en die keuze ligt vast — zie Collecties .

2. Een index maken

curl -s -X POST "$CORE_URL/knowledge/indexes" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "handboek", "collection_ids": [{"collection_id": "col_2Pf7..."}]}'

Een index kan meerdere collecties bevatten. Een collectie kan ook meerdere indexen voeden, elk met andere splitsinstellingen. Voeg "metadata_filter": {"afdeling": "finance"} toe aan een collectiekoppeling om alleen overeenkomende bronnen te selecteren.

Bronverwerking gaat door nadat het verzoek is beantwoord. Controleer GET /knowledge/indexes/{id}/sources voor queued, processing, ready of failed. Bij fouten staat er een vaste error_code. Bestaande bronnen worden niet automatisch verwerkt zodra je hun collectie koppelt. Start POST /knowledge/indexes/{id}/reindex om ze op te nemen en volg de voortgang met GET /knowledge/indexes/{id}/runs.

Een pdf die de parseerdienst weigert, wordt uit zijn tekstlaag gelezen. Stuurt de stap llama_parse een pdf naar de parseerdienst en weigert de dienst die om een reden die een nieuwe poging niet oplost, dan leest Core de eigen tekstlaag van de pdf. De bron is dan ready en doorzoekbaar, en haar rij in GET /knowledge/indexes/{id}/sources draagt parse_fallback met de reden: credits_exhausted, api_key_rejected, api_key_missing of file_rejected; het veld is null als de dienst het document heeft gelezen. Werkt de dienst weer, start dan een volledige herindexering van de index om deze documenten alsnog door de dienst te laten lezen; een pdf met file_rejected wordt opnieuw geweigerd. Een document dat de dienst voor deze index al heeft gelezen, wordt nooit door zijn tekstlaag vervangen: een herindexering tijdens een storing mislukt voor dat document, en de fragmenten die de dienst maakte blijven doorzoekbaar. Een time-out, een rate limit of een serverfout leidt nooit tot de tekstlaag, en een pdf zonder leesbare tekstlaag evenmin: de bron mislukt dan met haar error_code.

Een groot bestand wordt in delen geïndexeerd. Een tekstbestand -- platte tekst, Markdown, CSV of JSON -- een PDF, en een Word-, PowerPoint- of Excel-bestand, groter dan 4 MiB en tot 100 MiB, wordt deel voor deel gelezen en ingebed: een PDF per reeks pagina's, een Word-bestand per reeks alinea's, een presentatie per reeks dia's, een werkmap per reeks rijen, en een tabel die over twee delen valt houdt aan beide kanten haar kopregel. De fragmenten worden samen doorzoekbaar zodra het laatste deel binnen is; tot dan antwoordt een zoekopdracht met wat de index daarvoor had. Terwijl zo'n bestand opnieuw wordt geïndexeerd, kan een zoekopdracht een paar minuten lang zowel de oude als de nieuwe fragmenten vinden, dus in die tijd kunnen resultaten dubbel voorkomen. Een groter bestand, en een bestand van een ander type boven 25 MiB, wordt opgeslagen en is te downloaden, maar geen index neemt het op: het heeft in geen enkele index een status en geen error_code. Alleen een index die zijn collectie later via een herindexering opneemt, toont het, als failed met too_large. Een tekst-, PDF-, Word-, PowerPoint- of Excel-bestand boven 25 MiB in een index waarvan de pipeline meer doet dan laden, parsen, splitsen, inbedden en opslaan -- een samenvatting, verrijking, tabellen, afbeeldingen, een graaf of geheugen -- mislukt met too_large_for_pipeline; net als een Word-, PowerPoint- of Excel-bestand boven 25 MiB in een pipeline zonder parse-stap, die zo'n bestand tot 25 MiB laat mislukken met needs_parse. De standaardpipeline leest ze allemaal. Een Word-, PowerPoint- of Excel-bestand waarvan de inhoud uitgepakt veel groter is dan het bestand zelf, mislukt met too_large, en een dat niet te lezen is met parse_error. Een kennisgraaf leest een bron in zijn geheel, dus neemt er een tot 25 MiB en laat een grotere mislukken met too_large. Dit zijn de standaardlimieten van de installatie; de beheerder kan de 100 MiB verhogen.

Elk antwoord met een index bevat reindex_required: true vanaf het moment dat je het model, de pipeline of de gekoppelde collecties wijzigt, totdat een volledige herindexering is afgerond. Een collectie weghalen of een metadata_filter vernauwen werkt meteen door in zoekopdrachten; de herindexering verwijdert wat achterblijft. Een herindexering wordt in rekening gebracht zoals elk ander gebruik van de modellen. Een run die liep toen je de index wijzigde, eindigt met status cancelled en error_code superseded; zolang Core hem nog niet heeft beëindigd, antwoordt een nieuwe herindexering met 409.

3. De index doorzoeken

curl -s -X POST "$CORE_URL/knowledge/indexes/idx_8Bd3.../search" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"query": "hoe lang heb ik om te declareren", "top_k": 5,
         "metadata": {"afdeling": "finance"}}'

Zoeken combineert ranglijsten op trefwoorden en betekenis. Daarmee kun je zowel exacte termen als verwante formuleringen vinden. Het filter metadata beperkt de resultaten tot bronnen met overeenkomende waarden.

Core embedt je vraag voor de ranglijst op betekenis, en die embedding-tokens worden gemeten op je sleutel (Verbruik). Heeft je sleutel of je organisatie de maandlimiet bereikt, dan antwoordt de zoekopdracht met 429 en Retry-After, tenzij het embeddingmodel niets kost.

Een werkblad of een bestand met records wordt één tabel per werkblad, body of tabel in het document, elk met een eigen afgeleid schema. GET /knowledge/collections/{id}/sources/{sid}/tables somt ze op; POST /knowledge/collections/{id}/sources/{sid}/query filtert er één op velden, met table_id erbij — bij een bron met precies één tabel mag die weg. Een onbekend veld geeft 422 met de veldnaam.

Een sleutel waarvan het beleid een agent vereist (settings.agents_required) kan deze routes niet gebruiken, en ook POST /knowledge/retrieval-tools/{id}/query en POST /knowledge/pipelines/{id}/test niet: elk antwoordt 403 voordat er iets wordt opgezocht, omdat het je kennis leest buiten een beurt en dus buiten elke agent. Zijn agents zoeken nog wel, binnen hun beurten.

Een beleid waarvan de regels beperken welke modellen een sleutel mag aanroepen, moet ook het embedding-model toestaan: zonder die toestemming antwoorden deze routes 403 met The embedding model is not allowed for this key., en draait een beurt zonder in je kennis te zoeken.

Afbeeldingen gevonden met woorden

Een index kan afbeeldingen doorzoekbaar maken: een PNG of JPEG die je uploadt, en de afbeeldingen in een PDF of een Word-bestand. Kies de pipeline ingest/with-images als je de index maakt:

curl -s -X POST "$CORE_URL/knowledge/indexes" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "rapporten", "collection_ids": [{"collection_id": "col_2Pf7..."}],
         "ingest_pipeline": {"template": "ingest/with-images"}}'

De tekst wordt geïndexeerd zoals met de standaardpipeline, en elke afbeelding -- hooguit 50 per document, geen groter dan 20 MB -- wordt als afbeelding ge-embed. Een tekstvraag vindt hem dan: "een staafdiagram van de omzet" kan het diagram zelf opleveren. De index heeft een embeddingmodel nodig dat afbeeldingen leest; heeft de installatie er geen, dan geeft een pipeline maken van dit sjabloon 422, en faalt een document in een index waarvan het model geen afbeeldingen leest met image_embedding_unavailable. Een index maken of wijzigen zodat hij deze pipeline draait op een embeddingmodel dat geen afbeeldingen leest, geeft 422. Een afbeelding die je in een collectie uploadt, wordt opgeslagen welke pipeline de indexen ook draaien; een index op een tekstpipeline zet hem op failed met unsupported_media_type. Een gescande PDF zonder tekst wordt opgeslagen om zijn afbeeldingen. Afbeeldingen in bestanden die een connector synchroniseert worden overgeslagen, zoals op het platform; afbeeldingen in de PDF's en Word-bestanden die hij binnenhaalt worden gelezen.

Een paar grenzen voorkomen dat één document te veel werk vraagt: hooguit 200 afbeeldingen per document worden bekeken, een afbeelding groter dan 5000 bij 5000 pixels wordt overgeslagen, en de inhoud van de afbeelding zelf bepaalt het type, hoe het bestand ook heette.

Een afbeelding komt terug als treffer met een lege text en een veld image, nooit de bytes:

{"chunk_id": "chk_...", "source_id": "src_...", "text": "",
 "image": {"media_type": "image/png",
           "path": "/knowledge/indexes/idx_8Bd3.../chunks/chk_.../image"}}

Haal de afbeelding op door path achter de basis-URL van de API te zetten, met dezelfde sleutel:

curl -s "$CORE_URL/knowledge/indexes/idx_8Bd3.../chunks/chk_.../image" \
    -H "Authorization: Bearer $CORE_KEY" -o diagram.png

Dat geeft 404 overal waar de zoekopdracht je de treffer niet had kunnen tonen. Is het document sinds het indexeren zo veranderd dat de afbeelding niet meer op haar plek staat, dan geeft het ook 404 en vraagt het je opnieuw te indexeren. De tekst in een afbeelding leest deze pipeline niet; een index waarvan de pipeline de stap llama_parse bevat, leest de tekst in een geüploade afbeelding, zoals bij een gescande pagina.

4. Het model laten zoeken

Een zoektool is een zoekpipeline met bronnen: hij draait één versie van een pipeline van jezelf, en bereikt precies de bronnen die die pipeline noemt -- een index, een collectie. Je maakt dus eerst de pipeline, registreert daarop de tool en biedt de tool daarna op naam aan. Wat een tool mag bereiken bepaal jij; het model kiest alleen de vraag.

Begin bij een sjabloon en vul de index in. De bronblokken van een sjabloon zijn leeg, en een kopie komt er pas als elk ervan iets noemt:

curl -s -X POST "$CORE_URL/knowledge/pipelines/from-template" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"template": "retrieval/hybrid",
         "name": "handboek-zoeken",
         "sources": {"index": "idx_8Bd3..."}}'

Je krijgt de nieuwe pipeline terug met zijn versie 1. Registreer de tool op die versie:

curl -s -X POST "$CORE_URL/knowledge/retrieval-tools" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "handboek",
         "description": "Doorzoek het interne handboek.",
         "pipeline": {"pipeline_id": "pip_...", "version": 1},
         "filter_keys": ["folder"]}'
{
  "model": "<een model-id>",
  "tools": [{ "type": "tool", "name": "knowledge__handboek" }],
  "input": "Hoe lang heb ik om te declareren?"
}

Een tool zet altijd een versie van een eigen pipeline vast: een sjabloon op de tool zelf noemen geeft 422, want een sjabloon bereikt niets tot je het invult. Een nieuwe versie van de pipeline verandert niets tot je de pipeline van de tool er met PATCH op zet. Elke index en collectie die de versie noemt wordt tegen je sleutel gecontroleerd als je de versie schrijft, en opnieuw als je hem vastzet. De tool heeft geen eigen chatmodel — elke LLM-stap van zijn pipeline noemt die zelf, en valt anders terug op het model van de beurt. Een toolnaam is kleine letters en maximaal 48 tekens; het model ziet knowledge__<naam>.

De pipeline leest Met het bronblok Sjablonen
een index, op trefwoord en op betekenis index retrieval/hybrid, retrieval/keyword, retrieval/semantic en de andere
hele documenten achter de beste passages collection, aangesloten op fetch_documents retrieval/hybrid-documents
documenten van een collectie die geen index selecteert, op de woorden in hun titel collection, aangesloten op collection_lookup --
één tabel van een werkmap, een CSV of een JSON-body, per veld bevraagd collection of index, aangesloten op tabular_query retrieval/tables

Elke sleutel in filter_keys wordt één optioneel tekstargument op de tool, dus de tool hierboven wordt aan het model aangeboden als {"query", "top_k", "folder"}. De waarde die het model meegeeft geldt binnen het eigen metadata_filter van de tool: die versmalt de vraag en kan hem nooit verbreden, en een waarde die het eigen filter tegenspreekt levert geen treffers op. Met de echo-provider kun je die aanroep zelf sturen:

{
  "model": "<een echo-model-id>",
  "tools": [{ "type": "tool", "name": "knowledge__handboek" }],
  "input": "call:knowledge__handboek {\"query\": \"declareren\", \"folder\": \"hr\"}"
}

Een tool die geen filter_keys noemt wordt aangeboden als {"query", "top_k"}. Een tool op retrieval/tables biedt ook table_query aan: het model kiest een van de tabellen die de tool bereikt -- elk beschreven met de titel van het document, de naam van de tabel en de kolommen -- en geeft een vraag per veld over die tabel mee. Een argument dat de tool nooit aanbood, een tabel die hij niet aanbiedt en een vraag die niet bij de kolommen van de tabel past komen bij het model terug als "Tool arguments were not valid." of "Unknown table.", niet als een mislukte beurt.

Eén zoektool registreer je nooit. Elke beurt van je sleutel krijgt ook knowledge__uploads aangeboden, een zoekopdracht over de eigen collectie van je sleutel -- de bestanden die je uploadde en de bestanden die je tools maakten. Je noemt hem niet in tools[] en er valt niets aan te maken; een forbid-regel van de soort tool in je beleid die knowledge__uploads noemt haalt hem weg, net als het uitzetten van index_uploads in de files-configuratie van die sleutel, op POST /tenants/{id}/keys of PATCH /tenants/{id}/keys/{key_id} (de console heeft er vandaag geen veld voor).

Welke regels gelden als het model voor een persoon zoekt. Noemt een verzoek een persoon (Bouwen voor personen), dan komen in één zoekopdracht meerdere regels samen:

  • Elke index, collectie en graaf die de pipeline van de tool noemt gaat langs de regels van de soort knowledge_index, knowledge_collection en knowledge_graph, voor die persoon en evengoed voor een verzoek dat niemand noemt. Weigeren die er een, dan wordt een verzoek dat de tool aanbiedt geweigerd met 404, alsof de tool niet bestaat, of het de tool nu bij naam noemt of via een toolset. De weigering komt in het auditspoor.
  • De treffers worden versmald door de regels van de soort knowledge_source, die de labels van elke bron lezen, en voor uploads en artefacten door die van de soort stored_file. De regels over collecties en indexen halen nooit losse treffers weg.

Wil je een zoektool voor sommige mensen helemaal onbereikbaar maken, label dan de zoektool en schrijf een regel van de soort tool (Labels op alles). Geen regel over knowledge_collection verbergt de eigen collectie van je sleutel, waar haar uploads heen gaan, voor die sleutel, en geen regel over knowledge_index verbergt de index die Core erover bijhoudt voor knowledge__uploads: die zoekopdracht gaat langs geen van beide soorten, dus het eerste punt hierboven geldt er niet voor. Hij doorzoekt de eigen collectie van je sleutel: een PATCH die de labels of collection_ids van die index zou wijzigen, krijgt 409, vanaf een sleutel en vanuit de console, tenzij hij de index terugzet naar alleen die collectie, zonder filter en zonder labels. Wil je sommige mensen erbij weghouden, schrijf dan een forbid-regel van de soort tool die knowledge__uploads noemt. Je kunt hem ook een label geven met PUT /labels/tool/knowledge__uploads, vanaf elke sleutel of vanuit de console, en de regel over dat label schrijven; het label is van je organisatie en geldt voor de uploadtool van elke sleutel. De regels over knowledge_source en stored_file versmallen de treffers nog steeds. Een verzoek dat die index op id leest of doorzoekt, op de routes onder /knowledge/indexes, gaat wel langs de regels over knowledge_index, net als elk ander.

Eigen zoekpipelines

Bouw een eigen pipeline als geen sjabloon is wat je wilt, en noem zijn bronnen als blokken: een blok index voor elke index die een zoekstap leest (een zoekstap heeft er minstens één nodig), een blok collection voor elke collectie waarvan een stap de documenten heel leest. De naam en omschrijving van de tool vertellen het model wanneer het hem moet gebruiken:

curl -s -X POST "$CORE_URL/knowledge/pipelines/from-template" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"template": "retrieval/hybrid-documents",
         "name": "handboek-heel",
         "sources": {"index": "idx_8Bd3...", "collection": "col_2Pf7..."}}'

Test de tool buiten een modelbeurt met POST /knowledge/retrieval-tools/{id}/query. Je krijgt dezelfde zoekuitvoer terug, en hij neemt dezelfde table_query aan die een model zou meegeven. POST /knowledge/pipelines/{id}/test draait een versie nog voor een tool hem vastzet. Bekijk ingebouwde pipelines met GET /knowledge/pipeline-templates en beschikbare stappen met GET /knowledge/pipeline-components. De componentcatalogus toont voor welke modelafhankelijke stappen een model is ingesteld. Een niet-beschikbare component kiezen geeft 422.

Maak eigen pipelines aan met POST /knowledge/pipelines. Net als prompts hebben pipelines onveranderlijke versies. Een tool zet de gebruikte versie vast.

GET /knowledge/pipelines/{id}/models toont elke stap van de nieuwste versie die een chatmodel aanroept: de node_id en node_type van de stap, het model dat hij noemt, waar dat model draait (region, country) en of het beleid van je sleutel het toelaat (allowed). Een stap die je beleid weigert, faalt met policy_denied zodra hij draait; hier vind je hem vooraf. Een stap die geen model noemt, gebruikt dat van de beurt en staat erin met model: null. Een ingest-pipeline draait onder de sleutel die de bron aanmaakte, dus daar is allowed het antwoord voor bronnen die jouw sleutel aanmaakt. Een verzegeld gedeelde pipeline toont zijn modellen zonder stap-id's of -typen.

Wil je een pipeline binnen één map of één label houden, geef hem dan een filter-stap: input -> filter -> semantic_search -> output, met als metadata_filter een JSON-object dat tegen de metadata van een fragment wordt gematcht, bijvoorbeeld {"folder": "hr"}. Het sjabloon retrieval/filtered is precies die vorm. De stap versmalt elke vraag die de pipeline draait, ook de varianten van een herschrijfstap, en twee stappen stapelen waarbij de laatste een gedeelde sleutel wint. De eigen metadata_filter van een zoekstap wint van wat de stap zette, en het filter van de tool zelf geldt nog boven beide: een andere waarde vragen voor een sleutel die de tool vastzet, levert niets op in plaats van een bredere zoekopdracht.

Resultaten en bronverwijzingen

Zoekresultaten bevatten genummerde passages en bronvermeldingen. Opgehaalde passages zijn een toolresultaat: het model gebruikt ze voor het antwoord en ze krijgen geen instructie-autoriteit. Het model krijgt te horen dat het gegevens zijn als je sleutel trust_envelope heeft.

De lijst citations van het resultaat bevat één vermelding per passage, in de volgorde waarin de passages genummerd zijn: kind: "file", het id van het tekstblok als ref -- of het eigen id van het document, voor een heel document dat een collectieblok las -- en de titel van de bron. Schrijft het model daarna [n] in zijn antwoord, dan lost Core die markering op tegen diezelfde nummering en geeft die terug als annotatie bij de tekst van de assistent, zodat je de zin zelf kunt markeren in plaats van een lijst onderaan. Een tweede zoekopdracht in dezelfde beurt nummert door waar de eerste ophield ([4] na drie passages), zodat het model nooit één nummer voor twee bronnen ziet. Een markering die een passage noemt die geen zoekopdracht teruggaf, blijft gewone tekst (waar de bronnen van een antwoord staan).

Mislukt het zoeken tijdens een beurt, dan geeft de tool een vaste melding, zoals "Retrieval failed.", "Retrieval budget exhausted." of "Denied by policy.". De beurt gaat door.

Een aangehaalde passage lezen

Een bronvermelding noemt een passage bij het id van het tekstblok: ref in de citations van een resultaat, file_id in een file_citation-annotatie, chunk_id in een zoekresultaat. Wil je de passage zelf tonen, lees dan het tekstblok:

curl -s "$CORE_URL/knowledge/chunks/chk_..." -H "Authorization: Bearer $CORE_KEY"
{
  "id": "chk_...",
  "index_id": "idx_8Bd3...",
  "source_id": "src_...",
  "collection_id": "col_...",
  "source_title": "Declaratiebeleid",
  "position": 4,
  "text": "Declaraties moeten binnen 30 dagen worden ingediend ...",
  "truncated": false,
  "image": null
}

text is hoogstens 32.768 tekens lang, en truncated zegt of het tekstblok langer was. Een afbeelding heeft een lege text en een image-link, net als in een zoekresultaat. Een bronvermelding die een heel document noemt in plaats van een tekstblok -- een id dat niet met chk_ begint -- is geen tekstblok en krijgt 404.

Je leest een tekstblok overal waar je de zoekopdracht had kunnen doen die het vond: via een index waarin je sleutel mag zoeken, uit een bron die de persoon die je verzoek noemt mag lezen. Al het andere krijgt 404, net als een id dat nooit heeft bestaan, zodat de route nooit verraadt dat er een passage is die je niet mag zien. Een subject-token leest het als zijn persoon, zodat een browser de passages kan openen die zijn eigen antwoorden aanhaalden.

Verbruik

Indexeren registreert embedding-tokens, 258 per afbeelding. Zoeken registreert één retrieval per zoekstap, embedding-tokens voor de zoekvraag en modeltokens voor stappen die een model aanroepen. Hybride zoeken telt twee retrievals omdat het twee ranglijsten berekent. Zie Wat Core meet.