3.5 状态管理与上下文维护


3.5 状态管理与上下文维护

多轮对话里记忆去哪了?答案是每个 Agent 的 message_history 里——但它是易失的、有上限的。这一节讲上下文怎么累积、怎么截断、以及你要长期记忆时该怎么办。

上下文是怎么累积的

每次 send/receive,消息都追加进相关 Agent 的历史。下一轮回复时,模型拿到的就是这段累积历史。好处是连贯,坏处是越来越长、越来越贵。

from autogen import ConversableAgent import os cfg = {"model": "gpt-4o-mini", "api_key": os.environ.get("OPENAI_API_KEY")} agent = ConversableAgent("a", llm_config=cfg, system_message="你记性好。") # 发起几轮后查看历史长度 chat = agent.initiate_chat(agent, message="记一下:客户叫小明。", max_turns=2) print("历史条数:", len(agent.chat_messages.get(agent, [])))

截断策略:窗口与摘要

当历史超长,框架默认保留系统提示和最近若干轮,丢弃最早的部分。如果你任务依赖很早的信息(比如第一章定的目标),丢弃会丢上下文。两个办法:

  • 手动管理:定期把关键结论写进系统提示,相当于"记笔记"。
  • 摘要压缩:用模型把历史压成摘要再继续。
# 手动"记笔记":把关键事实固化进系统提示,避免被截断丢失 agent.system_message = "你是助手。已知背景:客户叫小明,项目deadline是月底。" # 之后即使早期对话被截断,背景仍在系统提示里保留

长期记忆:外部存储

需要跨会话记住东西(比如用户偏好),别指望对话历史——它随进程消失。正确做法是用工具把信息写进外部存储(数据库/文件),需要时再读。

memory_store = {} def remember(key: str, value: str) -> str: """把键值存入长期记忆。""" memory_store[key] = value return "已记住" def recall(key: str) -> str: """从长期记忆读取。""" return memory_store.get(key, "无记录") # 注册给 Agent 后,它就能跨轮次、甚至跨进程记住信息

状态一致性的坑

GroupChat 里多个 Agent 共享一段历史,但各自的私有状态(比如上面 memory_store)不在共享历史里。我们踩过坑:以为"群里说过的事每个 Agent 都记得",其实只在该 Agent 的历史里。需要共享的,要么进对话历史,要么进外部存储。

摘要压缩的具体做法

当对话很长,用模型把历史压成摘要再继续。下面示意"每 N 轮摘要一次"的思路,避免上下文无限涨。

# 定期摘要:把前几轮浓缩成一段,替换进历史起点 summary_prompt = "把以下对话浓缩成要点,保留关键结论:\n{history}" # 注意:摘要可能丢细节,关键结论应同时固化进系统提示

GroupChat 里的共享与私有

再强调一次:GroupChat 共享的是 messages 列表,但每个 Agent 的私有属性(比如你挂在 Agent 上的计数器)不在共享里。需要所有角色都知道的状态,要么写进某条消息,要么进外部存储。我们见过一个 bug:coordinator 记了"当前阶段",其他 Agent 根本看不到,导致各自理解不一致、协作崩坏。修法是把阶段写进每条广播消息。

# 把共享状态写进消息,而非藏在某个 Agent 私有变量 shared_note = {"stage": "design", "owner": "w1"} board_msg = {"role": "user", "content": f"[白板] 当前阶段 {shared_note['stage']}"} # 广播给所有 Agent,保证大家看到的阶段一致

长对话的成本提醒

上下文越长,每轮喂给模型的 token 越多,成本随轮数上升。所以压缩不仅是防超限,也是省钱。结合 5.1 的缓存,长对话的整体开销能压住。

一个记忆泄漏的真实教训

我们曾遇到一个会话越跑越慢,最后超时。排查发现 Agent 把每次工具返回的全量数据都留在历史里,几十轮后历史膨胀到数万字符。修复方式是工具返回时只留摘要,全量数据落外部存储按需取。这教训说明:记忆管理不是"要不要",而是"留什么、丢什么"。默认全留,迟早出事。

上下文窗口的硬约束

每个模型有上下文上限,这是物理边界,不是可调参数。历史超了框架只能截断最早的,而截断可能丢掉"第一章定的目标"这类关键信息。所以重要背景必须固化进系统提示(见前面示例),而不是指望它在历史里一直存活。我们团队的做法是:任何"跨轮必须记住"的事实,都进系统提示或外部存储,绝不依赖对话历史保留。

一个外部存储的落地

长期记忆用工具读写外部存储,对话里只传"键"。下面示意记忆 Agent 的读写。

_store = {} def remember(key: str, value: str) -> str: """存入长期记忆。""" _store[key] = value return "已记" def recall(key: str) -> str: """读取长期记忆。""" return _store.get(key, "无记录") # 注册给 Agent,跨轮次甚至跨进程都能记住,不依赖易失的对话历史

状态管理的取舍最终落到一句话:能放进消息历史的别另存,必须跨会话保留的才进外部存储。对话历史是 Agent 天然的状态载体,截断策略(摘要旧轮次、保留关键字段)优先于此;真正需要外部存储的是"重启后还要用"的记忆——用户偏好、任务进度、跨会话事实。实现时建议给每类记忆定一个明确的写入触发点和过期策略,避免"什么都存"导致存储膨胀与检索变慢。

本节要点回顾

  • 上下文累积在 Agent 历史里,超长会被截断。
  • 关键背景固化进系统提示,防截断丢失。
  • 长期记忆用外部存储,对话历史会随进程消失。

⚠️ 别假设 GroupChat 里"说过=都记得",私有状态不共享,共享信息得进历史或存储。

💡 状态管理是"对话即编排"的记忆层——没有它,编排出的协作每次都像失忆重来。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U