历史管理
运行时会为每个智能体会话保留对话历史,并向模型发送面向提供商的工作历史。两个互补的限制分别作用于不同的表示形式:
- Token-budget trimming 作用于面向提供商的
ChatMessage工作历史,并丢弃最旧的完整轮次,直到估计的上下文符合 token 预算。 - 结构化消息数量修剪在超出结构化 agent 的有效消息上限时,会修改 RPC、网关和 ACP
Agent轮次所使用的Agent::history(ConversationMessage)。调用旧版agent::run路径的守护进程通道循环使用下文所述的独立原始消息上限。
Token 预算裁剪和结构化消息数量限制以原子方式保留对话轮次。一个对话轮次从真实的用户消息开始,包含助手响应以及下一条用户消息之前的所有工具调用和工具结果。因此,裁剪不会将工具调用与其结果拆分开来。
整轮保留
history_trim::trim_to_recent_turns 强制执行 token 预算,而 history_trim::trim_conversation_to_recent_turns 强制执行结构化消息数量限制。即使最新的完整轮次本身超出了相关限制,二者也都会保留该轮次。这是有意为之的:保留一个完整的当前轮次,比通过丢弃其最新消息或破坏工具交互来满足数量上限更为安全。
前导系统消息会被保留。当不需要裁剪时,消息顺序和结构保持不变。
Token 预算
令牌预算来自 ResolvedRuntime::effective_context_budget():
- 当
history_pruning.enabled启用且history_pruning.max_tokens为正数时,预算取该值与max_context_tokens中的较小值。 - 否则预算为
max_context_tokens。
Token 数量由 history::estimate_history_tokens 估算:大约每四个字符对应一个 token,外加每条消息四个框架 token。这是一种启发式方法,并非提供商 tokenizer。
令牌预算修剪在以下情况下运行:当历史记录在轮次的第一次提供商调用之前已超出有效预算时,以及在工具循环迭代之间的提供商调用边界处(包括在提供商报告上下文窗口已超出时进行的被动响应)。它以完整轮次为单位进行保留,因此不会拆分工具交换。
结构化消息数量限制
max_history_messages 是代理运行时配置文件中的已配置值。显式配置的值对旧版原始路径和结构化代理历史记录均具有权威性,包括 0。由于结构化裁剪始终保留最新的完整轮次,因此值为 0 时会移除较早的轮次,但不会清除当前轮次。
当省略 max_history_messages 时,旧版原始上限仍为 50。结构化代理的有效上限由工具循环配额推导而来:
max(50, 2 * max_tool_iterations + 2)
每次工具迭代可以添加一次工具调用和一个工具结果;额外的两个槽位用于用户消息和最终的助手响应。使用默认的 max_tool_iterations = 10 时,派生出的限制仍为 50。
可见修剪
每当 token 预算裁剪或结构化消息数量限制丢弃较早的轮次时,运行时:
- 在首个保留的对话轮次之前插入面包屑,以便模型知道之前的上下文已被省略。
- 发出
HistoryTrimmed,包含已丢弃消息数量、保留的轮次数以及标识令牌预算或消息限制的原因。
该事件通过活跃的客户端传输以及仪表板和事件订阅者所使用的观察者路径进行传递。因此,修剪操作并非仅记录日志,对模型和已连接的客户端均不是静默的。
旧版 agent::run 在 loop_.rs 中的路径是一个未作更改的例外。其在 history::trim_history 中对原始 ChatMessage 的上限仍以消息为粒度,并且仅通过日志报告裁剪情况,不会产生面包屑或 HistoryTrimmed 事件。该路径既用于交互式使用,也用于一次性调用以及非交互式的守护进程、cron、子代理和 SOP 调用方。
配对安全
整轮保留是主要的工具配对保证:工具调用及其结果属于同一轮次,并会一起保留或一起丢弃。孤立项清理仍然是最后一道安全网,用于处理已经不一致的历史记录,例如已恢复或被外部修改的会话。
工具结果长度限制是独立的。max_tool_result_chars 用于在记录单个结果时限制其长度;它不会裁剪对话历史。提供方侧的上下文强制约束也是独立的,不过提供方溢出可能会触发运行时的响应式 token 预算裁剪。