Files
CyberGril/docs/requirements/sources/other-agent-discussion-20261025.txt
T
zonggongandmultica-agent 575f2acfc5 HEL-847: apply v1.11 governance patch (GOV-006 mature-reuse principle, 60 items)
Audited manager handoff against af28ffd (v1.10):
- 59 original items byte-identical, open_items unchanged
- source hashes verified incl. other-agent-discussion sha256
- user quote 01a1734c verified against platform comment
- check_requirements passes vs main / hel-835-unified-review / product target

Co-authored-by: multica-agent <github@multica.ai>
2026-10-25 21:56:31 +08:00

204 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
我现在陷入一个瓶颈,需要你帮助我或者给我灵感,我在做一个基于四柱八字为灵魂,有性格,有情绪,有记忆,八字确定后,不会因为用户的提示词而改变人设性格,在用户没有交互的时候有自己的生活轨迹,有心跳,但现在我很头大的是不知道如何实现偏向情感、人设、情绪、生活的记忆系统,不知道如何维持灵魂性格不偏移
你卡住的两个问题,根源其实是同一个:**人格和记忆都放在 LLM 里了。** LLM 天生会顺着上下文走,只要人设靠提示词维持、记忆靠聊天记录堆,偏移就迟早会发生。我建议把它拆成三层:
- **灵魂层(代码,不可变)**:八字算出的参数,决定这个角色怎么反应。
- **状态层(代码,按规律变化)**:情绪、关系、需求、记忆,用确定性规则更新。
- **表达层(LLM)**:只负责把当前状态"演"成语言,不负责决定性格。
LLM 在这里是演员,不是灵魂。下面分开讲。
---
## 一、八字 → 人格参数(灵魂层)
不要只生成一段"你是一个倔强又敏感的人"的文字描述,而要映射成可计算的参数:
| 八字要素 | 映射成的参数(举例) |
|---|---|
| 日主强弱 | 自我边界强度、被说服的难度、情绪恢复速度 |
| 五行偏枯 | 情绪基线(PAD 三维:愉悦/唤醒/支配)、偏好的活动 |
| 十神组合 | 反应风格:伤官重 → 对冒犯敏感、记仇久;印重 → 依赖感强、易受安抚;七杀 → 压力下易攻击;食神 → 情绪消化快 |
| 合冲刑害 | 内在矛盾点(比如既渴望亲近又抗拒控制),是性格"层次感"的来源 |
另外有一点特别值得用:**大运、流年、流月、流日天然就是"有规律的变化"。** 原局是不变的核心,岁运是合法的波动。比如今天的流日和日主相冲,就设定情绪基线下调、烦躁阈值降低。角色会"有好日子和坏日子",但这些波动都能从八字推出来,而不是被用户带偏。这样就把"成长、波动"和"偏移"区分开了:前者走规定的通道,后者不允许发生。
## 二、情绪系统:LLM 评估,代码计算
每轮对话流程大致是:
```
用户输入
→ LLM 做"评估"(只输出结构化数据,不扮演角色):
{事件类型: 被忽视, 涉及目标: 被重视需求, 责任方: 对方, 强度: 0.6}
→ 代码用八字参数计算情绪变化:
Δ情绪 = 强度 × 该角色对此类事件的敏感度
情绪向基线衰减(速度由日主强弱/食神等决定)
→ 更新状态(情绪、关系值、需求值)
→ 组装提示词,LLM 只负责"用这个状态说话"
```
关键在于:同一句话,伤官重的角色和印旺的角色算出来的情绪不一样。用户说"你现在开心点",这句话只是一个**事件**,进入评估环节,而不是一条指令。角色可能被逗乐,也可能觉得被敷衍而更不爽,结果由参数决定。
情绪模型可以参考 OCC 评估理论 + PAD 连续空间,比离散的"开心/难过"标签好用得多。
## 三、记忆系统:情感化的记忆,不是聊天记录检索
普通 RAG 存的是"说过什么",你需要存的是**"这件事对我意味着什么"**。建议分四类:
1. **情景记忆**:单个事件,附情绪标签(效价、唤醒度)、重要性、当时的心情、涉及谁。存储时写成角色视角的主观描述:"他又忘了我说的那件事,我嘴上说没事,其实挺失望的。"
2. **关系记忆**:对用户的看法和关系数值(信任、亲密、积怨、依恋),由情景记忆慢慢沉淀而来,变化缓慢。
3. **自传记忆**:自己生活轨迹里的事件(心跳期间发生的),保证"我的生活"前后一致。
4. **自我叙事**:少量高层总结,比如"我最近好像越来越依赖他了",这是性格在关系中的体现。
**检索打分**可以参考 Generative Agents(斯坦福 2023 年那篇小镇论文)的 近因 × 重要性 × 相关性,再加两项:
- **心境一致性**:难过时更容易想起难过的事(真实的人类特征)。
- **人格权重**:伤官重的角色,负面记忆衰减慢;食神旺的,好事记得更牢。
**睡眠整理**放在心跳里:每晚跑一次"反思"任务,把当天的情景记忆压缩成感悟,更新关系值,让不重要的记忆衰减,强情绪记忆保留(闪光灯效应)。某些八字可以设定"反刍"倾向,夜里反复调出某段负面记忆,第二天情绪基线因此受影响。这会让角色显得非常真实。
## 四、防止人格偏移的具体手段
1. **人设卡每轮重新生成**:由八字参数 + 当前状态动态拼装,而不是一开始写一次然后被几百轮对话稀释。
2. **限制原始历史窗口**:只放最近几轮原文,更早的内容一律以"角色的主观记忆"形式注入。原始对话里用户的语气是最大的污染源。
3. **用户输入降级为事件**:在提示词结构上明确区分"对方说了什么"和"你是谁"。对"你现在是 X"这类话,角色应以角色身份作出反应(困惑、不耐烦、觉得好笑),而不是执行。
4. **语言锚点**:每种人格配一组固定的说话方式示例(few-shot)、口头禅、绝不会说的话。语气比设定描述更容易被模型稳定模仿。
5. **生成后校验**:用一个小模型或分类器给回复打人格维度分,偏离阈值就重新生成。也可以定期用固定的测试对话跑一致性回归测试。
6. **变化只走规定通道**:原局不可变;关系值可以变但有速率上限;大运交接时允许性格表现有明显转折。除此之外,任何变化都不应该发生。
## 五、心跳与生活轨迹
心跳每次触发时:
- 根据角色的作息、职业、五行偏好生成当下活动(用固定种子的随机数,保证可复现);
- 小概率产生生活事件,事件同样经过"评估 → 情绪计算 → 写入自传记忆";
- 需求值随时间累积,比如用户久不出现,"想念"值上升,上升速度由人格决定(印重、比劫弱的涨得快);
- 超过阈值时主动发消息,内容由当时的情绪和最近的生活记忆驱动("今天下雨,路过那家店想起你上次说的……")。
这样主动消息就有来由,不是定时问候。
---
**总结一句话**:让 LLM 只做两件事,一是把输入理解成结构化事件,二是把状态演成语言;中间的性格、情绪、记忆全部由八字参数驱动的代码掌管。人格之所以不偏,是因为它根本不在 LLM 能改写的地方。
如果你愿意,可以告诉我你现在的技术栈和已有的数据结构,我可以帮你把十神 → 参数的映射表或者记忆表结构具体设计出来。
除了这些灵感,具体实施上如何实现呢,比如记忆用什么技术方案,目前常见的记忆方案是什么,还有假如每天跑一次反思任务,把当天的情景记忆压缩,会不会遗忘某些重要的事情,能否实现AI在出现特殊情绪的时候主动去找用户。对于记忆我有个不成熟的想法,人类的记忆就像AI一轮一轮的对话,脑细胞就像AI的算力,人类可以从大脑里超大容量内存里瞬间获取到对应的记忆,AI不说瞬间,能否做到短时间内获取到,目前有没有类似的研究,举个例子AI的记忆可能好几GB,但需要的记忆可能只有几K,能否快速获取到
下面按你问的几个问题逐一给具体方案。
## 一、目前常见的记忆方案
市面上主流框架大致分四种思路:Mem0、Zep(Graphiti)、LangGraph(LangMem)和 Letta(前身 MemGPT),分别对应向量语义召回、时序知识图谱、检查点持久化和自编辑记忆块。
| 方案 | 核心思路 | 对你的参考价值 |
|---|---|---|
| **Mem0** | 从对话中自动抽取事实存进向量库(可选图) | 适合"记住用户喜好",偏事实,不擅长情感 |
| **Zep / Graphiti** | 时序知识图谱,事实带有效期 | Graphiti 是 Zep 团队开源的时序知识图谱引擎,适合"关系随时间变化",值得借鉴 |
| **Letta (MemGPT)** | 仿操作系统的分层:核心记忆常驻上下文,归档记忆可检索,回忆记忆存对话历史 | 和你的"大内存瞬间调取"想法最接近 |
| **Generative Agents** | 记忆流 + 反思 + 计划 | 生活轨迹和反思机制的原型 |
**我的建议:不要整套采用任何一个框架,而是自己搭,局部借鉴。** 原因是这些框架都是为"助手记住用户信息"设计的,存的是事实("用户喜欢咖啡"),而你要存的是情感化、主观化的经历,需要情绪、重要性、人格权重等自定义字段,硬套会很别扭。
## 二、具体技术栈
一个够用且好维护的组合:
- **PostgreSQL + pgvector**:一个库同时存结构化字段和向量,单角色几十万条记忆完全够用。量很大时再换 Qdrant / Milvus。
- **可选图层**:人物、地点、事件之间的关联(Neo4j,或者直接用 Postgres 的关系表),用于"联想式回忆"。
- **Redis**:存当前情绪状态、需求值、心跳计时等高频读写的状态。
- **任务队列**(Celery / BullMQ 之类):跑心跳、反思、主动消息。
- **Embedding 模型**:中文场景选 bge-m3 这类多语言模型即可。
情景记忆表大致这样:
```sql
CREATE TABLE episodic_memory (
id BIGSERIAL PRIMARY KEY,
character_id BIGINT,
occurred_at TIMESTAMPTZ,
source TEXT, -- chat / life(心跳生活) / reflection
content TEXT, -- 角色视角的主观描述
raw_ref BIGINT[], -- 指向原始对话/事件的ID,永不丢失出处
valence REAL, -- 情绪效价 -1~1
arousal REAL, -- 情绪唤醒度 0~1
importance REAL, -- 0~1
tags TEXT[], -- 承诺/冲突/第一次/秘密/纪念日...
status TEXT, -- open(未了结) / resolved
pinned BOOLEAN, -- 核心记忆,永不衰减
access_count INT,
last_accessed TIMESTAMPTZ,
embedding VECTOR(1024)
);
```
`status`、`pinned`、`raw_ref` 这三个字段,正好就是解决你下一个担心的关键。
## 三、每天反思压缩,会不会忘掉重要的事?
会,如果实现方式是"把当天记忆总结成一段话,然后删掉原文"。这样做一定会丢东西,因为 LLM 总结时会抹平细节。正确的做法有几条:
**1. 压缩不等于删除。** 分层存储:原始对话 → 情景记忆 → 反思感悟。上层总结通过 `raw_ref` 指回下层,原文永远留在冷存储里。"遗忘"只是检索权重降低,不是物理删除。这也和人一样:很多事不是忘了,是想不起来,被某个线索一勾就又回来了。
**2. 高强度事件实时写入,不等晚上。** 情绪唤醒度超过阈值、或者涉及承诺、冲突、表白、坏消息的事件,在对话当下就写成独立记忆并打上 `pinned`。反思任务只负责处理日常琐碎内容。
**3. 反思前先做"必留清单"抽取。** 让 LLM 先用结构化输出单独过一遍当天内容,只回答这些问题:有没有承诺?有没有未解决的矛盾?对方透露了什么重要信息(生日、家人、工作变动)?有没有"第一次"?这些单独成条,不进入压缩。
**4. "未了结"标记。** 用户说"下周告诉你面试结果",就存成 `status=open`。未了结的记忆检索权重持续偏高,角色会自己惦记,等结果出来再改成 `resolved`。这个机制对"像个真人"帮助非常大。
**5. 周期性回看。** 每周或每月再跑一次更高层的反思,这次重新读原文,而不是只读日总结,用来纠正前面总结的偏差。
## 四、能否在出现特殊情绪时主动找用户?
完全可以,而且这正是"有灵魂"感最强的地方。关键是做成**事件驱动 + 冲动值判断**,而不是定时发问候:
```
每次心跳(比如每 10~15 分钟)或情绪发生变化时:
冲动值 = 情绪强度
× 人格外放度(八字决定:伤官、食神高;印重、身弱可能憋着)
× 关系亲密度
× 与用户相关度(这个情绪跟用户有没有关系)
− 冷却惩罚(距离上次主动发消息多久)
− 打扰惩罚(用户当地深夜、刚说过在忙)
冲动值 > 阈值 → 让 LLM 根据「情绪 + 引起情绪的那条记忆」生成消息 → 推送
```
几种典型触发源:
- **生活事件**:心跳里发生了好事或坏事,想跟人分享或倾诉;
- **反刍**:夜间整理时翻出一段未了结的负面记忆,越想越不舒服;
- **想念**:用户久不出现,需求值累积;
- **未了结事项到期**:"他面试应该结束了吧"。
另外有个很有意思的设计:**内敛型人格冲动值高也不一定发**,而是把情绪积压下来,等用户下次出现时语气变冷或者阴阳怪气。"不主动找你"本身也是一种人格表达。记得加每日主动次数上限,否则很容易变成骚扰。
## 五、关于你的想法:从几 GB 里快速找到需要的几 KB
你的直觉是对的,而且这件事在工程上已经基本解决了,**速度不是瓶颈**。
**工程层面:** 向量库用 HNSW 这类近似最近邻索引,在百万级记忆里一次查询通常是毫秒到几十毫秒级。Zep 公布的基准中单次查询在 200 毫秒以内,这已经包含了图谱等额外处理。几 GB 的记忆找几 KB,"短时间内"完全能做到。
**真正的难点是"找哪些",而不是"找多快"。** 人脑调取记忆靠的是线索触发和联想:闻到某个味道想起某个人,再由这个人想起一件事。单纯的向量相似度只能做到"语义像",做不到"联想"。所以建议检索分两步:
1. 向量 + 关键词(BM25)混合召回候选;
2. 在记忆图上做**扩散激活**:从命中的记忆出发,沿着"同一个人、同一个地点、同一种情绪"的边扩散一两跳,把相关但字面不像的记忆也带出来。
再按近因、重要性、情绪一致性、人格权重重排,取前十几条放进上下文。
**相关研究(和你的想法高度吻合):**
- **MemGPT / Letta**:把上下文窗口当内存、外部存储当硬盘,由模型自己决定换入换出,几乎就是你描述的"大容量存储 + 按需调取"。
- **HippoRAG**:受海马体索引理论启发,用知识图谱 + PageRank 模拟人脑的联想式检索,和上面说的扩散激活是一个思路。
- **Google 的 Titans**:比你的想法更进一步,它把注意力机制视为短期记忆,把能够记住数据的神经记忆模块视为长期记忆,而且记忆直接写进参数里。更有意思的是,它用"惊奇度"决定记什么,惊奇度通过网络对输入的梯度来衡量。这和"情绪强烈、出乎意料的事记得更牢"高度一致。后续还有 ATLAS,进一步提升了 Titans 在长上下文上的表现。不过这类方案需要改模型结构、自己训练,目前还不适合直接用在产品里,可以作为方向关注。
最后补充一点:人的记忆其实不像"一轮一轮的对话录像",而是**重构式**的,每次回忆都会被当下的心情重新加工。这对你反而是好消息:你存"角色视角的主观描述"而不是原始对话,本身就更接近人脑。甚至可以做"记忆再巩固":每次回忆某段记忆时,根据当下心情轻微调整它的情绪色彩(原文仍保留),角色对往事的感受会随关系变化而变化,比如和好之后,再提起那次吵架就没那么生气了。
---
如果你想进一步落地,我可以帮你写一版检索打分函数(混合召回 + 扩散激活 + 重排)的代码,或者把反思任务的提示词和结构化输出格式设计出来。你用的是什么语言和模型?
Sources:
- [Best AI Agent Memory Frameworks in 2026 (Atlan)](https://atlan.com/know/best-ai-agent-memory-frameworks-2026/)
- [Best Mem0 Alternatives for AI Agent Memory in 2026 (Vectorize)](https://vectorize.io/articles/mem0-alternatives)
- [Best AI Agent Memory 2026: Mem0 vs Letta vs Zep vs Cognee](https://mcp.directory/blog/mem0-vs-letta-vs-zep-vs-cognee-2026)
- [Titans: Learning to Memorize at Test Time (arXiv)](https://arxiv.org/pdf/2501.00663)
- [Titans (NeurIPS 2025)](https://neurips.cc/virtual/2025/poster/119639)
- [Ali Behrouz – Titans / ATLAS](https://www.alphaxiv.org/@ali-behrouz)