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_callitem withname: knowledge__handbookproves the agent searched. This is how an answer shows every tool Core ran for you. - The
file_citationwithfilename: Returns policyproves the answer rests on your document.indexis 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¶
- Knowledge: the parts, and when you choose an index or a graph.
- Knowledge through the API: source kinds, connected sources, filters and your own pipelines.
- Where an answer's sources are: showing the annotations in your own interface.
- Build for people: deciding which sources one person finds.
Next tutorial: Let an agent carry out an action with a tool.