对照项目 Agentium 的背景:Agentium 论文与开源项目介绍。本文图表及核心设计均来自开源项目Agentium,源码详见GitHub。
连载 06《AI 网关:路由、限流、内容与成本》 在网关处守住了 token 与内容安全。本篇回到「模型看见什么」:对话历史会涨、事实要跨会话保留、检索库又另有一套——若全塞进 prompt,窗口和账单一起爆。记忆系统要回答:什么留在短期上下文里、什么写进外部记忆、读写各走哪条路。
工程上至少拆两块:
messages 列表:system、最近若干轮 user/assistant。来源通常是会话消息表;组装时要过 上下文预算——软删旧轮、硬停或降级摘要。Chat 里常见组合:消息表管「原话流水」,Memory Service 管 摘录、抽取事实、会话摘要 等可检索片段;进模型前再由协调层决定 inject 哪些 scope(会话级 / 用户级)。
对外接口宜保持小: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 过滤。
Memory Service 下面应是 Backend 协议:append / query / purge_tenant。工厂按配置实例化:
协调层只依赖 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 降级),方便合规复盘。
模型窗口有限;即使用网关限流,单轮 prompt 仍可能过长。Context budget 典型三档:
system 消息通常 不参与丢弃,避免把 persona / 安全指令裁没。token 估算可用字符启发式(如 json 长度 ÷ 4),不必等每次调 tokenizer API——预算层要 快、可预测。
这与 Memory Service 互补:trim 管「原话流水」;MID 层的 running summary 管「裁掉之后还留什么语义」。
写(Turn 后)
读(Turn 前或 API)
tenant_id + layer + 可选 run_id_filter 拉记录;user_id / memory_scope;后台 Memory Consolidator(Background 平面)可周期性:SHORT 同 key 去重、存活够久的 promote 到 MID、冲突 payload 打 _conflict_with 标记——保守合并,不覆盖高置信旧记录。
记忆篇必须提 污染(contamination) ,否则实验结论不可信:
工程习惯:环境前缀、purge API、run_id / session scope 默认收紧、评测专用 tenant;外联泳道与 local_only 会话 物理上不能 fallback 到外网。
除「对话记忆」外,还有 文档库 / 知识块:按 tenant 隔离的 FTS 或向量检索,带 contextual prefix 提升命中。它可以是 Memory 子模块,也可以是独立 KB store——关键是 检索结果进 prompt 前仍过网关安全与 token 预算,别把整库 PDF 灌进去。

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)