GroundLens

io.github.groundlens-dev由 GitHub 账号 groundlens-dev 发布v6.0.5更新于 Oct 24, 2026

Guardrails for LLM answers: exact checks against sources, PASS/REVIEW/FAIL, signed records.

发布方自己的服务:Ground

概览

AI 生成的概览

GroundLens 让助手在作答前用来源段落核对草稿,给出 PASS、REVIEW 或 FAIL 结论并生成签名记录。

可以做什么
用提供的段落核对草稿答案,执行精确的数字、范围和意图检查,给出 PASS、REVIEW 或 FAIL 结论及原因(verify_answer)。核对整批答案,并查看已存储的核验记录,记录以哈希链串联并签名。检查在本地确定性运行,默认路径不含模型;可选的评审模型只能升级结论,不能放行。
适用场景
适合助手依据文档给出数字或论断、且希望在答案发出前做机械核对的场景,尤其是金融或受监管报告等需要签名审计轨迹的场合。
运行要求
以 stdio 本地进程运行,通过 uvx 从带 mcp 附加项的 groundlens 包启动,需要 uv(Python 3.10+)。默认检查无需账号、API 密钥、模型或网络访问。
安装前请注意
工具本身只读,但每次检查在返回前都会向配置的存储写入一条签名记录,存储失败则不返回结论。可选的评审模型会把问题、答案和段落发送给你配置的模型提供商。签名密钥可存放在外部密钥服务中。

安装

在 SourceWeft 中

  1. 打开 控制台中的 GroundLens,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

[GroundLens: the answer and its passages go through GroundLens first, then an LLM judge, then human review. A policy decides, and every check leaves a signed record.]

Guardrails for LLM answers


[PyPI] [License] [OpenSSF Scorecard] [SLSA]


GroundLens checks AI answers against your documents:

the figures, who they are about, and whether they answer the question.


On financial answers it had never seen, it stopped 3 of 4 wrong ones;

on public claims with numbers, half of the unsupported ones.

About 10 ms per check, deterministic, no LLM in the loop.1


🤔 Already running an LLM‑as‑judge?

Keep it. GroundLens runs the exact checks first, and a FAIL never reaches the judge. Your judge only sees the answers the exact checks could not reject.
DeterministicExact checks first, no LLM judge needed
ProvablePASS, REVIEW or FAIL, with a signed record
MeasuredMeasure the error rate of what passes, on your answers
PortableRust engine: the same hash on every OS

Works with

[Langfuse]      [Grafana]      [Model Context Protocol]      [OpenClaw]



[Add GroundLens to Cursor]  [Install GroundLens in VS Code]  [Add the GroundLens plugin to Claude Code]



[Langfuse: the GroundLens checks of a credit assistant filtered by level, a FAIL opened with its reasons, its score and the duration of each verifier.]

Quickstart

python
# pip install groundlensimport groundlens as gl
verifier = gl.Verifier(sink=gl.sinks.jsonl("records/"))record = verifier.check(answer="Revenue was €42.1 million.", passages=["Revenue reached €41.2 million."])print(record.decision, record.reasons)
text
FAIL ['c2: groundlens.numeric contradicted: €42.1 million not in sources; nearest 41200000 EUR in ctx-0 (relative distance 0.021)']

Runs locally: no account, no API key, no model, no network. The signed record is in records/ before check returns.


Integration

The same check, wired into your stack:

Langfuse · pip install "groundlens[langfuse]"
python
from langfuse import get_clientverifier = gl.Verifier(sink=gl.sinks.jsonl("records/"), observers=[gl.integrations.langfuse(get_client())])

Call check inside the observation that produced the answer, and its trace gets:

  • Score groundlens.decision: categorical, PASS, REVIEW or FAIL, on the check and on its trace. Filter traces by it, chart it, export it.
  • Observation groundlens.check: type guardrail. The reasons are its status message, one per line. Its level follows Langfuse's own scale: PASS is DEFAULT, REVIEW is WARNING, FAIL is ERROR.
  • Metadata of the check: decision, reason codes (groundlens.scope.contradicted), policy, and the id and hash of the signed record behind it.
  • Judge: a generation observation with the model name, next to your own model calls.
  • Spans: one per verifier, plus the store, each with its duration. Log View shows them in milliseconds.
  • SDK: Python v3 and v4. Tested on Langfuse Cloud and self-hosted Langfuse 4.58.

Grafana and OpenTelemetry · pip install "groundlens[otel]"
python
verifier = gl.Verifier(sink=gl.sinks.jsonl("records/"), observers=[gl.integrations.otel()])

Spans and metrics go through the providers your application already configured: Tempo, Prometheus, or any OpenTelemetry backend.

  • Metrics: groundlens_decisions_total{decision, policy}, groundlens_reasons_total{reason, decision}, groundlens_check_duration_seconds{stage} with stage total, engine, judge, sign, store.

  • Spans: groundlens.check with groundlens.decision, groundlens.reasons, record id and hash; children per verifier, the judge (gen_ai.*) and the store.

  • Dashboard: the one in the image. gl.integrations.grafana_dashboard() returns the path of its JSON; import it in Dashboards → New → Import.

  • PromQL, share of answers that failed:

    promql
    sum(rate(groundlens_decisions_total{decision="FAIL"}[15m])) / sum(rate(groundlens_decisions_total[15m]))
  • Alert when more than 5 % fail:

    yaml
    - alert: GroundLensFailRateHigh  expr: sum(rate(groundlens_decisions_total{decision="FAIL"}[15m])) / sum(rate(groundlens_decisions_total[15m])) > 0.05  for: 10m
  • Try it: docker run -p 3000:3000 -p 4318:4318 grafana/otel-lgtm, then python examples/observability/credit_copilot.py grafana --minutes 5. Tested on Grafana 12 with Tempo and Prometheus.

Claude, Codex, Cursor, VS Code and other MCP clients · uvx --from "groundlens[mcp]" groundlens mcp

Your assistant checks its own draft before it answers. Three read-only tools over stdio: verify_answer, verify_run, verify_records. Needs uv.

[Add to Cursor] [Install in VS Code]

  • Claude Code (plugin: the MCP server plus a skill that tells Claude when to check):

    bash
    claude plugin marketplace add groundlens-dev/groundlensclaude plugin install groundlens@groundlens
  • Codex:

    bash
    codex mcp add groundlens -- uvx --from "groundlens[mcp]" groundlens mcp
  • Any MCP client (Claude Desktop, Windsurf, Zed...):

    json
    { "mcpServers": { "groundlens": { "command": "uvx", "args": ["--from", "groundlens[mcp]", "groundlens", "mcp"] } } }

    With the package installed (pip install "groundlens[mcp]"), "command": "groundlens-mcp" is enough.

  • Nothing to install: the Hugging Face Space is also a remote MCP server, with verify_answer, at https://groundlens-groundlens.hf.space/gradio_api/mcp/.

OpenClaw · clawhub install groundlens

A skill on ClawHub, the skill registry of OpenClaw. Before your assistant sends an answer with figures taken from your documents, it runs groundlens verify on its draft:

  • PASS: the answer goes out.
  • FAIL: it fixes the figure from the source and checks again.
  • REVIEW: the answer goes out, saying which figure it could not confirm.

Runs on your machine, with no API key and no second model. The skill is in plugins/groundlens/skills/groundlens, the same one the Claude Code plugin uses. OpenClaw can also use the MCP server directly, with openclaw mcp add.

Where records are kept · sink=

check() returns only after the signed record is stored. If it cannot be stored, check() raises and no decision comes back. Each sink keeps a chain: every record carries the hash of the one before it.

SinkWhat you passHow it stays append-only
gl.sinks.jsonl("records/")a directory or a .jsonl pathone JSON line per record, flushed to disk and locked on every write. For development and single machines
gl.sinks.s3_object_lock(boto3.client("s3"), "bucket")your S3 client and a bucket created with Object Lockone object per record, written only if absent; retain_days= adds a retention date (COMPLIANCE mode by default)
gl.sinks.azure_blob(container_client)an azure.storage.blob.ContainerClientone blob per record, never overwritten; give the container a time-based immutability policy
gl.sinks.postgres(psycopg.connect(...))any DB-API connectionone row per record; the table is created if missing, and its keys stop a position from being written twice or a chain from forking
python
import boto3verifier = gl.Verifier(    signer=gl.signers.aws_kms(boto3.client("kms"), key_id),           # the key never leaves KMS    sink=gl.sinks.s3_object_lock(boto3.client("s3"), "audit-records", retain_days=3650),)

Several processes: give each its own chain (chain="worker-3") and verify them together with groundlens record verify. GroundLens depends on none of these SDKs: you pass the client you already use. Postgres, S3 Object Lock, AWS KMS, Azure Blob and PKCS#11 are tested in CI against real software (Postgres, MinIO, LocalStack, Azurite, SoftHSM); Azure Key Vault has no emulator and is experimental.


LLM-as-a-judge

The exact checks catch a figure that is wrong, miscalculated, from another company or of another kind than asked. They cannot catch a real figure taken from the wrong row of the right table, or a sentence the passages never say. For that you connect a model that reads: the judge. GroundLens ships no model; you pass the client you already use.

python
from openai import OpenAI
verifier = gl.Verifier(    "credit_desk.yaml",                                       # a policy that allows a judge, below    judge=gl.judges.openai_compatible(OpenAI(), "gpt-4.1"),    sink=gl.sinks.jsonl("records/"),)
AdapterClient
gl.judges.openai_compatible(client, model)openai.OpenAI, or any server with the same API: vLLM, Ollama, your gateway (base_url=)
gl.judges.azure_openai(client, deployment)openai.AzureOpenAI
gl.judges.bedrock(client, model_id)boto3.client("bedrock-runtime")

The shipped policies forbid a judge, so the policy that uses one says so:

yaml
id: credit_deskversion: 1.0.0extends: eu_ai_act_high_risk_v1verifiers:  forbidden: []          # allow llm_judge.*judge:  required: true         # no judge, or a judge that fails: REVIEW  contradicted: REVIEW   # or FAIL  unsupported: REVIEW

The judge can escalate, never pass. It labels each sentence supported, unsupported, contradicted or uncertain. supported resolves nothing on its own, so a judge that is wrong, or that a passage has talked into agreeing, cannot let through an answer the exact checks fail. A timeout, an error or a malformed reply is REVIEW.

You see exactly what it is sent. One prompt per answer, from a fixed template (groundlens.judge/1): what PASS means for you, the scope, the question, the answer and every passage with its provenance. Passages sit between markers they cannot contain, so text inside them is data, not instructions. groundlens judge show-template prints the exact prompt before anything is sent anywhere.

The record keeps the trail. The template id, the hash of the prompt, the model the provider says answered and the hash of its reply, with each sentence's label, the passages it cited and its one-line reason. In Langfuse the judge appears as a generation, next to your own model calls.


Architecture

GroundLens splits verification into five parts that never mix: claims, verifiers, evidence, policy and record.

  1. The answer is cut into claims: each figure and each sentence, with its character offsets.
  2. Verifiers examine each claim against the passages and return evidence (supported, contradicted, unsupported), never a verdict. The shipped ones are exact: numeric (exact fractions, locales, units, recomputed sums and ratios), scope, intent, and your rules. The judge, if you connect one, is the model you already use, asked through a fixed template.
  3. The evidence graph keeps what each verifier said about each claim, and where they disagree.
  4. The policy, a YAML file you own, turns evidence into a decision: PASS, REVIEW or FAIL, with reasons.
  5. The record holds the input hash, the evidence, the policy hash and the decision, chained to the previous record and signed. It is stored before check returns; observers are told after.

The engine is Rust, shipped as a Python wheel (abi3, Python 3.10+), with a standalone glv binary that checks records without Python. It runs in your process: no server, no network, no runtime dependencies. From Rust, cargo add groundlens; for the binary, cargo install groundlens-cli.


Parameters

Parameter of VerifierOptions
policygroundlens_default_v1 (default), eu_ai_act_high_risk_v1, or your YAML, which can extends either
signerlocal_key (development), aws_kms, azure_key_vault, pkcs11
sinkjsonl, s3_object_lock, azure_blob, postgres
judgeopenai_compatible (OpenAI, vLLM, Ollama…), azure_openai, bedrock; optional
observersintegrations.langfuse, integrations.otel, or your own
locale, roundinghow numbers are read and compared
Parameter of check
answer, passagesrequired; passages as strings or (id, text, provenance)
questionenables the intent check: the answer gives the kind and unit asked for
scopewhat the request is about ({"entity": "ACME"}): a figure from another entity fails
risk_classa class your policy can send to human review whatever the checks say

Policy

The checks produce evidence. The policy decides what that evidence means for you: what fails, what goes to a person, and what PASS allows. It is a short YAML file that you own and version with your code. The same answer under two policies can get two different decisions, and both are correct; that difference is your risk appetite, written down.

Two policies ship with GroundLens:

PolicyFor
groundlens_default_v1 (default)any application. A contradicted figure or a figure from outside the scope fails; an answer of the wrong kind or unit goes to review. No model decides.
eu_ai_act_high_risk_v1systems under Annex III of the EU AI Act. The same rules, with each decision mapped to Art. 15(1) accuracy, Art. 14(4)(a) human oversight and Art. 12(1) record keeping.

Write your own policy

Extend a shipped policy and write only what differs:

yaml
id: credit_deskversion: 1.2.0extends: eu_ai_act_high_risk_v1pass_means: "may be shown to the analyst as a summary of the filing"locale: es-ES                     # how the documents write numbers: 1.234,56rounding: half_even               # how a recomputed figure may roundscope:  required: [entity]              # every request must name the borrower  missing_scope: REVIEW           # or FAIL; never PASSrisk_classes:  default: credit_information  mandatory_review: [credit_limit_change]   # always seen by a person
python
verifier = gl.Verifier("policies/credit_desk.yaml")verifier.check(answer=a, passages=p, scope={"entity": "Talleres Norte"}, risk_class="credit_limit_change")
KeyWhat it sets
extendsa shipped policy to start from. Maps merge key by key, lists replace. Your policy must set its own id and version.
pass_meanswhat PASS allows in your application, in one sentence. A verifier's PASS is not a decision to publish; this says which one it is.
locale, roundinghow figures are read and compared: half_up, half_even or within_last_digit. Written here, they win over the request.
scopethe keys every request must carry, and what a missing one gets.
risk_classesclasses that always go to review. A class the policy does not list is an error, so a typo cannot route around review.
verifierswhich checks are required, recommended, optional, fallback or forbidden.
decisionwhich verifiers can fail an answer, which send it to review, and what unresolved or conflicting claims get.
judgewhether a judge is required and what its labels may do. See LLM-as-a-judge.
regulatory_mappingwhich control and article each decision evidences, in your own control ids.

Unknown keys are errors, not ignored: a misspelt rule never silently does nothing.

Failure is not a PASS

SituationDecision
a figure contradicts the passages, under the rounding ruleFAIL
a figure comes only from a passage outside the scopeFAIL
a required scope key is missingREVIEW (or FAIL, if the policy says so)
a risk class that requires reviewREVIEW, whatever the checks say
a check could not run, or the judge timed out or failedREVIEW
the record cannot be storedan exception, and no decision

The final decision is the worst outcome of all checks.

Check your policy

bash
groundlens policy lint policies/credit_desk.yaml                # errors before you deploygroundlens policy show --resolved policies/credit_desk.yaml     # every default filled in

The hash of the resolved policy goes into every record it decides, so each decision can be traced to the exact rules that produced it.

Before you change a policy,you have to measure it:

  • groundlens measure estimates the share of wrong answers among those that passed, from a small sample your team labels without seeing the verdicts, with a 95 % confidence interval, per risk class, and the cost per decision.
  • groundlens replay runs past traffic under the new policy and lists the decisions that would change.

Both reports are signed. Full reference: Writing policies.


Security

GroundLens sits on the path between a model and a decision, and its records are meant to be evidence. Both make it a target: an attacker may try to get a wrong answer through, or to alter the trail afterwards.

ThreatControl
A wrong answer passesExact checks with no model in the default path; errors and timeouts are REVIEW, never PASS; no stored record, no decision
Instructions hidden in passages steer the judgePassages are delimited data in a fixed, versioned template; the judge can only escalate; adversarial tests
A record is edited, deleted or reorderedSHA-256 content hash, hash chain, signature; groundlens record verify checks offline with the public key alone
The signing key leaksKeys stay in AWS KMS, Azure Key Vault or a PKCS#11 HSM (ECDSA P-256); GroundLens never holds them
Two writers fork a chainConditional writes in every sink; a lost race is retried, never overwritten
Data leaves through the verifierNo network code in the engine (cargo-deny bans HTTP and TLS crates); other systems are reached only through clients you pass in
Hostile input exhausts the parserBounded numeral grammar against regular-expression DoS; fuzzing of every parser
A tampered releasePinned and hashed CI dependencies, SLSA Build Level 3 provenance, signed release binaries, OpenSSF Scorecard

Report vulnerabilities privately through GitHub security advisories. See SECURITY.md.


Contributing

If GroundLens got one of your answers wrong, that case is the most valuable contribution there is. Checks, connectors and documentation are welcome too; CONTRIBUTING.md explains how to start.


Open source

Apache-2.0, all of it: engine, checks, policies, connectors, command line and MCP server. Verification evidence has to be checkable by anyone, without our software or our permission, and that only holds if the verifier is open.


Contact

You can contact the developer at [email protected]


1. GroundLens 6.0.5, default policy, no judge. Financial answers: 189 real answers by Claude Haiku from a RAG over FinQA 10-K pages (e5 embeddings, top 4 passages), on multi-step questions never used while developing GroundLens. Each answer labelled twice by a language model (Claude Opus 5.5, claude-opus-5-5) against fixed written criteria, blind to GroundLens, with a third pass on the 3 disagreements; labels were frozen before GroundLens ran. 22 answers were wrong (1 in 9): 17 got FAIL or REVIEW (77 %, 95 % CI 57–90 %). Of the 129 answers that passed, 5 were wrong (3.9 %, 95 % CI 1.7–8.8 %). The price: 60 of 189 answers (32 %) went to FAIL or REVIEW, including 38 of 110 correct ones. Median 10.6 ms per check in a fresh process. Public claims: LLM-AggreFact test split (29,320 claim–document pairs from 11 sets, human labels), never used while developing GroundLens. Of the 1,616 unsupported claims that contain a number, 796 got FAIL or REVIEW (49 %, 95 % CI 47–52 %); among the claims with a number that passed, 16.3 % were unsupported, against 25.4 % before the check. Claims without a number all passed: GroundLens caught none of their errors. Median 4.0 ms per check. All results, including the sets where it does not help, in BENCHMARK.md.

Copyright Groundlens 2026

来源:README.md,提交 a479066

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

4
  1. v6.0.5最新Oct 16, 2026
  2. v6.0.4Oct 15, 2026
  3. v6.0.1Oct 15, 2026
  4. v5.2.0Sep 21, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分

其他 Ground 集成