Agent的上下文处理
2026-03-23 Agent的上下文处理
引言
Agent 是基于 LLM 完成任务的系统,而上下文管理直接决定它能不能正确理解和执行任务。
上下文处理不是把所有历史记录一股脑塞给 LLM,而是要根据任务性质和上下文类型选择处理方式。对话轮次一多,上下文会持续膨胀,全量传递会带来几个直接问题:
- Token 消耗过大:成本急剧上升
- 响应延迟增加:模型处理时间变长
- 信息噪音干扰:关键信息被淹没
- 上下文窗口溢出:超出模型限制
所以,上下文策略是 Agent 工程里绕不开的一环。
上下文处理的常见方式
1. 上下文缓存
缓存是最基础也最有效的手段:把频繁使用的上下文预先算好存起来,避免重复处理。
实现方式:
class ContextCache:
def __init__(self, max_size=100):
self.cache = {}
self.max_size = max_size
def get(self, key):
return self.cache.get(key)
def set(self, key, value):
if len(self.cache) >= self.max_size:
# LRU 淘汰策略
oldest_key = next(iter(self.cache))
del self.cache[oldest_key]
self.cache[key] = value
应用场景:
- 系统提示词(System Prompt)缓存
- 常用工具描述缓存
- 用户偏好设置缓存
- RAG 检索结果的缓存
注意事项:
- 需要合理的缓存失效策略
- 注意缓存一致性问题
- 对于动态变化的内容要谨慎使用
2. 上下文压缩
压缩的目标,是在不丢关键信息的前提下减少上下文的 Token 数量。
常用压缩技术:
| 技术 | 原理 | 适用场景 |
|---|---|---|
| 语义压缩 | 提取核心语义,去除冗余表达 | 长对话历史 |
| 结构化压缩 | 将非结构化文本转为结构化格式 | 工具调用结果 |
| 截断压缩 | 保留最近 N 轮对话 | 简单对话场景 |
| 蒸馏压缩 | 用小模型生成压缩版本 | 大规模文档 |
示例:语义压缩
原始上下文(150 tokens):
"用户在 3 月 15 日询问了关于 Python 异步编程的问题,我详细解释了 async/await
的用法,并提供了三个示例代码。用户表示理解了,然后问了一个关于 asyncio 库的
问题,我解释了事件循环的概念..."
压缩后(30 tokens):
"对话主题:Python 异步编程;已解答:async/await、asyncio;用户状态:理解"
实现要点:
- 压缩比与信息保留需要权衡
- 关键决策点信息不能丢失
- 可使用 LLM 辅助压缩(但需考虑成本)
3. 上下文摘要
摘要和压缩类似,但它不只是提取关键点,更强调生成一段连贯的总结性描述。
摘要层级结构:
| 层级 | 名称 | 描述 |
|---|---|---|
| 第一层 | 全局摘要 | 整个会话的核心目标与进展 |
| 第二层 | 阶段摘要 | 最近 N 轮对话的总结 |
| 第三层 | 原始对话 | 最近 3-5 轮的完整上下文 |
摘要触发策略:
- 轮次触发:每 N 轮对话生成一次摘要
- 长度触发:上下文超过阈值时触发
- 阶段触发:完成一个子任务后触发
代码示例:
def should_summarize(messages, threshold=4000):
total_tokens = sum(estimate_tokens(m) for m in messages)
return total_tokens > threshold
def summarize_context(messages, llm):
summary_prompt = f"""
请总结以下对话的核心内容:
对话历史:
{format_messages(messages)}
请用简洁的语言总结:
1. 用户的核心需求
2. 已完成的工作
3. 待解决的问题
"""
return llm.generate(summary_prompt)
4. 上下文筛选
筛选指的是根据当前任务的相关性,动态决定哪些上下文值得放进窗口。
筛选维度:
- 时间相关性:优先保留最近的对话
- 主题相关性:保留与当前任务相关的历史
- 重要性权重:保留关键决策和结果
- 用户关注度:保留用户明确提及的内容
基于向量检索的筛选:
from sentence_transformers import SentenceTransformer
import numpy as np
class ContextSelector:
def __init__(self, model_name='all-MiniLM-L6-v2'):
self.model = SentenceTransformer(model_name)
def select_relevant(self, query, history, top_k=5):
# 编码查询
query_embedding = self.model.encode(query)
# 编码历史上下文
history_embeddings = self.model.encode(history)
# 计算相似度
similarities = np.dot(history_embeddings, query_embedding)
# 选择最相关的 top_k
top_indices = np.argsort(similarities)[-top_k:][::-1]
return [history[i] for i in top_indices]
筛选策略组合:
最终上下文 = 最近3轮对话 + 相关历史(检索) + 关键决策(标记) + 系统提示
最佳实践
分层上下文管理
Level 1: 系统级(始终保留)
└── 系统提示、工具定义、安全规则
Level 2: 会话级(摘要保留)
└── 会话目标、关键决策、重要结果
Level 3: 阶段级(压缩保留)
└── 当前任务相关历史
Level 4: 即时级(完整保留)
└── 最近 2-3 轮对话
动态调整策略
class AdaptiveContextManager:
def __init__(self):
self.strategies = {
'simple': SimpleTruncation(),
'moderate': CompressionStrategy(),
'complex': SummaryStrategy(),
}
def select_strategy(self, task_complexity, context_length):
if context_length < 2000:
return self.strategies['simple']
elif task_complexity == 'high':
return self.strategies['complex']
else:
return self.strategies['moderate']
总结
上下文处理是 Agent 架构的核心挑战之一。做得好的收益很直接:
- 降低成本:少花冤枉 Token
- 提升性能:响应更快
- 提高质量:模型的注意力集中在关键信息上
- 扩展能力:支撑更长的对话和更复杂的任务
实际应用里通常是多种策略组合使用、按场景动态切换。另外一个趋势是:随着上下文窗口越来越大,处理的重点已经从"怎么塞进去"变成"怎么组织得更有效"。
评论讨论