💡 一句话核心:Agent 不是 request/response 的无状态函数,它是一个有记忆的进程。记忆分层、状态持久化、崩溃可恢复,这三件事决定了 agent 能不能上生产。
引导问题:
答案:
对比:
| 维度 | Postgres | Redis |
|---|---|---|
| 持久性 | 强(fsync) | 弱(默认异步) |
| 延迟 | ms 级 | 微秒级 |
| 查询 | SQL / index | KV / 有限数据结构 |
| 事务 | ACID | 简单 MULTI |
| 适合 | Workflow state、审计日志 | Session、限流计数 |
分层设计:
引导问题:
三层 Memory:
| 层 | 存什么 | 存哪 | TTL |
|---|---|---|---|
| Working | 本轮对话最近 N 条 | Redis | 小时级 |
| Short-term | 本次 session 全部 | Redis / Postgres | 天级 |
| Long-term | 蒸馏后的 facts、preferences | Postgres + Vector DB | 永久 |
为什么 step 单独一张表?
关键点:
ON CONFLICT DO UPDATE 保证 idempotent — 重跑同一步不会插入两条SELECT ... FOR UPDATE 避免两个 worker 同时处理同一 workflow(看任务4)测试 resume:
Long-term memory 蒸馏(伪代码):
每个请求带 X-Idempotency-Key,服务端查 Redis 是否已处理过,防止重复。
UPDATE ... WHERE version = $old_version,冲突时重试。避免两个 worker 抢同一 workflow。
先写 intent(我打算做什么),再执行,最后写 result。崩溃时能判断"是不是已经做过"。
睡眠时大脑把短期记忆转成长期 — 我们每 N 轮触发 LLM 蒸馏。
LTRIM?不限制会怎样?docker-compose up postgres redis,跑 migration。
RunWithCheckpoint 能跑通,手动 kill 进程后 resume 成功。
实现 Append / Recent,写一个 handler /chat 使用它。
同一个 idempotency key 重复调用 /workflow,只创建一条记录。
Q: 为什么不把 session 也放 Postgres,一套到底?
A: 可以,但 session 访问频率高(每条消息一次),Postgres 顶不住。Redis 微秒级延迟是刚需。
Q: Checkpoint 太频繁会不会拖慢 workflow?
A: 每步写一条 log 通常 <10ms,对整个 workflow(秒-分钟级)可忽略。如果某步本身很快(<50ms),可考虑批量写。
Q: Long-term memory 会不会越存越多?
A: 会。策略:
Q: 用 event sourcing(只存事件)不是更优雅?
A: 是。但实现复杂度高 — 需要 replay、版本兼容。Temporal 就是 event sourcing。自研阶段用 snapshot + step log 够用。
题目: 给定账户列表,每个账户第一个元素是姓名,其余是邮件地址。属于同一人的账户(有相同邮件)需要合并,返回合并后的账户列表,每个账户的邮件排序。
思路: 并查集(Union-Find)。以 email 为节点,有相同账户内的 email 互相 union。最后按根节点分组,每组加上姓名输出。
复杂度: 时间 O(N·α(N))(α 为反阿克曼函数,近似 O(N)),空间 O(N)
变体/面试追问:
题目: 给 n 个节点(0 到 n-1)和边列表 edges,返回图中连通分量的数量。
思路: 并查集。初始每个节点自为一组,count = n。每次 union 两个不同组时 count--。最终 count 即为答案。也可用 DFS/BFS 逐个节点遍历未访问的节点。
复杂度: 时间 O((N+E)·α(N)),空间 O(N)
变体/面试追问:
题目: 给二叉树根节点,返回从右侧观察时每层能看到的节点值列表(即每层最右边的节点)。
思路: BFS 层序遍历,每层处理完后取队列中最后一个元素加入结果。也可用 DFS,优先遍历右子树,首次到达某层时记录该层值。
复杂度: 时间 O(N),空间 O(N)(队列/递归栈)
变体/面试追问:
明天我们做 Human Approval Flow — 让 agent 生成 draft,人批准后才执行。
准备问题: