AI & automation

Knowledge retrieval with verifiable source passages

Find relevant passages while retaining verifiable access, version and exact source content.

Symbolic source motif in AZ colours: an ordinary red cloth book on charcoal stone has a red clip marking a German approval-rule paragraph. An ivory reference card identifies Ablauf, v1 and paragraph one.

A knowledge answer begins with a verifiable passage

Retrieval means finding relevant content in an existing collection and providing it for a further task. A convincing answer alone is insufficient for dependable knowledge search. Its underlying passage must be identifiable. Someone checking a claim needs the specific document, version and paragraph rather than a list of approximately related filenames that leaves the evidence hidden inside an entire collection.

Our original example uses five explicitly fictional documents and an executed Python module with SQLite FTS5. It searches words, returns verbatim paragraphs and can resolve the resulting citations again. No language model is called and no answer is generated automatically. The evidence concerns the preceding retrieval stage on which a later explanation could build while retaining an inspectable connection to its sources.

Two boundaries matter. Only content the current context may access should enter the search. And a found passage is not automatically true or authoritative. A note may be stale or contain hostile instructions. This module treats passages as data, granting them neither additional permissions nor authority to override application rules. Relevance to a query does not confer operational trust.

Separate retrieval from answer generation

A language-model integration can use retrieved material as additional context for an answer. The RAG paper by Lewis and colleagues describes a connection between retrieval and generation. Our demonstration does not reproduce that research architecture. It shows a deliberately small lexical retrieval stage whose source relationship can be verified independently of the language later used to explain or summarise the result.

This separation helps locate failure. If the relevant passage was never found, rewriting an answer prompt has limited value. If the correct passage was available but misinterpreted, that is another problem. If a citation points to text that has since changed, its source version needs attention. The broad statement that AI was wrong conceals these different causes and the different corrections they require.

A developed product should therefore keep the stages inspectable: what was searched, which authorised material was available and which passages entered further processing? Record that relationship with appropriate minimisation. It does not justify copying every confidential query and document into a general log. Actual operational records need a separate design that preserves useful accountability without creating an unnecessary secondary store of sensitive content.

Lewis et al.: Retrieval-Augmented Generation

Organise the collection and its ownership first

Each fictional document has a unique identifier, tenant, permitted groups, version and text. Version describes editorial state, while a separate fingerprint is calculated from the complete text. Together these identify the content underlying a later citation. A title alone would be insufficient because several versions can share that title while stating materially different rules or retaining different contextual limitations.

The caller context also contains tenant and groups. It is trusted server context. This function implements no login and does not accept that identity from unchecked request JSON. A real application must derive it from its authoritative identity system. Choosing a group in an arbitrary form is not an access-control decision, even if the selected word happens to match a document’s permitted group.

The example rejects duplicate document identifiers. Otherwise resolving an identifier later might select a different document from the one retrieved. Version semantics also need agreement in the real collection. The fingerprint additionally detects text changes made without a version increment. An executed counterexample deliberately changes the underlying content while retaining its version label, showing why that extra connection is useful.

Apply access before indexing and ranking

The module first selects documents from the correct tenant with at least one shared permitted group. Inactive context receives no results. Only those authorised texts enter the small search index. This does more than hide forbidden result rows after searching. The experiment’s ranking statistics also derive solely from the content explicitly allowed before indexing, avoiding an accidental dependence on a forbidden document.

That distinction matters because a shared index can consider additional material when calculating relevance. For this bounded demonstration, every call creates an in-memory index from the allowed subset. Adding a very long unauthorised document changes neither the existing results nor their scores. The actual comparison is included in the test set and makes this chosen boundary observable rather than merely asserted in prose.

This simple approach is not a scaling design for a large organisation. A production retrieval service may need a different suitable indexing and permission model. What matters is a verified boundary before retrieval and downstream disclosure. It should not depend on asking a language model politely to avoid mentioning material it should never have received as context in the first place.

Tie paragraphs to their original source state

Example text is divided into paragraphs at explicitly agreed blank lines. Paragraph numbers refer to that split in the complete original text. Each result includes its number, verbatim content, document identifier, version and fingerprint. A citation can therefore be resolved again. Empty intervening parts do not silently renumber the original split because counting follows the full source structure used by this contract.

The rule is intentionally simple. Real PDF files, tables and editorial HTML require other units. A table value often needs its heading and row meaning, while an isolated sentence may lose a limitation explained immediately before it. Appropriate segmentation follows the actual material and needs review for whether the resulting unit still preserves enough meaning to be understood and assessed correctly.

Very large sections may exceed a downstream context boundary, while tiny fragments can separate qualifications from claims. A fixed number alone does not solve the problem. Review representative sources alongside representative questions. This experiment uses short inspectable paragraphs so every reference and countercheck remains understandable. It claims no universally optimal chunk size for all document formats or knowledge-search tasks.

Define the search meaning without overstating language understanding

The field accepts at most 200 characters and twelve distinct words. Input is normalised to NFC and lowercased; control characters and oversized words are rejected. All words must occur in one paragraph. There is no spelling correction, question interpretation or vector-based semantic similarity. The demonstration query zwei Personen is a keyword query, not evidence that the program understands arbitrary natural-language questions.

SQLite receives search values through bound parameters. Words are also constructed as literal search components, so entered OR is not executed as an unrestricted query operator. An asterisk does not create user-controlled prefix search. The code shows which punctuation disappears during tokenisation, and tests explicitly check that meaning. A successful ordinary query is not assumed to establish correct handling of every other input.

German words receive no automatic stemming, and missing umlaut marks are not casually treated as the same spelling. This may be inadequate for a real product, but the boundary is explicit and testable. Semantic retrieval would need its own quality cases while preserving the same permission and citation contract. More linguistic flexibility must not erase the inspectable relationship between an answer and its actual source passage.

Read ranking as order rather than truth

FTS5 supplies a BM25 score for matching paragraphs. The implementation orders by that score, then document identifier and paragraph number to settle ties deterministically. These are technical ranking values for this search run, not percentages expressing truth or reliability. A highly ranked passage can still be inaccurate, stale or unsuitable for the specific business decision someone wants to make.

Ranking depends on the actual indexed collection, so meaningful comparisons need a defined corpus and query. An arbitrary number from another collection is not a directly comparable confidence measure. The evidence retains actual scores in result objects but does not use them as business approval. Their role remains selecting a bounded set of passages that can subsequently be read and evaluated.

The result count is explicitly limited. That does not prevent a relevant passage from being omitted. A real application needs known questions and expected authoritative passages, then checks whether retrieval reaches them and which unsuitable passages appear alongside. A nicely formatted answer would not, on its own, establish retrieval quality because fluent wording can conceal a missing or inappropriate source.

SQLite: FTS5 und BM25

Execute the complete retrieval and resolution module

The following Python code covers registry checks, query handling, access filtering, indexing authorised paragraphs, searching and returning citations. Another function resolves a citation against the currently accessible collection. It checks version, fingerprint and the actual paragraph text. A previously valid identifier alone is not enough to establish that the same evidence remains available and unchanged at the time it is inspected.

Resolution also rechecks current access. Removing a group or document means an old citation cannot simply continue to count as available evidence. Tests exercise those changes explicitly. A real interface needs a suitable explanation for unavailable sources. It must not confuse a reference shown earlier with a currently authorised source that the user can still open or rely upon.

import hashlib
import re
import sqlite3
import unicodedata
from dataclasses import dataclass

@dataclass(frozen=True)
class Actor:
    tenant: str
    groups: frozenset[str]
    active: bool = True

@dataclass(frozen=True)
class Document:
    id: str
    tenant: str
    groups: frozenset[str]
    version: str
    text: str

def accessible(actor, doc):
    # Actor and registry are trusted server context, not request JSON.
    return actor.active and actor.tenant == doc.tenant and bool(actor.groups & doc.groups)

def fingerprint(doc):
    return hashlib.sha256(doc.text.encode("utf-8")).hexdigest()

def check_registry(documents):
    if len({doc.id for doc in documents}) != len(documents):
        raise ValueError("Dokumentkennungen müssen eindeutig sein.")

def query_terms(query):
    if not isinstance(query, str) or len(query) > 200:
        raise ValueError("Ungültige Suchanfrage.")
    if any(ord(c) < 32 or ord(c) == 127 for c in query):
        raise ValueError("Steuerzeichen sind nicht erlaubt.")
    normal = unicodedata.normalize("NFC", query).lower()
    words = list(dict.fromkeys(re.findall(r"[^\W_]+", normal, re.UNICODE)))
    if len(words) > 12 or any(len(word) > 40 for word in words):
        raise ValueError("Zu viele oder zu lange Suchwörter.")
    return words

def retrieve(actor, query, documents, limit=3):
    if type(limit) is not int or not 1 <= limit <= 5:
        raise ValueError("Ungültige Treffergrenze.")
    check_registry(documents)
    words = query_terms(query)
    if not words or not actor.active:
        return []
    # Access filtering happens before indexing and BM25 statistics.
    allowed = [doc for doc in documents if accessible(actor, doc)]
    by_id = {doc.id: doc for doc in allowed}
    con = sqlite3.connect(":memory:")
    try:
        con.row_factory = sqlite3.Row
        con.execute("CREATE VIRTUAL TABLE fragments USING fts5(doc_id UNINDEXED, paragraph UNINDEXED, quote, tokenize='unicode61 remove_diacritics 0')")
        for doc in allowed:
            for number, text in enumerate(doc.text.split("\n\n"), 1):
                if text.strip():
                    con.execute("INSERT INTO fragments(doc_id,paragraph,quote) VALUES(?,?,?)", (doc.id, number, text))
        # Tokens contain letters/numbers only; operators stay literal words.
        expression = " AND ".join('"' + word + '"' for word in words)
        rows = con.execute("SELECT doc_id,paragraph,quote,bm25(fragments) AS score FROM fragments WHERE fragments MATCH ? ORDER BY score,doc_id,CAST(paragraph AS INTEGER) LIMIT ?", (expression, limit)).fetchall()
        return [{"document_id": row["doc_id"], "version": by_id[row["doc_id"]].version,
                 "content_sha256": fingerprint(by_id[row["doc_id"]]),
                 "paragraph": int(row["paragraph"]), "quote": row["quote"], "score": row["score"]}
                for row in rows]
    finally:
        con.close()

def resolve_citation(actor, citation, documents):
    check_registry(documents)
    doc = next((doc for doc in documents if doc.id == citation.get("document_id")), None)
    if doc is None or not accessible(actor, doc):
        return {"ok": False, "reason": "unavailable"}
    if citation.get("version") != doc.version or citation.get("content_sha256") != fingerprint(doc):
        return {"ok": False, "reason": "source_changed"}
    number = citation.get("paragraph")
    parts = doc.text.split("\n\n")
    if type(number) is not int or not 1 <= number <= len(parts) or citation.get("quote") != parts[number-1]:
        return {"ok": False, "reason": "quote_mismatch"}
    return {"ok": True, "document_id": doc.id, "paragraph": number, "quote": parts[number-1]}

Make source changes visible

Changing text while retaining its version causes resolution to fail because the fingerprint differs. Changing only the version also prevents the old reference from silently becoming current evidence. Both cases are executed. This strict choice belongs to the small contract and avoids attaching an earlier assertion to a different source state without an explicit editorial or application decision.

A production collection may keep prior versions available to authorised readers. It then needs to identify whether a citation supports historical or current state. A fingerprint does not make its content trustworthy or replace approval of the source. It helps establish that the checked text is the same text being referenced. Trust, authority and applicability remain additional properties of the material and process.

Tampered paragraph numbers and invented quotations are rejected too. Resolution compares the complete assigned paragraph rather than looking for vaguely similar text somewhere in the document. This binds the reference to its agreed position. Other formats need a corresponding stable locator, such as page and text region or an editorial section with its own durable identifier and version relationship.

Retrieved instructions remain untrusted content

One fictional note explicitly demands ignoring rules and publishing secret data. Retrieval can find that note as matching text. Finding it does not establish authority to obey it. The module returns its literal content as data and executes none of it. The experiment contains no attached model call or writing-tool action that could transform that source passage into an operational command.

This boundary becomes especially important before answer generation. A source can contain relevant business information and an injected instruction simultaneously. Being present in a search index does not turn it into a system rule. Applications must separate source content from actual permissions. An additional screening mechanism should likewise not be described as a flawless detector of every possible instruction embedded in retrieved material.

Our counterexample does not test automatic detection of the hostile instruction. It shows that such content can still appear as a source. The honest consequence is appropriately bounded downstream processing. A knowledge answer should not acquire new data-access or publication authority from a paragraph it retrieved. Additional isolation depends on the real actions the surrounding system actually permits and therefore needs its own assessment.

OWASP: Prompt Injection Prevention

Actual citation for the German keyword query zwei Personen: document ablauf, version v1 and verbatim paragraph one. Counterchecks reject a changed source and withdrawn access; an unmatched query returns zero hits. Labels are German.
Actual results from the executed retrieval module. Paragraph, version and text fingerprint connect a citation to its original. The 51 checks concern retrieval and references, not measured language-model answer accuracy. Explanatory labels remain German.

Review 51 executed conditions for results and source references

The evidence comprises 51 executed conditions with Python 3.12.14 and SQLite 3.53.1. The keyword query zwei Personen finds paragraph one of the Ablauf document. Its exact text, version and fingerprint are checked. Every result for Freigabe is resolved again. A foreign tenant and disallowed group are absent from results, establishing the specific access cases described by the example.

Further cases include inactive contexts, missing groups, unknown tenants, empty queries and unmatched terms. AND semantics, literal operator words, absence of prefix search and the chosen Unicode behaviour are checked too. The long unauthorised additional document leaves the result list and its scores unchanged. Invalid limits and duplicate identifiers are explicitly rejected instead of relying on accidental library behaviour.

Changed text, revised versions, withdrawn access, removed sources and altered quotations produce the intended failures. The hostile instruction remains literal data. The illustration uses actual returns from this program. No language model was evaluated and no answer-accuracy measurement was made. Evidence concerns this retrieval implementation, its access boundary and the inspectable source relationship, not a general enterprise knowledge-service guarantee.

No matching passage is an acceptable outcome

An unknown query returns an empty list rather than an invented substitute answer. A real interface can explain that no matching passage was found in the accessible collection. That is more precise than saying no answer exists anywhere. The right document may be missing, or the selected wording may not reach its content under this deliberately limited lexical search contract.

For ambiguous questions, targeted clarification can be preferable to unsupported prose. Conflicting sources also need visible treatment. If two versions describe different approval rules, an answer should not simply select whichever sentence is easier to phrase. Source ownership, freshness and approval status need joint consideration. Retrieval order alone does not settle that conflict or turn a convenient paragraph into a business policy.

Even a complete sentence with a citation can be wrong if its passage does not support the assertion. A later generated answer needs review of whether each material claim is actually supported by its assigned evidence. This experiment supplies a precise reference for that work but implements no general automatic truth assessment of language subsequently generated from its retrieved text.

Measure quality using representative questions and expected passages

A useful evaluation set contains real typical tasks and known authoritative passages. Include unanswerable queries, ambiguous terms, access groups and changed sources. Assess separately whether the relevant passage was found and whether a later answer used it correctly. That identifies whether retrieval, source material or wording needs improvement, rather than changing the most visible component without understanding where the actual failure occurred.

The small collection here is a technical starting point, not a representative organisational evaluation or a general success rate. Lexical matching can miss different wording for the same idea. A semantic extension should be assessed against the same explicit cases. A larger model name alone does not establish improved retrieval quality or show that permission and citation boundaries have survived the architectural change.

Operations also include updating, deleting and changing access. A persistent index must reflect those transitions. This example rebuilds from the allowed subset per call, avoiding some synchronisation questions at the cost of limited scale. Replacing it with a persistent service creates responsibilities that need new evidence. The local demonstration cannot simply be transferred unchanged to a different architecture and workload.

Integrate a traceable knowledge search step by step

Begin with a bounded collection, known tasks and verifiable passages. Define who may access which content and how document state is identified. Only then add a language summary above that foundation. It can subsequently be traced to actual evidence and investigated when wrong, instead of merely sounding persuasive while the relevant source remains impossible for a reader to locate.

A useful interface brings claim and evidence together so verification does not require searching the entire document again. It distinguishes historical from current sources and explains unavailable references. It invents no truth percentages. This presentation is its own integration task. The module supplies concrete data for that work without claiming an enterprise search interface is already implemented or accepted.

Retrieval improves traceability only when access, source state and meaning remain connected. The following checklist helps review those relationships in a real workflow. A verifiable paragraph is a good starting point, but responsibility for its business interpretation and actual system actions still belongs to the application and the decision routes it deliberately provides to authorised people.

What a citation establishes

ComponentPurpose
Document identifierLocate the unique registered source
Version and fingerprintCheck the specific text state
Paragraph and verbatim quoteResolve the exact passage again
Access checkVerify current availability to this context
Retrieval scoreOrder results, not certify truth
Business assessmentJudge meaning, authority and applicability

Verifiable retrieval checklist

  • Define collection ownership and versions clearly.
  • Derive caller context from authoritative identity.
  • Exclude forbidden material before retrieval and disclosure.
  • Bind passages to original state and precise position.
  • Explain search semantics and their limits.
  • Do not present ranking as truth percentages.
  • Recheck changed sources and withdrawn access.
  • Do not promote source instructions into system authority.
  • Treat no matching passage as a valid outcome.
  • Evaluate retrieval and later answers separately with representative counterexamples.

Sources and review scope

Primary sources reviewed on 9 November 2026. Five original fictional documents and 51 executed Python/SQLite conditions. Lexical retrieval without a model call; no measured answer accuracy or comprehensive enterprise-search acceptance claimed.

Let’s move forward

What would you like to change?

An idea, a specific challenge or your next step with AI. We’re listening.

Let’s talk