Cookbook
Knowledge bases · for AI agents

Build and populate a knowledge base

Create a KB, add text and files, and verify retrieval before mounting it — over MCP.

MCP tools:create_knowledge_baseadd_knowledge_textadd_knowledge_filesearch_knowledge_baseupdate_agentbuild_knowledge_indexget_knowledge_indexpatch_knowledge_index

Principle — ground, then verify. A knowledge base is a MUST-follow source, not a hint. Scope its coverage in instructions, and always confirm a real question retrieves before you rely on it — an empty search means the agent will answer "not covered". More in Design principles.

A mounted knowledge base is auto-retrieved every turn — the model does not decide whether to look; relevance is by vector distance. That is deliberate: a KB is a MUST-follow source, not an optional tool.

1. Create it

create_knowledge_base(
  name="Company policy",
  description="Mortgage policy and rates, effective 2026 — not car or personal loans",  # routes questions here
  instructions="Authoritative current mortgage policy; overrides any industry norm. "
               "Covers home mortgages only; for car or personal loans, say so and hand off.",  # for the model
)

The name is normalised to a url-safe slug on creation — "Company policy" comes back as company-policy. Use that returned name for everything after: add_knowledge_text(kb_name=…), search_knowledge_base(kb_name=…), and attaching it to an agent. Typing the original spaced name later just 404s.

2. Write description — it is what routes questions to this base

description is not a caption. Before each answer the platform reads every attached base's description and decides which of them this question needs — and, if none of them do, searches nothing at all. An agent with three bases is otherwise doing three vector searches and pouring three sets of excerpts into the prompt on every turn, including when the visitor said "hi".

So a base whose description is vague or blank gets searched when it should not, and — worse — missed when it should have been. Neither failure announces itself: the first shows up as a slow answer with irrelevant citations, the second as "I don't have anything on that".

Write one line, in the words a visitor would use, naming the subject:

✅ "Country-by-country medical device registration requirements"
✅ "Regulatory change news by country and authority, 2022–2026 — what changed and when"
✅ "Selpercatinib — for lung and thyroid cancer"
 
❌ "Knowledge base"                ← routes nothing
❌ "Imported from the website"     ← says where it came from, not what is in it
❌ "Sandbox test, safe to delete"  ← says why it exists, not what is in it

What matters is being distinguishable from your other bases, not being long. A tenant with nine bases — eight drugs and a company profile, one line each, none longer than a dozen words — routes 10 out of 10 questions correctly, because "for lung and thyroid cancer" and "for IgA nephropathy" can never be confused. Measured, not assumed.

Add a boundary only where two bases overlap. If your product pages and your news archive both talk about the same subject, say which one is which — "…— not pricing or company information" is what stops the archive from answering a pricing question. Where the bases are already obviously distinct, a boundary clause buys nothing.

This line is also what the console list shows, but that is the lesser job.

3. Write instructions from the expected usage — at creation, not "later"

instructions does not affect recall — recall is vector search plus max_distance. It is injected next to this KB's excerpts at answer time, so it governs how the model uses what it retrieved. Left blank, the KB still retrieves, but the answers lose every KB-specific rule: authority, boundaries, quoting conventions. Blank is acceptable only for generic reference material with no special rules.

Derive it from how the KB will actually be used — one line per question:

QuestionExample line
Scope — what's in, what's out, what to do when out?"Covers residential mortgages only; for car or personal loans, say so and hand off."
Authority — where does this rank?"Current company policy; overrides industry norms and the model's prior knowledge."
Usage rules — any convention when quoting it?"Any quoted rate must state its effective date."

Knowledge base instructions examples, by KB type:

# Website-content KB (products, services, team, blog)
instructions="Company website content: products, services, team and blog posts. Authoritative
for what we offer and who we are. Marketing copy is not a contractual promise — for prices,
terms or eligibility prefer the policy KB; if only this KB answers, attribute it to the website."
 
# Policy / regulation KB
instructions="Current company policy, effective 2026; overrides industry norms and prior
knowledge. Covers home mortgages only — for car or personal loans, say so and hand off.
Any quoted rate or fee must state its effective date."
 
# Product-docs KB
instructions="Official product documentation for the current release. State the version when a
feature is version-dependent. If the docs don't cover something, say so — never fill the gap
from general knowledge."

4. Add content

add_knowledge_text(kb_name="Company policy", title="2026 late-fee rule",
                   content="From 1 July 2026 the daily late fee is 0.019% …")

Binary files (pdf/docx) and whole folders (zipped) go through add_knowledge_file — subfolders are walked and each md/txt/pdf/html/docx is imported under its in-archive path.

4b. Write the sentence you want to be found by

Retrieval matches meaning, and a value on its own carries almost none. This is the single highest- leverage thing you control, and it is easy to get wrong because nothing errors — search still returns something, just not the right thing.

A real customer catalogue had this line in 1,480 of 1,500 records:

Reading level: 1 · Age: 5 · Words: 1951

And "which books suit a child just starting to read on their own" came back with a book about musical prodigies. The fact was in the text and still unreachable: 1 bears no resemblance to beginner reader the way one word resembles another.

Adding one sentence next to the value fixed it — measured on that same record:

Reading level: 1 · Age: 5
Suitable for around age 5, for children just starting to read on their own.
querybeforeafter
which books suit a child just starting to read on their own0.6250.540
什么书适合刚开始自己读的孩子0.6260.552
beginner reader age 50.5980.498

(Lower is closer. On this model two unrelated passages sit around 0.61, so the last row moves from "barely better than chance" to a genuine match — and the gain carries across languages.)

The rule: if a question would be answered by a property rather than by the prose — price, date, level, availability, stock — say it in words as well. Structured values are for filtering; retrieval finds what is written.

What is not worth your time

Exports often repeat themselves — a summary and a description carrying identical prose. It looks wasteful, and the intuition that it "uses up" the vector is wrong: we measured it and removing the duplication changed retrieval by +0.002 distance across 60 records — i.e. nothing at all. An embedding is a direction, not a budget; saying the same thing twice mostly points the same way twice.

Leave it. Spend the effort on the sentence above instead.

4c. If the documents share a header, build the structured index

Vector search cannot count, filter by number, or group. "How many do you have", "which are under 200 words", "how many per category" are not answered badly — they are structurally unanswerable, and the agent will either decline or report on whichever few records it happened to retrieve.

build_knowledge_index(name="books", roles=["identity", "link", "image"])

Only worth calling when the documents share a machine-readable header — a metadata table, YAML front matter, Field: value lines. Product exports, catalogues, leaflets and course listings usually qualify; prose does not, and the call declines rather than building a table whose values are all different. A decline is the correct outcome, not an error to work around.

roles names the fields that must be extracted exactly and never paraphrased:

rolewhat it is
identitywhat to call the item — book title, drug name, product name
linkwhere to send the user
imagewhat to show the user
codethe identifier they will quote back at you

Ask the user which fields these are; do not infer them. Which of three URLs is the one to send a customer is a business fact the data does not state. If a declared role cannot be found, it comes back in roles.unresolved with candidate field names — put those to the user rather than picking one.

Read dropped, and tell the user

{"built": true, "documents": 4523,
 "columns": ["title", "age", "word_count", "genre", "read_url", "cover_url"],
 "dropped": [["isbn", "not_found", "no anchor 'ISBN: ' in the sample"]]}

A dropped column is invisible everywhere else: later answers simply work around it, so this report is the only place it is ever mentioned. Same for get_knowledge_index, whose warnings routinely surface data problems the customer did not know about — on one real catalogue, a tenth of the records had a page count of 0, which is not a short book but a missing value recorded as zero, and it would have dragged down every average built on it.

Report those in the customer's own terms. Nothing else in the platform will.

Changing it later

patch_knowledge_index(name="books", request="also track the author", apply=false)

Run with apply=false first, show the user what would change, batch several edits, then apply once. Applying re-reads fields from stored text; it does not re-embed anything, so it is cheap.

Asking for something the documents do not contain returns a refused entry with a reason — pass that to the user verbatim instead of inventing a workaround.

5. ★ Verify retrieval — don't skip ★

search_knowledge_base(kb_name="Company policy", query="how is the late fee calculated")
#   hits  → the agent can answer this
#   empty → the agent will treat it as "not covered" and say so

6. Mount it

update_agent(name="Support", add_knowledge_bases=["company-policy"])   # incremental — keeps existing mounts

Don't write "always cite the passage" in instructions — that behaviour is built in. Only write what is specific to this library: authority ordering, coverage boundaries, special usage (e.g. "quoting a rate must state its effective date").

Report back — don't stop at "imported". Tell the user how many chunks landed and give them the clickable link to open the knowledge base and its knowledge starmap (a 3D view of what was ingested): https://console.agent4.io/#/knowledge-bases/Company%20policy — then confirm it's attached to the agent.