#3178·CowAgent

[错误]_trim_messages(): 转数检查在令牌预算检查之前执行,导致不必要的 LLM 摘要调用

作者: liuns-yang创建于 2026年9月17日更新于 2026年9月17日

发生了什么事?#### 我观察到的情况 _trim_messages()agent/protocol/agent_stream.py:2454)运行了一个两阶段的剪辑管线text Step 2(第 2476 行):回转数检查 → 如果回转数 > max_context_turns(30):删除前半部分 + 触发摘要 LLM 调用 Step 3(第 2502 行):令牌预算检查 → 如果估计令牌 > 可用预算:分层策略(压缩或丢弃)**步骤 2 在步骤 3 之前触发**,这意味着:> 一个对话具有 **31 个或更多短回转**(例如 31 次快速 `ls` 调用,每次约 50 个令牌),即使总令牌数(约 1,550 个令牌)远低于模型的上下文预算。这浪费了每个回转上不必要的 LLM API 调用(通过 `_build_context_summary_callback()` 进行的摘要注入)。#### 根本原因 转回数检查(步骤 2)和令牌预算检查(步骤 3)是**独立的触发器,按顺序运行**,而不是统一的预算感知策略:Python # agent/protocol/agent_stream.py:2476-2486 如果 len(turns) > self.max_context_turns: removed_count = len(turns) // 2 # ← 总是删除一半 keep_count = len(turns) - removed_count discarded_turns = turns[:removed_count] turns = turns[-keep_count:] # 刷新到日常内存 + 注入上下文摘要(单个异步 LLM 调用) 如果 self.agent.memory_manager: # ... 触发 _build_context_summary_callback → LLM 调用 然后,步骤 3 独立检查令牌预算:```Python # agent/protocol/agent_stream.py:2502-2530 context_window = self.agent._get_model_context_window() output_reserve = self.agent._get_output_reserve_tokens() input_ceiling = max(1, context_window - output_reserve) # ... current_tokens = sum(self._estimate_turn_tokens(turn) for turn in turns) 如果 current_tokens + system_tokens <= max_tokens: # 在预算内 → 重构并返回(但步骤 2 已经剪辑了!) 两个问题: 1. 步骤 2 没有了解步骤 3 的预算。 它纯粹基于回转数,无论预算是否已满足。 2. 两个步骤都使用"删除一半"而不是仅删除所需的最少回转。 如果 31 个回转总共 200K 个令牌,并且预算为 50K,删除一半仍然会留下约 100K(超出预算),需要另一轮。此外,_smart_compact_to_budget()(第 2324 行,反应性溢出处理器)在循环中重复相同的"删除一半"策略,直到上下文适合为止 — 但这只会在 API 返回之前触发…

内容来源: zhayujie/CowAgent