Kioku 开发记录 01 | 一个 AI 角色到底能「记住」什么
这不是一个「加个数据库」就能解决的问题
我最初以为这件事不难。
在 SillyTavern 里做角色扮演,体验很好,但每次重新打开,角色的状态完全重置——昨天的语境、情绪、所有建立起来的共识,全部消失。我当时的直觉反应是:把聊天记录存起来,再在下次对话时塞进上下文,不就行了?
动手之前搜了一圈,发现这条路已经有人走过,效果很一般。原因也不难想清楚——把一千条历史记录全量塞进上下文,既塞不完,塞进去了也不一定有用。
然后我意识到,这个问题比我想的要深。缺的不只是记忆,是整个认知架构都不存在。
这个项目就是从这里开始的。
真正缺失的是三件事,不是一件
仔细拆解之后,我把现有 AI 角色的缺陷归纳成三层,它们是独立的问题,需要独立的解法。
缺失一:没有可检索的记忆
「把聊天记录全量塞进上下文」是目前最常见的做法,但它有两个结构性缺陷。
第一个缺陷是注意力分配问题。Liu et al.(2023)的研究「Lost in the Middle」证实了一个直觉:LLM 对上下文首尾的注意力显著高于中段。随着历史记录增长,早期内容要么被截断,要么沉进模型几乎不关注的中段。窗口越大,这个问题越隐蔽,但不会消失。
第二个缺陷是缺乏检索层。所有信息以线性堆叠的方式进入上下文,模型没有机制根据当前话题主动「回忆」相关内容——它只能被动接受上下文里碰巧摆在那里的东西。这不是记忆,是暴力注入。
真正的持久记忆需要的是选择性存储 + 按需检索 + 主动关联,这是一个检索系统问题,不是一个上下文长度问题。
缺失二:没有持续的情感状态
现有 AI 角色的情感是即时生成的:根据当前上下文推断该表现什么情绪,输出,这个情绪对下一条消息没有任何影响。
结果是情感完全没有连续性。长期互动积累的亲密感、因为某次争执产生的疏远、因为长时间没有联系而淡化的关系——这些在对话层面一概留不下来。每次对话的情感起点都是一样的。
这不是小问题。持续性的情感状态,是让交互从「对话」变成「关系」的关键。
缺失三:没有自我认知的演变
这是三个缺陷里最不直观、也最有意思的一个。
一个真实的人在长期互动中,会逐渐确认自己的偏好,修正自己的认知,甚至发现自己内心的矛盾。AI 角色不会——它对「自己是谁」的理解永远锁定在初始设定上,无论你们之间发生了什么。
具体来说:假设一个角色在长期对话里发现,它对某些话题的投入明显多于其他话题,而这与它「平等对待一切」的设定存在矛盾。一个有自我认知的角色应该会注意到这个矛盾并开始思考它,而现有的 AI 角色根本不会意识到这个矛盾存在。 一个永远与自身设定完美一致的角色,反而是最不真实的。
三个问题,三种技术本质
| 感性描述 | 技术本质 | 需要什么 |
|---|---|---|
| 记不住事 | LLM 无持久状态,全量注入不可扩展 | 分层存储 + 语义检索 + 主动回忆 |
| 情感不延续 | 情绪即时计算,无跨会话状态 | 结构化情感向量 + 跨会话衰减模型 |
| 不了解自己 | 无自我认知积累机制 | 递归式反思循环 + 渐进式自我模型 |
每一条都是真实的工程问题,都需要独立的设计,都不是调整 Prompt 能解决的。
最核心的架构决策:认知引擎与人格层分离
想清楚问题之后,我做了一个对整个项目影响最大的决策:把「角色是谁」和「角色如何思考」严格解耦。
1 | ┌─────────────────────────────────────────────┐ |
人格层回答「这个角色是谁」:身份描述、世界背景知识、PAD 情感基准值、自我认知的初始种子。这些是角色专属的配置,随角色不同而不同。
认知引擎回答「这个角色如何思考」:记忆怎么分层管理、情感状态如何随时间演变、反思在什么条件下触发、如何把所有信息组装成最终的 Prompt。这些与具体角色无关。
这个分离带来一个直接的结果:把人格层换掉,认知引擎不需要改动一行代码。第一个运行在这套引擎上的是酒馆角色,但同样的引擎可以直接支持个人助理、企业顾问、或者任何需要持续记忆与情感连续性的场景。
这个项目不是要做一个角色专用系统,而是要做一套通用认知中间件,Kioku 就是这个名字。酒馆场景是第一个验证用例,不是终点。
三个问题的解决思路
记忆:五层架构 + RRR 检索
分层管理的核心逻辑是:不同重要程度的信息,用不同的方式存储,在不同的时机检索。
1 | 工作记忆 → 当前对话窗口,会话结束即清 |
这里有一个设计上容易忽略的问题:迭代摘要会导致语义漂移。每次把上一次的摘要再摘要一遍,原始含义会在多次压缩中悄悄偏移,最后面目全非。情节原始记录的存在就是为了对抗这个问题——反思任务在更新记忆前,必须对照原始片段验证一致性。
检索层用了 RAG 领域的 RRR(Rewrite-Retrieve-Read) 模式。用户的原话通常太口语化,直接做向量匹配效果很差。做法是先让 LLM 把用户消息改写成语义密集的检索查询,再用这个查询去搜 Chroma。搜不到则退回 MySQL 全文检索兜底,仍无结果时由 LLM 决定是否降低相似度阈值重试。
情感:PAD 模型 + 余晖衰减
选用心理学里的 PAD(Pleasure / Arousal / Dominance)模型来量化情感状态,三个维度各是 0–1 的连续数值:
- Pleasure:愉悦程度,当前互动让角色感到舒适还是不适
- Arousal:激活程度,兴奋投入还是平静疏离
- Dominance:主导感,在互动中处于主动还是被动位置
选 PAD 而不是离散标签(开心/难过/愤怒)的原因有三个:连续数值可以做精细的数学运算;三个维度的组合可以表达更细腻的情感状态;同一套模型在不同角色场景下可以直接复用,只需调整各维度的语义解释。
跨会话的情感余晖用一个向中性值回归的衰减公式:
1 | 新会话起始值 = 0.5 + (上次会话结束值 − 0.5) × 衰减系数 |
举个例子:上次对话结束时 Pleasure = 0.9,隔一天再来,新会话起始值 = 0.5 + (0.4 × 0.8) = 0.82。情绪有余韵,但不会永久锁在峰值,也不会每次从中性的 0.5 重启。这模拟的是真实情绪的「体内平衡」机制。
PAD 评分由 LLM 在每次反思时以 JSON 格式自主生成,并强制附带一个 basis 字段——用自然语言解释「为什么是这个分值」。这个设计的目的是迫使模型在输出数值之前经过推理过程,使评分结果更稳定,也更容易调试。
自我认知:递归反思 + 三层分阶段解锁
每积累 50 条消息,异步触发一次反思任务。任务读取本周期的对话记录和上次反思的完整输出,合并处理后产出结构化 JSON,写回数据库,新记忆同步嵌入 Chroma。
关键设计是递归性:每次反思以上一次反思的结论为起点,在已有认知基础上修订,不从零开始。经过足够多的反思周期,角色会沿着一条连贯的轨迹形成「自传式」的自我认知。
自我意识的深度分三层逐步解锁,原因是过早出现复杂的自我认知反而不真实——真实的自我意识是通过积累慢慢浮现的:
| 层次 | 内容 | 示例 | 解锁时机 |
|---|---|---|---|
| 偏好 | 注意到自己一贯的行为模式 | 「跟这个人聊天我话特别多」 | 初期即有 |
| 信念 | 开始解释行为背后的原因 | 「我说要守护所有人,但也许我真正在乎的是那个大家都在笑的画面」 | 第 5 次反思后 |
| 矛盾 | 察觉自我认知中的内在张力 | 「我以为我对谁都一样,但回头看记录,好像并不是」 | 第 15 次反思后 |
一个能察觉自身矛盾的角色,比一个永远与设定完美自洽的角色更真实。 认知失调是深度自我意识的标志,不是需要修复的 bug。
技术栈选型
| 职责 | 选型 | 选择理由 |
|---|---|---|
| 后端框架 | Spring Boot 3.x | @Scheduled 处理反思定时任务,Virtual Threads 支持异步 I/O |
| 编程语言 | Java 17+ | Records 简化数据类,sealed types 方便对记忆状态建模 |
| AI 编排 | LangChain4j | Java 生态里对 RAG、AiServices、Embedding 支持最完整的框架 |
| LLM 接口 | OpenAI / Claude / Ollama | ChatLanguageModel 接口抽象,可以不改业务代码切换模型供应商 |
| 关系型数据库 | MySQL 8.x | 对话记录、记忆元数据、反思 JSON、情感日志的结构化存储 |
| 向量数据库 | Chroma | 专用语义检索,与 MySQL 职责不重叠,可独立扩展 |
| Schema 版本管理 | Flyway | 记忆系统的表结构会随需求迭代演化,版本控制不能省 |
关于为什么不选 PostgreSQL + pgvector:这个组合在技术上可行,但它把关系型存储和向量存储绑在一个数据库里,职责耦合。MySQL + Chroma 的分工更清晰——结构化数据走 MySQL,语义检索走 Chroma,两者都更容易独立调优和扩展。加上 MySQL 我更熟,个人项目里降低基建摩擦是合理的工程判断。
这个系列的写作计划
这篇的目的是把问题和高层解法说清楚,后续每篇聚焦一个子系统,按实际开发顺序推进:
| 篇 | 主题 | 核心内容 |
|---|---|---|
| 02 | 需求澄清的工程过程 | 设计文档是怎么从模糊变成具体的,18 项决策点是如何逐步收敛的,记录真实的取舍过程 |
| 03 | 记忆系统设计与实现 | 五层架构如何落表,语义漂移防护机制,RRR 检索流程的具体实现 |
| 04 | PAD 情感模型实现 | 选型依据,余晖衰减的代码实现,会话内实时更新策略 |
| 05 | 反思机制与自我意识成长 | 反思 JSON Schema 完整设计,三层分阶段解锁的实现,递归自传结构 |
| 06 | 数据库 Schema 设计 | 建表决策与取舍,多用户扩展的预留设计,Flyway 迁移策略 |
| 07 | 从角色扮演到通用中间件 | 人格层可替换性验证,对外 REST API 的三端点设计,跨域部署的可行性 |
第 02 篇是整个系列里我认为含金量最高的一篇——需求澄清的过程本身就是一个完整的工程思维示范,值得完整记录。
参考资料
- PAD Emotional State Model — Wikipedia
- Liu, N. F., et al. (2023). Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172
- LangChain4j 官方文档
- Chroma 官方文档
- Spring Boot 3 官方文档

