- 作者:@ahmedhesham6
- 支持者:@benbrandt
电梯演讲
您提议进行什么更改?向代理客户端协议添加标准化的使用情况和上下文窗口跟踪,使代理能够以跨实现的一致方式报告令牌消耗、成本估算和上下文窗口利用率。
现状
今天的事情是如何运作的,这导致了什么问题?我们为什么要改变?目前,ACP 协议没有标准化的方式让代理传达:
- 令牌使用 - 一轮或累计消耗了多少令牌
- 上下文窗口状态 - 模型上下文窗口的使用量
- 成本信息 - API 使用的估算成本
- 提示缓存指标 - 支持缓存的模型的缓存命中/未命中
- 无法了解资源消耗 - 客户端无法向用户显示他们的上下文预算使用量
- 没有成本透明度 - 用户无法跟踪支出或在操作前估算成本
- 没有上下文管理 - 客户端无法在接近上下文限制时警告用户或建议压缩
- 不一致的实现 - 每个代理以不同方式实现使用情况跟踪(如果有的话)
- LLM 提供商在 API 响应中返回累计令牌计数
- IDE 扩展显著显示上下文百分比(例如,显示”19%“的径向进度)
- 客户端在悬停/详细信息上显示绝对数字(例如,“31.4K/200K 个令牌”)
- 工具在阈值百分比(75%、90%、95%)警告用户
- 自动压缩功能在接近上下文限制时触发
- 成本跟踪侧重于累计会话总数而不是每轮分解
我们提议对此做什么
您提议如何改善情况?我们提议将使用情况跟踪分为两个不同的关注点:
- 令牌使用 - 在每轮之后在
PromptResponse中报告(每轮数据) - 上下文窗口和成本 - 通过
session/update通知报告,会话更新为"usage_update"(会话状态)
- 令牌计数绑定到特定的轮次,并在提示后立即有用
- 上下文窗口和成本是累计的会话状态,代理主动推送(可用时)
- 在
session/new响应中(如果代理可以立即查询使用情况) - 在
session/load/session/resume上(对于恢复/分叉的会话) - 在每个
session/prompt响应之后(当使用情况数据可用时) - 任何时候上下文窗口状态发生重大变化
- 支持在不提示的情况下获取当前使用情况的代理可以在创建、恢复或分叉聊天时立即发送更新
- 只在主动提示时提供使用情况的代理可以在发送新提示后发送更新
PromptResponse 中的令牌使用
在 PromptResponse 中添加 usage 字段以进行令牌消耗跟踪:
使用字段
total_tokens(数字,必需)- 整个会话中所有令牌类型的总和input_tokens(数字,必需)- 所有轮次中的输入令牌总数output_tokens(数字,必需)- 所有轮次中的输出令牌总数thought_tokens(数字,可选)- 所有轮次中的思维/推理令牌总数(用于 o1/o3 模型)cached_read_tokens(数字,可选)- 所有轮次中的缓存读取令牌总数cached_write_tokens(数字,可选)- 所有轮次中的缓存写入令牌总数
通过 session/update 的上下文窗口和成本
代理通过 sessionUpdate: "usage_update" 的 session/update 通知发送上下文窗口和成本信息:
上下文窗口字段(必需)
used(数字,必需)- 当前上下文中的令牌数size(数字,必需)- 上下文窗口的总大小(以令牌为单位)
remaining 为 size - used 和 percentage 为 used / size * 100。
成本字段(可选)
cost(对象,可选)- 累计会话成本amount(数字,必需)- 会话的总累计成本currency(字符串,必需)- ISO 4217 货币代码(例如,“USD”、“EUR”)
设计原则
- 关注点分离 - 令牌使用是每轮数据,上下文窗口和成本是会话状态
- 代理推送通知 - 代理在数据可用时主动发送上下文更新,遵循与其他动态会话属性相同的模式(
available_commands_update、current_mode_update、session_info_update) - 代理计算,客户端可以验证 - 代理最了解其模型并提供计算,但包含原始数据以供客户端验证
- 灵活的成本报告 - 成本是可选的,因为并非所有代理都跟踪它。支持任何货币,不要假设美元
- 提示缓存支持 - 包括支持缓存的模型的缓存读取/写入令牌
- 可选但推荐 - 使用情况跟踪是可选的以保持向后兼容性
- 灵活的时间 - 代理在可以时发送更新:对于具有按需 API 的代理立即,对于只在主动提示时提供使用情况的代理在提示后发送
美好的未来
一旦这个功能存在,事情将如何发展?对于用户:
- 可见性:用户看到带有百分比指示器的实时上下文窗口使用情况
- 成本意识:用户可以跟踪支出并随时检查累计成本
- 更好的规划:用户知道何时开始新会话或压缩上下文
- 透明度:清楚了解资源消耗
- 一致的 UI:所有客户端可以以标准方式显示使用情况(进度条、百分比、警告)
- 智能警告:客户端可以在 75%、90% 上下文使用量时警告用户
- 成本控制:客户端可以实现预算限制和警报
- 分析:客户端可以跟踪使用模式并进行优化
- 反应式更新:客户端通过通知接收上下文更新,当代理推送新数据时立即更新 UI
- 无需轮询:更新在代理有新信息时自动到达,消除了客户端轮询的需要
- 标准报告:关于报告什么和何时报告的清晰合同
- 灵活性:可选字段允许代理报告它们可以计算的内容
- 模型多样性:适用于任何模型(GPT、Claude、Llama 等)
- 缓存支持:一流支持提示缓存
实施细节和计划
告诉我更多关于您的实施。您的详细实施计划是什么?
-
更新 schema.json 以添加:
- 带令牌字段的
Usage类型 - 带
amount和currency字段的Cost类型 - 带
used、size(必需)和可选cost字段的ContextUpdate类型 - 在
PromptResponse中添加可选的usage字段 - 在
SessionUpdateoneOf 数组中添加UsageUpdate变体(带有sessionUpdate: "usage_update")
- 带令牌字段的
-
更新协议文档:
- 在
/docs/protocol/prompt-turn.mdx中记录usage字段 - 记录带有
sessionUpdate: "usage_update"变体的session/update通知 - 添加示例显示典型使用模式以及代理何时发送上下文更新
- 在
常见问题
在编写本文档的过程中或随后的讨论中出现了哪些问题?
为什么将令牌使用与上下文窗口和成本分离?
不同用户在不同的时刻关心不同的事情:- 令牌计数:在轮次完成后立即相关以了解分解
- 剩余上下文窗口:任何时候都相关,特别是在发出大提示之前。“我需要交接还是压缩?”
- 累计成本:会话级别状态,代理在可用时推送
- 更清晰的数据模型,其中每轮数据保留在轮次响应中
- 代理在数据可用时主动推送上下文更新
- 客户端无需轮询即可被动接收更新
为什么成本在 session/update 中而不是 PromptResponse 中?
成本是累计的会话状态,类似于上下文窗口:
- 用户想要跟踪总支出,而不仅仅是每轮成本
- 保持
PromptResponse专注于每轮令牌分解 - 成本和上下文窗口都是属于一起的会话级别指标
- 成本是可选的,因为并非所有代理都跟踪它
用户如何知道何时交接或压缩上下文?
上下文更新通知提供了所需的一切:used和size提供精确跟踪的绝对数字- 客户端可以计算
remaining为size - used和percentage为used / size * 100 size让客户端了解总预算
| 百分比 | 操作 |
|---|---|
| < 75% | 正常操作 |
| 75-90% | 黄色指标,建议”上下文正在填满” |
| 90-95% | 橙色指标,建议”开始新会话或总结” |
| > 95% | 红色指标,警告”下一个提示可能失败 - 推荐交接” |
- 提供”压缩上下文”或”总结对话”操作
- 自动建议开始新会话
- 在接近限制时实现自动交接
为什么不为成本假设美元?
代理可能以不同货币计费:- 欧洲代理可能以欧元计费
- 亚洲代理可能以日元或人民币计费
- 一些代理可能使用积分或点数
- 货币汇率会变化
如果代理无法计算某些字段怎么办?
除基本令牌计数外的所有字段都是可选的。代理报告它们可以计算的内容。客户端优雅地处理缺失的字段。这与流式响应如何工作?
- 在流式传输期间:代理可以通过
session/update通知随着使用情况变化发送渐进式上下文更新 - 最终响应:在
PromptResponse中包含完整的令牌使用情况 - 上下文窗口和成本:代理在数据可用时发送带有
sessionUpdate: "usage_update"的session/update通知(在提示完成后、会话创建/恢复时,或上下文状态发生重大变化时)
对于没有固定上下文窗口的模型怎么办?
- 报告有效的上下文窗口大小
- 对于具有动态窗口的模型,报告当前限制
- 如果发生变化则更新大小
- 如果真正无限则设置为
null(罕见)
关于速率限制和配额呢?
这个 RFD 侧重于令牌使用情况和上下文窗口。速率限制和配额是可以在未来的 RFD 中解决的单独问题。然而,这里的成本跟踪帮助用户了解他们对配额限制的使用情况。缓存的令牌是否计入上下文窗口?
是的,缓存的令牌仍然占据上下文窗口空间。它们只是处理起来更便宜。上下文窗口使用情况应包括所有令牌(常规 + 缓存)。为什么通知而不是请求?
使用session/update 通知而不是 session/status 请求提供了几个好处:
- 一致性:遵循与其他动态会话属性相同的模式(
available_commands_update、current_mode_update、session_info_update) - 代理灵活性:代理可以在数据可用时发送更新,无论是立即(对于具有按需 API 的代理)还是提示后(对于只在主动提示时提供使用情况的代理)
- 无需轮询:客户端无需轮询即可被动接收更新
- 实时更新:更新作为会话生命周期的一部分自然流动
如果客户端在会话中途连接怎么办?
当客户端连接到现有会话(通过session/load 或 session/resume)时,代理应该在它们有当前使用情况数据可用时发送上下文更新通知。这确保客户端 UI 可以立即显示准确的上下文窗口和成本信息。
对于只在主动提示时提供使用情况的代理,客户端 UI 可能在发送第一个提示之前不显示使用情况,这在给定代理能力的情况下是可以接受的。
您考虑了哪些替代方法,为什么选择这一个?
考虑的替代方案:-
全部在
PromptResponse中 - 更简单,但上下文窗口和成本是会话状态,用户可能希望独立于轮次跟踪。 -
请求/响应(
session/status) - 需要客户端轮询,并且一些代理在没有提示的情况下没有 API 来查询当前状态。通知方法更灵活,并与与其他动态会话属性一致的模式。 - 客户端计算一切 - 被拒绝,因为客户端不知道模型的标记器、确切的上下文窗口大小或定价。
- 只有百分比,没有原始令牌 - 被拒绝,因为用户想要绝对数字,客户端无法验证计算,并且透明度较低。
修订历史
- 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"以更好地反映有效负载语义(包括上下文窗口信息和累计成本)。