Chuyển tới nội dung chính

Knowledge (RAG)

beam_knowledge adds retrieval-augmented generation. You ingest text, it is split into chunks, embedded, and stored. When knowledge is attached to RiverBeam, every request retrieves the closest chunks to its text and prepends them to the conversation as a system message.

Attach knowledge to the pipeline​

The factory reads the environment and picks an embedder and a vector store:

worker/src/main.rs
use std::sync::Arc;

use beam_knowledge::{build_knowledge_from_env, KnowledgeScope};

let knowledge = Arc::new(build_knowledge_from_env(1536).await?);
knowledge
.ingest(
"Riverbeam runs agents behind an API.",
None, // document_id: a new UUID when None
Some(KnowledgeScope::system()),
None, // agent_id
None, // user_id
Some("intro.md".into()), // source
)
.await?;
beam.set_knowledge(knowledge);

On each request, the pipeline retrieves the top 4 chunks for the text of the request and renders them like this, ahead of the history:

Relevant knowledge:
[1] Riverbeam runs agents behind an API.
[2] …

The block is added only when there are hits.

Choosing an embedder and store​

build_knowledge_from_env(embedding_dim) reads these variables:

VariableValuesDefault
KNOWLEDGE_EMBEDDERopenai, nvidia, mock, hashAuto: nvidia when an API key is set and the base URL contains nvidia, openai when a key is set, otherwise hash
KNOWLEDGE_VECTOR_STOREpgvector, inmemory (or memory)pgvector
KNOWLEDGE_EMBEDDING_DIMa positive integerthe embedding_dim argument
KNOWLEDGE_EMBEDDER_MODELmodel namenvidia/llama-3.2-nv-embedqa-1b-v2 for nvidia, otherwise text-embedding-3-small
BEAM_API_KEYAPI key for the HTTP embeddersnone
BEAM_API_BASE_URLOpenAI-compatible base URLhttps://integrate.api.nvidia.com/v1
BEAM_DATABASE_URLPostgres for pgvectornone

Older fallback names are also read; see Fallback names.

The outcome:

SituationEmbedderStore
No API keyHashEmbedder (128 dimensions, no network)In-memory. The hash embedder is never paired with pgvector.
API key, database URL, vector extension available, feature postgresOpenAiCompatEmbedderPgVectorStore
API key, but no database URL, no vector extension, or no postgres featureOpenAiCompatEmbedderIn-memory, with a warning

Configuration errors stop startup:

ErrorCause
BEM-231KNOWLEDGE_EMBEDDER is openai or nvidia without an API key
BEM-233The embedder's width differs from KNOWLEDGE_EMBEDDING_DIM (pgvector only)
BEM-234pgvector storage with a dimension other than 1536

Enable the Postgres store with the crate feature:

beam_knowledge = { path = "../riverbeam/crates/beam_knowledge", features = ["postgres"] }

Building it yourself​

Skip the factory when you want explicit control:

use std::sync::Arc;

use beam_knowledge::{HashEmbedder, InMemoryVectorStore, KnowledgeManager, KnowledgeScope};

let manager = KnowledgeManager::new(
Arc::new(HashEmbedder::new(128)),
Arc::new(InMemoryVectorStore::new()),
);
let result = manager
.ingest(&document_text, None, Some(KnowledgeScope::system()), None, None, None)
.await?;
let hits = manager.retrieve("what is the capital of France", 1, None).await?;
TypeRole
EmbedderTurns text into vectors. HashEmbedder (hermetic), MockEmbedder (hash-based at any width, for tests), OpenAiCompatEmbedder::openai(...) / ::nvidia(...) (calls POST {base_url}/embeddings)
VectorStoreupsert, query, delete. InMemoryVectorStore (cosine) and PgVectorStore (feature postgres)
ChunkerSimpleChunker: splits on blank lines and packs paragraphs up to 800 characters by default
KnowledgeBuilderChunk, embed and upsert; use with_chunker to change chunking
KnowledgeManagerIngest, delete_document, retrieve; what the pipeline uses
RagContextStrategyRenders hits into the context block; the header is configurable with new(header)

Ingest​

ingest(text, document_id, scope, agent_id, user_id, source) returns an IngestionResult with the document_id, chunk_count, embedder_name, and stats. Ingesting with an existing document_id replaces that document's chunks. Empty input fails with BEM-230.

stats is an IngestStats: chunk count, total, mean, minimum and maximum tokens per chunk, and duplicate chunks. With the polars-ingest feature the statistics are computed with Polars; otherwise a pure-Rust pass gives the same values.

Retrieve​

retrieve(query, k, filter) returns up to k Hit values with text, score, document_id, chunk_index, source and scope. A RetrievalFilter narrows by scope, agent_id, user_id and document_ids.

Through the worker​

Two worker tasks wrap the manager, so an API process can manage documents over NATS:

TaskPayloadResult
ingest_knowledge_document{"text": "…", "document_id": "<uuid>?", "source_url": "…"?}IngestionResult
delete_knowledge_document{"document_id": "<uuid>"}{"deleted": <count>}

Ingest takes inline text only (BEM-004 otherwise). Both fail with BEM-045 when the worker has no knowledge attached.

Storage​

PgVectorStore::connect creates the vector extension and three tables in ref_beam: agent_knowledge_document, agent_knowledge_chunk (vector(1536) with an HNSW cosine index), and agent_memory. If the extension is not available, connect returns None and the factory falls back to the in-memory store. See Database schema.

Current limitations​

Retrieval in the pipeline is not scoped

The pipeline retrieves without a filter, so every request can see every ingested chunk, whatever its scope, agent or user. Do not ingest documents that only some users may read into a knowledge store attached to RiverBeam.

  • The factory's hash embedder always has 128 dimensions and ignores KNOWLEDGE_EMBEDDING_DIM.
  • The default base URL is NVIDIA's, even when KNOWLEDGE_EMBEDDER=openai. Set BEAM_API_BASE_URL for OpenAI.
  • Unrecognized values fall back silently: an unknown KNOWLEDGE_EMBEDDER auto-detects, and an unknown KNOWLEDGE_VECTOR_STORE means pgvector.
  • PgVectorStore::query applies k before the document_ids filter, so a filtered query can return fewer than k hits. Its agent_id and user_id filters also match rows where those columns are empty.
  • KnowledgeManager always uses the default chunker.