# xz-rag
Multi-channel Retrieval-Augmented Generation engine for the 小竹 AI ecosystem.
Build composable RAG pipelines with multiple retrieval channels, RRF fusion, query preprocessing (HYDE), optional reranking, and LLM generation.
## Architecture
`xz-rag` coordinates a pluggable retrieval pipeline:
```
Query → [Preprocessing] → Channel Executors → [Normalize] → [RRF Fusion] → [Rerank] → [Context Build] → [Generate]
│
┌──────────────────┼──────────────────┐
Semantic BM25 Metadata Graph
(vector search) (full-text via (structured (knowledge
tantivy) filters) graph)
```
### Channels
| **Semantic** | default | any `Embedder` + `SemanticSearch` impl | Dense vector similarity search |
| **BM25** | `bm25` | tantivy | Sparse keyword retrieval |
| **Metadata** | default | any `MetadataStore` impl | Structured field filtering |
| **Graph** | default | any `KnowledgeGraphSearch` impl | Entity-relation traversal |
### Pipeline stages
1. **Query preprocessing** -- HYDE (hypothetical document generation) or query expansion, both powered by `xz-provider` (feature `hyde`).
2. **Multi-channel retrieval** -- each channel runs independently, configured with per-channel weight and `top_k`.
3. **Score normalization** -- min-max normalization equalizes scores across heterogeneous channels.
4. **RRF fusion** -- reciprocal rank fusion (`RRFusion`) merges results by rank position, smoothing out score distribution differences.
5. **Reranking** -- optional cross-encoder reranking via `xz-rerank` (feature `rerank`) with Cohere/Jina support.
6. **Context assembly** -- token-budget-aware context window with configurable citation formatting.
7. **Generation** -- optional LLM response via `xz-provider` (feature `llm-generation`), with streaming support.
## Features
| `bm25` | no | BM25 full-text search via tantivy |
| `rerank` | no | Cross-encoder reranking via `xz-rerank` |
| `hyde` | no | HYDE query expansion and query variation generation |
| `llm-generation` | no | LLM integration via `xz-provider` for `retrieve_and_generate` |
| `caching` | no | In-memory result caching via moka |
| `query-expansion` | no | Alias for `hyde` (same provider dep) |
## Usage
### Basic retrieval with DefaultRagEngine
```rust
use std::sync::Arc;
use xz_rag::{
DefaultRagEngine, RagEngine,
channels::semantic::{Embedder, SemanticSearch, SemanticChannelExecutor},
pipeline::channel::{ChannelConfig, ChannelPipeline},
types::retrieval::RetrieveRequest,
};
// Provide your embedder and vector store implementations
let embedder: Arc<dyn Embedder> = Arc::new(MyEmbedder);
let store: Arc<dyn SemanticSearch> = Arc::new(MyVectorStore);
let engine = DefaultRagEngine::builder()
.embedder(embedder)
.semantic_store(store)
.pipeline(
ChannelPipeline::new(vec![
ChannelConfig::semantic(0.6, 10).with_min_score(0.2),
ChannelConfig::metadata(0.4, 5),
])
.with_rrf_k(60),
)
.build();
let request = RetrieveRequest::builder("what is RAG?")
.channels(vec![
ChannelConfig::semantic(0.6, 10),
ChannelConfig::metadata(0.4, 5),
])
.top_k(15)
.build();
let result = engine.retrieve(&request).await?;
println!("got {} hits from {} channels",
result.hits.len(), result.channel_report.len());
```
### With HYDE and reranking
Enable features in `Cargo.toml`:
```toml
[dependencies]
xz-rag = { version = "0.1", features = ["hyde", "rerank", "llm-generation"] }
```
```rust
use xz_rag::types::retrieval::{QueryPreprocessing, RetrieveRequest};
let request = RetrieveRequest::builder("explain attention mechanism")
.channels(vec![ChannelConfig::semantic(1.0, 20)])
.query_preprocessing(QueryPreprocessing::Hyde)
.top_k(10)
.build();
// engine built with .reranker(...) and .provider(...)
let response = engine.retrieve_and_generate(&rag_request).await?;
println!("{}", response.answer);
```
### YAML/JSON configuration
```yaml
engine:
name: "my-rag"
channels:
semantic:
weight: 0.5
top_k: 10
min_score: 0.1
bm25:
weight: 0.3
top_k: 8
metadata:
weight: 0.2
top_k: 5
fusion:
algorithm: "rrf"
rrf_k: 60
normalize_scores: true
reranking:
enabled: true
context:
max_context_tokens: 4096
citation_format: "numeric"
```
Load via `RagConfig::from_file()` and build the engine:
```rust
use xz_rag::types::config::RagConfig;
let config: RagConfig = serde_yaml::from_str(&yaml_str)?;
let engine = config.build_engine()?;
```
### Streaming generation
```rust
use futures::StreamExt;
use xz_rag::types::rag::RagStreamEvent;
let mut stream = engine.retrieve_and_generate_stream(&request).await?;
while let Some(event) = stream.next().await {
match event? {
RagStreamEvent::GenerationStarted { context_chunks, .. } => {
eprintln!("context: {} chunks", context_chunks);
}
RagStreamEvent::ContentDelta { delta } => print!("{}", delta),
RagStreamEvent::Done { citations, .. } => println!("\n[Done]"),
}
}
```
## Modules
- `engine` -- `DefaultRagEngine` and its builder
- `channels` -- per-retriever channel executors (semantic, bm25, metadata, graph)
- `pipeline` -- channel orchestration, RRF fusion, score normalization
- `context` -- token budget management and citation formatting
- `preprocessing` -- HYDE and query expansion
- `generation` -- LLM response generation via `xz-provider`
- `indexing` -- document chunking (fixed, recursive, separator-based)
- `types` -- shared types (configs, requests, responses, chunk metadata)
- `cache` -- optional in-memory result caching
- `error` -- typed RAG errors
## Crate features
All optional components are feature-gated. The default crate is lightweight with only semantic and metadata channels. Turn on `bm25`, `rerank`, `hyde`, `llm-generation`, or `caching` as needed.
## License
MIT OR Apache-2.0