3.1 状态持久化与上下文管理


文档摘要

3.1 状态持久化与上下文管理 — 循环长期可靠运行的基础 本节导读:掌握循环工程中最容易被忽视但最致命的问题——上下文窗口的管理与状态持久化策略,学会让循环可靠运行而不丢失关键信息。 学习目标 理解上下文窗口(Context Window)的限制及其对循环的影响 掌握上下文压缩(Compaction)的核心技术 学会设计外部状态持久化方案 理解上下文腐烂(Context Rot)的成因与对策 核心概念 上下文窗口:循环的内存瓶颈 LLM 的上下文窗口就像电脑的 RAM——有固定大小,装满了就得清理。不同的是,电脑的操作系统自动管理内存,而 LLM 的上下文需要你手动管理。 Tosea.

3.1 状态持久化与上下文管理 — 循环长期可靠运行的基础

本节导读:掌握循环工程中最容易被忽视但最致命的问题——上下文窗口的管理与状态持久化策略,学会让循环可靠运行而不丢失关键信息。

学习目标

  • 理解上下文窗口(Context Window)的限制及其对循环的影响
  • 掌握上下文压缩(Compaction)的核心技术
  • 学会设计外部状态持久化方案
  • 理解上下文腐烂(Context Rot)的成因与对策

核心概念

上下文窗口:循环的内存瓶颈

LLM 的上下文窗口就像电脑的 RAM——有固定大小,装满了就得清理。不同的是,电脑的操作系统自动管理内存,而 LLM 的上下文需要你手动管理。

Tosea.ai 的指南精准描述了这个问题:"In a long loop, every step appends thoughts, tool outputs, and errors, so the window fills up and the model starts to suffer 'context rot': as the transcript grows, it attends less reliably to what actually matters."

(在长循环中,每一步都追加思考、工具输出和错误,窗口逐渐填满,模型开始出现"上下文腐烂"——随着对话历史增长,它越来越不可靠地关注真正重要的内容。)

三种状态类型

上下文腐烂的成因

  1. 信息过载:对话历史太长,关键信息被淹没在大量噪声中
  2. 注意力稀释:模型对早期信息的注意力随轮次增加而减弱
  3. 矛盾信息:后续步骤可能产生与早期信息矛盾的输出
  4. 上下文预算:有限的窗口空间需要合理分配给不同类型的信息

环境准备 / 前置知识

  • 已完成第一、二章学习
  • 有 Claude Code 实际使用经验(尤其是长对话场景)
  • 了解基本的内存管理概念

分步实战

步骤 1:实现上下文压缩策略

""" context_manager.py - 上下文管理器 实现循环中的上下文压缩和状态管理 """ from dataclasses import dataclass, field from typing import List, Dict, Optional import json @dataclass class ContextWindow: """上下文窗口模拟""" max_tokens: int = 200000 current_tokens: int = 0 messages: List[Dict] = field(default_factory=list) def add(self, role: str, content: str, estimated_tokens: int = None): """添加消息到上下文""" tokens = estimated_tokens or len(content) // 3 self.messages.append({ "role": role, "content": content, "tokens": tokens }) self.current_tokens += tokens def usage_percent(self): return self.current_tokens / self.max_tokens def needs_compaction(self, threshold=0.7): return self.usage_percent() >= threshold class ContextCompactor: """上下文压缩器""" def compact(self, context: ContextWindow) -> ContextWindow: """ 压缩策略(按优先级): 1. 修剪工具输出的冗长内容 2. 将旧轮次摘要化 3. 外部化关键状态到文件 """ if not context.needs_compaction(): return context new_context = ContextWindow(max_tokens=context.max_tokens) total_tokens = 0 budget = context.max_tokens * 0.5 # 压缩到 50% # 保留系统指令(始终保留) for msg in context.messages: if msg["role"] == "system": new_context.add(msg["role"], msg["content"], msg["tokens"]) total_tokens += msg["tokens"] # 摘要化旧轮次 old_rounds = [m for m in context.messages if m["role"] in ("user", "assistant")] keep_recent = max(2, len(old_rounds) // 3) # 旧轮次 → 一行摘要 old_count = len(old_rounds) - keep_recent if old_count > 0: summary = self._summarize_old_rounds( [m["content"] for m in old_rounds[:old_count]] ) new_context.add("system", f"[历史摘要] {summary}") total_tokens += len(summary) // 3 # 保留最近几轮完整 for msg in old_rounds[-keep_recent:]: new_context.add(msg["role"], msg["content"], msg["tokens"]) return new_context def _summarize_old_rounds(self, contents: List[str]) -> str: """将旧轮次压缩为一行摘要(实际中由 LLM 完成)""" actions = [c[:100] for c in contents if len(c) > 10] return f"之前进行了 {len(actions)} 轮交互,主要尝试:{'; '.join(actions[:3])}"

步骤 2:设计外部状态方案

# progress.md - 循环外部状态文件模板 ## 基本信息 - **目标**: {{循环目标}} - **开始时间**: {{启动时间}} - **当前状态**: running / success / failed / escalated - **当前轮次**: {{第几轮}} ## 进度追踪 ### 已完成 - [x] {{完成事项 1}} (Round {{N}}) - [x] {{完成事项 2}} (Round {{N}}) ### 进行中 - [ ] {{当前任务}} (尝试 {{N}} 次) ### 未开始 - [ ] {{待处理事项}} ## 尝试历史(重要!避免重复) ### Round 1 - 尝试: {{做了什么}} - 结果: {{成功/失败}} - 学习: {{下次应该注意什么}} ### Round 2 - 尝试: {{做了什么}} - 结果: {{成功/失败}} - 学习: {{下次应该注意什么}} ## 关键发现 - {{发现 1}} - {{发现 2}} ## 需要人工介入 - {{如果有需要人工确认的事项}}

步骤 3:实现状态持久化

""" state_manager.py - 状态持久化管理器 """ import json import os from pathlib import Path from datetime import datetime class StateManager: """管理循环的外部状态""" def __init__(self, state_file: str = "progress.md"): self.state_file = Path(state_file) def load(self) -> dict: """加载上次循环的状态""" if not self.state_file.exists(): return {"rounds": 0, "completed": [], "attempts": []} with open(self.state_file, "r") as f: return json.loads(f.read()) def update(self, round_num: int, action: str, result: str, learning: str = ""): """更新循环状态""" state = self.load() state["rounds"] = round_num state["last_action"] = action state["last_result"] = result state["attempts"].append({ "round": round_num, "action": action, "result": result, "learning": learning, "timestamp": datetime.now().isoformat() }) # 避免状态文件过大:只保留最近 20 轮 if len(state["attempts"]) > 20: state["attempts"] = state["attempts"][-20:] self.save(state) def save(self, state: dict): """持久化状态""" with open(self.state_file, "w") as f: f.write(json.dumps(state, indent=2, ensure_ascii=False)) def get_recent_failures(self, n=3) -> list: """获取最近 N 次失败的尝试""" state = self.load() failures = [ a for a in state.get("attempts", []) if a["result"] == "failed" ] return failures[-n:]

常见问题 FAQ

Q1:上下文压缩会不会丢失重要信息?

A:会,但有策略降低风险。核心原则:压缩旧轮次,保留最近轮次完整。关键决策和发现写入外部状态文件,不依赖上下文窗口。压缩是"降低精度以换取容量"的取舍——宁可压缩时多留一些,也不要压缩得太过分。

Q2:为什么不能用对话历史作为唯一的状态存储?

A:因为 Agent 会遗忘。Osmani 的话精准概括:"The model forgets everything between runs so the memory has to be on disk and not in the context. The agent forgets, the repo doesn't."(模型在运行之间会遗忘一切,所以记忆必须在磁盘上而非上下文中。Agent 会遗忘,仓库不会。)

Q3:状态文件用 JSON 还是 Markdown?

A:推荐双轨制。JSON 格式适合程序读取(循环自动更新和解析),Markdown 格式适合人工阅读(审查循环进度)。Claude Code 的实践是用 Markdown 文件(如 progress.md),因为 Agent 天然擅长读写 Markdown,而人工审查也更直观。

最佳实践与避坑

  • 关键状态必须外部化:不要依赖上下文窗口存储任何关键信息
  • 及时压缩:在上下文使用率达到 70% 时就开始压缩
  • 状态文件要简洁:避免写入过多细节,只记录决策和结果
  • 包含尝试历史:避免 Agent 重复已失败的路径
  • 定期审查状态文件:防止状态文件中的信息过时或不一致

本节小结

本节深入探讨了循环工程中最容易被忽视但最致命的问题——上下文管理。上下文窗口是循环的"工作记忆",有限且会腐烂。解决方案是三层策略:上下文压缩(管理窗口)、外部状态持久化(长期记忆)和子代理隔离(独立上下文)。

下一节我们将讨论上下文漂移的检测与修复。

延伸阅读

关键词:Loop Engineering, 上下文管理, Context Window, 上下文压缩, 状态持久化, 上下文腐烂, Context Rot, 外部状态, 教程, 高级

难度:高级

预计阅读:14 分钟


发布者: 作者: 转发
评论区 (0)
U