@nightowlsdev/knowledge
Adapter/StorageAn optional pgvector document knowledge base, RAG over arbitrary documents (guides, brand voice, reference docs), plus a ready tenant-scoped `search_knowledge` tool.
What it does
A document knowledge base distinct from conversation memory (which is thread-scoped): agents retrieve reference material by meaning. `knowledgeMigration({ dimensions })` returns a migration for `nightowls.kb_documents`, a `vector(N)` column (sized to your embedder) with an HNSW cosine index, RLS, first-class scoping columns (`org_id`/`source_type`/`source_id`/`source_version`/`visibility`), and a `(org_id, source_id, source_version, chunk_idx)` idempotency key. `createKnowledgeStore({ pool | dbUrl, embedder })` ingests documents (deterministic chunk → embed → idempotent upsert), searches (embed the query → an ALWAYS org-scoped, optionally source_type/visibility-filtered pgvector cosine search), and prunes superseded versions. The embedder is injected (the swarm's text provider may not embed, e.g. Ollama Cloud has no embeddings endpoint). `searchKnowledgeTool(store)` is a ready read-only `search_knowledge` SwarmTool that takes the tenant from the run context (never a tool arg, so the model can't cross tenants) and fences retrieved snippets as untrusted reference material. ⚠ NOT for metrics: this plane holds text for semantic retrieval, so a number embedded in a document comes back through a similarity search with no notion of ordering, of "the value on the 3rd", or of summing a month. The neighbouring temptation is worse, `@nightowlsdev/graph`'s `corroborate` never updates an edge's properties, so a graph "fact" recording a number silently keeps the FIRST value it ever saw while its confidence rises with every repetition. Both failures are silent. Numbers that change over time belong in `@nightowlsdev/metrics`.
Install
pnpm add @nightowlsdev/knowledgeKey exports
- knowledgeMigration
- createKnowledgeStore (+ listSources / getSource / deleteSource / stats)
- createKnowledgeHandlers (FR-042: framework-agnostic Request/Response)
- searchKnowledgeTool
- chunkText
- Embedder
Usage
import { knowledgeMigration, createKnowledgeStore, searchKnowledgeTool } from "@nightowlsdev/knowledge";
import { defineAgent } from "@nightowlsdev/core";
// 1. Eject the migration (dimensions MUST match your embedder, e.g. 1536 for text-embedding-3-small).
export const MIGRATIONS = [/* …engine migrations… */ knowledgeMigration({ dimensions: 1536 })];
// 2. Build the store. The embedder is INJECTED (batch text -> vectors of length `dimensions`).
const kb = createKnowledgeStore({ pool, embedder });
// 3. Ingest documents (chunk -> embed -> idempotent upsert), then grant the ready tool to an agent.
await kb.ingest({ tenantId, sourceType: "guide", sourceId: "brand-voice", text: brandVoiceMd });
const editor = defineAgent({ slug: "editor", skills: [searchKnowledgeTool(kb)], /* … */ });
// The tool takes the tenant from the run context (never a tool arg) and fences retrieved snippets.