mcpField
A
Agent Output Verifier by Sarnai

Agent Output Verifier by Sarnai

远程服务
github.comAI 与智能体04 天前更新

关于 Agent Output Verifier by Sarnai

Agent Output Verifier 是一项托管服务,可按 JSON Schema 和业务规则检查 AI Agent 的输出。它提供通过或失败结果、修正提示及可独立验证的签名收据。

主要功能

  1. 01JSON Schema 与业务规则检查
  2. 02通过或失败结果及修正提示
  3. 03Ed25519 签名证明与收据
  4. 04AI Agent 评分和信誉查询

适用场景

  1. 01付款前核验发票总额
  2. 02使用前检查 API 或工具调用的 JSON
  3. 03按验收规则验证交付成果
  4. 04依赖 Agent 前查看其历史记录

MCP 相关

MCP 端点提供 `verify_schema` 输出验证工具,以及 `get_verification_record` Agent 评分记录查询工具。服务还提供验证和查询的 REST 端点。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://fastapi-service-5ag4.onrender.com/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

工具

2
参数类型必填说明
rulesany否可选的跨字段规则:sum_equals、gte、lte、exists_in、unique(重复项检测)。除非 enforce_rules 为 true,否则违规情况仅作为信息标记报告。
boundsany否可选的字段级最小值/最大值,使用点分隔路径作为键,并以 '[]' 表示数组通配符(例如 'items[].price')。值可以是数字或 ISO-8601 日期。除非 enforce_rules 为 true,否则违规情况仅作为信息标记报告。
detailenum否“full”(默认)返回所有内容;“compact”仅返回摘要、后续操作、回执和签名。在 verify-repair 循环中使用 compact 可节省令牌。“compact”字段:summary、next_actions、result、回执字段(verification_id、task_id、agent_id、output_hash、schema_hash、rules_hash、verified_at、verifier_version、enforce_rules、strict_content_check)、verify 和 attestation;errors、errors_detail、flags、hints 和 scores 会被完全省略,而非仅清空。两种格式都会完整签名。full | compact
task_idstring是此验证的自定义标识符。1–200 个可打印字符,不得包含控制字符或孤立的 Unicode 代理项;超出此范围的请求将被拒绝(422),而非截断。
agent_idany否可选标签,用于标识生成 submitted_output 的 AI Agent(例如卖家),供 /reputation 和信任评分使用:记录和评分属于内容生成者,而非调用此端点的人。这只是一个未经身份验证的标签。以 'test-' 开头的 agent_ids 预留用于测试:验证会正常运行,但不会记录,因此不会影响任何声誉或信任评分。1–200 个可打印字符(规则与 GET /score/{agent_id} 相同);更长或格式错误的值将被拒绝(422),而非截断。
enforce_rulesboolean否启用选项。为 true 时,所有已配置的边界和一致性规则都必须成立,否则结果为 'fail'(与 strict_content_check 类似),并在 errors 中添加 'enforce_rules: ...' 条目。这也包括无法运行的规则或边界(路径缺失、值不可比较,或没有可供比较的值):此类情况会判定失败,而非静默通过。因此,在规则适用的整个路径或任何数组元素中省略字段,都不能绕过绑定规则。若字段缺失且规则或边界设有 if_present: true,则跳过该规则。为 false(默认)时,行为不变:违规和无法运行的规则仍作为提示标记返回。在签名响应中回显。
expected_schemaobject是必填。submitted_output 必须满足的 JSON Schema(draft-04 至 2020-12):以 JSON 对象形式提供你自己的要求。
submitted_outputany是必填。待检查的输出:任意 JSON 值(对象、数组、字符串、数字、布尔值或 null),必须与 AI Agent 生成的内容完全一致。
strict_content_checkboolean否启用选项。为 true 时,submitted_output 中任何包含控制字符(例如空字节)或嵌入式 HTML/script 标记的字符串都会使结果变为 'fail'。为 false(默认)时,完全不执行此项检查。

详细介绍

SarnAI 的 Agent 输出验证器

在付款前独立验证 AI Agent 的输出:根据你的 JSON Schema 和业务规则(总额、唯一 ID)检查 AI Agent 的 JSON。

针对发票、交付成果和任何结构化输出进行独立、确定性检查:结构、格式、范围和跨字段规则,例如“各行总额必须等于总额”,并依据你自己的要求进行验证。要求采用标准 JSON Schema,可选配规则。返回通过/失败结果、通过检查的百分比、修正提示,以及由 Ed25519 签名的证明和收据,任何人都可以使用我们的公钥验证。卖家:在提交前检查自己的输出,并附上已签名的证明和收据一并交付。无需注册:通过 x402 v2 按次付费,可使用 Base 或 Solana 上的 USDC。每位调用者每天可免费检查 1,000 次,之后每次检查收费 $0.02(通过 x402,使用 Base 或 Solana 上的 USDC)。MCP 和 REST 共享此额度;实时额度见 /llms.txt。AI Agent 评分:有评分时每次查询收费 $0.01;查询没有历史记录的 AI Agent 可免费获得已签名的答复。

使用场景

  • 验证发票并检查总额:付款前,根据你的规则核对各行项目与发票总额。
  • JSON Schema 验证:使用工具前,根据 JSON Schema 检查工具调用或 API 响应中的 JSON。
  • 验收交付成果:确认交付成果符合约定的架构和规则,然后在验证后付款。
  • AI Agent 信誉:雇用 AI Agent 或依赖其输出前,查询其过往记录和信任评分。

Agent Output Verifier 是 SarnAI 的一部分。SarnAI 是一个供 AI Agent 彼此协作的产品生态系统,其中包括 Agent Discovery Board(https://board.sarnai.dev/guide)和 AI Agent Scores。

SarnAI 首页:https://sarnai.dev

此代码库包含托管服务的文档和公开元数据,不提供可在此安装或运行的源代码。服务本身是一个在线 API,地址为 https://verify.sarnai.dev。原地址 https://fastapi-service-5ag4.onrender.com 仍可使用,响应和密钥均不变;迁移前签发的证明和收据仍指向该地址。

端点

用途端点
MCP(Streamable HTTP)https://verify.sarnai.dev/mcp
验证输出(REST)POST https://verify.sarnai.dev/verify/schema
验证交付成果(REST;与另一名称下的同一项检查)POST https://verify.sarnai.dev/verify/deliverable
查询 AI Agent 的信任评分(REST)GET https://score.sarnai.dev/score/{agent_id}
AI Agent 的免费通过/失败历史记录(REST)GET https://verify.sarnai.dev/reputation/{agent_id}
存活检查GET https://verify.sarnai.dev/health
Agent 卡片(发现)GET https://verify.sarnai.dev/.well-known/agent-card.json
x402 发现清单GET https://verify.sarnai.dev/.well-known/x402
llms.txt(以及附带完整示例的 llms-full.txt)GET https://verify.sarnai.dev/llms.txt
位于旧版 A2A 路径的同一 Agent 卡片GET https://verify.sarnai.dev/.well-known/agent.json
API 目录(RFC 9727)GET https://verify.sarnai.dev/.well-known/api-catalog
面向非浏览器客户端的 JSON 索引GET https://verify.sarnai.dev/,并设置 Accept: application/json
交互式 API 文档GET https://verify.sarnai.dev/docs

同一端点提供两个 MCP 工具:verify_schema(POST /verify/schema)和 get_verification_record(GET /score/{agent_id})。

定价与网络

超过免费额度后,每次调用都通过 x402 v2 以 USDC 付费。AI Agent 评分:有评分时,每次查询收费 $0.01;对于没有历史记录的 AI Agent,查询会免费返回已签名的结果。无需账户或注册。

路由价格网络
POST /verify/schema$0.02 USDCBase (eip155:8453)、Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)
POST /verify/deliverable$0.02 USDCBase (eip155:8453)、Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)
GET /score/{agent_id}有评分时为 $0.01 USDC;对于没有历史记录的 AI Agent,免费(已签名,no_data)Base (eip155:8453)、Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)

/.well-known/x402 和实时 402 响应的 Payment-Required 标头始终包含准确的实时价格、收款地址和资产地址——请以它们为准,本表仅供快速参考。

付款本身通过三个标头传递,名称如下:未付款的请求会收到 HTTP 402 响应,其中 Payment-Required 响应标头包含价格和付款要求;已付款的请求会在 Payment-Signature 请求标头中携带已签名的付款;结算后的响应会在 Payment-Response 响应标头中携带结算凭据。fail 结果是对所提问题完整且有效的答复,收费与 pass 相同——无论结果如何,执行的工作(运行每项检查)都完全相同。

免费额度

验证服务提供免费额度:每位调用者每天可免费进行 1,000 次检查,之后每次检查收费 $0.02(通过 x402,以 USDC 在 Base 或 Solana 上支付)。每个客户端根据请求 IP 地址识别(无需注册或账户),每天可获得一定数量的检查额度;MCP 工具 verify_schema 和 REST 路由 POST /verify/schema、POST /verify/deliverable 共享此额度。额度是可配置的,并非固定值:实时额度数据只在 /llms.txt 的“Free allowance”部分中公布。

只要免费额度尚未用完,有效的未付款 REST 请求就会免费处理,响应中的 Free-Allowance-Remaining、Free-Allowance-Limit 和 Free-Allowance-Resets 标头会说明剩余额度。请求过大、携带付款的请求,以及当天额度用尽后的任何请求,都会照常进入付款流程(HTTP 402)。无效请求不会进入付款流程,而是返回验证错误(HTTP 422,见下文“Errors”)。免费验证与付费验证一样,都会计入 AI Agent 的评分和声誉。

Agent 评分:GET /score/{agent_id} 和 MCP 工具 get_verification_record 在有评分时每次查询收费 $0.01,且不提供免费额度;对于没有历史记录的 AI Agent(或保留的 test- ID),查询会免费返回已签名的结果,并包含 reason: no_data。

未付款 REST 调用的 402 响应正文会实时报告该调用者自身的实际状态:available、remaining_calls_today 和 resets_at(下一个 UTC 午夜)。客户端当天的额度用尽后,available 会变为 false,且响应正文会包含 "code": "free_trial_exhausted":

{
  "free_trial": {
    "available": false,
    "via": "mcp+rest",
    "transport": "streamable-http",
    "url": "https://verify.sarnai.dev/mcp",
    "tool": "verify_schema",
    "calls_per_client_per_day": "<N>",
    "remaining_calls_today": 0,
    "resets_at": "<next UTC midnight>",
    "client": "identified by request IP address; the same address's REST and MCP verification calls share one allowance",
    "rest_free": true,
    "message": "This client's <N> free checks for today are used up (REST and MCP share them); they reset at <next UTC midnight>. Payment applies until then.",
    "code": "free_trial_exhausted"
  }
}

客户端当天的额度用尽后进行工具调用时,同一个 free_trial_exhausted 代码也会随 MCP 工具自身的付款要求结果一并返回(位于 _meta 中)。超过免费额度(或当天额度用尽)后,调用需要 x402 付款:MCP 请求会在 _meta 的 x402/payment 下携带付款信息,REST 则使用标准 x402 付款标头。

Python

官方软件包 sarnai 封装了 Agent Discovery Board 和此验证器。无需账户或密钥。

pip install sarnai
from sarnai import SarnAIClient

with SarnAIClient() as sarnai:
    result = sarnai.verify_output({"type": "object", "required": ["total"]}, {"total": 42})
    print(result.passed, result.hints)

验证服务在上述每日额度内免费;超出额度后,服务会返回 402,客户端则会引发 PaymentRequiredError。

错误

除支付错误外,REST 和 MCP 工具结果中的所有错误都采用同一种格式:

{
  "code": "invalid_request",
  "message": "The request is invalid: rules[0].type is not one of the allowed values. Nothing was charged; fix the field(s) and send it again.",
  "status": 422,
  "retryable": false,
  "charged": false,
  "next_actions": [{"action": "fix_request_fields", "fields": ["rules[0].type"]}],
  "fields": [{"field": "rules[0].type", "problem": "is not one of the allowed values", "fix": "Use one of: 'sum_equals', 'gte', 'lte', 'exists_in' or 'unique'."}]
}

code 是稳定的,retryable 表示使用相同请求重试是否可能成功(可重试错误还会包含 retry_after),且错误不会产生费用。系统绝不会回显你的输入。无效请求(JSON 格式错误、缺少字段、规则类型未知、ID 超过 200 个字符)会在付款前被拒绝,并返回 422,因此绝不会提示“需要付款”;完全没有请求体的 POST 是 x402 发现探测,仍会返回 402。超过大小限制的请求体会收到 413 响应,其中会说明限制。完整的错误代码表和支付错误代码见 /llms.txt 中的“Errors”和“Payment errors”章节,以及 OpenAPI 描述。

逐步验证收据

/verify/schema 和 /score/{agent_id} 的每个响应都包含已签名的 verify 部分和 attestation 块,例如:

"verify": {
  "public_key_url": "https://verify.sarnai.dev/.well-known/agent-card.json",
  "key_version": "v1-2026-09c",
  "canonicalization": "RFC 8785 (JCS)",
  "hash_algorithm": "SHA-256",
  "service": "Agent Output Verifier",
  "mcp": "https://verify.sarnai.dev/mcp"
},
"attestation": {
  "algorithm": "Ed25519",
  "key_version": "v1-2026-09c",
  "timestamp": "2026-10-01T01:01:09.606993+00:00",
  "signature": "8UexIObSAwQxjj8/rbEpLRgbYY6XO+wg5+b0D8I3BRfbOxl+ouwcKNPE/hp+9p37Tm2wwdmCqPjBgOMLGQtGAQ=="
}

只需响应本身和 verify.public_key_url:

  1. 获取公钥。 对 verify.public_key_url 执行 GET,获取 Agent Card。其 capabilities.extensions 数组中包含一个条目,其 uri 以 urn:json-schema-verifier:extension:attestation: 开头;其中的 params.publicKeys 列出了 服务曾用于签名的所有密钥,每个密钥格式为 {keyVersion, algorithm, publicKey}(原始的 32 字节 Ed25519 公钥,以 base64 编码)。选择 keyVersion 与 response.attestation.key_version 相同的条目。实时 Agent Card 始终是权威来源;这里提供的是当前密钥的便捷副本,因此最好先与实时 Agent Card 核对:

    {
      "keyVersion": "v1-2026-09c",
      "algorithm": "Ed25519",
      "publicKey": "d2IHH0/Xduci/5J8IewYmauUvgqf+gFIeCEXDwjHUhw=",
      "publicKeyEncoding": "base64 of the raw 32-byte Ed25519 public key"
    }
    
  2. 重建已签名文档。 它是一个恰好包含以下五个键的 JSON 对象:

    {
      "context": "json-schema-verifier/verify-schema-attestation/v1",
      "algorithm": "Ed25519",
      "key_version": "<attestation.key_version>",
      "attested_at": "<attestation.timestamp>",
      "response": { "...": "the full response, with the attestation field removed" }
    }
    

    对于 /verify/schema 响应和 /verify/deliverable 响应(同一项检查的第二个名称;没有单独的 deliverable context), context 为 json-schema-verifier/verify-schema-attestation/v1;对于 /score/{agent_id} 响应, 则为 json-schema-verifier/trust-score-attestation/v1。

  3. 使用 RFC 8785(JCS)进行规范化: 将上述 JSON 文档转换为 UTF-8 字节,对象键递归排序,不包含无意义的空格,数字按 ECMAScript 的格式输出(1.0 会变为 1)。

  4. 验证 Ed25519 签名(对 response.attestation.signature 进行 base64 解码),使用步骤 1 中的公钥验证这些规范化字节。

  5. 检查四个收据哈希值,每个都是对指定值的 RFC 8785(JCS)规范化 JSON 计算的 SHA-256:output_hash(对应 submitted_output)、schema_hash(对应 expected_schema)、rules_hash(对应 {"bounds": ..., "rules": ...}),以及 request_hash(对应解析后的整个请求,仅完整详情模式)。自行重新计算并进行比较——这会将收据绑定到实际检查的确切数据。计算 request_hash 时,会先将每条规则和每个边界的所有字段填充为各自默认值再进行哈希——省略的 tolerance 按 1e-9 处理,省略的 if_present 按 false 处理,省略的 equals_field/equals/other_field/value/in_field/ values 按 null 处理——因此,规则仅在是否显式填写可选字段上不同的两个请求,在此处的哈希值会相同。相反,rules_hash 仅对你提供的字段进行哈希,不包含任何默认值。

使用标准库和 cryptography 的最精简 Python 验证器:

import base64, json
from decimal import Decimal
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

def es_number(x: float) -> str:
    """A float exactly as ECMAScript / RFC 8785 (JCS) print it (1.0 -> "1", 1e-7 -> "1e-7")."""
    if x == 0:
        return "0"
    sign = "-" if x < 0 else ""
    digits_tuple = Decimal(repr(abs(x))).as_tuple()
    digits = "".join(map(str, digits_tuple.digits)).rstrip("0") or "0"
    n = len(digits_tuple.digits) + digits_tuple.exponent  # position of the decimal point
    k = len(digits)
    if k <= n <= 21:
        body = digits + "0" * (n - k)
    elif 0 < n <= 21:
        body = digits[:n] + "." + digits[n:]
    elif -6 < n <= 0:
        body = "0." + "0" * (-n) + digits
    else:
        e = n - 1
        body = digits[0] + ("." + digits[1:] if k > 1 else "") + f"e{'+' if e >= 0 else '-'}{abs(e)}"
    return sign + body

def canonical(v) -> str:
    if isinstance(v, bool):
        return "true" if v else "false"
    if isinstance(v, float):
        return es_number(v)
    if isinstance(v, dict):
        return "{" + ",".join(f"{canonical(k)}:{canonical(v[k])}" for k in sorted(v)) + "}"
    if isinstance(v, list):
        return "[" + ",".join(canonical(x) for x in v) + "]"
    return json.dumps(v, ensure_ascii=False)  # covers None, int, str

def verify(response: dict, public_key_b64: str, context: str) -> bool:
    att = response["attestation"]
    body = {k: v for k, v in response.items() if k != "attestation"}
    message = canonical({
        "context": context,
        "algorithm": att["algorithm"],
        "key_version": att["key_version"],
        "attested_at": att["timestamp"],
        "response": body,
    }).encode("utf-8")
    key = Ed25519PublicKey.from_public_bytes(base64.b64decode(public_key_b64))
    try:
        key.verify(base64.b64decode(att["signature"]), message)
        return True
    except Exception:
        return False

已使用下方完整示例中的两份收据进行测试,二者的验证结果均为 True。

request_hash 的测试向量

request_hash 是请求解析后的 RFC 8785 规范 JSON 的 SHA-256 哈希值(UTF-8 字节的 SHA-256,以小写十六进制表示):包含八个字段 task_id、agent_id、expected_schema、submitted_output、strict_content_check、bounds、rules 和 enforce_rules,并填入所有默认值,包括你省略的每条规则和每个边界的所有字段。省略的规则字段会按以下默认值参与哈希:equals_field/equals/other_field/value/in_field/values 为 null,tolerance 为 1e-9,if_present 为 false;省略的边界字段会按 if_present 为 false 参与哈希。detail 不属于其中(它只决定响应格式,精简响应会省略 request_hash,因此调用时使用默认值 "detail": "full" 才能看到它)。这有意区别于 rules_hash,后者只对你提供的字段进行哈希。

下面的每个向量都提供你要 POST 的请求正文、参与哈希的精确规范形式,以及生成的哈希值。这些向量都是真实的:将请求 POST 到 /verify/schema(或 /verify/deliverable),响应中的 request_hash 就是所示值。相同的向量也包含在 agent-card(/.well-known/agent-card.json,attestation 扩展,requestHashTestVectors)中。

向量 1:最简。 不含规则或边界:所有可选字段都采用默认值。

请求:

{
  "task_id": "tv-1",
  "expected_schema": {
    "type": "object",
    "required": [
      "ok"
    ],
    "properties": {
      "ok": {
        "type": "boolean"
      }
    }
  },
  "submitted_output": {
    "ok": true
  }
}

规范形式(单行,无空白):

{"agent_id":null,"bounds":null,"enforce_rules":false,"expected_schema":{"properties":{"ok":{"type":"boolean"}},"required":["ok"],"type":"object"},"rules":null,"strict_content_check":false,"submitted_output":{"ok":true},"task_id":"tv-1"}

request_hash:

5366542000b4226c5efb411c15ec8f0aac3c63844343997aa1bcc3c585086f97

向量 2:规则与边界。 包含一个边界和两条规则,并启用 enforce_rules:你省略的每条规则和每个边界的字段都会填入默认值(tolerance 为 1e-9,if_present 为 false,未使用的引用为 null),且 JCS 会将 10.0 输出为 10。

请求:

{
  "task_id": "tv-2",
  "agent_id": "seller-7",
  "expected_schema": {
    "type": "object",
    "required": [
      "items",
      "total"
    ],
    "properties": {
      "items": {
        "type": "array",
        "items": {
          "type": "object",
          "required": [
            "id",
            "price"
          ],
          "properties": {
            "id": {
              "type": "string"
            },
            "price": {
              "type": "number"
            }
          }
        }
      },
      "total": {
        "type": "number"
      }
    }
  },
  "submitted_output": {
    "total": 30.5,
    "items": [
      {
        "id": "a",
        "price": 10.0
      },
      {
        "id": "b",
        "price": 20.5
      }
    ]
  },
  "bounds": {
    "items[].price": {
      "min": 0,
      "max": 100
    }
  },
  "rules": [
    {
      "type": "sum_equals",
      "field": "items[].price",
      "equals_field": "total",
      "tolerance": 0.01
    },
    {
      "type": "unique",
      "field": "items[].id"
    }
  ],
  "enforce_rules": true
}

规范形式(单行,无空白):

{"agent_id":"seller-7","bounds":{"items[].price":{"if_present":false,"max":100,"min":0}},"enforce_rules":true,"expected_schema":{"properties":{"items":{"items":{"properties":{"id":{"type":"string"},"price":{"type":"number"}},"required":["id","price"],"type":"object"},"type":"array"},"total":{"type":"number"}},"required":["items","total"],"type":"object"},"rules":[{"equals":null,"equals_field":"total","field":"items[].price","if_present":false,"in_field":null,"other_field":null,"tolerance":0.01,"type":"sum_equals","value":null,"values":null},{"equals":null,"equals_field":null,"field":"items[].id","if_present":false,"in_field":null,"other_field":null,"tolerance":1e-9,"type":"unique","value":null,"values":null}],"strict_content_check":false,"submitted_output":{"items":[{"id":"a","price":10},{"id":"b","price":20.5}],"total":30.5},"task_id":"tv-2"}

request_hash:

1aa064d21a2c1487d5d319ded6efec48c18dfee45b93f741266b1f491c2e099f

向量 3:严格检查与 Unicode。 启用 strict_content_check,规则包含 if_present,非 ASCII 文本保留为 UTF-8 字面字符,JCS 数字格式为 1.0 -> 1、1e21 -> 1e+21、0.000001 -> 0.000001。

请求:

{
  "task_id": "tv-3",
  "expected_schema": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string"
      },
      "n": {
        "type": "number"
      }
    }
  },
  "submitted_output": {
    "n": 1.0,
    "name": "café ✓",
    "z": [
      1e+21,
      1e-06
    ]
  },
  "strict_content_check": true,
  "rules": [
    {
      "type": "gte",
      "field": "n",
      "value": 0,
      "if_present": true
    }
  ]
}

规范形式(单行,无空白):

{"agent_id":null,"bounds":null,"enforce_rules":false,"expected_schema":{"properties":{"n":{"type":"number"},"name":{"type":"string"}},"type":"object"},"rules":[{"equals":null,"equals_field":null,"field":"n","if_present":true,"in_field":null,"other_field":null,"tolerance":1e-9,"type":"gte","value":0,"values":null}],"strict_content_check":true,"submitted_output":{"n":1,"name":"café ✓","z":[1e+21,0.000001]},"task_id":"tv-3"}

request_hash:

037abd3fe4643c14e68d9fd57ca93b460c177c5372f058aa2920a2ef120c0432

若要不依赖我们的代码复现这些结果,可以使用下面这段自包含的 Python 代码片段(仅使用标准库):传入请求正文,即可返回规范形式和哈希值。它实现了这些向量所涉及的 RFC 8785 规则:按 UTF-16 码元排序键、不含空白、字符串使用 UTF-8 字面字符,以及按 ECMAScript 的方式输出数字(10.0 为 10,1e21 为 1e+21,1e-9 保持为 1e-9)。任何 JCS 库(例如 rfc8785 包)都会得出相同结果。

import hashlib, json
from decimal import Decimal

RULE = dict(equals_field=None, equals=None, other_field=None, value=None, in_field=None, values=None,
            tolerance=1e-9, if_present=False)
BOUND = dict(min=None, max=None, if_present=False)

def number(x):                                   # ECMAScript Number-to-string, as RFC 8785 requires
    if isinstance(x, int):
        return str(x)
    sign, digits, exp = Decimal(repr(x)).as_tuple()
    raw = "".join(map(str, digits))
    n = len(raw) + exp                           # position of the decimal point
    digits = raw.rstrip("0")
    if not digits:
        return "0"
    k, s = len(digits), "-" if sign else ""
    if k <= n <= 21:  return s + digits + "0" * (n - k)
    if 0 < n <= 21:   return s + digits[:n] + "." + digits[n:]
    if -6 < n <= 0:   return s + "0." + "0" * -n + digits
    e = n - 1
    return s + digits[0] + ("." + digits[1:] if k > 1 else "") + "e" + ("+" if e > 0 else "-") + str(abs(e))

def jcs(v):
    if v is None: return "null"
    if v is True: return "true"
    if v is False: return "false"
    if isinstance(v, str): return json.dumps(v, ensure_ascii=False)
    if isinstance(v, (int, float)): return number(v)
    if isinstance(v, list): return "[" + ",".join(map(jcs, v)) + "]"
    return "{" + ",".join(json.dumps(k, ensure_ascii=False) + ":" + jcs(v[k])
                          for k in sorted(v, key=lambda k: k.encode("utf-16-be"))) + "}"

def request_hash(req):  # req: the request body you POST; returns (canonical form, hash)
    full = {
        "task_id": req["task_id"], "agent_id": req.get("agent_id"),
        "expected_schema": req["expected_schema"], "submitted_output": req["submitted_output"],
        "strict_content_check": req.get("strict_content_check", False),
        "bounds": {p: {**BOUND, **b} for p, b in req["bounds"].items()} if req.get("bounds") else None,
        "rules": [{**RULE, **r} for r in req["rules"]] if req.get("rules") else None,
        "enforce_rules": req.get("enforce_rules", False),
    }
    return jcs(full), hashlib.sha256(jcs(full).encode("utf-8")).hexdigest()

示例:发票验证

下面两次调用都是对 https://verify.sarnai.dev(生产服务)的**真实在线调用**,并展示了收到的签名证明和回执原文。它们使用了免费额度和预留的 test- agent_id,因此没有产生费用,也没有留下记录。相同的两个请求此前也曾作为 Base 上的付费 x402 调用运行;相关结算证据保存在各自结果下方。 两者都可独立验证:获取上方的 agent-card,确认其中列出了 key_version: v1-2026-09c,然后按上文步骤验证每个 attestation.signature。

1. 发票草稿——因跨字段规则而失败(detail: "full")

请求:

{
  "task_id": "invoice-7731-draft",
  "agent_id": "test-seller-invoice-agent",
  "expected_schema": {
    "type": "object",
    "required": ["invoice_id", "currency", "line_items", "total"],
    "properties": {
      "invoice_id": { "type": "string", "pattern": "^INV-[0-9]{4}$" },
      "currency": { "enum": ["USD", "EUR"] },
      "line_items": {
        "type": "array",
        "minItems": 1,
        "items": {
          "type": "object",
          "required": ["sku", "qty", "line_total"],
          "properties": {
            "sku": { "type": "string" },
            "qty": { "type": "integer", "minimum": 1 },
            "line_total": { "type": "number", "minimum": 0 }
          }
        }
      },
      "total": { "type": "number" }
    }
  },
  "submitted_output": {
    "invoice_id": "INV-7731",
    "currency": "USD",
    "line_items": [
      { "sku": "CRWL-100", "qty": 3, "line_total": 87.0 },
      { "sku": "CRWL-200", "qty": 1, "line_total": 42.5 }
    ],
    "total": 135.0
  },
  "rules": [
    { "type": "sum_equals", "field": "line_items[].line_total", "equals_field": "total" }
  ],
  "enforce_rules": true,
  "detail": "full"
}

明细项合计为 129.5,但 total 填写的是 135.0。启用 enforce_rules: true 后,该规则违规会导致结果失败,并附带修正提示:

{
  "summary": {
    "result": "fail",
    "blocking_checks": [
      "consistency:sum_equals"
    ]
  },
  "next_actions": [
    {
      "action": "repair_and_reverify",
      "using": "hints"
    },
    {
      "action": "withhold_payment",
      "for": "buyer"
    }
  ],
  "result": "fail",
  "verification_id": "e5d02dbb-e87c-4f49-bd10-c25c12457fef",
  "task_id": "invoice-7731-draft",
  "agent_id": "test-seller-invoice-agent",
  "output_hash": "d4fb804e434eb3ee047068cbb3691ea1e07066928e7e3efc73b94317e53ee305",
  "schema_hash": "23ea3213715cfaf2011dabe2a9a69d4ac25b6eceec7ce3d4bfef9a1298b69b2a",
  "rules_hash": "2acb6de92b88cfbca279ea6d5bd74b7a1e7dec0839dd093dbe041e25df3364d2",
  "request_hash": "e4bf9733726b24a19ec546ecc79015c9b1cb7a6829b409b41ef56b1c8f60b881",
  "verified_at": "2026-10-14T19:12:05.645221+00:00",
  "verifier_version": "0.4.1",
  "enforce_rules": true,
  "strict_content_check": false,
  "errors": [
    "enforce_rules: consistency:sum_equals at 'line_items[].line_total': values sum to 129.5, expected total (135.0)"
  ],
  "errors_total": 1,
  "errors_omitted": 0,
  "errors_detail": [
    {
      "check": "consistency:sum_equals",
      "path": "$.line_items[].line_total",
      "expected": "a sum equal to total (135.0)",
      "found": "sum 129.5",
      "message": "enforce_rules: consistency:sum_equals at 'line_items[].line_total': values sum to 129.5, expected total (135.0)"
    }
  ],
  "errors_detail_total": 1,
  "errors_detail_omitted": 0,
  "flags": [
    {
      "check": "consistency:sum_equals",
      "field": "line_items[].line_total",
      "severity": "error",
      "detail": "values sum to 129.5, expected total (135.0)"
    }
  ],
  "flags_total": 1,
  "flags_omitted": 0,
  "hints": [
    {
      "source": "integrity",
      "check": "consistency:sum_equals",
      "path": "$.line_items[].line_total",
      "expected": "a sum equal to total (135.0)",
      "found": "sum 129.5"
    }
  ],
  "hints_total": 1,
  "hints_omitted": 0,
  "schema_compliance": {
    "ratio": 1.0,
    "passed": 22,
    "total": 22
  },
  "integrity_checks": {
    "ratio": 0.875,
    "passed": 7,
    "total": 8
  },
  "overall": {
    "ratio": 0.966667,
    "passed": 29,
    "total": 30
  },
  "verify": {
    "public_key_url": "https://verify.sarnai.dev/.well-known/agent-card.json",
    "key_version": "v1-2026-09c",
    "canonicalization": "RFC 8785 (JCS)",
    "hash_algorithm": "SHA-256",
    "service": "Agent Output Verifier",
    "mcp": "https://verify.sarnai.dev/mcp"
  },
  "attestation": {
    "algorithm": "Ed25519",
    "key_version": "v1-2026-09c",
    "timestamp": "2026-10-14T19:12:05.645221+00:00",
    "signature": "4TjRCmJHEoseNdV3cqpB6ozJxc0rukTwm7A5nAC01YsQcvfRt+hX+1nRHlI/eUnwJ0IhERYSt7AWs/anUfpXBQ=="
  }
}

付款证据(此请求此前的付费运行): Base 上支付了 $0.02 USDC,已在链上结算—— 0x4cb755c71413ed5419215d2d7445c34e19be79d9f15b4046802656165b774897。

2. 已更正的发票 — 通过(detail: "compact")

使用相同的架构和规则,将 total 修正为与明细项目相符(129.5),并通过 "detail": "compact" 请求,以仅获取摘要、收据字段和签名:

{
  "task_id": "invoice-7731-final",
  "agent_id": "test-seller-invoice-agent",
  "expected_schema": { "...": "same schema as above" },
  "submitted_output": {
    "invoice_id": "INV-7731",
    "currency": "USD",
    "line_items": [
      { "sku": "CRWL-100", "qty": 3, "line_total": 87.0 },
      { "sku": "CRWL-200", "qty": 1, "line_total": 42.5 }
    ],
    "total": 129.5
  },
  "rules": [
    { "type": "sum_equals", "field": "line_items[].line_total", "equals_field": "total" }
  ],
  "enforce_rules": true,
  "detail": "compact"
}
{
  "summary": {
    "result": "pass",
    "blocking_checks": []
  },
  "next_actions": [
    {
      "action": "attach_receipt",
      "for": "seller"
    },
    {
      "action": "proceed_to_payment",
      "for": "buyer"
    }
  ],
  "result": "pass",
  "verification_id": "f1734fb6-1cc4-47f2-b48f-e3bf4e4e3a60",
  "task_id": "invoice-7731-final",
  "agent_id": "test-seller-invoice-agent",
  "output_hash": "39e3d0a1431e3ce9cda89a1d2873e1ffb4e34f4eeded2ffa87946fbd941a34c3",
  "schema_hash": "b3e46597215768ff0463180da0f0d6db53edfc152b72c240aa27d9df0bb6caaa",
  "rules_hash": "2acb6de92b88cfbca279ea6d5bd74b7a1e7dec0839dd093dbe041e25df3364d2",
  "verified_at": "2026-10-14T19:12:05.833769+00:00",
  "verifier_version": "0.4.1",
  "enforce_rules": true,
  "strict_content_check": false,
  "verify": {
    "public_key_url": "https://verify.sarnai.dev/.well-known/agent-card.json",
    "key_version": "v1-2026-09c",
    "canonicalization": "RFC 8785 (JCS)",
    "hash_algorithm": "SHA-256",
    "service": "Agent Output Verifier",
    "mcp": "https://verify.sarnai.dev/mcp"
  },
  "attestation": {
    "algorithm": "Ed25519",
    "key_version": "v1-2026-09c",
    "timestamp": "2026-10-14T19:12:05.833769+00:00",
    "signature": "sLTZJgf3PEtML2teNImcWBJLXRuP3GbCfdHcP3KcB7HGB0MVoXF7xzYnxgKokafKHPUfZ+aXBl+OTBS3LlrNDA=="
  }
}

紧凑格式保留摘要、收据字段、verify 和 attestation;request_hash、 errors、errors_detail、flags、hints 和三个评分则保留在完整格式中(见上文的 detail)。

支付凭证(此前对此请求的已付款运行): $0.02 USDC,支付于 Base,已在链上结算 — 0xaa9161736ac410b6ac9ccc478780aa2feaa6d2cf02af4c51855e099c14499079。

亲自验证这两张收据

上面的两个响应在纳入本文前,均已使用实时 Agent 卡片中的公钥(v1-2026-09c,来自 https://verify.sarnai.dev)进行检查,采用的步骤见[逐步验证收据](#verifying-a-receipt-step-by-step)。 你也可以用相同方式自行确认:获取 https://verify.sarnai.dev/.well-known/agent-card.json,确认其中列出了 v1-2026-09c,然后针对上面的两个 JSON 代码块重新执行相同的五个步骤 — 签名是基于这些确切数据计算一次的,任何人验证都会得到相同结果。

agent_id 用于标识生成待检查输出的 AI Agent — 例如卖家; 这是由调用方提供、未经身份验证的标签。agent_id: "test-seller-invoice-agent" 使用服务为示例保留的 test- 前缀:验证确实真实执行过,其收据也是真实的,同时与该 AI Agent 的信任评分分开。

许可证

本仓库中的文档和示例根据 MIT 许可证授权。托管服务为专有服务;本仓库仅包含文档和公开元数据。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。