您的位置:首页 > 手游攻略 > 上下文总结机制 summary

上下文总结机制 summary

作者:互联网  时间: 2026-07-16 18:04:08  

Agent summary 上下文总结机制分析

一、核心问题:每轮都强调 summary 吗?

答案:是的,但不是"每轮"——是"每轮没有显式调用工具时"强制要求。

summary 上下文总结机制

1.1 触发条件

ga.py:549-561turn_end_callback 中:

def turn_end_callback(self, response, tool_calls, tool_results, turn, next_prompt, exit_reason):_c = re.sub(r'```.*?```|<thinking>.*?</thinking>', '', response.content, ...)rsumm = re.search(r"<summary>(.*?)</summary>", _c, re.DOTALL)if rsumm: summary = rsumm.group(1).strip()# ← 提取了 summary 标签内容else:# 没有 <summary> 标签 → 强制要求next_prompt += "nnn[SYSTEM] 必须在回复文本中包含<summary>!nn"summary = f"{tool_name}, args: {clean_args}"# ← 用工具调用信息作为 fallback

触发逻辑:

  1. 每轮结束后,从 LLM 的回复中提取 <summary>...</summary> 标签
  2. 如果提取到了 → 用提取到的内容作为本轮摘要
  3. 如果没提取到 → 在下一轮的 prompt 中强制要求 [SYSTEM] 必须在回复文本中包含<summary>!
  4. 同时用工具调用信息(tool_name, args)作为 fallback 摘要

所以是"每轮都要求,但只在缺失时强制提醒"。

二、上下文总结的三层机制

2.1 第一层:<summary> 标签提取(每轮)

位置: ga.py:549-561

# turn_end_callback — 每轮结束后执行rsumm = re.search(r"<summary>(.*?)</summary>", _c, re.DOTALL)if rsumm: summary = rsumm.group(1).strip()else:next_prompt += "nnn[SYSTEM] 必须在回复文本中包含<summary>!nn"summary = f"{tool_name}, args: {clean_args}"summary = smart_format(summary.replace('n', ''), max_str_len=80)self.history_info.append(f'[Agent] {summary}')# ← 追加到 history_info 列表

作用:

  • 每轮提取 LLM 回复中的 <summary> 标签内容
  • 截断到 80 字符
  • 追加到 self.history_info 列表(格式: [Agent] {summary}
  • 这个列表最终会被注入到 working memory prompt 中

2.2 第二层:Turn 计数驱动的 checkpoint 提示

位置: ga.py:564-574

if turn % 175 == 0 and (not _plan):next_prompt += f"nn[DANGER] Turn {turn}. Must call ask_user to summarize progress..."elif turn % 7 == 0:next_prompt += f"nn[SYSTEM] Turn {turn}. Call update_working_checkpoint to save key context..."elif turn % 25 == 0:next_prompt += f"nn[SYSTEM] Turn {turn}. Write checkpoints/key findings to a **file**..."elif turn % 10 == 0: next_prompt += get_global_memory()# ← 重新注入 L0+L1+L2 记忆

完整的 Turn 周期表:

Turn 周期触发动作目的
每 7 轮[SYSTEM] Call update_working_checkpoint保存关键上下文到 working memory
每 10 轮重新注入 get_global_memory()刷新 L0+L1+L2 记忆
每 25 轮[SYSTEM] Write checkpoints to file将关键发现写入文件持久化
每 175 轮[DANGER] Must call ask_user强制向用户汇报进度(非 plan 模式)
Plan 模式每 5 轮[Plan Hint] 必须 file_read(plan.md)确认当前步骤
Plan 模式 190 轮[DANGER] 已达上限强制退出 plan 模式

2.3 第三层:Working Memory 注入(每轮)

位置: ga.py:537-547

def _get_anchor_prompt(self, skip=False):h = self.history_info; W = 30earlier = f'<earlier_context>n{self._fold_earlier(h[:-W])}n</earlier_context>n' if len(h) > W else ""h_str = "n".join(h[-W:])# ← 最近 30 轮的摘要prompt = f"n### [WORKING MEMORY]n{earlier}<history>n{h_str}n</history>"prompt += f"nCurrent turn: {self.current_turn}n"if self.working.get('key_info'): prompt += f"n<key_info>{self.working.get('key_info')}</key_info>"if self.working.get('related_sop'): prompt += f"n有不清晰的地方请再次读取{self.working.get('related_sop')}"

注入结构:

### [WORKING MEMORY]<earlier_context>[Agent] 第1轮摘要(折叠后)[Agent] 第2轮摘要(折叠后)...(超过30轮的部分被折叠)</earlier_context><history>[Agent] 第N-29轮摘要[Agent] 第N-28轮摘要...[Agent] 第N轮摘要(最近30轮)</history>Current turn: 42<key_info>当前任务的关键信息</key_info>

这个 prompt 在每轮工具调用后都会被注入(通过 _get_anchor_prompt()next_prompt)。

三、_fold_earlier 的折叠逻辑

位置: ga.py:523-535

def _fold_earlier(self, lines):FALLBACK = '直接回答了用户问题'parts, cnt, last = [], 0, ''def flush():if cnt:if FALLBACK in last: parts.append(f'[Agent]({cnt} turns)')# ← 合并为一行else: parts.append(f'{last}{cnt} turns)')# ← 保留最后一条内容 + 计数for line in lines:if line.startswith('[USER]'):flush(); parts.append(line); cnt = 0; last = ''else: cnt += 1; last = lineflush()return "n".join(parts[-70:])# ← 只保留最近 70 行

折叠规则:

  1. 遇到 [USER] 开头的行 → 刷新缓冲区,保留该行
  2. 连续的 [Agent] 行 → 计数合并
  3. 如果最后一条是"直接回答了用户问题" → 用 (N turns) 代替
  4. 否则 → 保留最后一条内容 + (N turns)
  5. 最终只保留最近 70 行

效果: 30 轮之前的对话历史被折叠成类似 [Agent] 直接回答了用户问题(5 turns) 的紧凑格式。

四、LLM 层的上下文管理

4.1 trim_messages_history(llmcore.py:97-110)

def trim_messages_history(history, sess):cap = sess.context_win * 3# 上下文窗口上限(字符数)target = int(cap * sess.trim_keep_rate)# 保留比例(默认 0.6)# 如果超过上限 → 从头部删除消息,直到低于 targetwhile len(history) > 9 and cost() > target:history.pop(0)while history and history[0].get('role') != 'user': history.pop(0)# ← 确保删除后第一条是 user 消息

触发时机: 每次 BaseSession.ask() 后自动调用(llmcore.py:572-573

4.2 compress_history_tags(llmcore.py:40-71)

def compress_history_tags(messages, keep_recent=10, max_len=800, force=False, interval=5):# 每 interval 轮触发一次# 压缩 <thinking>/<tool_use>/<tool_result> 等标签中的内容# 保留最近 keep_recent 条消息不压缩

触发时机: trim_messages_history 中调用,每 5 轮触发一次

五、完整的数据流图

每轮 LLM 回复│├─ 1) turn_end_callback 提取 <summary> 标签│ ├─ 有标签 → 提取内容(截断 80 字符)│ └─ 无标签 → 强制要求 + 用工具调用信息 fallback│ → 追加到 self.history_info│├─ 2) Turn 计数检查│ ├─ turn % 7→ 提示 update_working_checkpoint│ ├─ turn % 10 → 重新注入全局记忆│ ├─ turn % 25 → 提示写入文件 checkpoint│ └─ turn % 175 → 危险警告│├─ 3) _get_anchor_prompt 构造 working memory│ ├─ 取最近 30 轮 history_info│ ├─ 超过 30 轮的部分 → _fold_earlier 折叠│ ├─ 注入 key_info + related_sop│ └─ → 拼接到 next_prompt 中│└─ 4) LLM 层 trim_messages_history├─ 检查上下文大小是否超限├─ 超限 → 从头部删除旧消息└─ compress_history_tags 压缩标签内容

六、设计评价

6.1 优点

  1. 多层冗余: 三层机制(summary 标签 + turn 计数 + working memory)互为补充,不会因为单一机制失效而丢上下文
  2. <summary> 标签是显式约定: 强制 LLM 在回复中总结自己的行为,比隐式的"让模型自己记住"更可靠
  3. 折叠算法节省 token: _fold_earlier 把 30 轮之前的对话折叠成一行,大幅减少上下文占用
  4. Turn 计数的渐进式提醒: 7/25/175 轮的提醒频率设计合理——不是每轮都打扰,但在关键节点提醒

6.2 缺点

  1. 没有真正的 Token 感知: Turn 计数是粗粒度的,不知道每轮实际消耗了多少 token。可能 7 轮就超了上下文窗口,也可能 200 轮还很宽松
  2. <summary> 标签依赖 LLM 配合: 如果 LLM 不写 <summary> 标签,系统只能 fallback 到工具调用信息,丢失了语义总结
  3. Working Memory 是纯文本拼接: 没有结构化的摘要,只是把历史摘要字符串拼在一起
  4. 没有自动压缩触发: 不像 Claude Code 有 AUTOCOMPACT_BUFFER_TOKENS 的自动触发机制,GenericAgent 只在 trim_messages_history 中被动删除旧消息
  5. _fold_earlier 丢失细节: 折叠后的 (N turns) 丢失了中间轮次的具体内容,只保留最后一条

6.3 与 Claude Code 的对比

维度GenericAgentClaude Code
压缩触发Turn 计数(7/25/175)Token 阈值(AUTOCOMPACT_BUFFER_TOKENS)
压缩方式文本折叠 + 标签截断6 层管道(auto/micro/session/snip/reactive/cached)
摘要生成LLM 回复中的 <summary> 标签后台 forked agent 独立生成
Token 感知❌ 无(只靠字符数估算)✅ 精确 token 计数
自动压缩❌ 只在超限时被动删除✅ 接近阈值时主动触发
工具结果折叠❌ 无✅ Micro Compact 折叠 Read/Bash/Grep 结果

七、总结

GenericAgent 的上下文总结机制是**"显式标签 + Turn 计数 + 文本折叠"三层组合**:

  • 每轮: 从 LLM 回复中提取 <summary> 标签,追加到 history_info
  • 每 7 轮: 提示 Agent 调用 update_working_checkpoint 保存关键上下文
  • 每轮: _get_anchor_prompt 把最近 30 轮的摘要 + 折叠后的更早历史注入到 prompt 中
  • 超限时: trim_messages_history 从头部删除旧消息

但这不是真正的"上下文压缩"——它没有 Token 感知、没有自动触发、没有工具结果折叠。它更像是一个"手动档"的上下文管理:靠 Turn 计数和正则表达式,而不是靠 Token 计数和智能压缩。

这也正是 docs/ClaudeCode_对比与改造路线图.md 中 P0-2(Token 感知的上下文压缩) 被列为最高优先级改造项的原因。

最新游戏

更多

Copyright©2010-2019. All rights reserved | 波波三国游戏官网|[email protected]

备案编号:湘ICP备2022015115号-4