缓存策略的技术实现原理

一、总览:一条原则统领三层缓存

缓存能力分散在三个层次,但它们全部服从同一条地基原则——会话事件日志(append-only)是唯一权威,所有缓存都只是从日志派生出来的”快捷方式”,可以过时、可以丢失、但永远不会给出错误的值。理解了这一条,三层缓存的失效策略、写回时机、fail-soft 行为就都是它的自然推论。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
flowchart TB
LOG["会话事件日志\nappend-only,唯一权威"]

subgraph L1 ["第一层:Provider KV / 前缀缓存(省钱省延迟,命中在提供方侧)"]
DM["deriveMessages 纯投影\n每个 surface 节点只投影一次"]
SP["system-prompt 组装\n静态 section 在前 / 动态 context 置于历史之后"]
HDR["EpochHeader\nsystem + tools 快照"]
REQ["请求 = 前一次请求的追加延伸\n→ 前缀稳定是推论,不是管理出来的"]
DM --> REQ
SP --> HDR --> REQ
end

subgraph L2 ["第二层:会话投影缓存(唯一持久缓存,加速冷读列表)"]
PC["ctx.sessionProjectionCache\nsession_projcache 域"]
ROW["行 (key → ver,seq,val)\n是折叠快捷方式,永不作为权威"]
LADDER["冷读阶梯:缓存行 → readFrom 尾部 → restore → 回写"]
PC --> ROW --> LADDER
end

subgraph L3 ["第三层:进程内派生缓存(存活于单进程,随对象回收)"]
CRED["credentials-local / settings-file\n内存快照 + 文件监听热更新"]
WM["WeakMap 按对象缓存\nhook / effect / 配置刷新"]
end

LOG --> DM
LOG --> SP
LOG -->|turn/end、销毁、阈值触发写回| PC
LOG -.->|派生| CRED

REQ -->|发往提供方| PROVIDER["模型提供方\n按前缀 token 序列命中 KV 缓存"]
PROVIDER -->|usage 回传| METER["TokenUsage 不相交口径\ncacheReadTokens 单列计量"]

三层各自解决不同的成本问题,作用域和生命周期完全不同:

Read More

Agent记忆系统设计指南

一、背景与目标

Agent 记忆系统用于管理和复用长期上下文,使 Agent 在不同会话和任务之间保持连续性。它不同于聊天历史和知识库,关注的是可长期复用的用户偏好、协作方式、业务约定、历史反馈和任务经验。

核心目标:不是”记住一切”,而是”只记住未来有复用价值、且可治理的内容”。

Read More

Agent核心系统设计

一、起点:agent 的内核是一个循环

抛开一切框架和术语,一个 agent 能干活,靠的只是一个循环:拿到用户的输入,把它和历史一起交给模型,模型要么直接答复、要么要求调用某个工具;如果要求调用工具,就执行它、把结果塞回历史、再问一次模型;直到模型不再要求工具为止。这个循环用五十行代码就能跑通第一个版本,先把它跑通,再来谈设计。

真正的设计难度不在这个循环本身,而在它周围的四件事:状态存在哪里、消息历史怎么来、提示词怎么拼、工具怎么被看见和执行。下面这张图是一个成熟形态的完整往返,你会发现循环还是那个循环,只是每一步都从”直接做”变成了”向某个部件要”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
sequenceDiagram
participant L as 主循环
participant J as 事件日志
participant P as 提示词组装
participant M as 大模型
participant T as 工具注册表
L->>J: 记录「轮次开始」
loop 每个步骤,直到没有待处理的工作
L->>L: 从收件箱认领输入
L->>J: 记录「步骤开始」与用户消息
L->>P: 请求本次的提示词前缀
P-->>L: 静态片段、动态上下文、工具描述
L->>J: 从日志投影出消息历史
J-->>L: 消息列表
L->>M: 发起流式请求
M-->>L: 增量分片
L->>J: 记录分片与完整回复
opt 模型要求调用工具
L->>J: 记录工具调用
L->>T: 分发执行
T-->>L: 结果或结构化失败
L->>J: 记录工具结果
end
L->>J: 记录「步骤结束」
end
L->>J: 记录「轮次结束」与结束原因

图里有一条贯穿的主线值得先记住:主循环自己不保存任何状态,它只是反复地”读日志、问模型、写日志”。之所以要把它设计成这样,是因为 agent 有一个和普通后端服务完全不同的要求——它必须能被完整重放。用户刷新页面要看到全过程、程序崩溃后要能接着跑、上下文满了要能压缩后重试、出了问题要能复盘模型当时到底看到了什么。只要主循环里藏了一点只存在于内存的状态,这四件事就全都做不到。

Read More