Knowledge bases
A knowledge base (KB) lets Pia ground assistant answers in your own documents. This page is the end-to-end setup guide for administrators running their own instance. For how it works internally, see Architecture → Knowledge base.
The KB is off until you enable it. With no vector database configured the server runs exactly as before — nothing in the chat path changes. Enabling it is additive and reversible.
The KB needs two things before it does anything: the Knowledge license feature and a configured
vector database. Miss either and the chat path stays normal (ungrounded) chat and the admin KB pages stay
inert — see Architecture → Knowledge base for the exact
behaviour of each gate.
What you’ll need
Section titled “What you’ll need”- A running Pia Server (see Local Docker or Production deployment).
- A licence that includes the
Knowledgefeature (see Licensing). - A PostgreSQL + pgvector database — the bundled
pia-vectordbservice provides this. - An embedding model the server can reach (OpenAI-compatible, Azure, or a local Ollama model).
- Optionally, a chat model for contextual retrieval (improves recall; can be the same provider).
-
Bring up the vector database.
Both Compose files already define the
pia-vectordbservice (imagepgvector/pgvector:pg17). It is internal-only and has no public route.- Local:
docker compose -f docker-compose.local.yml upwires and starts it automatically. - Production: it is included in
docker-compose.prod.ymlwith a host-bound data volume.
Confirm it is accepting connections:
Terminal window docker exec pia-vectordb pg_isready -U pia -d pia_knowledge # → accepting connections - Local:
-
Point the server at the vector DB and set an ingest key.
In your
.env(production uses the raw config-key form, double underscore = config section separator):Terminal window Knowledge__ConnectionString=Host=pia-vectordb;Database=pia_knowledge;Username=pia;Password=<your-password>Knowledge__IngestApiKey=<a long random secret> # enables the REST ingest API; leave empty to disable itLeaving
Knowledge__ConnectionStringempty keeps the KB disabled. -
Choose the embedding model.
One embedding model per instance → one vector space and dimension.
ProviderIdreferences an entry in yourAi__Providerscatalog.Terminal window Embeddings__ProviderId=<an Ai__Providers__N__Id>Embeddings__Model=bge-m3Embeddings__Dimension=1024 -
(Optional) Enable contextual retrieval.
On by default, this prepends a one-sentence LLM description to each chunk before embedding — but it needs its own chat provider:
Terminal window Chunking__Context__Enabled=trueChunking__Context__ProviderId=<an Ai__Providers__N__Id of a chat model>If
Chunking__Context__ProviderIdis left empty, the server logs a warning and auto-disables contextualization at startup — the KB still works, documents just embed the raw chunk only. To turn it off explicitly, setChunking__Context__Enabled=false. -
Restart and confirm the KB is enabled.
Restart the server and check the logs for the absence of a
[Knowledge]warning, then open Admin → Knowledge bases (/admin/knowledge-bases). If the page shows a “vector DB required” notice, the connection string isn’t reaching the container — re-check step 2. -
Create a knowledge base.
On Admin → Knowledge bases, press Create knowledge base, type a name and press it again in the dialog; you land straight on its edit page. Its Settings panel holds the fields described in Per-KB settings below — leave them at their defaults for now.
-
Add documents.
Open the KB and upload documents — plain text (
.txt) and Markdown (.md/.markdown); both are ingested as raw text (no Markdown parsing). Each appears as Pending, then Ready once the worker has chunked and embedded it (or Failed with an error). You can also ingest via the REST API for CI pipelines. -
Enable the KB for a group.
A KB only becomes active for users in groups that allow it. On Admin → Groups, open a group and add the KB to its plugin allowlist. The change takes effect on the user’s next request.
To make a KB reachable only under one persona rather than in every chat, skip this step and bind it to a managed persona instead.
-
Verify.
As a user in that group, start an Assistant-mode chat and ask something answerable from your documents. You should get a grounded answer with sources. (Optimize mode and voice never engage the KB by design.)
Per-KB settings
Section titled “Per-KB settings”Everything a knowledge base configures on its own lives in the Settings panel of its edit page. There
are only four fields, because chunking and the embedding model are instance-wide (Chunking__*,
Embeddings__*) — one embedding model per instance means one vector space, so they cannot vary per KB.
System prompt addition. Free text prepended to the chat system prompt whenever this KB is in scope. Its
job is to tell the model when to call search_knowledge_base — “for questions about our HR policies,
search the knowledge base first” beats a description of the contents.
Generate with AI. Drafts that text for you. It samples the KB’s stored documents (up to about 40,000 characters, spread across documents so a large KB still shows something from each), sends one call to the Assistant-mode model, and puts a 2–6 sentence draft in the box. Three things to know:
- It does not save. The draft is yours to edit; nothing changes until you press Save settings.
- It costs one Assistant-model request, billed like any other and visible on Token usage.
- It needs documents and a configured Assistant model. On an empty KB, or where the Assistant mode has no provider, it fails with a message saying so rather than inventing text.
Default topK. How many chunks a retrieval returns, default 6, also shown as the Top K column on the catalog. Raise it when answers miss context that you know is in the documents; lower it when answers drag in irrelevant material or input tokens are climbing. Every retrieved chunk is input tokens on every turn that retrieves, so this is the one setting on the page with a direct, per-request cost.
Active and Sensitive knowledge base are covered under Retiring a KB and Sensitive knowledge bases.
Retiring a knowledge base
Section titled “Retiring a knowledge base”There is no delete. The catalog’s only row action is Manage, the edit page has no delete button,
and the REST API deletes documents (DELETE /api/kb/{kbId}/documents/{id}), not knowledge bases. To take
one out of service:
-
Open it and clear Active, then Save settings. An inactive KB is never retrieved from, and it stops counting against any group’s
KnowledgeBasesquota — that cap only counts active ones. -
Remove it from every group’s plugin allowlist on the group editor, and from any managed persona it is bound to. Step 1 alone leaves those grants in place, so re-ticking Active silently puts the KB back in front of the same users — and can be refused with
409if the group is now at its cap. -
If you want the storage back, delete its documents through the REST API. The catalog row itself stays, showing 0 documents.
Quotas
Section titled “Quotas”Per-group quotas are edited in the group editor:
| Quota | Meaning |
|---|---|
KnowledgeBases |
How many KBs a group may reach — directly, or through a managed persona it is assigned |
KnowledgeDocuments |
Documents per KB (checked at ingestion → 409) |
KnowledgeStorageBytes |
Total stored bytes per KB (checked at ingestion → 409) |
MonthlyEmbeddingTokens |
Embedding tokens per group per calendar month |
For a KB shared by several groups, the effective doc/byte/token limit is the most permissive of those groups; monthly embedding-token spend is charged to a single owner group (the lowest group id) so it is never counted twice.
Persona-bound KBs are not a way around KnowledgeBases. Binding a KB to a managed persona is a
routing decision, not an exemption: both the group assignment and the plugin binding are refused with
409 if the resulting count would exceed the cap.
Ingesting via the REST API
Section titled “Ingesting via the REST API”For automated pipelines, ingest with the instance ingest key:
curl -X POST https://your-host/api/kb/<kbId>/documents \ -H "X-Pia-Service-Key: $KNOWLEDGE_INGEST_API_KEY" \ -H "Content-Type: application/json" \ -d '{"title":"Onboarding","contentType":"text/plain","content":"…"}'# → 202 Accepted (queued); 403 if the Knowledge feature isn't licensed;# 404 for an unknown KB; 409 on a quota breach; 503 if the KB is disabled (no vector DB)Importing the Pia docs preset
Section titled “Importing the Pia docs preset”Want a knowledge base that already knows about Pia? Each release ships a docs preset — a zip of this documentation, cleaned for ingestion and with every internal link rewritten to its production URL so KB citations point back at the live docs.
-
Request the latest
pia-kb-preset-*.zipat kontakt@pia-ai.de and unzip it. -
From the unzipped folder, loop over
manifest.jsonand POST each document. It reuses the ingest key above; set your server URL and the target KB id:Terminal window export PIA_KB_SERVICE_KEY="<your Knowledge__IngestApiKey>"export SERVER="https://cloud.pia-ai.de" # your Pia Server base URLexport KB="<kbId>" # target knowledge base idjq -c '.documents[]' manifest.json | while read -r d; dop=$(jq -r .path <<<"$d")body=$(jq -n \--arg t "$(jq -r .title <<<"$d")" \--arg s "$(jq -r .sourceUri <<<"$d")" \--arg c "$(cat "$p")" \'{title:$t, sourceUri:$s, contentType:"text/markdown", content:$c}')curl -sS -X POST "$SERVER/api/kb/$KB/documents" \-H "X-Pia-Service-Key: $PIA_KB_SERVICE_KEY" \-H "Content-Type: application/json" -d "$body"done -
Watch progress with
curl -s "$SERVER/api/kb/$KB/documents" -H "X-Pia-Service-Key: $PIA_KB_SERVICE_KEY"until each document reachesReady.
Privacy
Section titled “Privacy”KB documents are stored unencrypted in the vector DB — the server must read them in plaintext to
chunk, embed, and search. Keep this separate from users’ end-to-end-encrypted personal data. To keep all
document text on your own infrastructure, point Embeddings__* and Chunking__Context__* at a local
Ollama model.
Sensitive knowledge bases
Section titled “Sensitive knowledge bases”Each KB carries a Sensitive knowledge base switch, on by default. While it is on, any guarded chat that retrieves from this KB is pinned to the private (on-prem) model, so retrieved content never reaches an external provider — even when the request itself was judged clean. The escalation is recorded on the guardrail decision row with a Sensitive KB pill and the model that actually answered.
Turn it off only for material that is genuinely public. The switch has no effect on groups that have not enabled guardrails: without a classifier and a private hit model there is nothing to pin the answer to.
Troubleshooting
Section titled “Troubleshooting”Uploading from the KB edit page reports failures as a toast reading Upload failed: <reason>. The two
you are most likely to meet are the same two gates as the REST API, in the admin UI’s own words:
| Toast | Same as | Fix |
|---|---|---|
Upload failed: vector database not configured |
503 knowledge_disabled |
Knowledge__ConnectionString is empty or isn’t reaching the container — re-check setup step 2 |
Upload failed: the Knowledge license feature is not enabled |
403 feature_not_licensed |
The licence has no Knowledge feature; see Licensing |
A file larger than the upload limit never reaches the server at all — the browser control rejects it, and it is reported at the end of the batch rather than as it is picked. Other uploads in the same batch still go through; the summary says how many of each.
| Symptom | Likely cause |
|---|---|
KB never grounds answers; ingest returns 403 feature_not_licensed |
The licence doesn’t include the Knowledge feature (see Licensing) |
| Admin page shows “vector DB required” | Knowledge__ConnectionString empty or not reaching the container |
| KB disabled in logs at startup | Embeddings__Dimension ≠ 1024 (dimension mismatch) |
Startup [Knowledge] warning, no contextualization |
Contextual retrieval is on but Chunking__Context__ProviderId is unset (auto-disabled) |
| Every document → Failed | Embedding provider unreachable / wrong Embeddings__ProviderId, or returns a wrong-width vector |
Ingest returns 409 |
A KnowledgeDocuments / KnowledgeStorageBytes / MonthlyEmbeddingTokens quota was hit |
Ingest returns 503 |
The KB is disabled (no vector DB configured) |
| Chat isn’t grounded | The user’s group doesn’t allow the KB, or the mode isn’t Assistant/Research |