Skip to content

Give an agent knowledge from your own business information

At the end of this tutorial the agent support answers questions from Noordwind's handbook, with a citation in the answer. You have put the handbook in a collection, made an index over it and given the agent a search tool.

Building blocks Knowledge: collection, index, search tool. Agents and Tools
You need a key with the capabilities agents and knowledge, and CORE_URL and CORE_KEY set; for the trace in the check, trace storage must be on for your organisation (Read a trace)
Builds on Build your first agent: the agent support and AGENT_ID

The way from document to answer has four links. A collection keeps the documents. An index makes them searchable. A search tool gives a search over that index a name. The agent gets that tool and decides per question whether to search.

1. Create a collection

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

The answer is 201 with the collection; the id starts with col_.

export COLLECTION_ID=col_2Pf7...

You make a collection per kind of content because a key's policy allows or refuses collections by name. The handbook and, say, Noordwind's contracts therefore do not belong in the same collection.

2. Create an index over the collection

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

The spelling "'"$COLLECTION_ID"'" puts the value of the variable between quotes in the JSON. You can also type the id in yourself.

You create the index before you add documents. A source you put in the collection after that is processed by itself. Sources that were already there when you linked the collection are processed only after you send POST /knowledge/indexes/$INDEX_ID/reindex.

Without further fields the index uses your installation's default pipeline and default embedding model. For a handbook of text that is enough.

3. Put the handbook in

Text you already have as a string is sent as a source of kind text:

curl -s -X POST "$CORE_URL/knowledge/collections/$COLLECTION_ID/sources" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"kind": "text", "title": "Returns policy",
         "text": "A customer can return an order within 30 days of delivery. The product must be unused and in its original packaging. The customer pays the shipping cost of the return, except for a defective product. The purchase amount is refunded within 5 working days of receipt."}'

A file, such as a PDF or a Word document, goes in with one call:

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

Each source is one document and belongs to exactly one collection. When your customer's handbook lives in SharePoint, Google Drive or another system, the collection can fetch it from there itself (Sync a collection).

4. Wait until the source is processed

curl -s "$CORE_URL/knowledge/indexes/$INDEX_ID/sources" \
    -H "Authorization: Bearer $CORE_KEY"

Processing happens in the background. Repeat the call until every source is ready (abridged):

{
  "items": [
    { "source_id": "src_9Qd2...", "title": "Returns policy", "status": "ready", "chunk_count": 1 }
  ]
}

The statuses are queued, processing, ready and failed. A failed source carries a fixed error_code.

5. Search the index yourself

curl -s -X POST "$CORE_URL/knowledge/indexes/$INDEX_ID/search" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"query": "how long may a customer return an order", "top_k": 3}'
{
  "items": [
    { "chunk_id": "chk_0KQ...", "source_id": "src_9Qd2...", "score": 0.83,
      "text": "A customer can return an order within 30 days of delivery. ..." }
  ]
}

The value of score is an example. This step separates what you test: when a search does not find the passage here, the cause is the content or the index and not the agent.

6. Create a retrieval pipeline

A search tool runs a version of a pipeline of your own. You copy a built-in template for that and fill in which index it searches:

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": "handbook-search",
         "sources": {"index": "'"$INDEX_ID"'"}}'

You get the pipeline back with version 1.

export PIPELINE_ID=pip_...

retrieval/hybrid searches by keyword and by meaning and merges both rankings. That finds an exact term as well as a question in other words. You cannot name a template directly on a tool, because it reaches nothing until you have filled in the index.

7. Register the search tool

curl -s -X POST "$CORE_URL/knowledge/retrieval-tools" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "handbook",
         "description": "Searches the Noordwind customer service handbook: returns, delivery, payment and warranty.",
         "pipeline": {"pipeline_id": "'"$PIPELINE_ID"'", "version": 1}}'

The model sees this tool as knowledge__handbook. The description is what the model reads to decide when to search, so name the subjects the handbook covers. The tool is pinned to version 1 of the pipeline: a later version changes nothing until you put the tool on it.

In the console you make the collection, the index and the search tool under Knowledge (Collections, Indexes in the console, Search tools).

8. Give the agent the search tool

curl -s -X POST "$CORE_URL/agents/$AGENT_ID/versions" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"model": "<a model id>",
         "instructions": "You are the support assistant of Noordwind. Answer briefly. For every question about policy, search the handbook first and refer to the passage with [n]. If the answer is not in it, say so.",
         "temperature": 0.2,
         "tools": [{"type": "tool", "name": "knowledge__handbook"}]}'

This is a new version of the agent, so you send the whole definition. The instruction says when the agent must search and what it does when the handbook has no answer. Without that sentence a model is more likely to fill a gap with a guess of its own.

9. Ask a question

curl -s -X POST "$CORE_URL/responses" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"agent": {"id": "support"},
         "input": "How long do I have to send an order back, and who pays for shipping?"}'

Check that it works

The answer (abridged) holds two items in output:

{
  "status": "completed",
  "output": [
    { "type": "mcp_call", "name": "knowledge__handbook", "server_label": "knowledge",
      "status": "completed" },
    { "type": "message", "role": "assistant",
      "content": [{
        "type": "output_text",
        "text": "You can send an order back within 30 days of delivery [1]. ...",
        "annotations": [
          { "type": "file_citation", "file_id": "chk_0KQ...", "filename": "Returns policy", "index": 60 }
        ]
      }] }
  ]
}
  • The mcp_call item with name: knowledge__handbook proves the agent searched. This is how an answer shows every tool Core ran for you.
  • The file_citation with filename: Returns policy proves the answer rests on your document. index is the position of [1] in the text; the value here is an example.

A file_citation is there only when the model itself writes [n] in its answer. That is why the instruction from step 8 asks for it. When you see an mcp_call but no annotation, the agent searched and left the marker out; the answer then still rests on what the tool returned. Make the sentence about [n] in the instruction more insistent in that case.

When the mcp_call is missing, the model did not search. Read the trace of the turn then: GET /responses/{id}/trace and after that GET /traces/{call_id} show under document.interpretation.tools whether the tool was offered (Read a trace).

If it does not work

You see Cause Fix
404 on the routes under /knowledge the capability knowledge is off on your key or in your organisation's ceiling ask your administrator to switch it on (Capabilities)
a source stays out of the index the source was already in the collection when you made the index POST /knowledge/indexes/$INDEX_ID/reindex
a source is failed the error_code says why, for example a file type the pipeline does not read see Ingestion status
403 with The embedding model is not allowed for this key. your key's policy narrows models and does not allow the embedding model have the embedding model added to the rule over Models (Keys)
no mcp_call in the answer, not even after a clear question your key's policy does not allow the tool; Core then does not offer it and the turn runs without it the trace shows the tool under document.interpretation.tools with denied_by_policy; read the rules over Tools with GET /key
404 on POST /responses once the agent names the tool the name knowledge__handbook does not exist in your organisation check the name with GET /tools?source=knowledge
the same 404, while the name is right your key's policy refuses an index, collection or graph the tool's pipeline names read the rules about Knowledge indexes, Knowledge collections and Knowledge graphs with GET /key; the refusal is in the audit trail (Knowledge through the API)
422 when registering the tool you named a template instead of a pipeline version of your own make the pipeline from step 6 first

Read on

Next tutorial: Let an agent carry out an action with a tool.