Skip to content

Quickstart

Lightweight: the Transformer

The fastest way to add verbatim grounding to your existing RAG system: pass a question and the chunks you retrieved, and get an answer whose citations point at the chunks' own text. Works with verbatim-core (lean) or verbatim-rag (full).

from verbatim_core import Chunk, Transformer
from verbatim_core.extractors import LLMSpanExtractor
from verbatim_core.llm_client import LLMClient
from verbatim_core.response_builder import ResponseBuilder
from verbatim_core.templates import TemplateManager

client = LLMClient("gpt-4o-mini")
transformer = Transformer(
    LLMSpanExtractor(client),
    TemplateManager(llm_client=client, default_mode="contextual"),
)

question = "What is the main finding?"
chunks = [
    Chunk(id="paper-a", text="The study found that X leads to Y.", metadata={"title": "Paper A"}),
    Chunk(
        id="paper-b",
        text="Results show Z is statistically significant.",
        metadata={"title": "Paper B"},
    ),
]

answer = transformer.transform(question, chunks)
print(answer.text)

# Each citation points at a span of a chunk, by id and offsets
for citation in answer.citations:
    print(citation.number, citation.chunk_id, citation.start, citation.end)

# A QueryResponse, with the highlights of each document
response = ResponseBuilder().build_from_answer(question, answer, chunks)
for doc in response.documents:
    for highlight in doc.highlights:
        print(f"  [{highlight.start}:{highlight.end}] {highlight.text}")

Full Pipeline: VerbatimRAG

For the complete RAG system with document processing, indexing, and retrieval:

from verbatim_core import Transformer
from verbatim_core.extractors import LLMSpanExtractor
from verbatim_core.llm_client import LLMClient
from verbatim_core.templates import TemplateManager
from verbatim_rag import VerbatimIndex, VerbatimRAG
from verbatim_rag.ingestion import DocumentProcessor
from verbatim_rag.vector_stores import LocalMilvusStore
from verbatim_rag.embedding_providers import SpladeProvider

# Process documents
processor = DocumentProcessor()
document = processor.process_url(
    url="https://aclanthology.org/2025.bionlp-share.8.pdf",
    title="KR Labs at ArchEHR-QA 2025",
)

# Create sparse embedding provider (CPU-only)
sparse_provider = SpladeProvider(
    model_name="opensearch-project/opensearch-neural-sparse-encoding-doc-v2-distill",
    device="cpu",
)

# Create vector store and index
vector_store = LocalMilvusStore(
    db_path="./index.db",
    collection_name="verbatim_rag",
    enable_dense=False,
    enable_sparse=True,
)

index = VerbatimIndex(
    vector_store=vector_store,
    sparse_provider=sparse_provider,
)
index.add_documents([document])

# Query: the Transformer writes the answers from the retrieved chunks
client = LLMClient("gpt-4o-mini")
transformer = Transformer(
    LLMSpanExtractor(client),
    TemplateManager(llm_client=client, default_mode="contextual"),
)
rag = VerbatimRAG(index, transformer=transformer)
response = rag.query("What is the main contribution?", k=5)
print(response.answer)

k and the other retrieval settings are arguments of each query, so one VerbatimRAG can serve concurrent queries.

Using the ModernBERT Extractor

Replace the LLM-based extractor with the current v2 encoder model and static rendering—no generative LLM API call is made:

from verbatim_rag.extractors import ModelSpanExtractor

extractor = ModelSpanExtractor.from_pretrained("KRLabsOrg/verbatim-rag-modern-bert-v2")
rag = VerbatimRAG(index, transformer=Transformer(extractor, TemplateManager()))
response = rag.query("Main findings?", k=5)

from_pretrained downloads and loads the model when you call it. TemplateManager() writes the answers with the static template.

Upgrading from 0.2.x? See Migrating to 0.3.0.