AI Agent中的上下文工程与记忆工程

在本文中,你将了解上下文工程和记忆工程如何解决 agentic AI 系统中的不同问题,以及这两个学科如何在检索到的记忆进入上下文窗口的交汇点相遇。
引言

随着 AI agents 进入更长的工作流程和多会话用例,一种熟悉的模式浮现出来。约束在任务中途被丢弃,检索到的信息在不该出现的时候重新浮出水面,上一步骤的上下文渗入当前步骤。这些故障很难定位,因为没有单个组件明显有问题。
大多数情况下,问题出在两个被一起构建、混为一谈或被跳过的领域:上下文工程 和 记忆工程。它们相关但不同,以不同的方式失败,并且需要不同的系统才能正确处理。
本文涵盖了每个学科背后的核心决策以及它们的交互点:
- 上下文工程涉及什么,以及决定 agent 在单次调用中能否良好推理的具体决策。
- 记忆工程涉及什么,以及写入策略、存储、检索和维护分别如何影响长期可靠性。
- 这两个学科如何在检索时共享边界,以及如何管理好这个边界。
分别地、综合地理解两者,是决定 agent 能否在实际工作负载中保持稳定表现的关键。
上下文工程与记忆工程概述
上下文工程 涵盖单次推理调用的设计:包含什么、压缩什么、把内容放在哪里、丢弃什么。范围内的所有内容都是临时的。当调用结束时,窗口被清空。
记忆工程 关注在单次与模型的交互之外存续的内容。它涵盖了负责写入、存储、检索、更新和管理信息的系统和策略,以便未来的交互能够利用这些信息。当一个 agent 从之前的会话中回忆信息、与另一个 agent 协调,或应用几天或几周前学到的用户偏好时,它依赖的就是记忆工程。
上下文工程决定模型在特定请求期间可获得哪些信息,而记忆工程决定哪些信息在多次请求之间持久化,以及这些信息如何随时间被维护、检索和信任。以下是一个概述:
| 维度 | 上下文工程 | 记忆工程 |
|---|---|---|
| 范围 | 单次推理调用 | 跨调用、会话、agents |
| 数据存放位置 | 模型的活跃窗口内 | 外部存储:向量数据库、K/V、关系型 |
| 核心问题 | 包含什么以及如何组织 | 持久化什么、检索什么、信任什么 |
| 失败时机 | 窗口填满、位置错误、噪声淹没信号 | 检索遗漏、数据过时、投毒、没有写入策略 |
| 工程面 | Prompt 结构、压缩、Token 预算 | 存储模式、检索策略、写入和更新策略 |
| 数据生命周期 | 一次 LLM 调用的时长 | 取决于记忆类型 |
上下文工程:组装最优上下文窗口
对于运行多步骤工作流的 agent,每次推理调用都会从多个来源组装一个上下文窗口:系统 prompt、任务描述、对话历史、工具输出、检索到的文档、子 agent 摘要。上下文工程是一组决策,决定每个组件贡献什么、以什么形式、在什么位置贡献。
选择性包含
并非所有可用的内容都应该进入上下文。一个返回数百行的数据库查询、一个返回五篇完整文章的网络搜索、一个记录冗长输出的代码执行器 所有这些都会使窗口膨胀,并在达到 Token 限制之前降低推理质量。决定什么逐字包含、什么压缩为关键事实、什么丢弃,是一个设计选择,而非默认行为。
结构放置
信息在窗口中的位置会影响模型使用它的可靠性。模型对长上下文开头和结尾的内容关注更强,而中间的材料获得的权重显著更低。这就是所谓的 "lost in the middle" 效应。
硬约束和任务关键指令应放在窗口顶部。与当前任务最相关的检索信息应放在上下文窗口末尾附近。
当前用户查询或任务通常应跟在检索信息之后,将相关上下文和当前目标尽可能靠近生成点放置。这种安排增加了模型在生成响应时有效使用检索信息的可能性。

到达即压缩
工具输出应在调用返回后立即压缩,而不是等到窗口填满。一个包含 3,000 个 Token 的原始 API 响应,其中 agent 只需要 150 个,应该在进入下一步骤的上下文之前进行摘要。等到窗口满了再匆忙截断,是对一个可以在源头通过压缩预防的问题的被动管理。
对话历史管理
对话历史的增长速度超过任何其他上下文组件。对于长时间运行的 agent,每次调用都携带完整历史会使后续每次推理更昂贵且更不可靠。压缩策略 滚动窗口、分层摘要或结构化状态提取 应在定义的间隔应用,而不是在窗口溢出时。
记忆工程:设计持久化 AI 记忆系统
一旦推理调用完成,记忆工程决定什么值得持久化以及在什么条件下再次使用。这涵盖四个不同的问题:写入什么、存储在哪里、如何检索,以及如何随时间保持准确。
写入策略设计
写入策略设计是记忆工程中最容易被忽视的方面之一,但它对长期记忆质量有着不成比例的巨大影响。虽然检索系统通常获得最多关注,但检索质量最终受限于最初进入记忆存储的内容。
一个定义良好的写入策略规定了:
- 哪些事件触发写入记忆。
- 哪些信息有资格被存储。
- 信息存储的格式,例如原始文本、结构化记录、提取的事实或摘要。
- 接受新条目的置信度或验证要求。
- 哪些 agents、工具或系统组件被允许写入特定的记忆命名空间。
- 如何处理更新、更正和冲突信息。
- 针对不同记忆类型的保留规则、过期策略和生存时间(TTL)要求。
没有明确的写入策略,系统往往会默认存储过多信息、对所有条目赋予相同的信任度,并无限期保留数据。随着时间的推移,低价值和过时的记忆不断累积,信噪比下降,检索质量退化。结果是记忆系统持续增长,却变得越来越没用。
存储层选择
不同的记忆类型服务于不同的目的,需要不同的存储后端。后端的选择也限制了可用的检索策略。
| 记忆类型 | 存储内容 | 存储后端 | 检索方法 |
|---|---|---|---|
| 工作记忆 | 活跃任务状态、中间结果 | 内存或短生命周期 K/V(Redis) | 直接键查找 |
| 情景记忆 | 过去的交互、任务运行、决策 | 向量存储(Pinecone、Weaviate、Chroma) | 语义相似度搜索 |
| 语义记忆 | 持久化事实、用户偏好、领域知识 | 向量存储 + K/V 混合 | 语义搜索或精确键查找 |
| 程序性记忆 | 学习到的工作流程、成功的行动模式 | 结构化存储或 prompt 注入 | 模式匹配、直接检索 |
OpenAI 的上下文个性化 cookbook 对于需要连续性的用例,在基于检索的记忆和基于状态的记忆之间做了一个有用的区分。基于检索的记忆将过去的交互视为松散相关的文档,对措辞变化和冲突更新很脆弱。结构化状态提取 写入类型化、经过验证的事实,而不是嵌入原始对话 对于需要在多个会话中可靠应用的事实,能产生更一致的结果。

检索策略
从记忆读取不是一个单一操作。
设计良好的检索层首先检查工作记忆(快速、廉价、精确键查找) 当没有相关内容出现时,回退到情景记忆或语义记忆中的语义搜索 在返回结果前应用元数据过滤器来筛选新近度和信任级别 并且只注入当前步骤需要的内容。
记忆维护
没有维护策略的存储会随时间退化。条目不断累积,过时的事实与当前事实竞争,随着信噪比下降,检索质量也随之下降。以下维护例程在实践中很重要:对易变事实的置信度衰减、语义相似条目的去重、基于 TTL 的工作记忆和时效性数据过期,以及将旧的情景记录定期压缩为会话级摘要。
一个直接编码这些关注点的 MemoryEntry 模式使写入和维护逻辑更容易推理:
class MemoryEntry(BaseModel):
content: str
memory_type: str # 工作 | 情景 | 语义 | 程序性
importance: float # 0.0–1.0,控制是否进入长期存储
confidence: float # 对易变事实随时间衰减
trust_level: float # 1.0 内部系统,0.5 用户输入,0.0 外部
created_at: datetime
expires_at: datetime | None
provenance: dict # agent_id, tool_name, session_id, input_hash
def should_write_to_long_term(entry: MemoryEntry) -> bool:
return (
entry.importance >= 0.6
and entry.confidence >= 0.7
and entry.trust_level >= 0.5
)
检索边界:连接记忆与上下文工程
记忆工程和上下文工程通常被当作独立的学科来讨论,但在实践中它们密切相关。两者都是为了解决同一个核心问题:确保模型在正确的时间获得正确的信息。
从高层来看:
- 记忆工程专注于持久化:哪些信息应该随时间被存储、更新、保留或遗忘。
- 上下文工程专注于利用:对于特定任务,哪些信息应进入活跃上下文窗口,以及如何组织这些信息。
检索是这两个学科交汇的边界。
记忆系统产生候选信息。然后上下文组装决定:
- 该信息是否应进入 prompt。
- 应包含多少。
- 它应放在上下文窗口中的什么位置。
管理好这个边界,是将记忆组件集合转化为一个连贯的 agent 系统的关键。
失败模式 #1:没有上下文预算的检索
最常见的失败之一是检索被独立于上下文组装来处理。
记忆搜索返回一组相关条目,上下文组装器将它们全部注入 prompt。随着更多记忆被添加,上下文窗口逐渐被检索内容填满,留给指令、工具输出、推理轨迹和任务特定信息的空间越来越少。
由此产生的症状往往具有误导性:
- 检索质量看起来很高。
- 相关记忆被成功找到。
- 系统性能仍然下降。
在许多情况下,记忆系统正确完成了它的工作。失败是因为上下文组装缺乏预算机制。
更好的方法是感知检索的上下文组装。不是在检索之后再做预算,而是上下文层在检索开始前分配 Token 预算。然后检索层只返回符合该预算的最高价值记忆。
async def retrieve_for_step(
self,
step: AgentStep,
max_tokens: int
) -> str:
candidates = await self.memory.search(
query=step.retrieval_query,
max_results=10,
filters={
"trust_level": {"gte": 0.5},
"expires_at": {"gt": datetime.now()}
}
)
selected = []
used = 0
for entry in sorted(
candidates,
key=lambda e: e.relevance_score,
reverse=True
):
cost = self.token_count(entry.content)
if used + cost > max_tokens:
break
selected.append(entry.content)
used += cost
return "\n\n".join(selected)
检索必须在上下文约束内运行,而不是假设下游有无限空间。
失败模式 #2:检索信息放置不当
仅凭检索质量是不够的。即使是高度相关的记忆,如果在上下文窗口内放置不正确,也可能失败。
一个常见问题是把检索纯粹当作搜索问题来处理,而忽略放置。检索到的记忆被随意追加到任何位置,而不考虑它们在当前推理步骤中的作用。
这在长上下文中影响更大。注意力在 prompt 中并非均匀分布。放在长上下文深处的信息,其影响力可能显著低于放在开头或结尾附近的信息。这导致一种微妙的失败模式:
- 正确的信息被检索到。
- 信息被插入上下文。
- 模型表现得好像信息不存在。
检索成功了,但放置失败了。因此,上下文组装应该同时优化两者:
- 选择:什么进入上下文窗口。
- 放置:它在上下文窗口中的什么位置出现。
必须影响当前步骤的检索信息应放在活跃推理区域附近,而不是随意追加。
总结
上下文工程和记忆工程是同一系统的两层,该系统控制模型知道什么、何时知道,以及如何使用这些知识。
上下文工程在推理时运行,塑造活跃信息窗口。记忆工程跨越时间运行,塑造哪些信息持久化以及以后如何检索它们。
| 维度 | 上下文工程 | 记忆工程 |
|---|---|---|
| 核心问题 | 模型现在应该看到什么,以及如何看到? | 系统应该保留什么,保留多久? |
| 主要产物 | 每次推理调用组装的上下文窗口 | 跨调用和会话持久化的记忆条目 |
| Token 管理 | 每个窗口组件的预算分配 | 每种条目类型的存储成本;每次查询的检索成本 |
| 压缩 | 工具输出在注入前摘要;历史滚动或提取 | 旧的情景记录被压缩;过时事实衰减或被剪除 |
| 新鲜度 | 滚动历史窗口;过时的轮次被丢弃 | 易变事实的 TTL;置信度随时间衰减 |
| 信任 | 来源层级决定组装顺序 | 每个条目追踪来源;低信任内容在写入前被净化 |
| 多 agent | 每个 agent 独立组装自己的窗口 | 每个 agent 有作用域命名空间;跨 agent 事实用共享命名空间 |
| 失败模式 | 溢出、注意力退化、噪声组装 | 投毒、过时、检索遗漏、无界增长 |
| 维护 | 在定义的间隔主动压缩 | TTL 过期、去重、置信度衰减、情景归档 |
| 交汇点 | 检索到的记忆进入上下文:预算和放置决定如何进入 | 上下文组装在 Token 预算约束内请求检索 |
总而言之,一个 agentic 系统只有在两层对齐时才能正常工作:记忆决定什么可用,上下文决定什么可执行。
- 原文链接: x.com/beamnxw/status/208...
- 鸿途知科网 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
版权声明
本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。
鸿途知科网
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。