# Cognee vs Build — Component-Level Study

**Date:** 2026-04-25
**Author:** Ben Booth (driven by sister-session analysis)
**Status:** Draft for decision; supersedes the "adopt now" stance in `project_cognee_assessment` (memory dated 2026-04-23 is treated as input, not commitment)

## Why we're re-opening this

The Cognee assessment memory recommended "adopt NodeSet + memify now for Prague." Writing ADR-033 made me realize that assessment was framed at the wrong granularity. Cognee is not one thing — it's a layered system, and the right adoption decision is per-component, not whole-system. Memory is too central to Axiom's differentiation to outsource by default.

This study works through Cognee's components against four questions:

1. **Sovereignty cost** — does adopting this component move a load-bearing capability outside Axiom's control?
2. **Build cost** — what does it take to implement this ourselves, and what do we learn by doing it?
3. **Switchability** — if we adopt and later need to leave, what's the lift?
4. **Profile fit** — does this component work for our Edge / Workstation / Server / Platform tiers (ADR-019), or does it pin us to one?

## Cognee's components, decomposed

```mermaid
flowchart TB
    subgraph C[Cognee]
        direction TB
        ING[Ingestion + chunking]:::comp
        EXT[LLM-driven entity / relation extraction]:::comp
        NODE[NodeSet primitive]:::comp
        ONTOL[Ontology grounding RDF/XML]:::comp
        MEMIFY[memify second-order enrichment]:::comp
        STORE_G[Graph storage adapters<br/>Neo4j / Kuzu / Postgres-AGE]:::adapter
        STORE_V[Vector storage adapters<br/>LanceDB / pgvector / Chroma / Weaviate]:::adapter
        RETR[Hybrid retrieval<br/>FEELING_LUCKY router + 15 strategies]:::comp
        DECAY[Memory consolidation / decay]:::comp
    end

    classDef comp fill:#3d2e5a,color:#f0e8fa,stroke:#7a5fb0
    classDef adapter fill:#2d4a3e,color:#e8f5ee,stroke:#5a8a72
    style C fill:#1a0d2a,color:#f0e8fa,stroke:#7a5fb0
```

## Component-by-component

### 1. Ingestion + chunking

**What it is:** Splits documents into overlapping chunks suitable for embedding + extraction.
**Sovereignty cost:** None — it's a utility, deterministic, no opinion encoded.
**Build cost:** Trivial. langchain-style splitters or our own; <1 day.
**Switchability:** N/A — no lock-in.
**Profile fit:** Works everywhere.

**Decision: BUILD.** Ingestion belongs in the L1 write path through `CompositionService`. We need it for our own provenance + chunking-aware retrieval anyway.

### 2. LLM-driven entity / relation extraction

**What it is:** Calls an LLM with a prompt template to extract entities + relations from text; returns structured triples.
**Sovereignty cost:** **High.** This is where most of the "magic" lives. It's also where:
- LLM provider lock-in happens (Cognee's defaults assume OpenAI; pluggable but weighted toward OpenAI quality)
- Classification leakage is most dangerous (LLM extraction over CUI material via OpenAI is a non-starter for NeutronOS)
- Domain-specific extraction (code AST, sim-run schemas, classified-aware redaction) needs control we don't have if we adopt
**Build cost:** Medium. The naive version is "prompt → JSON triples" — easy. The good version is iterative refinement, schema validation, confidence calibration, multi-extractor ensemble — multi-week work.
**Switchability:** **Painful** if we adopt and outgrow. Extraction quality determines graph quality determines projection quality; ripping it out means re-extracting the entire corpus.
**Profile fit:** Cognee assumes a hosted LLM is available; for Edge profile (Bonsai 1.7B) extraction quality drops and we need to know that floor.

**Decision: BUILD, study Cognee's prompts/templates as reference.** We need:
- A pluggable LLM provider (gateway already exists in Axiom)
- Per-extractor selection (text-LLM / code-AST / schema-driven / classified-redacting)
- Provenance tied to the extracting model (which model + which prompt produced this concept)
- Determinism flag (when we need replay, run a fixed seed against a pinned local model)

This is enough sovereignty cost to justify owning. We can still adopt Cognee's prompt patterns and chunking heuristics as starting points.

### 3. NodeSet primitive

**What it is:** Tag-at-ingest that becomes first-class subgraph nodes at "cognify" time. Lets you scope subgraphs without separate graph databases.
**Sovereignty cost:** Low — it's a data-modeling pattern, not a runtime dependency.
**Build cost:** Trivial. Add `scope` and `tags` fields to `Concept`; index by them; expose `graph.scope(s).query(...)`. Half a day.
**Switchability:** N/A — pattern, not implementation.
**Profile fit:** Works everywhere.

**Decision: ADOPT THE PATTERN, BUILD THE IMPLEMENTATION.** Per-cohort scoping is exactly what classroom needs; per-conversation scoping is what chat needs; per-classification scoping is what NeutronOS needs. The pattern is right; we don't need Cognee's code to express it.

### 4. Ontology grounding (RDF/XML domain ontologies)

**What it is:** Validates extracted entities + relations against declared domain ontologies; rejects or flags out-of-ontology extractions.
**Sovereignty cost:** Low — declarative.
**Build cost:** Medium. RDF tooling exists (rdflib); the integration work is wiring extraction → ontology check → confidence boost/demote.
**Switchability:** Easy — ontologies are data files, not code.
**Profile fit:** Useful for classified/regulated contexts (NRC has formal ontologies for reactor safety); overkill for v0 classroom.

**Decision: DEFER.** Not load-bearing for Prague or chat. Strong candidate when NeutronOS lands and we want validated extraction over reactor-physics ontologies. Until then, no reason to import the dependency.

### 5. memify (second-order enrichment)

**What it is:** A pass that runs over the existing graph and enriches nodes/edges by re-running extraction with the graph as context. Catches relations the first pass missed.
**Sovereignty cost:** Medium — it's a workflow pattern that touches the same LLM extractor (#2). Our extractor sovereignty extends here.
**Build cost:** Low if we own #2. memify is roughly "for each concept, query its neighborhood, re-prompt the LLM, merge new relations." Days, not weeks.
**Switchability:** Trivial — it's a pipeline step.
**Profile fit:** Server+ profiles only (cost-prohibitive on Edge). Schedule-driven, not request-time.

**Decision: ADOPT THE PATTERN, BUILD THE IMPLEMENTATION.** Useful primitive; we should own it because it consumes our extractor and because we need it to be classification-aware (memify pass over classified content stays inside the enclave).

### 6. Graph storage adapters (Neo4j / Kuzu / Postgres-AGE)

**What it is:** Pluggable backends for the graph. Same query API, different stores.
**Sovereignty cost:** **None if we own the protocol.** Adopting an adapter is just choosing a backend.
**Build cost:** Per-backend non-trivial. Kuzu adapter alone is days. Multi-backend matrix is weeks.
**Switchability:** Easy — adapters are pluggable.
**Profile fit:** This is exactly the tier story per `project_cognee_assessment`. Kuzu fits Edge / Workstation; Neo4j or Postgres-AGE fits Server / Platform.

**Decision: ADOPT SELECTIVELY — but only after building one ourselves first.** Kuzu (embedded, MIT, no daemon) is the right Edge choice and we should own a thin adapter. Neo4j adoption is post-Prague and only if we've validated Kuzu won't scale. We should NOT pull in Cognee's adapter layer wholesale — we want our own provenance + classification + per-fragment-tag decoration on every read/write that a vendored adapter wouldn't naturally carry.

**Specifically: write a SQLite-backed graph adapter first.** SQLite is already in Axiom's stack (FTS5, sqlite-vec, ArtifactRegistry's SQLiteBackend). For classroom v0 this is enough; it stays Edge-friendly; it's zero new dependencies. Kuzu is the next step when we hit SQLite's graph-query ceiling.

### 7. Vector storage adapters (LanceDB / pgvector / Chroma / Weaviate)

**What it is:** Pluggable vector stores.
**Sovereignty cost:** None.
**Build cost:** sqlite-vec is already in our stack. Wrapping it is hours.
**Switchability:** Easy.
**Profile fit:** sqlite-vec for Edge / Workstation; pgvector for Server (alongside Postgres); LanceDB for Platform if we need it.

**Decision: BUILD on sqlite-vec for v0.** No reason to import Cognee's vector adapter layer when we already have a vector backend.

### 8. Hybrid retrieval (FEELING_LUCKY router + 15 strategies)

**What it is:** A router that picks among 15 retrieval strategies (vector-only, graph-walk, hybrid, etc.) based on query shape.
**Sovereignty cost:** Medium — retrieval determines what the LLM sees, which is the primary lever for classroom learning quality.
**Build cost:** **High.** Building a competitive retrieval router is hard; tuning is harder; the "FEELING_LUCKY" stratification is the result of running real workloads.
**Switchability:** Medium — strategies are functions, but the router's trained heuristics aren't.
**Profile fit:** All profiles.

**Decision: DEFER, MEASURE FIRST.** The honest answer is: we don't know enough about our retrieval workload to commit. Build 2-3 strategies ourselves (vector-only, graph-walk-from-touched-concepts, hybrid fusion), run real classroom queries, measure. Then decide whether Cognee's router is worth importing or whether our naive 3-strategy heuristic is adequate. This is the right place to delay adoption — the cost of getting it wrong is high and the cost of measuring is low.

### 9. Memory consolidation / decay

**What it is:** Background process that compresses old memory, promotes frequently-accessed concepts, decays unused ones.
**Sovereignty cost:** Medium — decay policy is a sovereignty concern (which student questions get forgotten? when?).
**Build cost:** Medium. The concept is simple; the policy design is the work.
**Switchability:** Easy.
**Profile fit:** Server+ profiles (background job); not Edge.

**Decision: BUILD, study Cognee's policies.** Decay needs to interact with classification (some classified content is mandatory-retention; can't decay), with retraction (tombstoned content is decay-immune for audit), and with federation (decay propagates differently across cohorts). These are Axiom-specific; we own the policy and should own the implementation.

## Summary table

| Component | Decision | Why |
|---|---|---|
| Ingestion + chunking | **Build** | Trivial, belongs in L1 write path |
| LLM extraction | **Build** | Sovereignty + classification + provider lock |
| NodeSet pattern | **Adopt pattern, build impl** | Pattern is right, code is small |
| Ontology grounding | **Defer** | Not load-bearing for Prague |
| memify | **Adopt pattern, build impl** | Consumes our extractor, owns classification |
| Graph adapters | **Build SQLite first; adopt Kuzu later if needed** | Sovereignty over read/write decoration |
| Vector adapters | **Build on sqlite-vec** | Already in stack |
| Hybrid retrieval router | **Defer; measure** | Cost of wrong adoption > cost of measuring |
| Decay / consolidation | **Build, study Cognee policies** | Policy interacts with classification + federation + tombstones |

## What this means for ADR-033 Stage 2

Stage 2 (graph package + extractor) becomes:
- Ship the protocols (`Concept`, `ConceptEdge`, `ConceptGraph`, `ConceptExtractor`) — Axiom-owned, Cognee-irrelevant.
- Ship a SQLite-backed `ConceptGraph` implementation. Edge-friendly, zero new deps.
- Ship a text `ConceptExtractor` that wraps Axiom's existing LLM gateway. Classroom registers it for ask + material text.
- Adopt NodeSet pattern (scope + tags on Concept) without importing Cognee.
- Run real classroom workloads against the SQLite graph + naive retrieval.

Stage 2 deliverable is then **a working graph layer Axiom owns**, against which we can measure Cognee's value-add for the components we deferred (retrieval router, ontology grounding, Neo4j adapter). Decision on those becomes data-driven instead of speculative.

## Risk we're taking by deferring Cognee

- Cognee likely produces better graphs faster on text-heavy domains. We may ship a worse graph in v0 than we could with Cognee.
- Mitigation: the protocol boundary keeps Cognee swappable. If our naive extractor is meaningfully worse on real classroom data, we can swap behind the protocol without touching the rest of the stack.

## Risk we're avoiding by deferring

- Hard dependency on `litellm + openai + lancedb + kuzu + rdflib + sqlalchemy + alembic` chain. Edge profile compatibility, EU sovereignty (OpenAI hard dep), classified contexts (nothing through external services).
- Lock-in on LLM extraction patterns we may want to evolve as we learn classroom-specific extraction needs.
- Vendored adapter layer that doesn't naturally carry our provenance + classification decoration.

## Recommendation

**Stage 2 of ADR-033 ships with Axiom-owned implementations behind Axiom-owned protocols.** Cognee components are evaluated empirically once we have real workload data, not preemptively. The `project_cognee_assessment` memory should be updated to reflect this study's conclusion: protocol adoption ahead of implementation adoption.
