《从零实现 Agent 系统》连载 07|记忆系统:短期上下文 vs 长期外部记忆

对照项目 Agentium 的背景:Agentium 论文与开源项目介绍。本文图表及核心设计均来自开源项目Agentium,源码详见GitHub。

连载 06《AI 网关:路由、限流、内容与成本》 在网关处守住了 token 与内容安全。本篇回到「模型看见什么」 :对话历史会涨、事实要跨会话保留、检索库又另有一套------若全塞进 prompt,窗口和账单一起爆。记忆系统要回答:什么留在短期上下文里、什么写进外部记忆、读写各走哪条路


两类「记忆」,别混成一个数组

工程上至少拆两块:

  1. 短期上下文(working context)
    当前 Turn 要发给模型的 messages 列表:system、最近若干轮 user/assistant。来源通常是会话消息表;组装时要过 上下文预算------软删旧轮、硬停或降级摘要。
  2. 长期 / 分层外部记忆(external memory)
    结构化记录:租户、层(短/中/长)、键、载荷、时间戳。通过 Memory Service 读写,后端可换 SQLite、进程内、或外联 Mem0 一类平台;不等于把整段聊天记录原样 append 到 prompt。

Chat 里常见组合:消息表管「原话流水」 ,Memory Service 管 摘录、抽取事实、会话摘要 等可检索片段;进模型前再由协调层决定 inject 哪些 scope(会话级 / 用户级)。


Memory Service:薄门面,重隔离

对外接口宜保持小:remember (写)、recall (读)、purge (清租户)。每条记录带 tenant_id + layer + key + payload

租户隔离是硬 invariant :请求的 tenant_id 与目标租户不一致,直接拒绝并记审计------不能指望后端「碰巧查对了」。这样换 SQLite 还是外联平台,越权读写的形状不变。

Agentium 用三层枚举 SHORT / MID / LONG 表达时间尺度,而不是在业务里散落 magic string:

典型内容 生命周期
SHORT 单轮对话摘录、工具结果快照 短,可被后台整理
MID 会话级摘要、本会话抽取事实 session_id / run_id
LONG 跨会话用户偏好、稳定事实 user_id 走,scope 更宽

写路径在 Turn 结束后异步或 fire-and-forget:SHORT 先落盘 ,可选 LLM 抽取进 MID/LONG;读路径在下一轮组 prompt 或 API 查询时 按 layer + scope 过滤


后端可插拔:Protocol 在,实现可换

Memory Service 下面应是 Backend 协议append / query / purge_tenant。工厂按配置实例化:

  • in-memory:单测与 demo;
  • sqlite:本地持久、易备份;
  • mem0 等外联 :托管检索与向量能力,但要接受 数据出境 与 API 依赖。

协调层只依赖 Memory Service,不 import 具体后端------这是「可插拔边界」:企业内网部署换 sqlite,SaaS 试点换 mem0,不动 chat turn 逻辑。


泳道路由:同一会话,不同存储策略

当系统同时配置了 native(本地)mem0(外联) 两条泳道时,不能全局写死一条。更稳妥的做法:按会话 metadata 解析 ------例如 workspace 里标记 memory_plugin: mem0 走外联,默认走 native。

隐私先于成本(P0) :若会话标记 local_only / on_prem 一类 residency,禁止 mem0 泳道------即使用户偏好外联,也强制 native;若 native 未配置,应 返回 None / 明确失败,而不是悄悄把受监管数据送到公网记忆库。

这与连载 06 网关的 privacy class 同族:数据驻留约束要在记忆层再守一遍。路由结果宜打结构化日志(选了哪条泳道、是否因 privacy 降级),方便合规复盘。


上下文窗口预算:trim 比「硬塞」体面

模型窗口有限;即使用网关限流,单轮 prompt 仍可能过长。Context budget 典型三档:

  1. soft limit:超出则丢弃最旧的非 system 消息,保留最近几轮;
  2. hard limit :仍超则要么抛错,要么进入 safe degrade
  3. safe degrade:用短摘要或占位 user 消息替代大段历史,并保留尾部最近轮------用户至少还能接着聊。

system 消息通常 不参与丢弃 ,避免把 persona / 安全指令裁没。token 估算可用字符启发式(如 json 长度 ÷ 4),不必等每次调 tokenizer API------预算层要 快、可预测

这与 Memory Service 互补:trim 管「原话流水」;MID 层的 running summary 管「裁掉之后还留什么语义」。


读路径 vs 写路径

写(Turn 后)

  • SHORT:本轮 user/assistant 预览写入,key 含 session + pair_id;
  • MID:LLM 抽取会话事实、Hermes 式 running summary;
  • LONG:跨会话用户事实,写前 dedupe(normalize key,避免同义重复)。

读(Turn 前或 API)

  • tenant_id + layer + 可选 run_id_filter 拉记录;
  • 用户级 LONG 需再滤 user_id / memory_scope
  • 注入 prompt 时控制 条数与总 token,别 recall 200 条全塞进去。

后台 Memory Consolidator (Background 平面)可周期性:SHORT 同 key 去重、存活够久的 promote 到 MID、冲突 payload 打 _conflict_with 标记------保守合并,不覆盖高置信旧记录。


「污染」:评测、生产、租户之间

记忆篇必须提 污染(contamination) ,否则实验结论不可信:

  • 评测集事实写进生产 LONG 层 → 模型「偷看答案」;
  • A 租户 recall 混入 B 租户 key → 隔离失败(所以 Service 层硬拦 cross-tenant);
  • dev 环境 sqlite 拷到 prod → 脏事实永久化;
  • 外联 mem0 与本地 native 双写不一致 → 同一会话两条真相。

工程习惯:环境前缀purge APIrun_id / session scope 默认收紧 、评测专用 tenant;外联泳道与 local_only 会话 物理上不能 fallback 到外网


上下文检索扩展(KB)

除「对话记忆」外,还有 文档库 / 知识块 :按 tenant 隔离的 FTS 或向量检索,带 contextual prefix 提升命中。它可以是 Memory 子模块,也可以是独立 KB store------关键是 检索结果进 prompt 前仍过网关安全与 token 预算,别把整库 PDF 灌进去。


一张白板图:从 Turn 到记忆再回上下文


伪代码:memory 与 context budget

scss 复制代码
function MemoryRecall(ctx, layer, query_scope) -> List[Record]:
    assert ctx.tenant_id matches target tenant
    lane := MemoryLaneRouter.Resolve(ctx.session_id, privacy_tier)
    if lane is None:
        return []
    records := lane.recall(ctx, layer, limit=N, run_id_filter=scope)
    return filter_by_user_scope(records, query_scope)


function MemoryWrite(ctx, layer, key, payload, policy):
    assert policy.allows_write(layer, payload)
    lane := MemoryLaneRouter.Resolve(ctx.session_id, privacy_tier)
    if lane is None:
        return Denied
    lane.remember(ctx, layer, key, payload)


function ContextWindowTrim(messages, soft, hard, safe_degrade) -> messages:
    if estimate_tokens(messages) <= soft:
        return messages
    kept := [system] + drop_oldest_non_system_until_soft(messages, soft)
    if estimate_tokens(kept) <= hard:
        return kept
    if safe_degrade:
        return [system, budget_notice] + tail_recent(kept, k=4)
    raise ContextHardStop

几句容易踩坑的地方

只有 messages 表没有 Memory Service ------跨会话偏好全靠用户重复自我介绍。Memory 无 tenant 校验 ------一次 bug 就是跨客户泄露。local_only 会话仍 fallback mem0 ------合规直接击穿。recall 不 limit ------prompt 隐形爆炸。SHORT 永不 consolidate ------库膨胀、检索变慢。评测与生产共库 ------指标虚高。把 KB 检索结果绕过内容安全------脏片段直进模型。


收束一下,下一篇讲什么

本篇应能说清:短期上下文与外部记忆的分工 、Memory Service 读写与 backend 边界、泳道与 privacy 门 、context budget 三档、以及 污染 在工程里指什么。

下一篇进入 编排与工作流:从单轮 Chat 到任务图/DAG------协调层怎样串多个 Turn、制品如何贯穿审计。(连载 08)

相关推荐
星辰AI2 小时前
LLM 安全与对齐技术:构建可信赖的人工智能
人工智能·ai·语言模型
圣殿骑士-Khtangc2 小时前
CloakBrowser 深度解析:C++ 源码级反检测浏览器,Playwright 的终极替代品
人工智能
05候补工程师2 小时前
从算法理想向工程现实的跨越:SLAM 核心架构、思维误区与 Nav2 实战避坑指南
人工智能·算法·安全·架构·机器人
dinl_vin2 小时前
FastAPI 系列·(三):依赖注入——用 Depends 构建分层架构
架构·fastapi
threelab3 小时前
Three.js 加载 3D Tiles 瓦片数据 | 三维可视化 / AI 提示词
开发语言·前端·javascript·人工智能·3d·着色器
韦胖漫谈IT3 小时前
不当输出处理 - 大语言模型 OWASP TOP 10系列
人工智能·语言模型·自然语言处理
JavaAgent架构师3 小时前
前端AI工程化(九):AI Agent平台前端架构设计
前端·人工智能
阿里云大数据AI技术3 小时前
DataWorks Data Agent:助力淘宝闪购一句话搞定数据开发,让周期从天级到分钟级
人工智能·agent
qcx233 小时前
【系统学AI】03 LLM训练全流程:预训练→SFT→对齐五条路线
人工智能·llm·sft·预训练·奖励模型·对齐·路线