九维我操你爹
「第一记忆模式和第二记忆模式:Kapy 的记忆系统设计」

Kapy (short for Kapybara) 是我假期做的一个通用 agent,由于我略长于别人的假期 (thanks to dify.ai ) 就要结束了,先把 devlog 写了,精装修的 GitHub repo 之后再发。

我思考记忆系统的设计很久了,我想每一个做过 chatbot 的人应该都思考过。但我一直没有形成清晰的结论,接着这个机会逼迫自己一把,算是把我的答卷做出来了。其实做 Kapy 是我近期手写代码最多的时候,手写代码就像雕刻家一样(我想象中的雕刻家,因为我也不知道真正的雕刻家什么样),是在打磨的过程中完善思想的过程。

进入正题。这个记忆系统打一开始我就决定要做统一式的,不做基于 thread 的拆分,以原始信息作为 single source of truth。此时包括 bub 在内的 agent 已经把这条路跑通了,因此这个决策对我来说不仅是想法,是一个完全明确的方向。

统一式的架构定了,应用层总得有 compact 吧?我一直报有 turn 重要于 thread 的想法:
- 很多任务是以 turn 为单位的,比如搜索任务大概率不会有追问;
- Agent 语境下的 turn 不仅是输入-输出对,而是:输入 -> tool call loop -> 输出
- 之前在推特上我就表达过,tool call loop 不是记忆,而是工作日志。agent-human 之间的交互记忆和 tool call loop 的细节几乎没有关联。

于是 compact 的方案也自然敲定了:以一个 turn 里的 tool call loop 为最小单位做 compact,形成一个 compact node。

那记忆的基本单位就成型了:一个 memory record 对应一个 turn,包含其原始工作 log 和 compact node

那既然不要 thread,每个 turn 之间怎么串联起来呢?我立即想到了 git,也就是一个 DAG。这个形状非常合理:连边表示时序和因果,一个 memory record 上接 parents 不就构成因果链了吗?

那 parents 怎么接上呢?我试过不同方案,比如就用 thread 强制串起来?但是 agent 如果自主去查询自己的记忆,不仅限于最近的记忆了,那不也构成因果了吗?最终我认为这里大可交给 agent 自己决定:完成任务后你自己告诉我你参考了哪些过去的记忆,我帮你把这个 DAG 结构落库就好了。

很好,写入逻辑敲定了,读出呢?首先最基本的近期记忆肯定是要的,然后 agent 要自主查询它可能感兴趣的过去的记忆、包括过去的具体工作日志。这时候我已经意识到基于文件系统做事很爽,我把 memory record 的存储落在文件系统里,然后写了一个小脚本。

这个脚本有三路召回,前两路是按 thread 和 user(Kapy 会识别不同的 human user)筛选召回最近的 memory records,其中 thread 这一路自然就起到基本的最近语境上下文的作用;第三路要依赖 agent 自己传进来的 keywords 参数,按 keywords 检索过去的记忆。

这一个脚本虽然是 agent 自己调用的,但其实是半固定注入:我要求 agent 在每一个 turn 的起始第一个 tool call 必须使用这个脚本。

有了这个基础记忆,agent 至少能理解语境了。接下来,agent 或许会想起来有价值的细节需要探查:比如看完 compact 还得查一下原始日志,或者出现了新的关键词要在过去的记忆里查一查。还记得 memory records 都落在文件系统上吗?你自己用 rg sed 之类的去查吧。记得最后把 referenced memories id 告诉我就行。

至此,Kapy 的记忆系统就完成了,当然中间我也或多或少地调整了设计,总之十数天的使用下来证明它 work 的非常好。

总而言之,这个系统的原则在于两种记忆模式的并存:一是固定式的注入的记忆,这个记忆是宽泛的,大体的,把握全局的,以 compact 信息为主的;二是 agent 自主的记忆查询,这个记忆是具体的、有明确目标的, agent 知道自己在寻找什么。

实现细节上,目前自然有点粗糙,但总之要符合上述两种模式:
- 对第一个模式要提供一个高级的 API,让 agent 从零上下文的状态下就搞明白我是谁我在哪我要干什么。这个高级 API 的参数要尽量少,能写死的就写死,调用也是强制或半强制的;
- 对第二个模式则要提供一个低级的 API。Agent 此时已经明确知道自己想要什么信息,要给它尽可能大的灵活性去找到它要的信息。

比如 Kapy 的第一记忆模式目前是三路检索,前面提到的 DAG 因果链还没用上呢;第二记忆模式基于文件系统,虽然够低级,但 rg 毕竟只是基于关键字,要是加上语义检索全文检索的能力,总归能给它多两只手。

说到这里其实可以引出一个新的问题:第二记忆模式、知识库、skills,都是 agent 利用一个工具从外部去查询,它们之间是什么关系,有什么区别,是统一的同一件东西吗?还是应该区分,有不同的索引结构?我也还没想好,留作后话。

( Nowledge Mem 从记忆的方法论的角度做了一些工作,我觉得很有价值)
一篇新的文章,希望可以帮大家了解从 coding 到生活,从 coding agent 到 openclaw 或者 bub ,背后的问题、范式和模型的一些变化

https://psiace.me/zh/posts/carpenter-hammer-nail/
魅族要倒闭了。

有魅族用户可以来帮忙存档吗?(项目群)
朋友圈和北语音乐剧社认识的朋友聊天,问她如果在成都法语联盟报班上课是否可以给她贡献一个安利指标过去
话赶话都说到这里了,立此为证吧。如果能找到理想的工作那今年一定好好学法语
去年整年都没再去过日本,我看今年干脆去法国见见朋友
#TIL
今天刷力扣才注意到官方推荐用 slices.SortFunction 替代 sort.Slice 函数,顺便还学了个 Strict Weak Ordering
https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings
你不需要为 AI 每日天翻地覆的变化感到担忧,这些东西虽然都是动态的,但是都围绕着一个事情在转:提好的问题。这才是所有人都应该每日努力打磨的能力。
AI native 的 API 设计要求

过去我们倾向于将 API 设计成高级的、抽象的,这是一种人体工学 UX,因为我们预期人类用户是偷懒的、喜欢用预设值的、目标导向的。高级 API 的缺陷也很明显,就是它失去了灵活性,复杂的参数组合被隐藏起来了。

但 AI 和人类不同,它习惯于将能填的参数填满,它多打几个 token 也不会手疼,人体工学 UX 对它来说没有意义。这意味着面向 AI 的 API 应当是有充分的可组合性的,足够做低级操作的。

两者的对比也可以描述 MCP 和 skills 的区别:前者是建立在 openapi 风格上的,其习惯是高级的;后者是建立在 shell 风格上的,其习惯是低级的。

其实 best practice 是没有变的:优秀的抽象,充分正交的参数和合理的默认值,这对 AI 和人类来说都是可以降低心智负担而保持灵活性的。

尤其是当我们考虑到,一套 API 可能既要被人类使用又要被 AI 使用。比如我在设计 agent memories 时,既要用代码强制注入一部分 memories,又要允许 agent 自主查询 memories。

Best practice 其实没有变,而是更重要了。
这个有意思,OpenAI 给 Responses API 添加了 websocket 模式。

主要是为了增量输入,长时间运行,多工具调用的流程。

这个模式我相信很快就会被广泛采纳的,这个要方便的多,尤其是多轮对话,频繁工具调用的场景,这个优势会被放大。

(但是如果是普通 chat,简单的一两轮对话就算了,还会带来 TTFT 的开销


https://developers.openai.com/api/docs/guides/websocket-mode/ WebSocket Mode | OpenAI API
辅导已经读到小学四年级的表妹学习,她们寒假作业要求是 3 分钟内口算 16 道题,其中不乏 252x28 这样有点难度的题目,我觉得不用纸纯口算这个时间对我来说都不一定很轻松
不过我发现她基础有点薄弱,随口问了一下 52-48 等于多少,她想了半天。之前辅导她的时候就觉得她之前的学年欠账有点严重了,现在这个情况不知道怎么办才好
很想拉她一把,但是我的口算都是靠的脑子里有一张大大的草稿纸,可以把上面那种题变成 250x28+56,1000x7+56。我不知道怎么样可以让她的脑子里也有这么大的草稿纸……总之现在回成都了,平时多训练她记数字吧

看她学得累了我就让她放松一会儿,然后把今天的力扣练了。结果我看了题解的思路以后还是写不对递归,就感觉挺好笑的。我也笨成这样,有什么资格说她不聪明呢……我觉得脑子里能清晰地勾画许多数字是理所当然的事情,对很多人来说,算法题也是如此吧
Back to Top