1. 项目概述:为AI Agent构建一个轻量级按次付费轨道
最近在捣鼓AI Agent的落地应用,发现一个挺有意思的痛点:当你的Agent需要调用外部服务或执行特定技能时,如何实现清晰、可信的计费和结算?比如,一个Agent帮你分析了财报,或者调用了一个付费的API,这个“动作”的价值怎么量化,又怎么让用户心甘情愿地付钱?传统的订阅制或者预充值模式在这种高频、小额、场景多变的Agent交互里,显得有点笨重。
这就是shinertx/agentpay-core这个项目想解决的问题。它本质上是一个为AI Agent技能设计的按次付费(Pay-Per-Call)核心框架,目前主要基于Base链上的USDC稳定币。你可以把它想象成Agent世界的“支付轨道”或“计费中间件”。它不处理复杂的钱包交互或前端界面,而是专注于定义一套标准化的API,来管理从报价、授权、计量到生成凭证的整个付费生命周期。
它的核心设计哲学是**“链下优先”**。项目初期选择不把每一步都上链,而是通过链下服务管理预存资金和生成可验证的收据(Receipt),这样能快速迭代和验证模式。等到模式跑通,再逐步引入链上签名验证和余额检查,实现更去中心化的信任。这种务实的设计思路,对于想快速集成支付能力的Agent开发者来说,门槛低了很多。
简单来说,如果你在构建一个需要为不同技能或动作收费的AI Agent,或者想在你的多Agent系统中引入清晰的经济激励,AgentPay Core提供了一套开箱即用的“收费基础设施”。
2. 核心设计思路与架构拆解
2.1 为什么是“按次付费”和“链下优先”?
在AI Agent领域,动作(Skill)的价值往往是离散的、一次性的。用户可能只为一次复杂的代码生成、一次深度的市场调研报告,或者一次特定的API调用付费。按次付费模型最贴合这种使用场景,它让消费和成本一一对应,清晰透明。
那么,为什么一开始选择链下优先?这主要基于几个现实的考量:
- 开发速度与成本:每一步操作都上链(On-chain)意味着Gas费、确认延迟和复杂的智能合约开发。对于一个需要快速验证商业模式和用户接受度的新项目来说,这是巨大的负担。链下优先允许团队用熟悉的Web2技术栈快速搭建原型,把核心业务流程跑通。
- 用户体验:用户不需要为每一次微小的授权或计量操作支付Gas费,体验更接近传统的Web应用。这对于吸引非加密原生的用户至关重要。
- 灵活性与迭代:链下服务可以快速修复Bug、升级API、调整计费策略,而无需经历繁琐的智能合约升级和迁移过程。
但这不意味着放弃区块链的优势。项目的路线图清晰地指向了“渐进式去中心化”:先通过链下服务生成带有密码学签名(如Ed25519)的收据,这些收据本身是不可篡改、可验证的凭证。其他Agent或服务可以信任这些收据,而不必完全信任生成收据的中心化服务器。未来再引入链上存款验证,确保资金托管的透明性。
2.2 四个核心状态与标准化API
AgentPay Core将一次付费调用抽象为四个清晰的状态,每个状态对应一个核心API端点。理解这个状态机是理解整个系统的关键。
报价(Quote):这是起点。Agent向计费服务询问:“执行某个动作(SKU),需要多少单位(Units),总价是多少?” 服务返回一个包含价格、有效期和唯一
quoteId的响应。这允许实现动态定价(比如根据网络拥堵情况)和价格确认。授权(Authorize):用户(或代表用户的Agent)同意支付后,需要创建一个授权。这个授权是有范围(scope,限定于某个quote)和有时间限制(ttlSeconds)的。更重要的是,它是幂等的(通过
idempotencyKey实现),这意味着用相同的参数重复请求不会重复扣款,防止网络重试导致的双重支付。principal字段标识了支付主体(如用户钱包地址)。用量记录(Usage Record):授权成功后,Agent执行技能。执行过程中或结束后,Agent需要向计费服务报告实际消耗的“单位数”(
unitsUsed)。这个操作也是幂等的。runId将这次用量记录与某一次具体的技能执行关联起来,proofs字段(未来可能)用于提交执行证明(如零知识证明)。收据(Receipt):当一次调用生命周期结束(可能是一次用量记录,也可能是多次),计费服务可以生成一个最终的收据。这个收据是一个可验证的凭证,包含了支付摘要、相关授权和用量ID等信息。其他Agent或审计服务可以获取并验证这个收据,作为支付完成的可靠证据。
这个设计巧妙地将支付流程与业务执行流程解耦。Agent开发者只需要在关键节点调用这几个API,复杂的计费、对账、凭证生成都由AgentPay Core负责。
2.3 技术栈与运行依赖
从项目代码来看,它很可能是一个基于Node.js的服务(npm i和npm run dev提示了这一点)。本地开发服务器运行在localhost:8787,这很像Cloudflare Workers(端口8787是其Wrangler开发服务器的默认端口)或类似轻量级边缘计算平台的配置。这种选择符合其“轻量级核心”的定位,意味着它可以被低成本地部署和扩展。
项目强依赖USDC稳定币,特别是Circle发行的原生USDC。它硬编码了Base主网、Base Sepolia测试网和Solana上的标准USDC合约地址。这样做是为了防止因使用相似的、流动性不足的“假”USDC而导致的资产损失风险,体现了对金融安全性的重视。
3. API深度解析与实操指南
3.1 报价接口:动态定价的入口
GET /v1/quote?sku=<action>&units=<n>
这个接口是交互的起点。sku(Stock Keeping Unit)是一个字符串,用于唯一标识一个可付费的“技能”或“动作”,比如"code-review"、"market-analysis-v2"。units参数代表请求的数量,某些技能可能按“次数”计费(units=1),有些可能按“字数”或“计算时长”计费。
内部逻辑推测与实现建议:服务端应该维护一个sku配置表,映射到单价(price per unit)。当收到请求时,会进行如下计算和操作:
- 验证
sku是否存在且有效。 - 根据当前定价策略(可能从数据库或配置中心读取)计算总价:
total_price = unit_price * units。 - 生成一个唯一的
quoteId(通常是UUID),并将报价详情(sku, units, unit_price, total_price, expiry_timestamp)存储在缓存或数据库中,并设置一个较短的过期时间(例如5分钟)。 - 返回包含上述信息的JSON响应。
实操心得:SKU的设计在设计SKU时,建议采用有层次的命名法,例如
{service}.{resource}.{action},像openai.completion.gpt-4或github.repo.analysis。这便于后续的统计、分析和权限管理。同时,在SKU配置中,除了价格,还应考虑并发限制、调用频率限制等业务规则。
3.2 授权接口:创建安全的支付许可
POST /v1/authorize请求体示例:
{ "quoteId": "quote_abc123", "principal": "0xuserAddress123...", "scope": ["skill:execute"], "ttlSeconds": 300, "idempotencyKey": "req_unique_key_456" }这是整个流程中最关键的安全环节。它的目的是创建一个有时效性和范围限制的支付授权令牌。
- quoteId:关联之前获取的报价,锁定价格。
- principal:支付主体。在链下优先阶段,这可能是一个数据库内的用户ID;未来上链后,就是钱包地址。系统需要验证该主体是否有足够的余额或信用。
- scope:授权范围。示例中的
["skill:execute"]是一个简单的字符串数组,未来可以扩展为更复杂的权限表达式,如["skill:read", "data:export"],实现细粒度控制。 - ttlSeconds:授权存活时间。必须短于报价有效期,且设置合理,防止授权被长期滥用。例如,一个代码生成技能授权5分钟是合理的,但一个数据分析报告生成可能需要30分钟。
- idempotencyKey:幂等键。这是防止重复扣款的基石。客户端必须为每一次授权意图生成一个全局唯一的键(如UUID)。服务端会记录这个键,当收到相同键的请求时,直接返回第一次创建授权时的响应,而不会重复创建和扣款。
服务端处理流程:
- 幂等检查:首先根据
idempotencyKey查询。如果已处理,直接返回已创建的授权记录。 - 验证:检查
quoteId是否有效且未过期;检查principal状态是否正常(如余额充足);校验scope是否在quoteId对应的SKU允许范围内。 - 扣款/预留:从
principal的账户中扣除或预留quote中的总价金额。在链下,这通常是在数据库事务中更新余额字段。 - 生成授权:创建一条授权记录,包含唯一的
authorizationId、状态(如active)、过期时间(created_at + ttlSeconds)以及所有关联信息。 - 返回:将
authorizationId和过期时间等信息返回给客户端。
注意事项:余额管理与并发在链下实现余额扣减时,必须处理好并发问题。两个同时发生的请求可能都通过“余额充足”的检查,导致超额支出。解决方案是使用数据库的行级锁或乐观锁(版本号)在更新余额时确保原子性。例如,在SQL中可以使用
UPDATE accounts SET balance = balance - ? WHERE id = ? AND balance >= ?这样的语句,利用数据库的原子操作和条件判断。
3.3 用量记录接口:精准计量与证明
POST /v1/usage/record请求体示例:
{ "authorizationId": "auth_xyz789", "runId": "run_20240401_001", "unitsUsed": 1, "proofs": ["optional_proof_data"], "idempotencyKey": "usage_req_789" }技能执行完毕后,Agent通过此接口上报实际消耗。
- authorizationId:关联的活跃授权。
- runId:由Agent生成的、唯一标识本次技能执行的ID。用于将用量与具体的业务日志关联,方便对账和调试。
- unitsUsed:实际使用的单位数。这里有一个重要设计:它可以小于或等于报价中的
units,但不能超过。这支持了“按实际使用量”计费的模式。如果技能执行失败或部分执行,可以上报更少的用量。 - proofs:可选字段,用于未来提交执行证明。例如,一个“图像生成”技能可以提交最终图像的哈希值;一个“API调用”技能可以提交响应体的哈希。这为构建可验证的计算市场奠定了基础。
- idempotencyKey:同样,用量记录也必须是幂等的,防止因重试导致重复计量。
服务端处理流程:
- 幂等检查。
- 授权验证:检查
authorizationId是否存在、是否处于active状态、是否未过期,以及unitsUsed是否未超过授权对应的报价单位数上限。 - 记录用量:创建用量记录。系统可能需要根据
unitsUsed和授权对应的单价,重新计算最终费用(如果支持按实际使用量结算)。 - 状态更新:如果此次用量记录消耗完了授权中的所有单位(或达到了某个阈值),服务端可能会将授权状态标记为
consumed或closed。 - 触发后续操作:可以触发生成收据的流程,或者通知相关系统。
3.4 收据接口:获取可验证的支付凭证
GET /v1/receipt/:id
收据是整个流程的“毕业证书”。它是一个只读的、包含完整支付摘要的凭证。其他Agent或服务可以获取它来验证某次支付是否真实发生。
一个收据的响应体可能包含:
{ "receiptId": "rcpt_123456", "status": "completed", "principal": "0xuser...", "sku": "code-review", "quoteId": "quote_abc123", "authorizationId": "auth_xyz789", "usageRecordIds": ["usage_1", "usage_2"], "totalUnits": 1, "totalAmount": "1.50", // USDC金额 "currency": "USDC", "createdAt": "2024-04-01T10:30:00Z", "metadata": {} // 可附加业务自定义数据 }未来演进:签名收据根据路线图,未来的收据将包含基于Ed25519等算法的数字签名。服务端用私钥对收据内容签名,客户端可以用公钥验证。这样,即使不信任AgentPay Core服务本身,任何第三方只要持有公钥,就能独立验证这张收据的真实性和完整性,实现了“链下生成,链上可验”的信任模型。
4. 部署、开发与扩展实践
4.1 本地开发环境搭建
按照README,本地运行非常简单:
git clone <repository-url> cd agentpay-core npm install npm run dev运行后,可以通过访问http://localhost:8787/healthz来检查服务是否健康。这通常是一个简单的端点,返回{"status":"ok"}。
开发环境配置要点:
- 数据库:项目需要一个持久化存储来存放报价、授权、用量、收据以及用户余额。本地开发可以使用SQLite或Docker启动一个PostgreSQL/MySQL实例。你需要查看项目源码中的迁移文件(如
migrations/文件夹)来初始化数据库表结构。 - 配置管理:SKU价格、USDC合约地址、服务私钥等应通过环境变量或配置文件管理。创建一个
.env.local文件,定义如DATABASE_URL、JWT_SECRET(如果用于内部鉴权)、BASE_RPC_URL(为未来链上验证准备)等变量。 - 测试:使用
npm test运行单元测试和集成测试。为API编写测试时,重点测试幂等性、余额并发扣减、授权过期等边界情况。
4.2 集成到你的Agent系统
将AgentPay Core集成到你的Agent中,通常意味着在你的技能执行逻辑中嵌入几个关键的API调用。以下是一个伪代码示例:
async function executePaidSkill(userId, skillSku, skillParams) { // 1. 获取报价 const quote = await agentPayClient.getQuote(sku: skillSku, units: 1); // 2. 创建授权 (前端或Agent钱包应在此步骤介入,获得用户确认) const auth = await agentPayClient.createAuthorization({ quoteId: quote.id, principal: userId, scope: ['execute'], ttlSeconds: 600, idempotencyKey: generateIdempotencyKey() }); // 3. 执行技能(使用授权ID) let result; let unitsUsed = 0; try { result = await actuallyExecuteSkill(skillParams); unitsUsed = calculateActualUsage(result); // 根据结果计算实际用量 } catch (error) { // 执行失败,用量为0 unitsUsed = 0; // 仍然需要记录一次用量(失败记录),以便关闭授权循环 } finally { // 4. 记录用量 await agentPayClient.recordUsage({ authorizationId: auth.id, runId: generateRunId(), unitsUsed: unitsUsed, idempotencyKey: generateIdempotencyKey() }); } // 5. (可选)获取收据,附在结果中返回给用户 const receipt = await agentPayClient.getReceiptForAuthorization(auth.id); return { result, receiptId: receipt.id }; }4.3 安全与生产就绪考量
- 认证与授权(API层面):AgentPay Core的API本身需要被保护。在生产环境中,你应该为每个集成的Agent服务颁发API密钥(API Key)或使用JWT令牌。所有对
/v1/端点的请求都必须携带有效的认证信息,防止未授权的调用和资金盗用。 - 幂等键生成:
idempotencyKey必须确保全局唯一且与业务请求强相关。一个最佳实践是使用{principal}_{timestamp}_{random_nonce}的格式,或者直接使用UUID v4。服务端应为幂等键设置合理的存储过期时间(如24小时),之后清理旧记录。 - 监控与告警:监控关键指标:各SKU的调用频率、授权失败率、余额不足事件、平均响应时间。设置告警,例如当某个principal的授权失败率突然升高时,可能意味着攻击或bug。
- 数据备份与审计:所有的报价、授权、用量、收据记录都是财务数据,必须定期备份。这些日志也是后续对账和审计的核心依据。
5. 路线图解读与未来生态展望
项目的Roadmap揭示了其从“可用的最小化产品”向“健壮的去中心化协议”演进的路径。
签名令牌与收据签名:这是迈向“无需信任”的第一步。授权令牌和收据本身将被签名。其他服务可以仅通过验证签名来信任这些凭证,而不必回调到AgentPay Core的中心化服务器进行验证。这大大降低了系统的耦合度和单点故障风险。
链上存款验证:目前链下余额管理依赖于对中心化数据库的信任。下一步是集成BaseScan或Coinbase RPC,直接读取用户在链上的USDC存款合约中的余额。这样,资金完全由用户自己控制(在智能合约钱包或普通钱包中),AgentPay Core服务只负责验证链上余额并签发授权,实现了资金的“非托管”。这是建立完全信任的关键一步。
余额与消费上限:为每个
principal设置全局或基于SKU的消费上限(Caps)。例如,单个用户每天在“高级代码生成”技能上最多消费50 USDC。这既是风险控制手段(防止盗用造成的无限损失),也能作为用户自主管理的预算工具。x402风格的单次调用授权:这是一个非常前瞻性的概念。它可能指的是类似以太坊EIP-4337(账户抽象)或更灵活的授权标准,允许用户对单次调用(而非整个合约或无限额度)进行精细授权。这可以做到极致的按需付费,用户授权Agent执行一次价值1 USDC的动作,即使Agent被恶意控制,它也无法盗走用户钱包里的其他资产。
生态展望:AgentPay Core如果成功,可以成为多Agent经济系统的“支付层协议”。不同的Agent平台、技能市场都可以接入这套标准。一个Agent在平台A赚取的USDC收入,可以凭借可验证的收据,在平台B作为信用或抵押物使用。它有望成为连接AI Agent价值创造与加密经济流动性的关键基础设施。
6. 常见问题与故障排查实录
在实际集成和测试中,你可能会遇到以下典型问题:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
调用GET /quote返回404或400 | 1. SKU不存在或拼写错误。 2. units参数格式错误(非数字)。 | 1. 检查服务端SKU配置表,确认SKU已正确发布。 2. 确保客户端传递的 units是正整数。 |
调用POST /authorize返回403 Forbidden或402 Payment Required | 1. API密钥无效或缺失。 2. principal余额不足。3. quoteId已过期。 | 1. 检查请求头中的Authorization字段。2. 查询该 principal的账户余额。3. 检查报价记录的 expires_at时间戳。 |
重复调用POST /authorize扣了两次款 | 客户端未正确实现幂等性,每次请求生成了不同的idempotencyKey。 | 这是严重错误!检查客户端逻辑,确保对于同一笔支付意图,idempotencyKey保持不变。服务端已正确处理幂等,问题出在客户端。 |
调用POST /usage/record返回409 Conflict | 1. 使用的idempotencyKey已关联了另一条用量记录(内容不同)。2. 授权已过期或状态不是 active。 | 1. 确保每个独立的用量记录事件都有唯一的idempotencyKey。2. 检查授权记录的 status和expires_at字段。 |
unitsUsed超过授权限制 | Agent业务逻辑计算实际用量时出错,或者报价时units预估不足。 | 服务端应拒绝此次记录(返回400)。客户端需要捕获此错误,并可能触发一个“补偿”流程,例如引导用户为超出的部分创建一个新的授权。 |
| 服务响应缓慢 | 1. 数据库连接池不足或查询未优化。 2. 幂等键检查的缓存/数据库压力大。 | 1. 检查数据库慢查询日志,对authorizations、idempotency_keys等表的关键字段建立索引。2. 考虑将幂等键的短期存储移至Redis等内存数据库,加快检查速度。 |
| 本地开发服务器无法连接数据库 | 1. 数据库服务未启动。 2. .env文件中的DATABASE_URL配置错误。3. 数据库迁移未运行。 | 1. 确认PostgreSQL等数据库进程在运行。 2. 核对连接字符串的主机、端口、用户名、密码和数据库名。 3. 运行 npm run migrate或类似命令来执行数据库迁移。 |
踩坑心得:幂等键的管理初期我们曾将idempotencyKey简单存储在内存中,在服务重启后全部丢失,导致客户端重试时生成了新的键,造成了重复扣款。必须将幂等键持久化到数据库,并建立复合索引(idempotency_key, principal)以提高查询效率并确保唯一性约束。同时,要有一个后台任务定期清理过期(如24小时前)的幂等键记录,防止表无限膨胀。
另一个教训是关于余额检查的时机。我们最初在创建授权时才检查余额,但在高并发下,用户可能在检查余额后、扣款前瞬间将余额转走。解决方案是采用“乐观锁”或“预扣款”模式。在检查余额的同时,在一个数据库事务内直接进行扣减操作,如果余额不足,事务会失败回滚。这确保了资金操作的原子性。