anchor-guard
Claim-level groundedness scoring for RAG-generated answers.
anchor-guard closes the blind spot that anchor-eval leaves open: a retrieval pipeline can score 95% on span recall and still produce hallucinated answers. anchor-guard decomposes each generated answer into atomic claims, attributes every claim to the exact character range in the source document that supports it, and blocks CI deploys when groundedness regresses — entirely on-prem, with no data egress.
The problem anchor-guard solves
anchor-eval proves that your retriever found the right spans. It does not prove that the LLM built a grounded answer from them.
Consider this scenario:
- The retriever correctly surfaces clause §4.2(b) from
contract.pdf— anchor-eval scores a hit. - The LLM ignores that clause and asserts the indemnification cap is $500K when the clause says $1M.
- anchor-eval scores: pass. The answer is wrong.
This failure is not hypothetical. It is the default mode of every RAG pipeline that evaluates retrieval but not generation. For a legal, healthcare, or financial services deployment, a confident wrong answer is worse than no answer.
anchor-guard catches it by asking a different question: is every claim in this answer actually entailed by the source span it was supposedly drawn from?
How it works
Your RAG pipeline
─────────────────────────────────────────────────────────
User query ──► Retriever ──► Context spans ──► LLM ──► Answer
│ │
│ (spans + answer go to anchor-guard) │
└───────────────────────────────────────┘
│
anchor guard check
│
┌─────────────────▼──────────────────────┐
│ 1. Decompose answer into atomic claims │
│ 2. Attribute each claim to a source span│
│ 3. Score NLI entailment per claim │
│ 4. Check abstention correctness │
│ 5. Detect PII / restricted content │
│ 6. Generate claim-level citations │
│ 7. Gate CI on regression vs. baseline │
└────────────────────────────────────────-┘
│
GuardReport (JSON)
│
┌─────────────────┼─────────────────┐
│ │ │
anchor-review anchor-ledger CI exit code
(SME HTML bundle) (audit evidence) (0 / 1 / 2)
anchor-guard runs entirely out of the production request path. It consumes answers after the fact — from files, OTel traces, or test fixtures. It cannot cause an outage and never adds to production latency.
Quickstart
pip install anchor-guard
# or: uv add anchor-guard
# Run a groundedness check on a set of answers
anchor guard check answers.json --corpus ./docs
# Save a groundedness baseline for CI regression gating
anchor guard baseline answers.json --output guard_baseline.json
# Regression gate: exits 1 if groundedness drops > 2%
anchor guard check answers.json --corpus ./docs \
--baseline guard_baseline.json
# Generate an HTML review bundle for an SME
anchor guard review guard_report.json --output review.html
answers.json format
anchor-guard expects a JSON array of answer objects. The minimal shape:
[
{
"answer_id": "ans_001",
"question_id": "q_001",
"raw_answer": "The indemnification cap is $1M per the contract.",
"retrieved_spans": [
{
"doc_id": "contract.pdf",
"char_start": 4210,
"char_end": 4580,
"text": "The aggregate liability of either party shall not exceed one million dollars ($1,000,000)..."
}
]
}
]
When anchor-eval's QuestionSet is provided via --question-set, each answer is
additionally checked against its expected_span — the character range the correct
answer should be drawn from. This is the stronger, two-layer check described in
Dependency on anchor-eval.
Commands
| Command | Description |
|---|---|
anchor guard check |
Run the full pipeline: decompose → attribute → disclose → cite → gate. |
anchor guard baseline |
Save current metrics as the regression baseline for a (prompt_version, model_version) pair. |
anchor guard review |
Generate a self-contained HTML bundle for SME claim attribution review. |
anchor guard check
anchor guard check <answers.json> [options]
Options:
--question-set, -q PATH anchor-eval QuestionSet JSON (enables IOU gating against expected_span)
--corpus, -C PATH Source document directory (required for span text lookup)
--config, -c PATH GuardConfig JSON (thresholds, NLI model, features)
--output, -o PATH Write GuardReport JSON to this path
--review-bundle,-r PATH Also write an anchor-review HTML bundle
--baseline, -b PATH Compare against this baseline; exits 1 on regression
Exit codes follow the same convention as anchor ci:
| Exit | Meaning |
|---|---|
0 |
All checks passed, no regression |
1 |
Groundedness or abstention regressed beyond tolerance |
2 |
Corpus drift or configuration mismatch |
anchor guard baseline
anchor guard baseline <answers.json> [options]
Options:
--output, -o PATH Baseline file path (default: guard_baseline.json)
--config, -c PATH GuardConfig JSON
Saves groundedness_rate, abstention_pass_rate, and ungrounded_claim_rate keyed to the
(prompt_version, model_version) pair in the config. All three metrics participate in
regression gating — a rise in ungrounded_claim_rate beyond tolerance triggers exit 1 the
same as a drop in groundedness. When the version pair changes —
which happens often in on-prem deployments where teams swap local models for
GPU cost reasons — and no baseline exists for the new pair, a warning is emitted
rather than an error.
anchor guard review
anchor guard review <guard_report.json> [options]
Options:
--output, -o PATH HTML bundle path (default: guard_review.html)
Generates the anchor-review bundle. See SME calibration for the full workflow.
The five checks
1. Claim decomposition
The answer is split into atomic, independently verifiable claims. Each claim must be a complete declarative sentence that can be checked against a single source span without requiring context from other claims.
Primary: LLM-backed decomposition via any OpenAI-compatible /v1/chat/completions
endpoint — local or external, whichever is configured for the environment.
Fallback: Sentence-boundary splitting when no LLM endpoint is configured (lower
confidence, flagged in the report).
Answer: "The termination notice period is 30 days. Either party may terminate for
convenience. Liquidated damages apply only to Section 12 breaches."
Claims:
[0] "The termination notice period is 30 days."
[1] "Either party may terminate for convenience."
[2] "Liquidated damages apply only to Section 12 breaches."
2. Span-level attribution
Each claim is attributed to the retrieved span that best supports it. Attribution uses a two-stage pipeline:
Stage 1 — NLI entailment: a small local model scores whether the span text entails the claim. This is a narrow task (span-level entailment, not open-domain NLI) that small models handle well — no frontier judge required.
Stage 2 — IOU gate (when anchor-eval QuestionSet is provided): the winning span
is compared against the question's expected_span using intersection-over-union. A
claim is only classified as GROUNDED when both NLI confidence and IOU meet their
thresholds.
Claim: "The termination notice period is 30 days."
Span: contract.pdf §4210–4580 (NLI confidence: 0.92, IOU vs expected_span: 0.71)
Status: GROUNDED ✓
Claim: "Liquidated damages apply only to Section 12 breaches."
Span: contract.pdf §7100–7350 (NLI confidence: 0.41)
Status: UNGROUNDED ✗ ← LLM hallucinated; span does not entail this claim
| Status | Meaning |
|---|---|
GROUNDED |
NLI confidence ≥ threshold and (IOU ≥ threshold or no expected_span) |
UNGROUNDED |
No candidate span achieves NLI confidence ≥ threshold |
ABSTAINED |
System correctly declined to answer a question with no corpus answer |
PENDING_SME |
Attribution awaits SME review via anchor-review |
Default thresholds (configurable in GuardConfig):
| Parameter | Default |
|---|---|
iou_threshold |
0.5 |
nli_confidence_threshold |
0.7 |
3. Abstention correctness
For questions where no corpus answer exists (expected_span: null in the QuestionSet,
corresponding to anchor-eval's negative_existence and implicit_negative archetypes),
anchor-guard verifies the system responds with a recognizable abstention rather than
fabricating an answer.
Two-sided check:
- Should abstain, didn't: system hallucinated where it should have said "not in corpus." Flagged as high-severity.
- Should answer, abstained: system incorrectly declined a question it should have answered.
abstention_pass_rate is a top-level metric in GuardReport and participates in regression gating.
When enable_abstention_check is false, it is omitted (null) from the report and skipped in the regression gate — a disabled check never triggers a spurious regression.
4. Disclosure detection
Scans generated answer text for PII and restricted content. Runs entirely locally — no external API calls, no data egress.
| Finding type | Severity | Pattern |
|---|---|---|
pii_email |
High | Email addresses |
pii_phone |
High | US phone numbers |
pii_ssn |
Critical | Social Security Numbers |
pii_credit_card |
Critical | Credit card numbers |
restricted_term |
Medium | Terms from the active guard pack |
Disclosure findings are reported independently from groundedness — a clean answer can still contain PII, and a hallucinated answer may not. They are separate quality dimensions.
5. Citation generation
For every GROUNDED claim, anchor-guard emits a claim-level citation:
{
"claim_id": "c_001",
"claim_text": "The termination notice period is 30 days.",
"doc_id": "contract.pdf",
"char_start": 4210,
"char_end": 4580,
"nli_confidence": 0.92
}
Inline format: [contract.pdf §4210–4580]
Document-level citations ("Source: contract.pdf") are insufficient for legal and regulated use — the precise clause is required. Claim-level citations enable auditors to trace each assertion to the exact character range.
LLM backend configuration
Recommendation: use a model running inside your private network. NLI scoring and claim decomposition send retrieved span text and generated answer text to the configured endpoint. For legal, healthcare, and financial deployments that data is almost certainly regulated. A local model keeps it inside your infrastructure with no additional controls required. External providers are supported but require an explicit opt-in — see Data residency and the allow_external_calls flag below.
anchor-guard sends NLI scoring and claim decomposition prompts to a
/v1/chat/completions endpoint. Which endpoint that is — and whether it sits inside
your private network or reaches an external service — is entirely your decision.
# Any OpenAI-compatible server on the private network
# (vLLM, llama.cpp server, LM Studio, LocalAI, Ollama, HF TGI, …)
anchor guard check answers.json --corpus ./docs
# Custom endpoint and model
anchor guard check answers.json --corpus ./docs \
--config guard_config.json
guard_config.json:
{
"nli_provider": {
"model": "llama3.2:3b",
"base_url": "http://gpu-host:8000",
"timeout_seconds": 60,
"api_key": null
},
"iou_threshold": 0.5,
"nli_confidence_threshold": 0.7,
"enable_disclosure_check": true,
"enable_abstention_check": true,
"enable_citation_generation": true
}
base_url is the scheme + host + port of the server. anchor-guard always POSTs to
{base_url}/v1/chat/completions. Any server that exposes this path works without
additional configuration.
Credential resolution
api_key in the config file is optional. anchor-guard reads credentials from environment
variables at runtime — no library files to edit, no secrets in version control:
| Env var | Used for |
|---|---|
ANCHOR_NLI_API_KEY |
NLI scoring endpoint (nli_provider) |
ANCHOR_DECOMPOSE_API_KEY |
Claim decomposition endpoint (decompose_provider); falls back to ANCHOR_NLI_API_KEY if unset |
export ANCHOR_NLI_API_KEY=sk-...
anchor guard check answers.json --config guard_config.json
Separate decomposition and scoring models
By default, both claim decomposition and NLI scoring use nli_provider. To use a
larger model for decomposition (higher fidelity) and a faster model for NLI (lower
latency), add a separate decompose_provider:
{
"decompose_provider": {
"model": "llama3.1:70b",
"base_url": "http://big-gpu:8000",
"timeout_seconds": 120
},
"nli_provider": {
"model": "llama3.2:3b",
"base_url": "http://fast-gpu:8000",
"timeout_seconds": 30
}
}
When decompose_provider is absent, nli_provider is used for both stages.
Built-in provider wrappers
For external services, anchor-guard ships named wrappers that pre-configure the right endpoint and read API keys from standard environment variables:
| Provider | NLI scorer | Claim splitter | Env var |
|---|---|---|---|
| OpenAI | OpenAINLIScorer |
OpenAIClaimSplitter |
OPENAI_API_KEY |
| Anthropic | AnthropicNLIScorer |
AnthropicClaimSplitter |
ANTHROPIC_API_KEY |
| Azure OpenAI | AzureOpenAINLIScorer |
AzureOpenAIClaimSplitter |
AZURE_OPENAI_API_KEY |
| Mistral AI | MistralNLIScorer |
MistralClaimSplitter |
MISTRAL_API_KEY |
| Groq | GroqNLIScorer |
GroqClaimSplitter |
GROQ_API_KEY |
| Together AI | TogetherNLIScorer |
TogetherClaimSplitter |
TOGETHER_API_KEY |
Import from anchor_guard.attribute.providers (scorers) and
anchor_guard.decompose.providers (splitters).
Azure OpenAI via config file: set base_url to the resource base URL and model to the deployment name:
{
"nli_provider": {
"model": "my-gpt4o-deployment",
"base_url": "https://mycompany-oai.openai.azure.com",
"api_key": null
},
"allow_external_calls": true
}
The factory extracts the resource name from the hostname and routes to AzureOpenAINLIScorer / AzureOpenAIClaimSplitter automatically. AZURE_OPENAI_API_KEY is read from the environment when api_key is null.
Custom backends
For any other service — gRPC, a proprietary REST API, an in-process Python model — subclass the abstract base and implement one method:
from anchor_guard.attribute.nli_scorer import NLIScorer
class MyNLIScorer(NLIScorer):
def score(self, premise: str, hypothesis: str) -> float:
return my_internal_service.entailment_confidence(premise, hypothesis)
The same pattern applies to ClaimSplitter in anchor_guard.decompose.claim_splitter.
Data residency and the allow_external_calls flag
External provider wrappers are blocked by default. Calling one without opting in
raises ExternalCallBlockedError immediately, before any network request is made.
The flag is checked on every call, not once at construction time.
There are two usage paths, each with its own opt-in point:
Via the pipeline (CLI / GuardConfig)
Set allow_external_calls=True in GuardConfig. The pipeline factory
(build_nli_scorer / build_claim_splitter in anchor_guard.pipeline) inspects
base_url, detects known external provider hosts, routes to the appropriate named
provider class, and passes config.allow_external_calls through. Every subsequent
score() and split() call re-checks the flag — there is no way to call an external
provider through the pipeline without it being True.
{
"nli_provider": {
"model": "gpt-4o-mini",
"base_url": "https://api.openai.com",
"api_key": null
},
"allow_external_calls": true
}
Via the Python API (direct construction)
When constructing a provider class directly — outside the pipeline — pass
allow_external_calls=True to the constructor:
from anchor_guard.attribute.providers import OpenAINLIScorer
from anchor_guard.decompose.providers import OpenAIClaimSplitter
scorer = OpenAINLIScorer(allow_external_calls=True) # reads OPENAI_API_KEY from env
splitter = OpenAIClaimSplitter(allow_external_calls=True)
For OpenAI-compatible local/private endpoints (the default path), use ANCHOR_NLI_API_KEY
instead of the provider-specific env vars above.
CI / zero-dependency fallback
For CI environments without a running LLM endpoint, set
"model": "sentence-boundary-fallback" to use the deterministic sentence-splitting
fallback. Attribution quality is lower but the pipeline runs with zero dependencies.
In this mode the NLI scorer is replaced with a fixture that returns full confidence (1.0) for every claim, so the pipeline completes without a false regression exit. This mode is for smoke-testing pipeline wiring only — groundedness scores produced in fallback mode are not meaningful and must not be committed as a baseline.
Dependency on anchor-eval
anchor-guard has two operating modes depending on whether anchor-eval's QuestionSet
is provided.
Standalone mode
anchor-guard runs independently. You provide answers and the retrieved spans from your own pipeline. No anchor-eval installation required.
anchor guard check answers.json --corpus ./docs
What you get:
- NLI-based claim attribution against your provided retrieved spans
- Abstention correctness (if your answer objects include
retrieval_required) - Disclosure detection
- Claim-level citations for grounded claims
- Regression gating against a saved baseline
What you don't get:
- IOU-gated attribution against a ground-truth expected span
- Per-archetype groundedness breakdown
- Correlation between retrieval quality and generation quality
Paired mode (recommended)
When anchor-eval's QuestionSet is available, each answer is additionally checked
against its expected_span. This is the two-layer assurance claim:
"The retriever found the right span (anchor-eval), AND the LLM built its answer from that span (anchor-guard), AND the answer is grounded in the retrieved text (NLI) AND attributable to the ground-truth location (IOU)."
# Step 1: Generate and commit a QuestionSet with anchor-eval
anchor generate --corpus ./contracts --domain legal --output question_set.json
# Step 2: Run your RAG pipeline, collect answers (your code)
python run_rag.py --questions question_set.json --output answers.json
# Step 3: Score retrieval with anchor-eval
anchor ci --question-set question_set.json --corpus ./contracts \
--baseline anchor_baseline.json
# Step 4: Score generation with anchor-guard
anchor guard check answers.json --corpus ./contracts \
--question-set question_set.json \
--baseline guard_baseline.json
The two baselines are independent. A retrieval regression exits from anchor ci; a
groundedness regression exits from anchor guard check. Both gates run in the same
CI pipeline.
Shared identity key
Both products use (doc_id, char_start, char_end) in Unicode codepoints as the
durable span identity. This key survives re-chunking, re-embedding, and model swaps —
the same property that makes anchor-eval benchmarks stable across pipeline changes
applies equally to anchor-guard's attribution records.
SME calibration — anchor-review
The local NLI model is a judge, and judges make mistakes. anchor-guard's calibration track lets a domain expert (lawyer, clinician, compliance officer) review and correct individual claim attributions without touching the command line.
Workflow
anchor guard check answers.json \
--question-set question_set.json \
--review-bundle guard_review.html
│
▼
guard_review.html ◄── open in any browser, no server required
│
SME clicks through each claim attribution:
[accept] — span correctly entails this claim
[reject] — span does not support this claim
[abstain] — SME cannot determine
+ free-text comment
│
Export calibration.json (ed25519-signed)
│
anchor guard check answers.json \
--calibration calibration.json \
--baseline guard_baseline.json
│
PENDING_SME claims resolved → final groundedness_rate updated
Design properties
- Zero dependencies. The HTML bundle has all CSS and JS inlined. Opens in Chrome, Firefox, Safari, or Edge. Works in fully air-gapped environments.
- Signed export. The
calibration.jsonis signed with the reviewer's ed25519 key. anchor-guard verifies the signature before ingesting it. - Tamper-evident. The signature covers the canonical JSON of all calibration records. Any post-export modification fails verification.
- Auditable. Every calibration ingestion is logged to anchor-ledger (when installed) with reviewer identity, timestamp, and verdict.
CI/CD integration
anchor-guard fits into the same CI pipeline as anchor-eval without additional infrastructure.
GitHub Actions example
- name: Score retrieval (anchor-eval)
run: |
anchor ci \
--question-set question_set.json \
--corpus ./contracts \
--baseline anchor_baseline.json
# exits 1 on retrieval regression, blocks deploy
- name: Score generation (anchor-guard)
run: |
anchor guard check answers.json \
--corpus ./contracts \
--question-set question_set.json \
--baseline guard_baseline.json
# exits 1 on groundedness regression, blocks deploy
Updating baselines
When you intentionally improve the pipeline (new model, better chunking, tuned prompt), update both baselines:
anchor ci --question-set question_set.json --corpus . --save-baseline anchor_baseline.json
anchor guard baseline answers.json --output guard_baseline.json
Commit both files. The baselines are version-controlled alongside the corpus and the question set — they define what "acceptable" means for this version of the pipeline.
The Anchor suite
anchor-guard is the second module in the six-module Anchor assurance suite. All modules are designed for fully on-prem, air-gapped deployment — no customer data transits a vendor API.
| Module | Purpose | Phase |
|---|---|---|
| anchor-eval | Span-anchored retrieval evaluation, archetype diagnosis, CI gating | Shipped (v0.3.1) |
| anchor-guard | Claim-level groundedness, abstention, citation, disclosure | Phase 1 (this repo) |
| anchor-ledger | OTel ingestion, append-only evidence store, compliance export | Phase 1.5 |
| anchor-mirror | Production query drift, PII-redacted intent distillation | Phase 2 |
| anchor-shield | Corpus security: injection detection, poisoning, ACL integrity | Phase 2 |
| anchor-review / anchor-studio | SME calibration UI (static bundle → local web workspace) | Phase 1 → 3 |
How they connect
anchor-eval anchor-guard anchor-ledger
─────────── ──────────── ─────────────
QuestionSet ────────► expected_span OTel traces ──► local OTLP
(expected_span) IOU gate endpoint
│
GuardReport ─────────────────────────► evidence store
│
guard_review.html ──► SME ──► calibration.json
│
anchor-guard
(ingested back)
Every artefact exchange is local — files, a localhost OTLP endpoint, or a local web server. No external service dependencies at any integration point.
Governing rule
No component of the Anchor suite sits in the live production request path.
anchor-guard runs out-of-band against answer files or sampled OTel traces. The one exception — a local proxy sidecar for dev/staging/CI — is explicitly scoped to non-production environments. It is never permitted in production.
Key concepts
| Term | Definition |
|---|---|
| AtomicClaim | A single, independently verifiable assertion extracted from an answer. The unit of attribution. |
| ClaimSet | All atomic claims extracted from one answer. |
| SpanRef | (doc_id, char_start, char_end) in Unicode codepoints. The shared identity key across anchor-eval and anchor-guard. |
| NLI confidence | Entailment probability that a source span text logically supports a claim. Scored by a local model. |
| IOU threshold | Minimum intersection-over-union between the winning span and the ground-truth expected_span for a claim to be classified as GROUNDED. Only active when a QuestionSet is provided. |
| groundedness_rate | Fraction of claims classified as GROUNDED in a run. Top-level GuardReport metric. |
| abstention_pass_rate | Fraction of abstention-required questions where the system correctly declined to answer. |
| GuardReport | The complete output of an anchor-guard run: attributions, disclosure findings, regression findings, citations. |
| CalibrationExport | Signed JSON produced by an SME in anchor-review containing accept/reject verdicts per claim. |
| guard pack | JSON bundle of abstention signal phrases and restricted terms. guard-base-v0 ships with the package. |
Packs
Guard packs configure abstention detection and restricted-term scanning. The active
pack is resolved from the --corpus domain or set explicitly.
| Pack | Domain | Ships with |
|---|---|---|
guard-base-v0 |
General | anchor-guard (default) |
Custom packs:
{
"pack_id": "guard-legal-v0",
"version": "0.1.0",
"abstention_signals": [
"not addressed in the agreement",
"the contract is silent on",
"outside the scope of this document"
],
"restricted_terms": ["PRIVILEGED", "CONFIDENTIAL", "ATTORNEY-CLIENT"]
}
Python API
anchor-guard can be used as a library inside evaluation scripts and notebooks.
from anchor_guard.decompose.claim_splitter import SentenceBoundaryFallback, OpenAICompatibleClaimSplitter
from anchor_guard.attribute.nli_scorer import OpenAICompatibleNLIScorer
from anchor_guard.attribute.span_matcher import SpanMatcher
from anchor_guard.citation.generator import CitationGenerator
from anchor_guard.disclosure.detector import DisclosureDetector
from anchor_guard.models.span import SpanRef
# Decompose
splitter = SentenceBoundaryFallback()
claim_set = splitter.split(
answer_id="ans_001",
question_id="q_001",
raw_answer="The indemnification cap is $1M. Notice must be given 30 days in advance.",
)
# Attribute — point base_url at any OpenAI-compatible server in the private network
nli_scorer = OpenAICompatibleNLIScorer(base_url="http://localhost:11434", model="mistral:7b")
candidate_spans = [
(SpanRef(doc_id="contract.pdf", char_start=4210, char_end=4580), "...shall not exceed one million dollars..."),
]
matcher = SpanMatcher(nli_scorer=nli_scorer, candidate_spans=candidate_spans)
result = matcher.attribute(claim_set, expected_span=SpanRef(doc_id="contract.pdf", char_start=4210, char_end=4580))
print(f"Groundedness rate: {result.groundedness_rate:.0%}")
# Groundedness rate: 100%
# Cite
citations = CitationGenerator().generate(result)
for c in citations:
print(c.to_inline())
# [contract.pdf §4210–4580]
# Disclose
findings = DisclosureDetector().detect("ans_001", "The cap is $1M.")
# findings == [] (no PII in this answer)
Air-gapped deployment
anchor-guard is designed for fully disconnected environments. By default, no component makes outbound network calls. External providers (OpenAI, Anthropic, Azure, etc.) are available as opt-in wrappers — egress happens only when you explicitly configure one.
| Requirement | How it's met |
|---|---|
| No egress (default) | Default config points to a local /v1/chat/completions endpoint. No outbound calls unless an external provider is configured. |
| Offline license | ed25519-signed token at ~/.config/anchor/license.token. Verified locally. |
| Offline packs | Guard packs are versioned signed archives distributed via sneakernet or internal artifact registry. |
| Reproducible runs | A prior anchor guard check run is exactly reproducible on the same pack versions months later — an audit requirement. |
License
The guard:claims entitlement is required for anchor guard check and
anchor guard review. anchor guard baseline is always unlicensed — baselines
must be writable in CI environments before a license is provisioned.
# Install license (air-gapped: copy token file manually)
anchor license install --token <path-to-token>
# Verify guard entitlement
anchor license verify
# guard:claims ✓
Content capture and OTel
anchor-guard can consume answers directly from OpenTelemetry GenAI traces when anchor-ledger is installed and an OTLP endpoint is configured.
Production OTel collectors frequently omit message content (gen_ai.prompt,
gen_ai.completion) for privacy reasons. anchor-guard handles this gracefully:
| Mode | What's available | Capability |
|---|---|---|
| Full content (dev/staging/CI) | Spans + answer text + retrieved text | All five checks |
| Metadata-only (production) | Spans + latency + token counts | Trajectory and abstention checks only |
| Sampled (production with content) | Full fidelity on a configured sample rate | All five checks on sampled traffic |
The GuardReport records which mode was active. This constraint is a scoping item to raise during customer onboarding — discovering it mid-POC reads as a product defect.
Roadmap
v0.1.0 (current)
- Folder structure, data models, and module stubs
DisclosureDetector— PII and restricted-term scanning (fully implemented)SpanRef— shared identity model compatible with anchor-evalRegressionGate— baseline comparison logicSentenceBoundaryFallback— zero-dependency claim splitter for CI smoke tests- anchor-review HTML bundle generator stub
v0.2.0 (planned)
Attribution pipeline
SpanMatcher.attribute()— two-stage attribution (NLI confidence + IOU gate)AbstentionChecker.check()— two-sided abstention correctness against guard pack signal phrases
Calibration loop
ReviewBundleGenerator.generate()— complete anchor-review static HTML bundleCalibrationIngester— ed25519 signature verification + GROUNDED/UNGROUNDED verdict application fromCalibrationExport
CLI (all commands fully implemented)
anchor guard check— full pipeline: decompose → attribute → disclose → cite → gate; includes--calibration(ingest SME verdicts) flaganchor guard baseline— save groundedness metrics as regression baseline with version-mismatch warninganchor guard review— generate anchor-review HTML bundle from a saved GuardReport
Infrastructure
LicenseGate.check()— offline ed25519guard:claimsentitlement verificationGuardReportRenderer.render_console()— Rich terminal summary with severity-grouped disclosure counts and regression highlightingschema_version: "1"inGuardReport,ClaimSet, andCalibrationExportoutput filescontent_mode: "full" | "metadata_only"inGuardReport- MAX_CLAIMS_PER_ANSWER enforcement with
ClaimSet.truncatedflag
Already implemented in v0.1.0 (ships with v0.2.0)
OpenAICompatibleClaimSplitter,OpenAICompatibleNLIScorer- Built-in provider wrappers: OpenAI, Anthropic, Azure OpenAI, Mistral, Groq, Together AI
CitationGenerator
v0.3.0 (current)
- Custom guard pack authoring —
GuardPackmodel,GuardPackLoader,anchor guard pack validate/new content_mode: "full" | "metadata_only"inGuardConfig— skips NLI pipeline in metadata-only mode--corpusflag onanchor guard checkandanchor guard baseline— reads span text directly from source document files when not embedded inanswers.jsonspan_textstored onClaimAttributionat attribution time —anchor guard reviewnow shows full span context without needing the originalanswers.json
v0.4.0 (planned — blocked on external modules)
The following items are explicitly blocked on modules that do not exist yet in the Anchor suite. They cannot be implemented inside anchor-guard alone.
| Item | Blocked on | Phase |
|---|---|---|
| OTel trace ingestion → GuardReport evidence records | anchor-ledger (Phase 1.5) | 1.5 |
| Sampled production monitoring with configurable sample rate | anchor-ledger (Phase 1.5) | 1.5 |
| SME review queues and judge-drift dashboards | anchor-studio (Phase 3) | 3 |
Once anchor-ledger exists, anchor-guard will add:
anchor guard check --from-traces <otlp_export.json>— consume GenAI OTel spans as input instead ofanswers.jsonGuardReportExporter— write GuardReport events as OTLP-compatible JSON for anchor-ledger ingestion- Sampled-mode evidence records: full fidelity on a configured sample rate, metadata-only on the remainder
Once anchor-studio exists, the static anchor guard review HTML bundle will be
promoted into an SME review queue with multi-reviewer support, verdict history, and
judge-drift dashboards.
License
anchor-guard is licensed under the Business Source License 1.1.
anchor guard baseline always runs without a license.
anchor guard check and anchor guard review require a guard:claims entitlement.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file anchor_guard-0.3.0.tar.gz.
File metadata
- Download URL: anchor_guard-0.3.0.tar.gz
- Upload date:
- Size: 62.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7abe0a03df2304d493148f2aa05be42c38cb01c344f892077d63545e494b54df
|
|
| MD5 |
07c80c6f160120ed1d0586fb27a66598
|
|
| BLAKE2b-256 |
12ec6be12efd3aaa77d33ffa45cacaa8c5c036d44569512ebf67ea5b735e009c
|
File details
Details for the file anchor_guard-0.3.0-py3-none-any.whl.
File metadata
- Download URL: anchor_guard-0.3.0-py3-none-any.whl
- Upload date:
- Size: 70.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d547ff869a3c60e4fbf33b557b228b23417bcdf90ec14a21d2508d91e9033d46
|
|
| MD5 |
8cc2ccc39d04a8fa4011ee463371b441
|
|
| BLAKE2b-256 |
cfd00116350e73ebd916b3fcf80eace79711fc0e6d9cc43078c3c25005e23866
|