Skip to content

Implementation

A build prompt for an AI coding agent: exactly what to implement for the Knowledge subsystem, in what order, against which contracts. Hand the block below to an agent; the sections after it are the reference it should follow.

This page is an agent prompt

Every section in these docs gets an Implementation page — a precise, copy-paste task spec the agent executes. Read Overview and LightRAG first; they are the source of truth this prompt points to.

The prompt

text
ROLE: You are implementing the Knowledge subsystem of Agentfy.ai 2.0 (NestJS + Prisma + CleanSlice).

GOAL: Give each agent its own queryable knowledge base — the store's catalog, docs and policies —
backed by LightRAG (graph-RAG) running as a SEPARATE Python service. The NestJS `agent/knowledge`
slice is a THIN gateway: knowledge-base + document CRUD (metadata in Postgres), document feeding,
and proxied retrieval. Ingestion/graph/query collapse INSIDE LightRAG — they are NOT our sub-slices.
The agent reaches retrieval OVER MCP (a brain tool), not as part of the brain.

DELIVERABLES
1) `agent/knowledge` slice (api, L5) — CleanSlice structure:
   - domain/: IKnowledgeGateway (abstract — KB/Document metadata, Prisma-backed),
     IRagGateway (abstract — the LightRAG contract: insert/query/dropWorkspace),
     knowledge.service.ts (orchestrates both), knowledge.types.ts (IKnowledgeBaseData,
     IDocumentData, RetrievalModeTypes = naive|local|global|hybrid, DocumentStatusTypes), errors/.
   - data/: knowledge.gateway.ts (Prisma impl of IKnowledgeGateway),
     rag.gateway.ts (impl of IRagGateway → delegates to LightRagRepository + RagMapper),
     lightRag.repository.ts (HTTP client wrapping the LightRAG service API — a REPOSITORY,
     not a Prisma gateway: it wraps an external SDK/API), knowledge.mapper.ts.
   - knowledge.prisma (KnowledgeBase, KnowledgeDocument), knowledge.controller.ts
     (@Controller('knowledge-bases'); Swagger operationId on every route for SDK gen), dtos/.
2) MCP exposure — `@Tool` retrieval surfaced via `setup/mcp` (e.g. knowledge_search). ALL knowledge
   access is over MCP; `agent/knowledge` is the only direct caller of LightRAG. TWO clients query the
   same endpoint: the BRAIN (orchestrator, in-process, during the LLM loop) AND the WORKER (mid-task,
   dialing the api MCP endpoint with its short-lived session token — no round-trip through the brain).
   Both carry the same server-derived workspace, so isolation holds for either client.
3) Ingestion pipeline — feeding a document enqueues a budgeted `ingestion` job on `runtime/task`
   (BullMQ); the consumer calls LightRAG insert and advances KnowledgeDocument.status. NEVER index
   inline on the request path. Incremental: re-index only changed docs (catalog deltas).
4) LightRAG service deployment — Helm values for the HKUDS `lightrag` image on Postgres
   (pgvector + Apache AGE), workspace-keyed instance pool. Borrow the Ranch chart (see reference).

CONTRACTS: implement exactly the rows, enums, repository client and job kind in the reference below.
Follow CleanSlice conventions (gateway vs repository, `I`-prefixed abstract DI tokens, singular slice
folders, `Types`-suffixed enums, camelCase DTO files, `#` aliases, no `any` — use `unknown` + guards).

SECURITY (non-negotiable): the workspace is ALWAYS derived from the authenticated team/kb on the api
side — NEVER accept a client-supplied workspace (empty/spoofed = cross-tenant leak). Workspace must be
non-empty. The `api` is the SOLE caller of the internal LightRAG service (network-isolated, no public
ingress). Deleting a KB tears down its workspace (delete-by-doc + drop the AGE graph).

ACCEPTANCE: see the checklist at the bottom. Start at the v0.x MVP scope.

Reference — contracts to implement

Knowledge-base & document rows (Prisma, metadata only)

The corpus itself lives in LightRAG; Postgres holds only metadata + status:

prisma
model KnowledgeBase {
  id        String   @id            // = the LightRAG `workspace`
  teamId    String                  // tenant; workspace derived from this + id
  agentId   String?                 // null = team-shared base
  name      String
  createdAt DateTime @default(now())
}

model KnowledgeDocument {
  id        String   @id
  baseId    String                  // → KnowledgeBase.id
  source    String                  // catalog | upload | url
  externalId String?                // e.g. OpenCart product id (for incremental delta)
  checksum  String?                 // skip re-index when unchanged
  status    String                  // pending | indexing | indexed | failed
  error     String?
  updatedAt DateTime @updatedAt
}

Retrieval modes

RetrievalModeTypes = naive | local | global | hybrid. The caller (agent) picks per question; default hybrid. Cheap questions take naive (pure vector) — the cost-lean path from Overview → scaling.

MCP clients — brain and worker

Retrieval is reached only over MCP (agent/knowledge exposes the @Tools via setup/mcp; the api hosts the endpoint). Both the brain and the worker are clients:

ClientAuthPath
Brain (orchestrator)in-processcalls the MCP tool directly during the LLM loop
Worker (the hands)short-lived session tokendials the api MCP endpoint mid-task; no round-trip through the brain

The worker's token scopes it to its (agent, session), and the api derives the workspace from that — the worker never supplies one. So a worker querying knowledge gets the same isolation as the brain. (This supersedes the earlier "retrieval is brain-only" tool-split note — retrieval over MCP is open to both; what stays brain-only is secret/KEK material, never the worker.)

LightRAG repository (the external-service client)

lightRag.repository.ts wraps the LightRAG HTTP API — it has its own types, knows nothing about the domain, and is converted to domain types by RagMapper:

ts
interface ILightRagRepository {
  insert(workspace: string, docs: RagDoc[]): Promise<void>;          // POST /insert
  query(workspace: string, q: string, mode: string): Promise<RagHit[]>; // POST /query
  dropWorkspace(workspace: string): Promise<void>;                   // KB teardown
}

IRagGateway (domain) is the abstract contract; rag.gateway.ts (data) catches repository/HTTP errors and converts them to domain errors. workspace is passed in by the service — derived from the authed KB, never the client.

Ingestion job

ingestion (BullMQ, budgeted): { baseId, documentIds[] } → consumer loads docs, calls rag.insert(workspace, ...), advances KnowledgeDocument.status, respects a per-base token budget + rate limit. Lives in runtime/task; the same queue subsystem the Worker uses.

LightRAG service (deploy)

HKUDS ghcr.io/hkuds/lightrag on Postgres pgvector + AGE, LIGHTRAG_{KV,VECTOR,GRAPH,DOC_STATUS}_STORAGE=PG*, pinned version, fixed EMBEDDING_DIM. Instance pool keyed by workspace (lazy-init + idle-evict). Reference: cleanslice/ranch/k8s/platform/lightrag/* is ~working — adapt its Helm chart/values. See LightRAG and GitOps.

Ordered tasks

  1. v0.x MVPagent/knowledge slice (Prisma KB/Document CRUD) · LightRagRepository + IRagGateway (insert + query, hybrid) · knowledge_search MCP tool · one workspace per KB. Deploy the LightRAG service from the Ranch chart. Proves create base → feed doc → agent queries over MCP → grounded answer.
  2. + Async ingestion — move insert onto the ingestion BullMQ job with status tracking; never inline.
  3. + Incremental & budgetchecksum/externalId delta skip; per-base token budget + rate limit.
  4. + Modes & cheap path — expose naive|local|global|hybrid; default cheap, graph where it earns it.
  5. + Lifecycle — KB delete → dropWorkspace (delete-by-doc + drop AGE graph); cold-start warm/cache.

Acceptance criteria

  • [ ] An agent retrieves grounded facts from its base over MCP (knowledge_search), never inventing.
  • [ ] Both the brain (in-process) and a running worker (via its session token) can query the base over MCP, under the same workspace isolation.
  • [ ] The workspace is derived server-side from the authed team/kb; a client cannot supply one.
  • [ ] One LightRAG service serves many bases via workspace-keyed instances; no cross-tenant leak.
  • [ ] Feeding a document enqueues an ingestion job; the request path never blocks on indexing.
  • [ ] Unchanged docs (matching checksum) are skipped; only catalog deltas re-index.
  • [ ] LightRAG is wrapped as a Repository (external API); agent/knowledge stays a thin gateway.
  • [ ] Deleting a KB tears down its workspace (drops the AGE graph); no orphaned vectors/graph.

See also