NEWpromptev for Android and Chrome: your agents on your phone and in your browser. Get the apps→

Knowledge tool reference

The one tool that reads your knowledge base: search_knowledge_base. It is the same tool, with the same actions, in the Python SDK, the JavaScript SDK (searchKnowledgeBase), the HTTP API, the project MCP server and the agents you build in promptev. This page lists every action, its fields and what it returns.

Overview

The knowledge tool is one tool with actions. Instead of a separate tool for search, reading, listing and computing, a model gets one name, search_knowledge_base, and picks what it does with the action field. One description carries the rules once, and discover tells the model what exists before it guesses.

There is one tool behind every door. The SDKs, the HTTP API, the MCP server and agents in the app all call the same tool, so an answer cannot differ between them.

You (or the model) set

action and the fields in the table below: what to look for, which sources or documents to narrow to, how many rows.

promptev sets

Who is asking, which project they reach, and the project’s redaction rules. None of them is a field, so a caller or a model cannot reach another project or turn masking off.

A refusal is a sentence you can show. An unknown action, an action that is off, or a missing field returns success: false with an error sentence. Over MCP that comes back as the tool’s answer. Over the HTTP API it is a 422 with the sentence as its message, and the SDKs raise ValidationError.

Where to call it

All doors need a project API key, made in the promptev app under the project’s API keys. The key reads only its own project.

From the SDKs

The action is the first argument and its fields follow. The list of actions is exported as KNOWLEDGE_ACTIONS.

Python
pip install promptev

from promptev import PromptevClient, KNOWLEDGE_ACTIONS

client = PromptevClient(api_key="fk_...")   # a project API key

overview = client.search_knowledge_base("discover")
found = client.search_knowledge_base("search", query="how many days do customers have to ask for a refund", top_k=5)
for hit in found["hits"]:
    print(hit["document_name"], hit["score"])

# Async: await client.asearch_knowledge_base("search", query="how many days do customers have to ask for a refund")
TypeScript
npm install @promptev/client

import { PromptevClient, KNOWLEDGE_ACTIONS } from "@promptev/client";

const client = new PromptevClient({ apiKey: "fk_..." }); // a project API key

const overview = await client.searchKnowledgeBase("discover");
const found = await client.searchKnowledgeBase("search", { query: "how many days do customers have to ask for a refund", top_k: 5 });
for (const hit of found.hits) console.log(hit.document_name, hit.score);

Over HTTP

POST https://api.promptev.ai/api/v1/knowledge with a JSON body: action plus that action’s fields. The answer is the tool’s JSON. See the HTTP API reference and the command line page.

Shell
curl -X POST https://api.promptev.ai/api/v1/knowledge \
  -H "Authorization: Bearer fk_..." \
  -H "Content-Type: application/json" \
  -d '{"action": "search", "query": "how many days do customers have to ask for a refund", "top_k": 5}'

Over MCP

Every project has an MCP server. Its search_knowledge_base tool is this tool, with the same actions and fields, and its upload_document tool adds a file. Any MCP client can use it, such as Claude, n8n or Zapier. The model reads the tool’s description and sends the fields as JSON.

In agents

Agents you build in promptev use the knowledge base on their own. You do not call the tool for them: they pick the action, and the answer cites its sources.

Fields

Every field a caller may set. Only action is always required; each action checks its own required fields.

FieldTypeUsed byMeaning
actionenum of 13allWhich action to run. Required.
querystringsearch, query_meta, compute, map_reduce, community_summaryWhat to look for, in words, or a bare content identifier. For compute and map_reduce it is the instruction.
document_idstring (uuid)get_doc, get_chunksOne document’s system id, from discover, list or a search hit.
source_idsstring[]discover, list, search, query_meta, compute, map_reduce, graph actionsNarrow to these sources. Ids outside the project are ignored, not refused.
document_idsstring[] (uuids)search, compute, get_docs, map_reduce, discover, listNarrow to these documents. Combines with source_ids. Any other action refuses it.
entitystringtraverse, get_neighborsThe start: an entity id from an earlier graph answer (preferred, and the only handle that survives masking) or its name.
depthintegertraverseHops, 1 to 5. Default 2.
categoryenumtraverse, find_relatedHIERARCHICAL, MEMBERSHIP, CREATION, TEMPORAL, SPATIAL, REFERENCE, FUNCTIONAL, QUANTITATIVE.
labelstringfind_relatedExact relationship wording as extracted, for example reports_to. Narrower than category.
entity_typeenumfind_relatedPERSON, ORG, PRODUCT, LOCATION, REFERENCE, TEMPORAL, CONCEPT.
top_kintegersearchPassages to return. Default 10.
mode"hybrid" | "graph"searchOmit it and the search decides from the documents in scope. "graph" also searches the Context Graph.
limitintegerdiscover, list, query_meta, map_reduce, graph actionsHow many rows. Defaults per action are listed with each action below.
cursorstringdiscover, listA previous page’s next_cursor, sent back as it came.
startintegerget_chunksFirst passage position, 0-based.
endintegerget_chunksLast passage position, inclusive. At most 25 per call.
max_charsintegerget_docsTotal text budget. Default 200,000.

A search hit’s document_id is a system id. Content identifiers written inside documents, such as invoice numbers, belong in query, never in document_id.

The 13 actions

The order below is the order of KNOWLEDGE_ACTIONS. Examples show the SDK call; over HTTP and MCP the same fields go as JSON. Answers are shortened: ... marks left-out content, and // comments are explanations, not part of the answer.

discover

Needs: Always available

When to use it. First, whenever the agent does not already know what is in scope. One call returns the documents, what is inside each one and which actions are on, so later calls can use real sheet, column, section and field names.

FieldTypeNotes
actionstring"discover"
source_idsstring[]Narrow to these sources. Ids outside the project are ignored.
document_idsstring[]Narrow to these documents. Also asks for their structure in full, past the cap a whole-page discover applies.
limitintegerDocuments per page. Default 50, clamped to 1 to 200.
cursorstringThe next_cursor of a previous page, sent back verbatim.
Python
answer = client.search_knowledge_base("discover")
TypeScript
const answer = await client.searchKnowledgeBase("discover");
Returns
{
  "success": true,
  "documents": [
    {"id": "...", "name": "costs.xlsx", "source_id": "finance",
     "kind": "spreadsheet",            // "spreadsheet" or "text", decided by MIME
     "document_type": "...", "mode": "hybrid", "failure_reason": null,
     "structure": {...}}               // sheets / sections + last_page / keys / chunks
  ],
  "count": 50, "has_more": true,
  "next_cursor": "...", "next_page": {"action": "discover", "cursor": "..."},
  "document_types": [...],             // census of the whole scope
  "fields_by_type": {...},             // extracted field names, types, counts
  "available_actions": {"search": "...", "compute": "... (OFF)", ...},
  "next_action": {"for a clause or a fact": {"action": "search", "query": "..."}, ...}
}

A spreadsheet structure lists sheets with name, column headers, row count and frame_key (the name compute will give that sheet). A document with headings lists sections and last_page, JSON lists keys, anything else falls back to chunks.

Lists inside a structure are capped, with the rest reported as more_columns, more_sections, more_keys or more_sheets. Naming the documents in document_ids returns them whole.

fields_by_type and document_types are computed over the whole scope, not only the current page.

available_actions covers the other twelve actions. An action that is off for this workspace is listed with " (OFF)" appended, never hidden.

Needs: Always available

When to use it. Questions answered by reading text. To look up an ID, code, SKU or invoice number, send the bare identifier alone (for example "2525"), not the whole question.

FieldTypeNotes
actionstring"search"
querystringRequired. Words, or a bare content identifier.
source_idsstring[]Narrow to these sources.
document_idsstring[]Narrow to these documents (intersects with source_ids).
top_kintegerHow many passages. Default 10.
mode"hybrid" | "graph"Omit it and the search decides from the documents in scope. "graph" also searches the Context Graph.
Python
answer = client.search_knowledge_base("search", query="annual leave carry over")
TypeScript
const answer = await client.searchKnowledgeBase("search", { query: "annual leave carry over" });
Returns
{
  "success": true,
  "hits": [
    {"document_id": "...", "document_name": "HR leave policy", "chunk_text": "...",
     "score": 0.83, "source_id": "hr", "chunk_idx": 4}
  ],
  "usage": {...},
  "hint": {"action": "compute", "reason": "aggregate question over a spreadsheet"}  // only on a mixed scope
}

chunk_idx is where the passage sits in its document. Pass it to get_chunks as start to read around it.

A query that names a file (for example "what is in report_2024.xlsx") searches document names instead and adds "matched_by": "filename" to the same hit shape. mode is ignored on that path. If no visible document has that name, the ordinary search runs.

An aggregate question (sum, total, average, count, top, per, a "by column" grouping, a numeric comparison and so on) over a scope where every document is a spreadsheet is refused with a next_action pointing at compute. See refusals below.

get_doc

Needs: Always available

When to use it. Reading one whole document by its system id, taken from discover, list or a search hit. Never pass an invoice number or other content identifier here.

FieldTypeNotes
actionstring"get_doc"
document_idstring (uuid)Required.
Python
answer = client.search_knowledge_base("get_doc", document_id="3f2b8c1e-5a4d-4e8f-9b61-2c7d0a9e4f10")
TypeScript
const answer = await client.searchKnowledgeBase("get_doc", { document_id: "3f2b8c1e-5a4d-4e8f-9b61-2c7d0a9e4f10" });
Returns
{
  "success": true,
  "document": {
    "id": "...", "name": "...", "text": "...", "acl": [...], "status": "completed",
    ...,
    "failure_reason": null,      // one FAILURE_REASONS code, or null
    "failure_message": null      // the fixed sentence for that code
  }
}

A document that failed to upload carries failure_reason, one fixed code, and failure_message, its fixed sentence. See the table under More.

A document that is absent, not visible to the caller, or outside the project all answer the same "document not found: <id>".

get_docs

Needs: Always available

When to use it. Several whole documents at once, by id.

FieldTypeNotes
actionstring"get_docs"
document_idsstring[]Required.
max_charsintegerTotal document text to return before stopping. Default 200,000, floor 1,000.
Python
answer = client.search_knowledge_base("get_docs", document_ids=["3f2b8c1e-5a4d-4e8f-9b61-2c7d0a9e4f10", "8d1e7f02-6b3c-4a59-8e2d-1f0c9b7a6e35"])
TypeScript
const answer = await client.searchKnowledgeBase("get_docs", { document_ids: ["3f2b8c1e-5a4d-4e8f-9b61-2c7d0a9e4f10", "8d1e7f02-6b3c-4a59-8e2d-1f0c9b7a6e35"] });
Returns
{
  "success": true,
  "documents": [{...}, {...}],   // same per-document shape as get_doc
  "count": 2,
  "remaining_document_ids": ["..."],                               // only when the budget ran out
  "next_page": {"action": "get_docs", "document_ids": ["..."]}
}

An id that is absent or not visible is simply missing from documents, never an error.

The first document is always returned, even if it alone exceeds max_chars.

get_chunks

Needs: Always available

When to use it. Walking one long document in order, a range at a time, or reading around a search hit by starting at its chunk_idx.

FieldTypeNotes
actionstring"get_chunks"
document_idstring (uuid)Required.
startintegerFirst chunk position, 0-based. Default 0.
endintegerLast position, inclusive. At most 25 chunks come back per call whatever is asked.
Python
answer = client.search_knowledge_base("get_chunks", document_id="3f2b8c1e-5a4d-4e8f-9b61-2c7d0a9e4f10", start=4)
TypeScript
const answer = await client.searchKnowledgeBase("get_chunks", { document_id: "3f2b8c1e-5a4d-4e8f-9b61-2c7d0a9e4f10", start: 4 });
Returns
{
  "success": true,
  "document_id": "...", "total_chunks": 120, "start": 4, "end": 28,
  "chunks": [{"position": 4, "text": "...", "language": "en"}, ...],
  "has_more": true, "next_start": 29,
  "next_page": {"action": "get_chunks", "document_id": "...", "start": 29}
}

list

Needs: Always available

When to use it. Browsing documents without searching. Cheaper than discover: it does not fetch structure, fields or the census.

FieldTypeNotes
actionstring"list"
source_idsstring[]Narrow to these sources.
document_idsstring[]Narrow to these documents.
limitintegerDefault 50, clamped to 1 to 200.
cursorstringThe next_cursor of a previous page.
Python
answer = client.search_knowledge_base("list", source_ids=["hr"], limit=20)
TypeScript
const answer = await client.searchKnowledgeBase("list", { source_ids: ["hr"], limit: 20 });
Returns
{
  "success": true,
  "documents": [{"id": "...", "name": "...", "source_id": "hr", "kind": "text",
                 "document_type": null, "mode": "hybrid", "failure_reason": null,
                 "status": "completed"}],
  "count": 20, "has_more": true,
  "next_cursor": "...", "next_page": {"action": "list", "cursor": "..."}
}

A malformed cursor is refused with "invalid cursor", never read as page one.

query_meta

Needs: On unless discover marks it (OFF)

When to use it. The structured fields a question names (dates, amounts, parties) and each document’s value for them. It does not filter or compare by value: read the values and compare them yourself.

FieldTypeNotes
actionstring"query_meta"
querystringRequired. A question naming a field from fields_by_type.
source_idsstring[]Narrow to these sources.
limitintegerCandidates to return. Default 20, clamped to 1 to 100.
Python
answer = client.search_knowledge_base("query_meta", query="invoice amount and invoice date")
TypeScript
const answer = await client.searchKnowledgeBase("query_meta", { query: "invoice amount and invoice date" });
Returns
{
  "success": true,
  "question": "...",
  "resolved_fields": [...],
  "documents": [{"id": "...", "source_id": "...", "name": "...",
                 "document_type": "...", "structured_data": {...}, ...}],
  "count": 3
}

Refused for document_ids. The same aggregate refusal as search applies over an all-spreadsheet scope.

compute

Needs: Code execution on for the workspace (on by default)

When to use it. Any figure derived from spreadsheets (total, average, count, ranking, margin, comparison) and finding the exact row that matches one id or value. It runs over every row of the sheets in scope, not a sample.

FieldTypeNotes
actionstring"compute"
querystringRequired. What to compute, in plain words, with columns named as discover spelled them.
source_idsstring[]Narrow to these sources.
document_idsstring[]Narrow to these documents.
Python
answer = client.search_knowledge_base("compute", query="total amount by region in costs.xlsx")
TypeScript
const answer = await client.searchKnowledgeBase("compute", { query: "total amount by region in costs.xlsx" });
Returns
{
  "success": true,              // whether the generated code ran
  "result": ...,
  "code": "...", "stdout": "", "error": null,
  "execution_time": 0.41,
  "documents_used": ["..."],
  "provider_tokens": {"llm_input": 812, "llm_output": 164},
  "attempts": 1                 // 1 or 2 code-generation calls
}

Nothing tabular in scope comes back as a result, for example {"success": false, "error": "no tabular (CSV/TSV/XLSX) documents found in scope for compute()"}, not as a transport error.

The project’s redaction rules apply to the whole answer, except the top-level list of document ids.

map_reduce

Needs: On unless discover marks it (OFF)

When to use it. Asking the same question of every document in scope and getting one answer per document, for example "which contracts mention X", where search would return a handful of passages and miss the rest. Not for figures: that is compute.

FieldTypeNotes
actionstring"map_reduce"
querystringRequired. The question to ask of each document.
source_idsstring[]Narrow to these sources.
document_idsstring[]Narrow to these documents.
limitintegerDocuments to read, newest first. Default 25, at most 200.
Python
answer = client.search_knowledge_base("map_reduce", query="Does this contract have an auto-renewal clause?")
TypeScript
const answer = await client.searchKnowledgeBase("map_reduce", { query: "Does this contract have an auto-renewal clause?" });
Returns
{
  "success": true,
  "results": [
    {"document_id": "...", "document_name": "...", "data": {...}},
    {"document_id": "...", "document_name": "...", "error": "..."}
  ],
  "processed": 24, "failed": 1, "considered": 25,
  "hint": {"action": "compute", "reason": "..."}   // only when some targets are spreadsheets
}

Refused for any instruction when every document the call would read is a spreadsheet, because it reads a slice of each document and would merge partial totals. The check runs before any model call.

get_neighbors

Needs: Documents uploaded in graph mode (Context Graph)

When to use it. What is one step from a named thing, and which way each link points.

FieldTypeNotes
actionstring"get_neighbors"
entitystringRequired. An entity id from an earlier graph answer (preferred), or its name as written in the documents.
source_idsstring[]Narrow to these sources.
limitintegerDefault 20, clamped to 1 to 200.
Python
answer = client.search_knowledge_base("get_neighbors", entity="Acme Corp")
TypeScript
const answer = await client.searchKnowledgeBase("get_neighbors", { entity: "Acme Corp" });
Returns
{
  "success": true,
  "entity": {"id": "...", "name": "acme corp", "type": "ORG"},
  "neighbors": [{"id": "...", "name": "...", "type": "PERSON", "category": "MEMBERSHIP",
                 "label": "works_for", "evidence": "...", "direction": "incoming"}],
  "count": 7
}

traverse

Needs: Documents uploaded in graph mode (Context Graph)

When to use it. Everything within a few steps of a named thing.

FieldTypeNotes
actionstring"traverse"
entitystringRequired. An entity id (preferred) or name.
depthintegerHops, 1 to 5. Default 2.
categoryenumOnly follow this kind of connection.
source_idsstring[]Narrow to these sources.
limitintegerPaths. Default 50, clamped to 1 to 200.
Python
answer = client.search_knowledge_base("traverse", entity="Acme Corp", depth=2, category="HIERARCHICAL")
TypeScript
const answer = await client.searchKnowledgeBase("traverse", { entity: "Acme Corp", depth: 2, category: "HIERARCHICAL" });
Returns
{
  "success": true,
  "entity": {"id": "...", "name": "...", "type": "ORG"},
  "paths": [{"target_id": "...", "target": "...", "target_type": "PERSON",
             "category": "HIERARCHICAL", "label": "reports_to", "evidence": "...", "hops": 2}],
  "count": 12
}

Needs: Documents uploaded in graph mode (Context Graph)

When to use it. Connections of a kind across the corpus, with no starting entity (an entity is refused; for how two named things connect, traverse from one). Also the way in when every name in an answer is masked: take a source_id or target_id from its result.

FieldTypeNotes
actionstring"find_related"
categoryenumThe kind of connection. At least one of category, label or entity_type is required.
labelstringThe exact relationship wording as extracted, for example reports_to.
entity_typeenumKeep only relationships with an entity of this type on one end.
source_idsstring[]Narrow to these sources.
limitintegerRelationships. Default 50, clamped to 1 to 200.
Python
answer = client.search_knowledge_base("find_related", category="CREATION", entity_type="PRODUCT")
TypeScript
const answer = await client.searchKnowledgeBase("find_related", { category: "CREATION", entity_type: "PRODUCT" });
Returns
{
  "success": true,
  "relationships": [{"source_id": "...", "source": "...", "source_type": "ORG",
                     "target_id": "...", "target": "...", "target_type": "PRODUCT",
                     "category": "CREATION", "label": "makes", "evidence": "..."}],
  "count": 9
}

community_summary

Needs: Context Graph documents, and community summaries on

When to use it. The themes each source groups into, ranked against a question.

FieldTypeNotes
actionstring"community_summary"
querystringOptional. Without one, the main themes. A question no summary clears the relevance floor for gets the closest themes, marked loose_match.
source_idsstring[]Narrow to these sources.
limitintegerCommunities. Default 3, clamped to 1 to 10.
Python
answer = client.search_knowledge_base("community_summary", query="supplier risk")
TypeScript
const answer = await client.searchKnowledgeBase("community_summary", { query: "supplier risk" });
Returns
{
  "success": true,
  "communities": [{"summary": "...", "entity_count": 14, "relationship_count": 22,
                   "hierarchy_level": 0, "relevance": 0.7121}],
  "count": 3
}

Community summaries can be off for a workspace. Then discover marks this action (OFF) and never suggests it, and a call is refused with a sentence that says why.

A community is built and read inside one source. Summaries are withheld (success false, with an error saying why) from a caller who cannot see every passage in scope, and refused when the call names documents, since a summary covers a whole source.

Graph answers and masking. Entity names, types, labels, evidence and community summaries come from document text, so every string in a graph answer goes through the project’s redaction rules. The entity ids (id, source_id, target_id) are exempt: they carry no text, and they are the only handle a model can pass back as the next entity when the names are masked. An id the caller cannot see answers entity not found, the same as an unknown name. Rebuilding the graph makes new ids.

Refusals and off actions

Which actions can run depends on the workspace and on how its documents were uploaded:

compute: code execution is on for the workspace. It is on by default, and an admin can turn it off in Settings.

traverse, find_related, get_neighbors, community_summary: documents uploaded in graph mode, which needs an extraction model in Settings.

Whatever the reason, discover lists an action that is off with “(OFF)”, so a model never has to guess.

An off action, if called anyway, returns a fixed sentence. The common refusals look like this:

Answers
// Aggregate question over a scope that is all spreadsheets (search, query_meta)
{"success": false,
 "error": "these documents are spreadsheets; searching their rows would answer from a sample. Use compute over the whole sheet.",
 "next_action": {"action": "compute", "query": "total spend by region",
                 "suggestion": {"frame_key": "...", "measure": "amount", "group_by": "region"}}}

// An action that is off for this workspace
{"success": false, "error": "compute is off: ..."}

// document_ids sent to an action that does not honour it
{"success": false, "error": "document_ids is not honored by get_doc ..."}

In the spreadsheet refusal, next_action.query is the question handed back, so the redirect is a call the model can make. suggestion is vocabulary, not code: frame_key is the name compute will give the newest sheet in scope, measure its first numeric column, group_by its first text column. Any of them can be null. A scope that mixes text and spreadsheets is still searched and only gets an extra hint.

Answers may also carry next_page or next_action: the next call, already filled in. The tool description tells the model to follow it rather than guess.

Over the HTTP API and the SDKs a refusal arrives as a 422 whose message is the error sentence; over MCP the whole answer, next_action included, is returned.

Permissions and scope

Every answer contains only what the caller may see. A project API key reads its whole project and nothing outside it. A person in the app, or an agent answering a person, sees only the documents that person’s access allows.

Narrow, never widen

source_ids and document_ids can only narrow what the caller already reaches. An id outside it is dropped quietly, because an error would reveal which ids are real. A request left with nothing in scope returns nothing.

Not found means not found

A document the caller may not see reads exactly like one that does not exist, in get_doc, get_chunks and every list.

Only six actions honour document_ids (search, compute, get_docs, map_reduce, discover, list). The others refuse it rather than answer over the whole scope as if it had been honoured.

Who may see which document is set in the app when a source is added or a file is uploaded. See Settings.

Redaction

A project can have redaction rules, set in the app on the project’s Redaction page. They mask what the knowledge base returns, in every action: document text, passages, search hits, computed results and graph answers. A caller cannot turn them off, because they are not a field.

More

What a call costs

A knowledge call costs 1 credit. A search costs 1 more, or 5 more when the Context Graph is searched. From the SDKs and the HTTP API there is no extra API-call charge. Uploading costs 1 credit per page, slide or MB (minimum 1).

Why a document failed

A failed document carries failure_reason, one fixed code, with failure_message as its fixed sentence. A later successful upload clears both.

failure_reasonfailure_message
input_too_largethe file is too large to process in one go. Split it into smaller files and upload those
provider_rejectedthe embedding provider refused the request. Check the model name and key
provider_unavailablethe embedding provider was unavailable. Try again later
extraction_failedthe file could not be read
chunk_set_changedthe document was re-ingested while it was being processed. Re-ingest or resume
embedding_dimension_mismatchthe embedder returned vectors of a different size than this database stores. Check the embedding model and its dimension
unknownprocessing failed

The provider and dimension codes matter most to a workspace on its own embedding model; see Settings. An unrecognised code reads as unknown rather than as no failure.

1,000 free credits for a 30-day trial · No card required · No subscription1,000 free credits · 30-day trial · No card