cd /news/ai-tools/knowa-open-source-llm-context-optimi… · home topics ai-tools article
[ARTICLE · art-18052] src=github.com pub= topic=ai-tools verified=true sentiment=↑ positive

Knowa – Open-Source LLM Context Optimizer

Knowa has released an open-source hybrid retrieval library and knowledge base server that reduces LLM context costs by 90-99% by indexing documents as vector chunks, full-text pages, and a named-entity graph, then extracting only relevant chunks per query. The tool, which ingests from local directories, Notion, and Confluence, aims to solve the problem of sending millions of tokens per request at high cost — a 1,000-page knowledge base can cost tens of thousands of dollars monthly at scale. Knowa's precision retrieval approach, which includes optional LLM entity enrichment for domain-specific terms, is positioned as critical for scaling AI from prototype to company-wide infrastructure without breaking budgets.

read21 min publishedMay 29, 2026

The naive approach to building AI-powered apps is to load your documents into the prompt and let the LLM figure it out. It works in demos. It breaks in production.

A 1,000-page knowledge base is roughly 2–4 million tokens. At current API pricing, sending that on every request costs dollars per query. At 10,000 queries a day that is tens of thousands of dollars a month — for context that is 95% irrelevant to the question being asked. As AI usage scales across teams and products, this becomes the dominant cost line.

Knowa's core job is to solve this. It indexes your documents once — as vector chunks, full-text pages, and a named-entity graph — then for each question extracts only the handful of chunks that are actually relevant, typically 1,000–3,000 tokens out of a large corpus. At scale that is a 90–99% reduction in context sent to the LLM, with no loss in answer quality. Every query response includes a measured token savings figure so you can track this in production. Savings are corpus-size dependent — see Understanding token savings for what to expect at different scales.

This matters now and will matter much more as AI usage goes from prototype to product to company-wide infrastructure. Precision retrieval is not a nice-to-have — it is the difference between an AI feature that scales and one that breaks your budget.

Knowa is a hybrid retrieval library and knowledge base server. It ingests documents from local directories, Notion, Confluence, and any custom source you connect — storing them as vector chunks and full-text pages in PostgreSQL, and as a named-entity knowledge graph in PostgreSQL (default), Neo4j, or Kuzu. For each question it retrieves only the most relevant context across all three representations and returns it ready to inject into any LLM or AI pipeline you choose.

The knowledge graph is built during indexing and gives retrieval a structural dimension that pure vector search misses — connecting people, products, organisations, and concepts across your entire document corpus so entity-centric questions ("what teams work on X?", "which pages mention both Y and Z?") get precise, targeted answers.

How the graph is populated depends on how much coverage you need:

spaCy NER (default)— runs locally at indexing time with zero API cost. Recognises standard entity types (people, organisations, locations, dates, and more). You can swap the model to improve accuracy or target a specific domain:en_core_web_trf

(transformer-based, better on ambiguous names), scispaCy models for scientific or biomedical text, or any spaCy-compatible model. - LLM entity enrichment (opt-in)— an additive second pass using any OpenAI-compatible model (gpt-4o-mini, Qwen3, Kimi2, and others). The LLM extracts domain-specific entities that a general NER model misses: product features, internal codenames, technical standards, abstract concepts — anything with meaning in your organisation's language. Runs concurrently across pages to keep indexing fast at scale, and costs roughly $0.30 per 1,000 pages with gpt-4o-mini.

  • Python 3.11+ Docker(for PostgreSQL)- OpenAI API key
docker compose up -d

This starts a PostgreSQL 16 instance with pgvector pre-installed, exposed on localhost:5432

. Data is persisted in a Docker volume across restarts.

python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

OR - if you have miniconda installed

conda create -n knowa python=3.12
conda activate knowa
pip install -r requirements.txt
python -m spacy download en_core_web_sm
pip install -e .                 # registers the `knowa` CLI command
cp .env.example .env

Edit .env

and fill in the required values:

Variable Required Notes
DATABASE_URL
Yes Pre-filled to match docker compose — no change needed
OPENAI_API_KEY
Yes Your OpenAI API key
OPENAI_MODEL
Yes e.g. gpt-5.4
API_KEY
Yes Any random secret — used to protect the REST API
NOTION_API_KEY
No Required only for Notion sources
CONFLUENCE_*
No All four vars required for Confluence sources
SPACY_MODEL
No NER model for graph extraction (default: en_core_web_sm )
ENTITY_LLM_MODEL
No Enable LLM entity enrichment, e.g. gpt-4o-mini (see below)

Generate a strong API key:

python3 -c "import secrets; print(secrets.token_urlsafe(32))"
knowa index /path/to/docs --name "My Docs"

Migrations run automatically on the first command. Supported file types: .md

, .txt

, .pdf

, .docx

knowa chat "What would you like to know?"
uvicorn knowa.api.main:app --reload --port 8000

Open http://localhost:8000/admin/ui

— search, browse sources, and trigger rebuilds from the browser.

As a Python library — embed Knowa directly in your app. You own the LLM call; Knowa handles indexing, retrieval, and context formatting. Works with Anthropic, OpenAI, Gemini, local models, LangChain, LlamaIndex, or any other framework.

As a REST API — run the FastAPI server and hit /query

to get complete answers with citations from any language or tool, no Python required.

As a CLI tool — use the knowa

command to index directories and chat with your knowledge base directly from the terminal, no server or code required.

Up to 90–99% token reduction at scale— three-path hybrid retrieval (vector search, full-text search, property graph) surgically extracts only what is relevant; every query reports measured token savings so you can track efficiency in production. SeeUnderstanding token savingsfor realistic expectations at different corpus sizes.Hierarchical chunking— documents are split into small child chunks for precise vector search, then expanded to larger parent chunks for full context — maximising relevance without losing surrounding informationPluggable embeddersOpenAIEmbedder

(default) orSentenceTransformerEmbedder

(fully local, no API key needed at query time); implement theEmbedder

protocol to add your ownMultiple sources— Notion, Confluence Cloud, and local directories (.md

,.txt

,.pdf

,.docx

)Incremental sync— only re-processes files changed since the last run** Zero LLM calls at index time by default**— spaCy handles entity extraction; no OpenAI spend during indexing regardless of knowledge base size. For richer entity coverage, an optional LLM enrichment pass can be enabled (seeGraph entity extraction)Admin UI— per-source sync/rebuild controls, query interface, token savings tracking, and interactive entity graph visualization** CLI**— full index and chat management without running the server

The token savings figure shown after each query measures how much of your indexed corpus Knowa avoided sending to the LLM:

savings % = 1 − (tokens retrieved by this query / all parent-chunk tokens in the DB)

This is a corpus-size-relative metric. With a small corpus, retrieval may cover most of it on every query — and savings will be near 0%. That is expected and correct; it is not a bug or a misconfiguration.

By default Knowa retrieves the top 5 child chunks by vector similarity (TOP_K_CHUNKS=5

), then expands each to its parent chunk (~2,048 tokens). That means roughly 10,000 tokens of context per query, regardless of corpus size.

Savings only accumulate once your total indexed content significantly exceeds that window.

The estimates below assume:

Default settingsTOP_K_CHUNKS=5

, parent chunk size ~2,048 tokensTypical document sizes— wiki pages, Notion pages, Confluence pages: 500–3,000 words → 1–4 parent chunks each. Short README-style files (under 500 words) may produce a single chunk; long PDFs or DOCX files (5,000+ words) may produce 5–15 chunks each.Mixed file types shift the breakpoints: a corpus of 10 long PDFs can behave like 50+ short markdown files in terms of total chunk count.

Corpus Approx. parent chunks Expected savings
5 short docs 5–10 0–10% — retrieval covers most of the corpus
20–30 substantial pages 30–50 40–60%
100+ pages 100–200 70–85%
500+ pages 500+ 85–95%
Large wiki (1,000+ pages) 1,000+ 90–99%

A 0% savings reading does not mean retrieval is broken or that Knowa is not helping. It means your corpus is small enough that the retrieval window covers essentially all of it — which is the correct behaviour. The answer quality benefit (finding the right pages and synthesising a coherent answer) is present at any corpus size.

The savings badge becomes a useful production monitoring signal once your knowledge base grows large enough that retrieval is genuinely selective — typically 20–30 substantial documents or more.

If you have a large corpus but still see low savings, consider reducing TOP_K_CHUNKS

in your .env

. The default of 5

is conservative; dropping to 3

roughly halves retrieved context and increases savings, at the cost of slightly lower recall on broad questions.

The CLI connects directly to the database — no server needed.

knowa index /path/to/docs
knowa index /path/to/docs --name "Engineering Docs"   # attach a friendly label
knowa index /path/to/docs --full                       # force full rebuild
knowa index /path/to/docs --workers 4                  # parallel indexing (4 threads)

knowa list

knowa chat                                    # interactive REPL
knowa chat "What is our refund policy?"       # single-shot
knowa chat --source "Engineering Docs"        # scoped to one source
knowa chat --debug                            # show retrieval path before each answer
knowa chat --no-index /path/to/docs           # bypass index, read docs directly

knowa debug

knowa bench questions.json

knowa clear /path/to/docs
knowa clear --source-id <notion-workspace-id>
knowa clear --source-id company.atlassian.net/ENG
knowa clear --all

Supported file types: .md

, .txt

, .pdf

, .docx

To quickly populate the knowledge base for testing the graph visualization:

python scripts/fetch_sample_docs.py --out /tmp/knowa_sample_docs
knowa index /tmp/knowa_sample_docs --name "Knowa Sample Docs"

This downloads ~20 Wikipedia articles (AI companies and researchers) and indexes them.

from knowa import KnowledgeBase

kb = KnowledgeBase()
kb.index("/path/to/docs", label="Engineering Docs")

context = kb.get_context("What is our deployment process?")
python
import anthropic
from knowa import KnowledgeBase

kb = KnowledgeBase()
client = anthropic.Anthropic()

def answer(question: str) -> str:
    context = kb.get_context(question)
    message = client.messages.create(
        model="claude-opus-4-7",
        max_tokens=1024,
        system=f"Answer using only this context:\n\n{context}",
        messages=[{"role": "user", "content": question}],
    )
    return message.content[0].text

retrieve()

gives you structured results before synthesis — filter, rerank, or log them:

from knowa import KnowledgeBase, RetrievedChunk

kb = KnowledgeBase()
chunks: list[RetrievedChunk] = kb.retrieve("What is our SLA?")

for c in chunks:
    print(f"  [{c.retrieval_type}] score={c.score:.3f} | {c.page_title}")

filtered = [c for c in chunks if c.score >= 0.3 or c.retrieval_type == "fts"]
context = kb.format_context(filtered)
pip install "knowa[st]"
python
from knowa import KnowledgeBase
from knowa.embedders.sentence_transformers import SentenceTransformerEmbedder

embedder = SentenceTransformerEmbedder("all-MiniLM-L6-v2")
kb = KnowledgeBase(embedder=embedder)
kb.index("/path/to/docs", full=True)
context = kb.get_context("What is our refund policy?")  # no OpenAI calls
kb.index("/docs/engineering", label="Engineering")
kb.index("/docs/legal", label="Legal")

eng_context = kb.get_context("deployment process", source_id="Engineering")
legal_context = kb.get_context("compliance requirements", source_id="Legal")

Manage history in your application and pass only the retrieved context to each turn:

from knowa import KnowledgeBase

kb = KnowledgeBase()
history: list[dict] = []

def chat(user_message: str) -> str:
    context = kb.get_context(user_message)
    messages = history[-10:] + [{"role": "user", "content": user_message}]
    response = your_llm(system=f"Answer using this context:\n\n{context}", messages=messages)
    history.append({"role": "user", "content": user_message})
    history.append({"role": "assistant", "content": response.text})
    return response.text

Retrieval is synchronous. Wrap with asyncio.to_thread

to avoid blocking the event loop:

import asyncio
from knowa import KnowledgeBase

kb = KnowledgeBase()

async def handle_request(question: str, source: str | None = None) -> str:
    context = await asyncio.to_thread(kb.get_context, question, source)
    async with your_async_llm_client() as client:
        return await client.complete(context, question)
python
from functools import lru_cache
from fastapi import Depends, FastAPI
from knowa import KnowledgeBase

app = FastAPI()

@lru_cache(maxsize=1)
def get_kb() -> KnowledgeBase:
    return KnowledgeBase()

@app.post("/ask")
async def ask(question: str, source: str | None = None, kb: KnowledgeBase = Depends(get_kb)):
    import asyncio
    chunks = await asyncio.to_thread(kb.retrieve, question, source)
    context = kb.format_context(chunks)
    answer = await your_llm(context, question)
    return {"answer": answer, "citations": [{"title": c.page_title, "url": c.url} for c in chunks if c.page_title]}

KnowledgeBase(database_url=None, embedder=None)

database_url

falls back to DATABASE_URL

env var; embedder

defaults to OpenAIEmbedder

. The embedder must match what was used when the index was built.

Method Returns Description
index(path, label=None, full=False)
dict
Index a local directory. Returns {"indexed": N, "deleted": N, "errors": N} .
retrieve(question, source_id=None)
list[RetrievedChunk]
Hybrid retrieval — no LLM calls.
format_context(chunks)
str
Format chunks into [Title]\n--- blocks for LLM injection.
get_context(question, source_id=None)
str
retrieve() + format_context() + graph relationships in one call.

RetrievedChunk

fields: content

, score

, page_id

, page_title

, source_id

, source_type

, url

, retrieval_type

("vector"

or "fts"

).

Concern Guidance
Embedder reuse
Create one KnowledgeBase per process and reuse it. Re-creating per request is wasteful.
Dimension consistency
Set EMBEDDING_DIMENSIONS before the first index() call. Changing the embedder later requires a full rebuild — mixing dimensions returns wrong results silently.
Thread safety
KnowledgeBase is safe to call from multiple threads — the psycopg2 pool is thread-safe.
Async blocking
Use asyncio.to_thread(kb.retrieve, ...) in async frameworks.
Context size
get_context() returns all retrieved chunks untruncated. For small context windows, filter by score and pass format_context(filtered) .
Re-indexing
Call kb.index(path) on a schedule for directories that change. Each call runs incrementally.
Error handling
index() returns {"errors": N} . Check this value and alert if non-zero.
curl -X POST "http://localhost:8000/query" \
  -H "X-API-Key: <key>" \
  -H "Content-Type: application/json" \
  -d '{"question": "What is our refund policy?", "response_mode": "with_citations"}'

Response modes: answer_only

· with_citations

· full

(includes raw retrieved chunks)

Knowa builds a property graph from your documents by extracting named entities during indexing. Two mechanisms are available and can be used together.

Runs entirely locally, zero API cost, no latency added. Recognises standard entity types: people, organisations, locations, dates, and 14 others from the OntoNotes scheme.

SPACY_MODEL=en_core_web_sm    # default — fast, lightweight
SPACY_MODEL=en_core_web_trf   # transformer-based, better accuracy on ambiguous names
SPACY_MODEL=en_core_sci_lg    # scientific text (pip install scispacy + model)
SPACY_MODEL=en_ner_bc5cdr_md  # biomedical (pip install scispacy + model)

After changing the model, install it:

python -m spacy download en_core_web_trf

An optional second pass that runs after spaCy and adds domain-specific entities spaCy misses — products, technologies, frameworks, abstract concepts, and any entity type relevant to your domain. Runs concurrently across pages (configurable parallelism) so it does not serialize indexing.

Supports any OpenAI-compatible provider. Examples:

ENTITY_LLM_MODEL=gpt-4o-mini
ENTITY_LLM_API_KEY=sk-...           # optional — falls back to OPENAI_API_KEY

ENTITY_LLM_MODEL=qwen3-7b
ENTITY_LLM_API_KEY=sk-...
ENTITY_LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1

ENTITY_LLM_MODEL=moonshot-v1-8k
ENTITY_LLM_API_KEY=sk-...
ENTITY_LLM_BASE_URL=https://api.moonshot.cn/v1

Concurrency is controlled by ENTITY_LLM_CONCURRENCY

(default 5

). Leave ENTITY_LLM_MODEL

blank to keep the default zero-LLM-at-index-time behaviour.

Two-phase indexing: when you combine --workers

with ENTITY_LLM_MODEL

, the two phases run sequentially — Phase 1 (OCR, chunking, embedding, spaCy) completes across all pages first, then Phase 2 (LLM enrichment) runs concurrently at ENTITY_LLM_CONCURRENCY

parallelism. The two concurrency settings are independent: --workers

controls how many files are read and embedded at once; ENTITY_LLM_CONCURRENCY

controls how many LLM API calls fire in the enrichment pass.

Domain Recommended model Install What it adds over en_core_web_sm
General wikis, internal docs, HR, support en_core_web_sm
built-in — baseline
Same domains, higher accuracy en_core_web_trf
python -m spacy download en_core_web_trf
Transformer-based; better on ambiguous names and abbreviations
Scientific papers, research reports en_core_sci_lg
pip install scispacy + model
Chemicals, genes, proteins, species, experimental methods
Biomedical / clinical en_ner_bc5cdr_md
pip install scispacy + model
Diseases, drugs, adverse drug reactions (BC5CDR corpus)
Legal (case law, UK) en_blackstone_proto
pip install blackstone
Legal concepts, case references, court names — UK-focused, not actively maintained
Finance, engineering, security any of the above General models catch orgs, people, dates well; domain-specific terms need LLM enrichment (see below)

Note:scispaCy models must be downloaded separately afterpip install scispacy

. See[allenai.github.io/scispacy]for the full model list and download URLs.

spaCy extracts what it was trained on. LLM enrichment fills the gaps — domain-specific terms, internal jargon, and relationship types that no pre-trained NER model knows about.

Domain spaCy alone Add LLM enrichment when…
General wikis / internal docs
Good — people, orgs, locations covered You need product names, team names, internal codenames, or project-specific concepts indexed as graph nodes
Legal
Partial — parties, orgs, dates caught; clause types and regulatory citations missed Almost always — contract clause types (Indemnification, Force Majeure), party roles (Licensor, Licensee), regulation references (GDPR Art. 17, SOX §404) require LLM extraction
Finance
Partial — companies, dates, monetary values caught; financial instruments and risk terms missed When indexing analyst reports, earnings calls, or contracts — extracts instruments (CDO, SPAC), risk categories, regulatory filings (10-K, 8-K)
Biomedical / clinical
Good with scispaCy — diseases, drugs, genes covered When you need treatment protocols, trial phases, or mechanism-of-action relationships beyond standard NER types
Engineering / code docs
Weak — standard NER misses most technical entities Almost always — APIs, services, error codes, config flags, version numbers, and dependency names are invisible to general NER models
Security / infosec
Weak — CVE IDs and threat actors are not standard NER types Almost always — CVEs, attack techniques (MITRE ATT&CK), threat actors, vulnerability classes, affected products
HR / people ops
Good — people and org names covered When role titles, skill taxonomies, or org-structure concepts matter for graph traversal
Customer support
Partial — product names and people caught; issue categories and feature areas missed When support tickets reference internal product areas, error messages, or workflow steps that aren't in any NER vocabulary

Rule of thumb: if the entities that matter most in your domain are not people, organisations, locations, or dates — add LLM enrichment. The cost is low (gpt-4o-mini runs at roughly $0.30 per 1,000 pages) and the graph coverage improvement is substantial for technical and domain-specific corpora.

  • Go to notion.so/my-integrationsNew integration→ give it a name → copy the** Internal Integration Token**(starts withsecret_

) - Set NOTION_API_KEY=secret_...

in your.env

  • In Notion, open each root page you want indexed → ⋯ → Connections → Add connection → select your integration. Sub-pages inherit the connection automatically. - Trigger an initial full index:
curl -X POST "http://localhost:8000/admin/rebuild?full=true" -H "X-API-Key: <key>"

Sub-pages are indexed recursively. Container pages (no body content, only child pages) produce 0 chunks — their children are indexed normally. For a 5,000-page workspace the initial index takes 30–90 minutes depending on Notion API rate limits.

:

CONFLUENCE_BASE_URL=https://yourcompany.atlassian.net
CONFLUENCE_USERNAME=you@yourcompany.com
CONFLUENCE_API_TOKEN=<token from step 1>
CONFLUENCE_SPACE_KEY=ENG
  • Trigger an initial full index:
curl -X POST "http://localhost:8000/admin/rebuild?full=true" -H "X-API-Key: <key>"

All four vars must be set to enable the connector — leaving any blank disables it without error. Large spaces (1,000+ pages) may take 10–30 minutes.

Knowa is a single Docker container + PostgreSQL.

docker build -t knowa .
docker run -d --env-file .env -p 8000:8000 knowa
docker logs -f knowa

pgvector must be available. All major providers support it:

Provider Notes
AWS RDS / Aurora
Enable pgvector in Parameter Groups, then CREATE EXTENSION IF NOT EXISTS vector;
Google Cloud SQL
Enable the pgvector database flag, then CREATE EXTENSION IF NOT EXISTS vector;
Azure Database for PostgreSQL
Built-in extension — run CREATE EXTENSION IF NOT EXISTS vector;
Supabase
pgvector pre-installed; run CREATE EXTENSION IF NOT EXISTS vector; from the SQL editor
Neon
pgvector pre-installed; run CREATE EXTENSION IF NOT EXISTS vector; from the console
Render / Railway
Create a PostgreSQL service; connect via psql and run the extension command

After provisioning, set DATABASE_URL

in your environment.

The app exposes port 8000

and reads all config from environment variables:

Provider Approach
AWS ECS / Fargate
Push to ECR, create a Fargate task, inject env vars via Secrets Manager
Google Cloud Run
Push to Artifact Registry, deploy as a Cloud Run service (--port 8000 )
Azure Container Apps
Push to ACR, deploy as a Container App, configure env vars in the portal
Render
Connect GitHub repo, set Runtime to Docker, set Port to 8000
Railway
Connect GitHub repo or push a Docker image, add env vars in the dashboard
Fly.io
fly launch auto-detects the Dockerfile; set secrets with fly secrets set
sudo apt update && sudo apt install -y docker.io
git clone <your-repo> && cd knowa
cp .env.example .env && nano .env
docker build -t knowa .
docker run -d --env-file .env -p 8000:8000 --restart unless-stopped knowa

Three layers — run in order.

pip install pytest
pytest tests/unit/ -v

python -m spacy download en_core_web_sm   # +8 spaCy graph extractor tests
pip install markdownify requests          # +13 Confluence connector tests

Tests the full ingestion pipeline with fixture markdown files. No Notion API or OpenAI calls — embeddings use deterministic fake vectors.

createdb knowa_test
psql knowa_test -c "CREATE EXTENSION IF NOT EXISTS vector;"

TEST_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/knowa_test \
OPENAI_API_KEY=sk-anything API_KEY=test \
pytest tests/integration/ -v

Tests the full system end-to-end against real Notion pages with deterministic content.

One-time setup:

  • Create three pages in your Notion workspace with exact content from tests/e2e/notion_test_pages.md

:Knowa Test — FAQKnowa Test — PricingKnowa Test — Handbook

  • Connect your integration to each page (⋯ → Connections)
  • Copy the 32-char hex ID from each page's URL (strip dashes and page title)
  • Create .env.test

:

TEST_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/knowa_test
TEST_NOTION_FAQ_PAGE_ID=<32-char id>
TEST_NOTION_PRICING_PAGE_ID=<32-char id>
TEST_NOTION_HANDBOOK_PAGE_ID=<32-char id>

Run:

export $(cat .env.test | xargs)
pytest tests/e2e/ -v -s

E2E tests auto-skip if any required env var is missing — they will not break CI runs without Notion credentials. Each full run costs ~$0.05 in OpenAI API calls.

Use after schema changes, embedding model updates, or suspected index corruption:

curl -X POST "http://localhost:8000/admin/rebuild?full=true" -H "X-API-Key: <key>"
knowa index /path/to/docs --full
SELECT source_id, source_type, label, last_synced_at FROM sync_state;
SELECT
  relname AS table,
  pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
SELECT source_id, COALESCE(label, source_id) AS name,
       last_synced_at, NOW() - last_synced_at AS age
FROM sync_state
ORDER BY last_synced_at ASC;

At least one cloud connector must be fully configured: either NOTION_API_KEY

or all four CONFLUENCE_*

vars. This error does not apply to local directory sources — use knowa index <dir>

for those. Verify env vars are loaded:

docker exec knowa env | grep -E 'NOTION|CONFLUENCE'

Run knowa debug

to check raw node/edge counts. If nodes = 0:

spaCy model not installed— runpython -m spacy download en_core_web_sm

then trigger a full rebuildPurely technical content— code files, configs, and API references have few named entities. Graph works best with prose containing people, organisations, and places. Consider addingLLM entity enrichmentfor technical domains.Indexed before graph extraction was set up— trigger a full rebuild from the Admin tab to re-run extraction on all pages.

SELECT COUNT(*) FROM chunks WHERE chunk_type = 'child';

If 0, run a full rebuild. If non-zero, check for OpenAI API errors in the server logs — the query embedding may be failing.

Embedding requests are batched at 100 texts per call (knowa/indexing/embedder.py

). If hitting rate limits, reduce the batch size to 50 or request a rate limit increase from OpenAI.

The Notion integration must be explicitly connected to each page: ⋯ → Connections → Add connection. Sub-pages inherit the connection automatically, but top-level pages do not.

CREATE EXTENSION IF NOT EXISTS vector;

On AWS RDS, ensure pgvector

is in your Parameter Group's shared_preload_libraries

before creating the extension.

The IVFFlat index needs at least lists × 3

rows to be useful. With lists=100

that is ~300 child chunks. This warning appears on small initial datasets and resolves automatically as the index grows.

No built-in metrics endpoint yet. Recommended additions for production:

Structured logging— replacelogging.basicConfig

withstructlog

for JSON logsRebuild alerts— ifstats["errors"] > 0

after a rebuild, send a Slack/email notificationQuery latency— add a FastAPI middleware timer and log P95 latency** Index freshness**— alert iflast_synced_at

is older than your sync interval (the Operations query above makes a good health check)

For full architecture documentation, schema, API spec, cost model, and extension points see DESIGN.md.

PostgreSQL + pgvector · FastAPI · OpenAI (embeddings + completions) · spaCy (configurable NER model) · tiktoken · Neo4j / Kuzu (optional graph backends) · any OpenAI-compatible LLM for entity enrichment (gpt-4o-mini, Qwen3, Kimi2, etc.)

── more in #ai-tools 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/knowa-open-source-ll…] indexed:0 read:21min 2026-05-29 ·