这不是一个「加个数据库」就能解决的问题

我最初以为这件事不难。

在 SillyTavern 里做角色扮演,体验很好,但每次重新打开,角色的状态完全重置——昨天的语境、情绪、所有建立起来的共识,全部消失。我当时的直觉反应是:把聊天记录存起来,再在下次对话时塞进上下文,不就行了?

动手之前搜了一圈,发现这条路已经有人走过,效果很一般。原因也不难想清楚——把一千条历史记录全量塞进上下文,既塞不完,塞进去了也不一定有用。

然后我意识到,这个问题比我想的要深。缺的不只是记忆,是整个认知架构都不存在。

这个项目就是从这里开始的。


真正缺失的是三件事,不是一件

仔细拆解之后,我把现有 AI 角色的缺陷归纳成三层,它们是独立的问题,需要独立的解法。

缺失一:没有可检索的记忆

「把聊天记录全量塞进上下文」是目前最常见的做法,但它有两个结构性缺陷。

第一个缺陷是注意力分配问题。Liu et al.(2023)的研究「Lost in the Middle」证实了一个直觉:LLM 对上下文首尾的注意力显著高于中段。随着历史记录增长,早期内容要么被截断,要么沉进模型几乎不关注的中段。窗口越大,这个问题越隐蔽,但不会消失。

第二个缺陷是缺乏检索层。所有信息以线性堆叠的方式进入上下文,模型没有机制根据当前话题主动「回忆」相关内容——它只能被动接受上下文里碰巧摆在那里的东西。这不是记忆,是暴力注入。

真正的持久记忆需要的是选择性存储 + 按需检索 + 主动关联,这是一个检索系统问题,不是一个上下文长度问题。

缺失二:没有持续的情感状态

现有 AI 角色的情感是即时生成的:根据当前上下文推断该表现什么情绪,输出,这个情绪对下一条消息没有任何影响。

结果是情感完全没有连续性。长期互动积累的亲密感、因为某次争执产生的疏远、因为长时间没有联系而淡化的关系——这些在对话层面一概留不下来。每次对话的情感起点都是一样的。

这不是小问题。持续性的情感状态,是让交互从「对话」变成「关系」的关键。

缺失三:没有自我认知的演变

这是三个缺陷里最不直观、也最有意思的一个。

一个真实的人在长期互动中,会逐渐确认自己的偏好,修正自己的认知,甚至发现自己内心的矛盾。AI 角色不会——它对「自己是谁」的理解永远锁定在初始设定上,无论你们之间发生了什么。

具体来说:假设一个角色在长期对话里发现,它对某些话题的投入明显多于其他话题,而这与它「平等对待一切」的设定存在矛盾。一个有自我认知的角色应该会注意到这个矛盾并开始思考它,而现有的 AI 角色根本不会意识到这个矛盾存在。 一个永远与自身设定完美一致的角色,反而是最不真实的。


三个问题,三种技术本质

感性描述 技术本质 需要什么
记不住事 LLM 无持久状态,全量注入不可扩展 分层存储 + 语义检索 + 主动回忆
情感不延续 情绪即时计算,无跨会话状态 结构化情感向量 + 跨会话衰减模型
不了解自己 无自我认知积累机制 递归式反思循环 + 渐进式自我模型

每一条都是真实的工程问题,都需要独立的设计,都不是调整 Prompt 能解决的。


最核心的架构决策:认知引擎与人格层分离

想清楚问题之后,我做了一个对整个项目影响最大的决策:把「角色是谁」和「角色如何思考」严格解耦

1
2
3
4
5
6
7
8
9
┌─────────────────────────────────────────────┐
│ 人格层(可替换) │
│ 角色身份 · 世界背景 · 情感基准值 · 初始自我 │
└─────────────────────────────────────────────┘
↕ 唯一的变化点
┌─────────────────────────────────────────────┐
│ 认知引擎(完全领域无关) │
│ 记忆系统 · 情感模型 · 反思循环 · 上下文组装 │
└─────────────────────────────────────────────┘

人格层回答「这个角色是谁」:身份描述、世界背景知识、PAD 情感基准值、自我认知的初始种子。这些是角色专属的配置,随角色不同而不同。

认知引擎回答「这个角色如何思考」:记忆怎么分层管理、情感状态如何随时间演变、反思在什么条件下触发、如何把所有信息组装成最终的 Prompt。这些与具体角色无关。

这个分离带来一个直接的结果:把人格层换掉,认知引擎不需要改动一行代码。第一个运行在这套引擎上的是酒馆角色,但同样的引擎可以直接支持个人助理、企业顾问、或者任何需要持续记忆与情感连续性的场景。

这个项目不是要做一个角色专用系统,而是要做一套通用认知中间件,Kioku 就是这个名字。酒馆场景是第一个验证用例,不是终点。


三个问题的解决思路

记忆:五层架构 + RRR 检索

分层管理的核心逻辑是:不同重要程度的信息,用不同的方式存储,在不同的时机检索

1
2
3
4
5
工作记忆     →  当前对话窗口,会话结束即清
短期记忆 → 完整对话记录,MySQL,按策略归档
长期记忆 → 提炼后的关键事实,向量化存入 Chroma,语义可搜
情节原始记录 → 原始对话片段,MySQL,作为反思的真实性锚点
反思产物 → 结构化 JSON,MySQL,永久保留

这里有一个设计上容易忽略的问题:迭代摘要会导致语义漂移。每次把上一次的摘要再摘要一遍,原始含义会在多次压缩中悄悄偏移,最后面目全非。情节原始记录的存在就是为了对抗这个问题——反思任务在更新记忆前,必须对照原始片段验证一致性。

检索层用了 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 篇是整个系列里我认为含金量最高的一篇——需求澄清的过程本身就是一个完整的工程思维示范,值得完整记录。


参考资料