Skip to content
tnsaijava agent framework

RAG diagnostics

com.tnsai.intelligence.rag.binding.RagDiagnostics is @since 0.15.0 (TnsAI@32e17445, PR #199 / TAN-5888). It ships in 0.15.0 and later — see Installation for the coordinates.

R5's four code items are one sentence: a RAG downgrade is visible. RagDiagnostics is the pull half of that sentence — you can ask what a Role's declarations actually resolved to before a bad answer costs a debugging session.

What it does

The snapshot answers the questions the binding already computed and used to hide:

  • which EmbeddingFunction is in use, and whether it is the bundled hash fallback
  • whether a reranker, query expander, or graph-store provider is installed — absence is a reported fact, not an exception
  • per source: name, type, detected format, document count, chunk count, and ingest fingerprint
  • the effective @Retrieval configuration after resolution

It does not change retrieval behaviour. It is not an annotation, not a log line, and not telemetry. You ask; the framework answers.

How to ask

RagDiagnostics.of(roleType) never ingests. It snapshots a binding that already exists for that Role class. If nothing has been built yet, use the explicit flag: RagDiagnostics.of(roleType, true) constructs the binding (and therefore runs ingest).

report() returns unstable human-readable text. Paste it into an issue; do not parse it. Typed accessors on the snapshot are the supported surface for tests and health checks.

package com.example.tnsai.docs;

import com.tnsai.annotations.ActionSpec;
import com.tnsai.annotations.KnowledgeSource;
import com.tnsai.annotations.Retrieval;
import com.tnsai.enums.ActionType;
import com.tnsai.intelligence.rag.binding.RagDiagnostics;
import com.tnsai.models.role.RoleIdentity;
import com.tnsai.roles.Role;

public final class RagDiagnosticsExample {
    private RagDiagnosticsExample() {}

    @KnowledgeSource(
        name = "product-docs",
        type = KnowledgeSource.KnowledgeType.FILE,
        path = "knowledge/products")
    @Retrieval(
        strategy = Retrieval.Strategy.SEMANTIC,
        sources = "product-docs",
        queryParam = "question",
        topK = 5)
    public static final class FaqRole extends Role {
        @Override
        public RoleIdentity getIdentity() {
            return new RoleIdentity("faq", "FAQ answers", "support");
        }

        @ActionSpec(
            type = ActionType.LLM,
            description = "Answer from product documentation")
        public String answer(String question) {
            return question;
        }
    }

    public static String inspectExistingBinding() {
        return RagDiagnostics.of(FaqRole.class).report();
    }

    public static String inspectByBuilding() {
        return RagDiagnostics.of(FaqRole.class, true).report();
    }
}

inspectExistingBinding() is the cheap question: what did this Role already become? inspectByBuilding() is the explicit "build it so I can see it" path. Do not call the two-argument form from a health endpoint unless you intend to pay ingest.

Four silent downgrades, now visible

These four used to fail as plausible but worse answers. They now surface as a warning, a named exception, a startup configuration error, or a diagnostic you can ask.

Hash embedding fallback

No installed EmbeddingFunction plus a vector-backed @Retrieval strategy used to rank by token overlap with no log line. Framework main (TnsAI@89d41d1b, PR #195 / TAN-5885) emits one process-lifetime WARN per Role class when SEMANTIC, HYBRID, HIERARCHICAL, or TEMPORAL resolve to the bundled hash embedding. KEYWORD, GRAPH, REASONING, and MULTI_QUERY stay silent on this path. The fallback still runs — the defect was the silence.

What you see, what it means, and what to do: Embeddings — hash-embedding fallback.

RagDiagnostics distinguishes the fallback case from an installed provider without throwing.

Absent expander and reranker messages

TnsAI ships no bundled Reranker and no bundled QueryExpander. RerankerRegistry already named the cause, the registered providers, and two remedies. QueryExpanderRegistry used to throw only "No QueryExpander provider supports mode … and model '…'".

TnsAI 0.15.0 (TnsAI PR #197 / TAN-5886) brings the expander message to the same skeleton: registered providers, that TnsAI ships no built-in expander, and a remedy that matches the (strategy, queryExpansion) pair — not a copy-pasted queryExpansion = NONE on every shape.

RagDiagnostics reports installed or absent status for both seams without throwing. Use that when you want the fact before the first query; use the exception message when a query already failed.

Eager reranker and expander validation

On Maven Central 0.14.1, a typo in rerankerModel or queryExpansionModel compiles, starts, and throws on the first query — inside the span @Retrieval.onFailure guards. CONTINUE can then emit an ungrounded answer. GRAPH already refused that shape: which provider can serve the annotation is fixed by the classpath, never by request data.

TnsAI 0.15.0 (TnsAI@cb14410d, PR #198 / TAN-5887) resolves those two model names when Role.create establishes the retrieval configuration. An unservable name is a wiring-time configuration error, above onFailure. Failures a provider raises while ranking or expanding still follow onFailure.

Roles with no effective @Retrieval, and declarations that do not ask for rerank or expansion, do not load those provider graphs.

See Annotation runtime status.

Inspectability

The three fixes above announce a problem. RagDiagnostics is the positive case: everything resolved, here is what you got — including corpus counts and the ingest fingerprint — plus the fallback bit you can assert in a test.

Not this page

  • A new annotation or automatic dump at startup
  • A metrics or time-series surface for ingest
  • Maven Central 0.14.1 behaviour — this type does not exist there
  • How to install an EmbeddingFunction provider — see Strategies