Ga naar inhoud

Bouw een agent die knowledge én tools combineert

Aan het eind van deze tutorial beantwoordt de agent support een vraag uit het handboek en maakt hij in dezelfde beurt een ticket aan als het handboek geen oplossing biedt. Je hebt de tools in één toolset gezet, de agent geleerd in welke volgorde hij ze gebruikt, en gezien wanneer een kennisgraaf iets toevoegt.

Bouwstenen Agents, Kennis: index en graaf. Tools: toolsets. Inzicht: traces
Je hebt nodig een sleutel met de capabilities agents, knowledge en tooling; voor de trace bij de controle moet de opslag van traces aanstaan voor je organisatie (Een trace lezen)
Bouwt voort op Geef een agent kennis: de zoektool knowledge__handboek, INDEX_ID. Laat een agent een actie uitvoeren: de koppeling api__ticket_aanmaken

Een agent met één zoektool en een agent met één actie zijn elk eenvoudig. Samen ontstaat de vraag die deze tutorial beantwoordt: wie bepaalt wanneer de agent zoekt en wanneer hij handelt? Dat ben jij, op drie plekken: de omschrijving van elke tool, de instructie van de agent en de grens op het aantal toolrondes.

1. Zet de tools in een toolset

curl -s -X POST "$CORE_URL/toolsets" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "support",
         "description": "Wat de supportassistent nodig heeft: het handboek en tickets.",
         "tools": ["knowledge__handboek", "api__ticket_aanmaken"]}'
export TOOLSET_ID=tls_4Hw9...

Een toolset bevat patronen. Een exacte naam is ook een patroon; mcp__tickets__* zou elke tool van de MCP-server uit de vorige tutorial opnemen. Controleer wat de set nu bevat:

curl -s "$CORE_URL/toolsets/$TOOLSET_ID/tools" -H "Authorization: Bearer $CORE_KEY"

Je gebruikt een toolset in plaats van twee losse namen op de agent, omdat je de set later kunt uitbreiden zonder een nieuwe agentversie. Dat is ook de keerzijde: een agentversie legt de naam van de set vast en niet haar inhoud. Wil je dat een versie precies vastlegt welke tools er draaien, noem ze dan los.

Een toolset geeft geen rechten. Elke tool erin wordt bij elke aanroep getoetst aan de policy van de sleutel.

2. Leg de volgorde vast in de agent

curl -s -X POST "$CORE_URL/agents/$AGENT_ID/versions" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"model": "<een model-id>",
         "instructions": "Je bent de supportassistent van Noordwind. Antwoord kort en in het Nederlands.\n\n1. Zoek bij elke vraag eerst in het handboek en verwijs naar de passage met [n].\n2. Lost het handboek de vraag op, maak dan geen ticket.\n3. Lost het handboek de vraag niet op, of vraagt de klant om een ticket, maak er dan één aan. Zet in de omschrijving wat de klant meldde en wat het handboek erover zegt.\n4. Noem het ticketnummer en zeg wat de klant kan verwachten.",
         "temperature": 0.2,
         "max_tool_rounds": 4,
         "tools": [{"type": "toolset", "name": "support"}]}'

De instructie beschrijft een werkwijze: eerst zoeken, dan beslissen, dan handelen. Zo staat de volgorde op één plek, en zie je in een trace welke stap een beurt oversloeg.

max_tool_rounds begrenst hoe vaak het model in één beurt tools mag aanroepen. Deze taak heeft twee rondes nodig: zoeken en een ticket aanmaken. Vier laat ruimte voor een tweede zoekvraag en voorkomt dat een beurt blijft doorzoeken. Bereikt een beurt de grens, dan eindigt ze met status: incomplete.

3. Test de weg zonder ticket

curl -s -X POST "$CORE_URL/responses" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"agent": {"id": "support"},
         "input": "Ik heb de doos weggegooid. Kan ik mijn bestelling nog terugsturen?"}'

Het handboek zegt dat een retour in de originele verpakking moet zitten. Het antwoord hoort dus één mcp_call te bevatten, voor knowledge__handboek, en geen ticket.

4. Test de weg met ticket

curl -s -X POST "$CORE_URL/responses" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"agent": {"id": "support"},
         "input": "Mijn windmeter gaf na zes weken geen meting meer. Ik wil dat iemand hiernaar kijkt."}'

Het handboek noemt de retourtermijn van 30 dagen en zegt niets over een defect na die termijn. De klant vraagt bovendien om hulp van een persoon. Het antwoord hoort twee toolaanroepen te bevatten.

Controleer of het werkt

Het antwoord op stap 4 (ingekort):

{
  "id": "resp_0KQ2mR...",
  "status": "completed",
  "output": [
    { "type": "mcp_call", "name": "knowledge__handboek", "server_label": "knowledge",
      "status": "completed" },
    { "type": "mcp_call", "name": "api__ticket_aanmaken", "server_label": "api",
      "arguments": "{\"onderwerp\": \"Windmeter defect na zes weken\", \"omschrijving\": \"...\"}",
      "status": "completed" },
    { "type": "message", "role": "assistant",
      "content": [{ "type": "output_text", "text": "De retourtermijn van 30 dagen is verstreken [1]. Ik heb ticket T-1044 aangemaakt ..." }] }
  ]
}

De volgorde in output is de volgorde waarin de agent werkte. De trace laat per ronde zien wat er gebeurde:

curl -s "$CORE_URL/responses/resp_0KQ2mR.../trace" -H "Authorization: Bearer $CORE_KEY"

Elke regel is één modelaanroep, met de toolaanroepen waar het model om vroeg onder tool_calls. Lees de regels van boven naar beneden om te zien in welke ronde de agent zocht en in welke hij het ticket aanmaakte. Zoekt de agent niet, of maakt hij te snel een ticket, dan lees je in GET /traces/{call_id} onder document.interpretation welke tools het model kreeg aangeboden en wat het antwoordde (Een trace lezen).

Wanneer een kennisgraaf erbij hoort

Voor het handboek heb je geen graaf nodig. Een index beantwoordt "welke passage gaat hierover", en dat is wat een handboek vraagt.

Een graaf verdient zijn plek als een vraag over een verband gaat dat niet in één passage staat. Noordwind verkoopt producten van tientallen leveranciers, en garantie loopt via de leverancier. De vraag "van welke leverancier komt de Windmeter WM-2, en wat zegt het handboek over garantie?" heeft twee soorten kennis nodig: een feit uit een lijst (wie levert wat) en een passage uit het handboek. Een index over de leverancierslijst vindt hooguit rijen die op de vraag lijken. Een graaf kent de relatie zelf.

Heb je zo'n lijst niet, sla dit deel dan over. Een graaf vraagt een schema en onderhoud, en voegt voor losse documenten niets toe.

Maak een collectie en een graaf die eruit leest. Het sjabloon graph/from-tables maakt uit elke rij van een tabel entiteiten en relaties, zonder model. Het leest de kolommen supplier en product als de types Company en Product met de relatie supplies; het schema hieronder gebruikt daarom die namen.

curl -s -X POST "$CORE_URL/knowledge/collections" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "leveranciers", "description": "Leverancierslijst van Noordwind"}'
export SUPPLIERS_ID=col_7Rk4...
curl -s -X POST "$CORE_URL/knowledge/graphs" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "leveranciers",
         "description": "Welke leverancier welk product levert.",
         "visibility": "shared",
         "schema": {
           "entity_types": [
             {"name": "Company", "description": "Een leverancier van Noordwind."},
             {"name": "Product", "description": "Een product dat Noordwind verkoopt."}],
           "relation_types": [
             {"name": "supplies", "description": "De leverancier levert het product.",
              "from": ["Company"], "to": ["Product"]}]},
         "collection_ids": [{"collection_id": "'"$SUPPLIERS_ID"'"}],
         "ingest_pipeline": {"template": "graph/from-tables"}}'
export GRAPH_ID=kg_5Wn1...

Zet de lijst erin. Een CSV-bestand met de kolommen supplier en product:

printf 'supplier,product\nVento BV,Windmeter WM-2\nVento BV,Regenmeter RM-1\nPolaris Instruments,Kompas K-7\n' > leveranciers.csv

curl -s -X POST "$CORE_URL/knowledge/collections/$SUPPLIERS_ID/sources/upload" \
    -H "Authorization: Bearer $CORE_KEY" \
    -F "file=@leveranciers.csv"

GET /knowledge/graphs/$GRAPH_ID/sources toont de bron met status: ready en een fact_count groter dan nul zodra de graaf is gevuld.

Laat de zoektool beide doorzoeken. Het sjabloon retrieval/hybrid-graph zoekt in een index en in een graaf en voegt passages en feiten samen:

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

Zet de bestaande zoektool op de nieuwe pipeline. Het id van de tool (rtl_...) lees je met GET /knowledge/retrieval-tools:

curl -s -X PATCH "$CORE_URL/knowledge/retrieval-tools/rtl_2Vc6..." \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"pipeline": {"pipeline_id": "pip_...", "version": 1},
         "description": "Doorzoekt het handboek klantenservice van Noordwind en weet welke leverancier welk product levert."}'

De tool heet nog steeds knowledge__handboek, dus de toolset en de agent veranderen niet. Vraag de agent nu "Van welke leverancier komt de Windmeter WM-2, en hoe werkt garantie?". Het resultaat van de zoektool bevat dan naast passages een feit zoals Vento BV -- supplies -> Windmeter WM-2.

Meer over schema's, eigen mappings en feiten uit lopende tekst halen staat in Grafen via de API.

Als het niet werkt

Je ziet Oorzaak Oplossing
404 op POST /responses de toolset support bestaat niet voor je sleutel controleer de naam met GET /toolsets
dezelfde 404, terwijl de toolset bestaat de policy van je sleutel weigert een index, collectie of graaf die de pipeline van een zoektool in de set noemt lees de regels over Kennisindexen, Kenniscollecties en Kennisgrafen met GET /key (Kennis via de API)
de agent krijgt maar één tool het andere patroon past op niets, of de policy van je sleutel staat die tool niet toe GET /toolsets/$TOOLSET_ID/tools, en de regels over Tools in GET /key
status: incomplete de beurt bereikte max_tool_rounds lees de trace; verhoog de grens of maak de instructie scherper
de agent maakt een ticket zonder te zoeken de instructie of de omschrijving van de tickettool laat dat toe zet de volgorde in de instructie, en in de omschrijving van de koppeling wanneer hij bedoeld is
een bron van de graaf is failed met no_schema of no_pipeline no_schema: de graaf heeft geen schema. no_pipeline: de pipeline die de graaf vastzette, is verwijderd stuur het schema, of zet een bestaande pipeline in ingest_pipeline, en lees opnieuw uit (Opnieuw uitlezen)
de graaf blijft leeg en GET .../sources toont geen bronnen de graaf heeft geen ingest_pipeline; hij leest dan geen enkele bron zet ingest_pipeline met PATCH en lees opnieuw uit
fact_count is 0 de kolommen heten anders dan supplier en product pas de kolomnamen aan, of maak een eigen pipeline met een eigen mapping (De pipeline kiezen)

Verder

  • Tools in de console: het overzicht, de pagina van een tool en hem proberen, en toolsets met hun regels.
  • Kennis: een index of een graaf.
  • Evaluaties via de API: de twee testvragen vastleggen als suite, zodat je bij elke nieuwe versie ziet of het gedrag standhoudt.
  • Een agent als tool: een deeltaak overlaten aan een tweede agent.

Volgende tutorial: Richt een nieuwe klantorganisatie in.