当 Agent 在长对话中使用了大量 Skill,上下文窗口超出 LLM 限制时,如何处理 Skill 产生的历史信息?
面试官:“在一个长对话中,用户可能调用了大量 Skill,产生的历史信息越来越多,超出 LLM 上下文窗口限制时,你怎么处理这些 Skill 产生的信息?”
候选人:
“这是个很现实的问题,就像让一个人记住过去一年的每一天午餐吃了什么,既不必要也不可能。我设计的核心原则是:不让 LLM 看所有原始信息,而是看分层加工后的记忆摘要,并根据当前任务按需检索细节。
我把处理方案分成四层策略,由简到深,层层递进。
策略一:关键结果提取与摘要压缩
这是第一道防线。每个 Skill 执行完毕后,编排引擎不仅拿到完整的结构化输出,还会生成一份摘要信息,专门用于给 LLM 看。摘要只保留对后续决策真正有用的字段,丢弃冗余细节。
比如天气 Skill 返回了一大段 JSON,包含逐小时预报、风速、湿度等。但摘要只提取:“北京 26°C 晴,降水概率 5%”。这个摘要随着对话历史一起发给 LLM,原始完整数据存入外部存储(如会话上下文缓存),需要时才查。
很多 Skill 的输出 80% 是机器可读的结构,只有 20% 需要人类或 LLM 理解。摘要就是把那 20% 提炼出来。这可以直接调用一个轻量模型做总结,或者用 Skill 注册时就写好的摘要模板。
策略二:滚动窗口 + 关键帧
当摘要累积也超出上下文窗口时,就不能把所有历史摘要都放进去了。我引入滚动时间窗口 + 关键帧机制。
-
近窗口:保留最近 N 轮对话的完整摘要(比如最近 5 轮交互)。这些是最新鲜、最相关的上下文。
-
远窗口:对更早期的历史,只保留“关键帧”——也就是那些可能影响当前决策的转折点。比如“用户在上述城市改成了深圳”、“用户选择了商务舱”等状态变更标记。这些关键帧由编排引擎根据 Skill 副作用自动识别:如果一个 Skill 写了某个会话级变量,这就构成一个关键帧。
这样,给 LLM 的上下文就变成:近 5 轮完整摘要 + 早期关键帧列表 + 长期用户偏好。总长度可控,同时不丢失状态变迁。
策略三:外部向量存储 + 按需检索
当对话特别长,或者中间用户做了很早期的决定现在可能还想引用时,只能靠检索了。我把所有 Skill 的执行摘要和关键输出,以向量化方式存入专门为这个会话准备的长期记忆库(可以用向量数据库或轻量级内存索引,如 Faiss 的会话级索引)。
每次需要生成响应前,编排引擎根据当前用户意图,生成一个检索查询(比如“之前决定去哪个城市了?”),从长期记忆库中检索最相关的历史 Skill 结果,将检索到的信息补充进上下文窗口。这就是 RAG 思想在会话历史管理上的应用。
注意,这个检索是针对当前会话的隔离索引,不是全局的。会话结束时销毁,成本可控。
策略四:结构化上下文编排,选择性注入
不是所有 Skill 的历史结果都要给 LLM 看。编排引擎在调用 LLM 前,会根据当前正在处理的意图和 Skill,动态决定哪些历史信息是相关的。
比如用户刚才调了天气 Skill 和航班 Skill,现在问“那酒店订在哪里合适”。编排引擎知道需要酒店 Skill 的输入参数(城市、日期、预算),因此只从历史中提取“航班目的地=深圳”、“日期=下周二”,而不是把天气 Skill 逐小时预报也塞进去。
这需要编排引擎在 DAG 构建和 Skill 元信息中维护依赖关系图——Agent 知道哪些历史输出是当前 Skill 的“前置依赖”,只注入这些。