让自主 Agent 安全付款:用确定性信任门、会话预算与凭证隔离控制风险

2026-09-01 40 预计阅读时间: 1 分钟
来源: aws.amazon.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

当 AI Agent 可以自行调用付费接口时,支付不再只是一次 API 请求,而是一个实时授权问题:这个端点是否可信、当前会话还能花多少钱、执行任务的 Agent 是否应该接触支付凭证?t54 构建的 x402-secure 将信任判断放在 Amazon Bedrock AgentCore 支付流程之前,对每个端点评分,再由确定性规则决定是否放行。据来源摘要,这套机制已经治理超过 2000 万笔由 Agent 发起、无人逐笔介入的交易。

支付之前必须多一道机器可执行的判断

传统支付系统通常假设消费者已经完成选择,系统只需验证身份、余额和交易合法性。自主 Agent 改变了这个前提:模型不仅提出需求,还可能选择服务商、解析报价并触发付款。

这会引入几类新风险:

  • Agent 可能访问第一次出现、缺少历史记录的端点。
  • 服务端返回的价格可能超出任务价值或会话预算。
  • 模型输出具有概率性,同一个任务可能产生不同的调用路径。
  • 长期支付凭证一旦进入模型上下文、日志或工具参数,泄露范围很难控制。
  • 单笔金额很小并不代表风险很低,高频调用可能迅速累积成本。

x402-secure 的关键思路,是把“模型认为可以购买”与“系统允许付款”拆成两个决定。模型负责规划,信任层负责执行不可绕过的政策。端点评分可以吸收历史成功率、身份验证状态、价格异常、争议记录等信号,但最终付款决策应落到明确、可审计的布尔规则上。

三层控制分别解决什么问题

1. 端点信任评分

每次付款前都评估目标端点,而不是只在供应商接入时审核一次。评分能反映端点状态变化,也能让新端点、异常报价和失败历史进入同一套判断流程。

评分本身不能直接等同于安全。工程上还需要记录评分版本、输入信号和决策原因,否则事后只能看到一个无法解释的数字。对于缺失信号,也应采取明确策略,例如将未验证的新端点送入更严格的额度,而不是让模型自行补全结论。

2. 会话预算

会话预算把风险限制在一次任务或一段短生命周期内。它至少应包含:

  • 单笔交易上限;
  • 会话累计上限;
  • 最大交易次数;
  • 预算过期时间;
  • 允许的币种、网络或服务类别。

检查预算与扣减预算必须是原子操作。否则两个并发工具调用都可能看到“余额充足”,随后共同突破上限。生产环境可以使用数据库事务、条件更新或带幂等键的账本完成预留与结算。

3. 支付凭证隔离

Agent 不应直接获得钱包私钥、长期 API Token 或底层支付服务的管理员权限。更稳妥的方式是让 Agent 只提交付款意图,由独立支付执行器完成签名和发送。

支付执行器应只接受结构化字段,例如端点标识、金额、币种、会话 ID 和幂等键。凭证保存在密钥管理系统或专用签名服务中,不进入提示词,也不回传给 Agent。即使模型受到提示注入影响,攻击者拿到的也只是一个受预算和政策限制的付款入口。

可以这样实践:一个可运行的确定性信任门

来源摘要没有披露 x402-secure 的具体评分公式或内部 API。下面是一个可直接运行的最小示例,用来演示端点评分、会话预算、幂等控制和凭证隔离之间的边界。生产系统应把内存状态替换为事务数据库,并将 execute_payment 接入受保护的支付执行服务。

将以下内容保存为 trust_gate.py,使用 Python 3.11 或更高版本运行,无需安装第三方依赖:

from dataclasses import dataclass, field
from decimal import Decimal
from hashlib import sha256
from typing import Final

MIN_TRUST_SCORE: Final = 80
MAX_SINGLE_PAYMENT: Final = Decimal("2.00")


@dataclass(frozen=True)
class EndpointEvidence:
    identity_verified: bool
    success_rate: Decimal
    price_anomaly: bool
    dispute_count: int


def score_endpoint(evidence: EndpointEvidence) -> int:
    score = 50
    score += 25 if evidence.identity_verified else -30
    score += 20 if evidence.success_rate >= Decimal("0.99") else 0
    score -= 35 if evidence.price_anomaly else 0
    score -= min(evidence.dispute_count * 10, 30)
    return max(0, min(score, 100))


@dataclass
class SessionBudget:
    limit: Decimal
    spent: Decimal = Decimal("0")
    payment_ids: set[str] = field(default_factory=set)

    def reserve(self, amount: Decimal, payment_id: str) -> None:
        if payment_id in self.payment_ids:
            raise ValueError("duplicate payment_id")
        if amount <= 0 or amount > MAX_SINGLE_PAYMENT:
            raise ValueError("single-payment policy rejected the request")
        if self.spent + amount > self.limit:
            raise ValueError("session budget exceeded")

        # This mutation must be a database transaction in production.
        self.spent += amount
        self.payment_ids.add(payment_id)


def authorize_payment(
    endpoint: str,
    amount: Decimal,
    evidence: EndpointEvidence,
    budget: SessionBudget,
    request_id: str,
) -> dict[str, str]:
    trust_score = score_endpoint(evidence)
    if trust_score < MIN_TRUST_SCORE:
        raise PermissionError(f"endpoint rejected: trust_score={trust_score}")

    payment_id = sha256(
        f"{request_id}:{endpoint}:{amount}".encode("utf-8")
    ).hexdigest()
    budget.reserve(amount, payment_id)

    # Return a narrow payment intent. No wallet key or long-lived token is exposed.
    return {
        "endpoint": endpoint,
        "amount": str(amount),
        "currency": "USD",
        "payment_id": payment_id,
        "policy_version": "trust-gate-v1",
        "trust_score": str(trust_score),
    }


def execute_payment(intent: dict[str, str]) -> None:
    # Production assumption: call an isolated signer/payment service here.
    print("AUTHORIZED", intent)


if __name__ == "__main__":
    budget = SessionBudget(limit=Decimal("5.00"))
    evidence = EndpointEvidence(
        identity_verified=True,
        success_rate=Decimal("0.995"),
        price_anomaly=False,
        dispute_count=0,
    )
    intent = authorize_payment(
        endpoint="api.example.test/data",
        amount=Decimal("1.25"),
        evidence=evidence,
        budget=budget,
        request_id="agent-session-42-step-7",
    )
    execute_payment(intent)

运行命令:

python3 trust_gate.py

这个例子刻意让授权结果保持确定性:相同证据、政策版本、金额和预算状态应产生相同决定。LLM 可以生成付款意图,但不能修改 MIN_TRUST_SCORE、绕过 reserve,也不能调用持有真实凭证的签名接口。

接入 AgentCore 支付流程时的工程边界

可以把信任门设计成 Agent 与支付执行器之间的独立服务。一个典型调用链是:Agent 选择端点并形成付款意图,信任服务加载端点证据和会话预算,政策引擎返回允许或拒绝,支付执行器仅在允许时使用隔离凭证完成交易。

生产部署还应补齐以下能力:

  • 为每次决策记录端点、证据摘要、评分、政策版本、预算前后值和拒绝原因。
  • 使用幂等键抵御 Agent 重试、网络超时和消息重复投递。
  • 将“预算预留”和“支付结算”分成状态机,处理支付失败后的额度释放。
  • 对评分数据设置时效,避免长期复用已经过期的信誉信息。
  • 给政策变更准备影子运行和回放测试,观察新阈值会拒绝或放行哪些历史交易。
  • 默认拒绝证据缺失、币种未知、金额无法规范化以及端点身份不匹配的请求。

采用时不要只盯着评分模型

信任分数容易成为最显眼的部分,但真正决定系统能否承受无人值守交易的,是它周围的强约束:预算是否原子扣减、凭证是否与 Agent 隔离、授权是否可重放审计、失败是否默认关闭。

上线前可以用一份简短清单验收:LLM 无法直接访问支付密钥;每个会话都有金额和次数上限;所有付款都有稳定幂等键;端点评分带有版本和证据;任何拒绝都能给出机器可读的原因;政策更新经过历史流量回放。做到这些,Agent 的自主性才会停留在可控范围内,而不会变成无限额度的自动执行权限。


相关推荐