# klieo-memory-graph-rag
Graph-first RAG composer over `klieo-memory-graph::KnowledgeGraph` +
`klieo-core::LongTermMemory`.
**Status:** stable at 3.x. Trait freeze contract recorded in [ADR-039](../../docs/adr/adr-039-graphrag-1-0-promotion.md).
## Layers
- `klieo-core` (Layer 0) — frozen at 3.2.0
- `klieo-memory-graph` (Layer 1) — `KnowledgeGraph` trait
- `klieo-memory-graph-neo4j` (Layer 2) — Neo4j backend
- `klieo-memory-qdrant` (Layer 1) — vector store with `FilterableLongTermMemory`
- **`klieo-memory-graph-rag`** (Layer 2) — this crate
## What's in the crate
- `BuiltinExtractor` (regex + caller-hint dedup)
- `FallbackExtractor` (primary → secondary on empty)
- `LlmEntityExtractor` (klieo-core::llm + JSON-array prompt)
- `NullProvenance` stub (M4 replaces with a `klieo-provenance` bridge)
- `GraphAwareLongTerm` — drop-in `LongTermMemory` impl
- `KnowledgeIngestionPipeline` + `ImportSource` extension point
## Quickstart — builder facade
`GraphAwareLongTerm::builder()` defaults the extractor
(`BuiltinExtractor`) and the metrics counter (`RecallMetrics::default()`)
so callers only supply the two mandatory backends — the authoritative
vector store and the graph index:
```rust
use std::sync::Arc;
use klieo_memory_graph::{FilterableLongTermMemory, KnowledgeGraph};
use klieo_memory_graph_rag::GraphAwareLongTerm;
# fn run(vector: Arc<dyn FilterableLongTermMemory>, graph: Arc<dyn KnowledgeGraph>) {
let long_term = GraphAwareLongTerm::builder().build(vector, graph);
# let _ = long_term; }
```
Override defaults via the builder's setters:
```rust
use std::sync::Arc;
use klieo_memory_graph::RecallMetrics;
use klieo_memory_graph_rag::{
FallbackExtractor, BuiltinExtractor, GraphAwareLongTerm, LlmEntityExtractor,
};
# fn run<V, G>(vector: Arc<V>, graph: Arc<G>, llm: Arc<dyn klieo_core::llm::LlmClient>)
# where
# V: klieo_memory_graph::FilterableLongTermMemory + 'static,
# G: klieo_memory_graph::KnowledgeGraph + 'static,
# {
let metrics = Arc::new(RecallMetrics::default());
let extractor = Arc::new(FallbackExtractor::new(
BuiltinExtractor::default(),
LlmEntityExtractor::new(llm),
));
let long_term = GraphAwareLongTerm::builder()
.extractor(extractor)
.metrics(Arc::clone(&metrics))
.min_graph_hits(2)
.build(vector, graph);
# let _ = long_term; let _ = metrics; }
```
Read `metrics.snapshot()` from an ops/metrics endpoint to see graph
hit-rate vs vector-fallback rate.
## Full M2 wiring
```rust
use std::sync::Arc;
use klieo_memory_graph_rag::GraphAwareLongTerm;
use klieo_memory_graph_neo4j::Neo4jKnowledgeGraph;
use klieo_memory_neo4j::MemoryNeo4j;
use klieo_memory_qdrant::MemoryQdrant;
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let qdrant = MemoryQdrant::connect("http://localhost:6334").await?;
let neo4j = MemoryNeo4j::connect("bolt://localhost:7687", "neo4j", "pw").await?;
let graph = Arc::new(Neo4jKnowledgeGraph::new(neo4j.neo4j_handle()));
let long_term = GraphAwareLongTerm::builder().build(qdrant.qdrant_long_term, graph);
// `long_term` is a drop-in `Arc<dyn LongTermMemory>`.
# let _ = long_term; Ok(()) }
```
## Advanced wiring — direct constructor
`GraphAwareLongTerm::new(vector, graph, extractor, metrics)` stays
available for callers that want explicit control over every component.
For ingest from external sources (Confluence pages, regulatory texts,
…) implement `ImportSource` in your own crate and pass it to
`KnowledgeIngestionPipeline::new`.
## License
MIT.