Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

历史管理

运行时会为每个智能体会话保留对话历史,并向模型发送面向提供商的工作历史。两个互补的限制分别作用于不同的表示形式:

  1. Token-budget trimming 作用于面向提供商的 ChatMessage 工作历史,并丢弃最旧的完整轮次,直到估计的上下文符合 token 预算。
  2. 整轮保留会将运行时配置文件中设定的历史记录上限应用于结构化的 Agent::history(ConversationMessage),以及旧版代理循环所使用的面向提供方的 ChatMessage 历史记录。

两种限制都以原子方式保留对话轮次。一个轮次从真实的用户消息开始,包括助手的回复,以及在下一条用户消息之前的所有工具调用和工具结果。因此,裁剪不会将工具调用与其结果分开。文本恰好以 [Tool results] 开头的用户消息会被视为前一个轮次的一部分,而不是新的轮次。

整轮保留

history_trim::trim_to_recent_turns 负责执行 token 预算。history_trim::trim_to_recent_turn_count 和 history_trim::trim_conversation_to_recent_turns 分别针对两种历史表示形式执行已配置的轮次上限。即使配置值为 0,三者都会保留最新的一个完整轮次。这是有意为之的设计:保留完整的当前轮次,比丢弃其最新消息或破坏工具调用交互更安全。

前导系统消息会被保留。当不需要裁剪时,消息顺序和结构保持不变。

Token 预算

令牌预算来自 ResolvedRuntime::effective_context_budget():

  • 现有配置文件在未设置 max_context_tokens 和 context_compact_ratio 时,会保留历史的 32,000 token 预算;若所选模型的配置容量更小,则以该容量为上限。
  • max_context_tokens 仍然是一个绝对预算。显式设置为 0 时将禁用主动的 token 预算裁剪。
  • 设置 context_compact_ratio 即可启用与模型相关的预算:其预算值为所选提供商别名/模型的 context_window(若其容量未知,则回退为 32,000)乘以该比例。超出 (0.0, 1.0] 范围的值将被视为未设置。当两项设置同时存在时,max_context_tokens 会对由比例推算出的预算进行向下限制。
  • 当设置了 history_pruning.enabled 且 history_pruning.max_tokens 为正数时,该值会降低(而非提高)预算上限,使运维人员能够更早地进行修剪。
  • 每个正数的有效预算都受所选模型的_已配置_容量上限约束。32,000 的回退值只是兼容性占位,并非模型的真实容量:当提供商配置未声明 context_window,而运行时配置设置了正数的 max_context_tokens 时,该预算将被采用,并同时作为窗口操作数(使比例作用于它),而不会被截断为 32,000。显式的零值哨兵仍保持为零,并继续禁用主动裁剪。

有效预算是主动裁剪的目标值,而不是请求的硬性上限。在丢弃所有符合条件的较早轮次后,即使最新的完整轮次仍超出该目标值,运行时也会保留它。当准备好的消息、图像、钩子和工具 schema 能够放入解析后的模型上下文窗口时,才会发送请求。仍超出该容量的请求会在分发给提供方之前失败;仅提高主动裁剪目标值无法使其符合要求。当主动裁剪被禁用(max_context_tokens = 0)时,以及在达到工具迭代上限后的最终摘要请求中,同样会进行此容量检查。

容量与预算会针对当前活动的提供商/模型路由一并解析。分类器提示和显式会话切换使用与提供商分发相同的路由选择,因此下一次模型调用、主动裁剪、溢出诊断、成本归因以及客户端上下文计量器,都会使用所选的提供商/模型及其解析出的配对信息。如果所选模型与该提供商配置档案中配置的模型不一致,则将容量视为未知,而不是借用该配置档案中另一模型的元数据。内部的 32,000 兼容回退值仍可用于安全计算,但对于未知容量,接入客户端不会收到 model_context_window 值。

history::estimate_history_tokens 估算 token 数:约每 4 个字符计为 1 个 token,每条消息另加 4 个框架 token。这是一种启发式方法,而非服务商的分词器。可加载的 [IMAGE:...] 标记仅在图片会被实际派发的消息中按每张图片的固定成本计费,即用户轮次以及当前用户轮次中工具结果的消息。工具结果的图片是该工具声明的附件,而不是其输出中的标记文本。较早的工具结果按其消息字节数计费,系统消息和助手文本按文本计费。

在轮次开始、第一次调用提供商之前,如果历史记录已超出有效预算,或在工具循环迭代之间的提供商调用边界处,会主动执行 token 预算裁剪。若提供商报告上下文窗口溢出,现有的被动恢复路径会在裁剪到当前估算历史大小的三分之二后重试,而不会根据模型容量推导新的恢复目标。两种路径都保留完整的轮次,因此都不会拆分工具调用往返。

整轮对话上限

max_history_messages 是智能体运行时配置文件中历史设置的旧称。在结构化智能体和旧版智能体循环中,其单位是一个完整的用户轮次,而不是单独的消息行。工具调用和工具结果仍属于其所在的轮次,不会各自占用独立的名额。

默认值为 50 轮。显式配置的值具有最高优先级,包括 0;最新的完整一轮仍会被保留。该字段名出于架构兼容性而保留,未来的架构版本中可能会重命名。

Channel 发送方缓存仍是例外:它们按单条消息记录计数,并保留现有的 0 表示使用默认值的行为。

可见修剪

当 token 预算裁剪或整轮次数限制丢弃较早的轮次时,运行时会:

  1. 在首个保留的对话轮次之前插入面包屑,以便模型知道之前的上下文已被省略。
  2. Emits HistoryTrimmed 事件,包含被丢弃的消息数量、保留的轮次数,以及标明触发原因的字段(token 预算或轮次上限)。

该事件通过活跃的客户端传输以及仪表板和事件订阅者所使用的观察者路径进行传递。因此,修剪操作并非仅记录日志,对模型和已连接的客户端均不是静默的。

传统的 agent::run 路径位于 loop_.rs 中,它仅通过日志报告基于数量的历史裁剪,不会生成 breadcrumb,也不会触发 HistoryTrimmed 事件。该路径既服务于交互式使用,也服务于一次性(one-shot)以及非交互式的 daemon、cron、subagent 和 SOP 调用方。

配对安全

整轮保留是主要的工具配对保证:工具调用及其结果属于同一轮次,并会一起保留或一起丢弃。孤立项清理仍然是最后一道安全网,用于处理已经不一致的历史记录,例如已恢复或被外部修改的会话。

工具结果长度限制是独立的。max_tool_result_chars 用于在记录单个结果时限制其长度;它不会裁剪对话历史。提供方侧的上下文强制约束也是独立的,不过提供方溢出可能会触发运行时的响应式 token 预算裁剪。