Skip to main content

电梯演讲

您提议进行什么更改?
向代理客户端协议添加标准化的使用情况和上下文窗口跟踪,使代理能够以跨实现的一致方式报告令牌消耗、成本估算和上下文窗口利用率。

现状

今天的事情是如何运作的,这导致了什么问题?我们为什么要改变?
目前,ACP 协议没有标准化的方式让代理传达:
  1. 令牌使用 - 一轮或累计消耗了多少令牌
  2. 上下文窗口状态 - 模型上下文窗口的使用量
  3. 成本信息 - API 使用的估算成本
  4. 提示缓存指标 - 支持缓存的模型的缓存命中/未命中
这造成了几种问题:
  • 无法了解资源消耗 - 客户端无法向用户显示他们的上下文预算使用量
  • 没有成本透明度 - 用户无法跟踪支出或在操作前估算成本
  • 没有上下文管理 - 客户端无法在接近上下文限制时警告用户或建议压缩
  • 不一致的实现 - 每个代理以不同方式实现使用情况跟踪(如果有的话)
行业研究显示 AI 编码工具中的常见模式:
  • LLM 提供商在 API 响应中返回累计令牌计数
  • IDE 扩展显著显示上下文百分比(例如,显示”19%“的径向进度)
  • 客户端在悬停/详细信息上显示绝对数字(例如,“31.4K/200K 个令牌”)
  • 工具在阈值百分比(75%、90%、95%)警告用户
  • 自动压缩功能在接近上下文限制时触发
  • 成本跟踪侧重于累计会话总数而不是每轮分解

我们提议对此做什么

您提议如何改善情况?
我们提议将使用情况跟踪分为两个不同的关注点:
  1. 令牌使用 - 在每轮之后在 PromptResponse 中报告(每轮数据)
  2. 上下文窗口和成本 - 通过 session/update 通知报告,会话更新为 "usage_update"(会话状态)
这种分离反映了用户消费这些信息的方式:
  • 令牌计数绑定到特定的轮次,并在提示后立即有用
  • 上下文窗口和成本是累计的会话状态,代理主动推送(可用时)
代理在适当时发送上下文更新:
  • session/new 响应中(如果代理可以立即查询使用情况)
  • session/load / session/resume 上(对于恢复/分叉的会话)
  • 在每个 session/prompt 响应之后(当使用情况数据可用时)
  • 任何时候上下文窗口状态发生重大变化
这种方法为不同的代理实现提供了灵活性:
  • 支持在不提示的情况下获取当前使用情况的代理可以在创建、恢复或分叉聊天时立即发送更新
  • 只在主动提示时提供使用情况的代理可以在发送新提示后发送更新

PromptResponse 中的令牌使用

PromptResponse 中添加 usage 字段以进行令牌消耗跟踪:
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "sessionId": "sess_abc123",
    "stopReason": "end_turn",
    "usage": {
      "total_tokens": 53000,
      "input_tokens": 35000,
      "output_tokens": 12000,
      "thought_tokens": 5000,
      "cached_read_tokens": 5000,
      "cached_write_tokens": 1000
    }
  }
}

使用字段

  • total_tokens(数字,必需)- 整个会话中所有令牌类型的总和
  • input_tokens(数字,必需)- 所有轮次中的输入令牌总数
  • output_tokens(数字,必需)- 所有轮次中的输出令牌总数
  • thought_tokens(数字,可选)- 所有轮次中的思维/推理令牌总数(用于 o1/o3 模型)
  • cached_read_tokens(数字,可选)- 所有轮次中的缓存读取令牌总数
  • cached_write_tokens(数字,可选)- 所有轮次中的缓存写入令牌总数

通过 session/update 的上下文窗口和成本

代理通过 sessionUpdate: "usage_update"session/update 通知发送上下文窗口和成本信息:
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123",
    "update": {
      "sessionUpdate": "usage_update",
      "used": 53000,
      "size": 200000
    }
  }
}

上下文窗口字段(必需)

  • used(数字,必需)- 当前上下文中的令牌数
  • size(数字,必需)- 上下文窗口的总大小(以令牌为单位)
注意:客户端可以根据需要计算 remainingsize - usedpercentageused / size * 100

成本字段(可选)

  • cost(对象,可选)- 累计会话成本
    • amount(数字,必需)- 会话的总累计成本
    • currency(字符串,必需)- ISO 4217 货币代码(例如,“USD”、“EUR”)
带有可选成本的示例:
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123",
    "update": {
      "sessionUpdate": "usage_update",
      "used": 53000,
      "size": 200000,
      "cost": {
        "amount": 0.045,
        "currency": "USD"
      }
    }
  }
}

设计原则

  1. 关注点分离 - 令牌使用是每轮数据,上下文窗口和成本是会话状态
  2. 代理推送通知 - 代理在数据可用时主动发送上下文更新,遵循与其他动态会话属性相同的模式(available_commands_updatecurrent_mode_updatesession_info_update
  3. 代理计算,客户端可以验证 - 代理最了解其模型并提供计算,但包含原始数据以供客户端验证
  4. 灵活的成本报告 - 成本是可选的,因为并非所有代理都跟踪它。支持任何货币,不要假设美元
  5. 提示缓存支持 - 包括支持缓存的模型的缓存读取/写入令牌
  6. 可选但推荐 - 使用情况跟踪是可选的以保持向后兼容性
  7. 灵活的时间 - 代理在可以时发送更新:对于具有按需 API 的代理立即,对于只在主动提示时提供使用情况的代理在提示后发送

美好的未来

一旦这个功能存在,事情将如何发展?
对于用户:
  • 可见性:用户看到带有百分比指示器的实时上下文窗口使用情况
  • 成本意识:用户可以跟踪支出并随时检查累计成本
  • 更好的规划:用户知道何时开始新会话或压缩上下文
  • 透明度:清楚了解资源消耗
对于客户端实现:
  • 一致的 UI:所有客户端可以以标准方式显示使用情况(进度条、百分比、警告)
  • 智能警告:客户端可以在 75%、90% 上下文使用量时警告用户
  • 成本控制:客户端可以实现预算限制和警报
  • 分析:客户端可以跟踪使用模式并进行优化
  • 反应式更新:客户端通过通知接收上下文更新,当代理推送新数据时立即更新 UI
  • 无需轮询:更新在代理有新信息时自动到达,消除了客户端轮询的需要
对于代理实现:
  • 标准报告:关于报告什么和何时报告的清晰合同
  • 灵活性:可选字段允许代理报告它们可以计算的内容
  • 模型多样性:适用于任何模型(GPT、Claude、Llama 等)
  • 缓存支持:一流支持提示缓存

实施细节和计划

告诉我更多关于您的实施。您的详细实施计划是什么?
  1. 更新 schema.json 以添加:
    • 带令牌字段的 Usage 类型
    • amountcurrency 字段的 Cost 类型
    • usedsize(必需)和可选 cost 字段的 ContextUpdate 类型
    • PromptResponse 中添加可选的 usage 字段
    • SessionUpdate oneOf 数组中添加 UsageUpdate 变体(带有 sessionUpdate: "usage_update"
  2. 更新协议文档
    • /docs/protocol/prompt-turn.mdx 中记录 usage 字段
    • 记录带有 sessionUpdate: "usage_update" 变体的 session/update 通知
    • 添加示例显示典型使用模式以及代理何时发送上下文更新

常见问题

在编写本文档的过程中或随后的讨论中出现了哪些问题?

为什么将令牌使用与上下文窗口和成本分离?

不同用户在不同的时刻关心不同的事情:
  • 令牌计数:在轮次完成后立即相关以了解分解
  • 剩余上下文窗口:任何时候都相关,特别是在发出大提示之前。“我需要交接还是压缩?”
  • 累计成本:会话级别状态,代理在可用时推送
分离它们允许:
  • 更清晰的数据模型,其中每轮数据保留在轮次响应中
  • 代理在数据可用时主动推送上下文更新
  • 客户端无需轮询即可被动接收更新

为什么成本在 session/update 中而不是 PromptResponse 中?

成本是累计的会话状态,类似于上下文窗口:
  • 用户想要跟踪总支出,而不仅仅是每轮成本
  • 保持 PromptResponse 专注于每轮令牌分解
  • 成本和上下文窗口都是属于一起的会话级别指标
  • 成本是可选的,因为并非所有代理都跟踪它

用户如何知道何时交接或压缩上下文?

上下文更新通知提供了所需的一切:
  • usedsize 提供精确跟踪的绝对数字
  • 客户端可以计算 remainingsize - usedpercentageused / size * 100
  • size 让客户端了解总预算
推荐的客户端行为:
百分比操作
< 75%正常操作
75-90%黄色指标,建议”上下文正在填满”
90-95%橙色指标,建议”开始新会话或总结”
> 95%红色指标,警告”下一个提示可能失败 - 推荐交接”
客户端还可以:
  • 提供”压缩上下文”或”总结对话”操作
  • 自动建议开始新会话
  • 在接近限制时实现自动交接

为什么不为成本假设美元?

代理可能以不同货币计费:
  • 欧洲代理可能以欧元计费
  • 亚洲代理可能以日元或人民币计费
  • 一些代理可能使用积分或点数
  • 货币汇率会变化
最好报告实际的计费货币,让客户端在需要时进行转换。

如果代理无法计算某些字段怎么办?

除基本令牌计数外的所有字段都是可选的。代理报告它们可以计算的内容。客户端优雅地处理缺失的字段。

这与流式响应如何工作?

  • 在流式传输期间:代理可以通过 session/update 通知随着使用情况变化发送渐进式上下文更新
  • 最终响应:在 PromptResponse 中包含完整的令牌使用情况
  • 上下文窗口和成本:代理在数据可用时发送带有 sessionUpdate: "usage_update"session/update 通知(在提示完成后、会话创建/恢复时,或上下文状态发生重大变化时)

对于没有固定上下文窗口的模型怎么办?

  • 报告有效的上下文窗口大小
  • 对于具有动态窗口的模型,报告当前限制
  • 如果发生变化则更新大小
  • 如果真正无限则设置为 null(罕见)

关于速率限制和配额呢?

这个 RFD 侧重于令牌使用情况和上下文窗口。速率限制和配额是可以在未来的 RFD 中解决的单独问题。然而,这里的成本跟踪帮助用户了解他们对配额限制的使用情况。

缓存的令牌是否计入上下文窗口?

是的,缓存的令牌仍然占据上下文窗口空间。它们只是处理起来更便宜。上下文窗口使用情况应包括所有令牌(常规 + 缓存)。

为什么通知而不是请求?

使用 session/update 通知而不是 session/status 请求提供了几个好处:
  1. 一致性:遵循与其他动态会话属性相同的模式(available_commands_updatecurrent_mode_updatesession_info_update
  2. 代理灵活性:代理可以在数据可用时发送更新,无论是立即(对于具有按需 API 的代理)还是提示后(对于只在主动提示时提供使用情况的代理)
  3. 无需轮询:客户端无需轮询即可被动接收更新
  4. 实时更新:更新作为会话生命周期的一部分自然流动

如果客户端在会话中途连接怎么办?

当客户端连接到现有会话(通过 session/loadsession/resume)时,代理应该在它们有当前使用情况数据可用时发送上下文更新通知。这确保客户端 UI 可以立即显示准确的上下文窗口和成本信息。 对于只在主动提示时提供使用情况的代理,客户端 UI 可能在发送第一个提示之前不显示使用情况,这在给定代理能力的情况下是可以接受的。

您考虑了哪些替代方法,为什么选择这一个?

考虑的替代方案:
  1. 全部在 PromptResponse - 更简单,但上下文窗口和成本是会话状态,用户可能希望独立于轮次跟踪。
  2. 请求/响应(session/status - 需要客户端轮询,并且一些代理在没有提示的情况下没有 API 来查询当前状态。通知方法更灵活,并与与其他动态会话属性一致的模式。
  3. 客户端计算一切 - 被拒绝,因为客户端不知道模型的标记器、确切的上下文窗口大小或定价。
  4. 只有百分比,没有原始令牌 - 被拒绝,因为用户想要绝对数字,客户端无法验证计算,并且透明度较低。

修订历史

  • 2025-12-07:初始草稿
  • 2025-12-13:从 session/status 请求方法更改为带有 sessionUpdate: "context_update"session/update 通知。使 cost 可选并删除 remaining 字段(客户端可以计算为 size - used)。这种方法为代理提供了更大的灵活性,并遵循与其他动态会话属性相同的模式。
  • 2025-12-17:将 reasoning_tokens 重命名为 thought_tokens 以与 ACP 术语保持一致。移除 percentage 字段(客户端可以计算为 used / size * 100)。
  • 2025-12-19:将 sessionUpdate: "context_update" 重命名为 sessionUpdate: "usage_update" 以更好地反映有效负载语义(包括上下文窗口信息和累计成本)。