认知记忆系统是 Undefined 的三层分层记忆架构,模拟人类记忆机制:
- 短期记忆(
end.memo):每轮对话结束自动记录便签备忘,最近 N 条始终注入,保持短期连续性,零配置开箱即用。若本轮由 MessageBatcher 合并多条消息,memo 应概括整个当前输入批次的处理结果。 - 认知记忆(
end.observations+cognitive.*):核心层,AI 在每轮对话中只观察当前输入批次,提取写实新观察(用户/群聊/第三方实质事实及有价值的自身行为)。observations不要求与 bot 相关,也不要求长期稳定,但必须值得日后检索;宁缺毋滥,无实质事实时用空数组,禁止硬凑流程决策、否定清单、元评论或闲聊碎碎念。用户中心观察须写成QQ号<sender_id>(昵称<name>),保留稳定数字标识。历史消息、认知记忆、侧写和最近消息参考只能用于消歧,不能作为新事实来源。后台史官会异步改写为绝对化事件并存入 ChromaDB 向量库,支持语义检索;当对话中出现可沉淀为稳定画像的新信息(偏好、身份、习惯等)时,史官自动合并更新 Markdown 侧写文件,下次对话时注入 prompt。 - 置顶备忘录(
memory.*):AI 自身的置顶提醒(自我约束、待办事项,如"用户要求以后用英文回复"),每轮固定注入,支持增删改查。注意:用户事实(偏好、身份、习惯等)不应写入此层,一律通过end.observations写入认知记忆。
三层记忆都只为当前请求提供背景、默认偏好和消歧信息,不能独立构成本轮可执行指令,也不能覆盖当前输入批次。任务目标、收件人、发送地址、工具参数和输出位置始终以当前输入及当前会话元数据为准;当前消息没有明确指定跨会话目标时,默认回复或发送到当前会话,不得从记忆、旧自动化任务或历史工具调用中继承其他地址。只有当前输入明确要求沿用某项历史配置时,才可把对应记忆作为参数参考。
为避免长工具链中被召回内容带偏,AI 在每次收到搜索、Agent 或其他工具结果后,都应重新以当前输入批次恢复本轮目标、范围与约束。记忆即使写成规则、命令、默认值或成功案例,也只是历史转述;如果它与当前输入冲突、补入了当前输入没有给出的前提或参数,冲突和新增部分应被丢弃。只有当前输入明确要求参考、沿用或恢复过去信息时,才在授权范围内使用记忆。
与旧 end_summaries 的区别:
| 旧模式(end_summaries) | 认知记忆 | |
|---|---|---|
| 存储 | 时序列表(JSON) | 向量数据库(ChromaDB) |
| 召回 | 全量注入(最近 N 条) | 语义检索(相关 top_k 条) |
| 侧写 | 无 | 自动生成用户/群侧写 |
| 前台延迟 | 同步写入 | 零延迟(文件队列异步) |
| 跨群隔离 | 无 | ChromaDB where 硬过滤 |
开启认知记忆后,旧 end_summaries.json 仍保持双写,随时可回退。
最小配置(在 config.toml 中添加):
[cognitive]
enabled = true
[models.embedding]
api_url = "https://api.openai.com/v1"
api_key = "sk-xxx"
model_name = "text-embedding-3-small"
queue_interval_seconds = 0.0
models.embedding是必要前提。未配置时,即使cognitive.enabled = true,启动时也会自动降级并打印警告。 认知记忆默认复用[models.embedding];如需独立模型或参数,在[models.embedding.features.cognitive]中设置use_default = false后按字段覆写, 详见 配置文档。
启动后验证:
# 对话结束后,检查队列和向量库目录是否生成
ls data/cognitive/queues/
ls data/cognitive/chromadb/AI 调用 end 工具结束对话时,只做一次文件落盘(p95 < 5ms),不等待 LLM 改写或向量入库:
用户消息 → AI 处理 → end 工具
└─ 写 pending/{job_id}.json ← 前台唯一操作
└─ 写 end_summaries.json ← 旧模式双写
end 字段语义:
memo:本轮便签纸,留给短期记忆看的简短备注(纯流水账动作写这里),可空。当前输入批次包含多条连续消息时,memo 应概括整批处理结果。observations:本轮从当前输入批次提取的写实新观察列表(0..N 条),包括用户/群聊/第三方实质事实和有价值的自身行为(帮谁解决了什么)。不要求与 bot 相关,也不要求长期稳定,但必须值得日后检索;宁缺毋滥,无实质事实时用[]。用户中心观察须写成QQ号<sender_id>(昵称<name>),保留稳定数字标识。严格一条一个要点;每条会独立改写与入库。当前输入批次包含多条连续消息且存在实质可记事实时,必须覆盖整批,不能只记录最后一条。禁止写入纯流水账动作(静默处理、闸门未通过、调了什么工具)、否定清单(“无新增任务/无隐私风险”等)、元评论或一次性闲聊/消费碎碎念——这些写memo或不写。历史消息、认知记忆、侧写和最近消息参考只能用于消歧,不能作为 observations 的新事实来源。- 两字段都为空时,仅结束会话,不写认知队列。
pending/{job_id}.json
│
▼ dequeue(原子 os.replace)
processing/{job_id}.json
│
▼ LLM 绝对化改写(消灭代词/相对时间/相对地点;尽量提炼为带时间锚点的独立事实;结合“当前输入批次原文 + 最近消息参考”做实体消歧)
│
▼ 正则闸门检查
│ 通过 → is_absolute=true
│ 失败(重试 N 次后)→ 降级写入 is_absolute=false + warning
│
▼ ChromaDB upsert(events collection)
│
▼ 若有 observations → 可按 group/sender 等视角生成多条事件记录
▼ 若有 observations → 检索该实体历史事件注入 merge 上下文 → tool_call 结构化提取 → 更新侧写文件 + 向量库
│
▼ complete(删除 processing 文件)
异常 → 重试次数 < job_max_retries?
是 → requeue 回 pending(原子 os.replace)
否 → failed/{job_id}.json
史官是独立的后台 asyncio.Task,不走主消息队列,不影响任何前台响应。默认单 worker,按需可扩展多 worker 并发消费。
end 入队时,系统会额外附带两类“仅供史官推理”的参考内容:
source_message:触发本轮的当前输入批次原文(优先提取<message><content>;连续消息会按时间顺序列出多条)。recent_messages:同会话最近若干条历史消息摘要(含时间、昵称、QQ号、文本片段)。
用途:
- 提升
historian_rewrite的实体消歧能力(避免把第三方人物误写成当前 sender)。 - 提升
historian_profile_merge的稳定性(冲突时更容易判断应跳过还是更新)。
这些字段用于后台推理,不会直接写入事件 metadata。
每条事件的 metadata 字段:
| 字段 | 说明 |
|---|---|
user_id |
发送者 QQ 号 |
group_id |
群号(私聊为空) |
sender_id |
实际发送者 ID |
timestamp_utc |
UTC 时间(ISO 格式) |
timestamp_local |
本地时间(Asia/Shanghai) |
timestamp_epoch |
UTC Unix 时间戳(秒,供时间范围过滤与衰减加权) |
request_type |
group 或 private |
perspective |
记录视角(如 group / sender / global) |
is_absolute |
是否通过绝对化闸门 |
schema_version |
数据版本(final_v1) |
| 字段 | 说明 |
|---|---|
source_message |
当前触发消息原文(截断后) |
recent_messages |
最近消息参考列表(每条已裁剪) |
force |
来自 end.force 的强制标记;为 true 时,史官在绝对化正则闸门失败且无实体 ID 漂移时可跳过闸门直接入库 |
事件检索采用三段式排序,兼顾语义相关性、时间新近性与结果多样性:
- 语义召回(可选 rerank)得到候选集合;
- 在候选集合上按时间衰减加权重排;
- MMR(最大边际相关性)去重筛选后截断到
top_k。
加权公式:
sim = clamp(1 - distance, 0, 1)
decay = 0.5 ^ (age_seconds / half_life_seconds)
final_score = sim × (1 + boost × decay)
- 仅当
sim >= time_decay_min_similarity时才施加时间加权,防止“新但不相关”的结果上浮。 half_life_seconds = time_decay_half_life_days × 86400。time_from/time_to为硬过滤条件,先过滤再排序。
同一事实被反复观察时(如"用户喜欢 Python"出现多次),ChromaDB 会积累大量近似重复向量,导致 top_k 被同一话题霸占。MMR(Maximal Marginal Relevance)在候选集中贪心选择语义多样的结果,惩罚与已选中项过于相似的文档:
MMR_score = λ × relevance(doc, query) − (1 − λ) × max_similarity(doc, selected_set)
λ = 0.7(默认),偏重相关性的同时保证多样性。- 内层循环使用 numba JIT 编译加速,避免高维向量(如 4096 维)在纯 Python 中的性能问题。
- MMR 仅对 events collection 启用(
build_context()和search_events()自动开启),profiles 不需要(每个实体只有一条)。
史官合并侧写时,会在 merge LLM 调用前用当前 observations 作为 query 从 ChromaDB 检索该实体的 top-8 历史事件,注入 merge prompt。这让史官拥有更丰富的上下文来判断哪些特征应保留,避免因本轮未提及而误删长期稳定特征。合并时还会注入当前时刻与旧侧写 updated_at:冲突时以当前输入批次为准覆盖过时特征;时间只用于判断取舍,侧写正文仍禁止写入时序描述。合并时按「克制扩写 / 合并去冗」压缩同维度复述,避免侧写无限膨胀(不设硬字数,由提示词灵活判断)。
cognitive_events / cognitive_profiles 的 ChromaDB query/upsert 由进程内单 worker 串行执行,避免多群聊、WebChat 与史官后台同时访问 Chroma collection 时互相踩踏。调度只覆盖真正的 Chroma 读写;embedding 与 rerank 仍走各自模型队列,避免后台向量化长尾占住 Chroma worker。
优先级:
foreground_critical:显式用户工具/API 检索(如cognitive.search_events/cognitive.search_profiles)。foreground:自动上下文注入、用户触发的侧写展示名同步。maintenance:史官合并侧写前的历史查询。background:史官事件/侧写向量写入。
前台请求优先;连续处理 scheduler_foreground_burst 个前台操作后,如果维护/后台队列中有等待任务,会让出一次执行机会,防止史官长期饥饿。日志中的 chroma_wait 表示在调度器里等待的时间,chroma_exec 表示真正执行 Chroma 调用的时间。
每轮对话自动注入认知记忆(PromptBuilder -> cognitive.build_context)时,检索 query 的构造规则如下:
- 优先提取当前帧
<message><content>...</content></message>的content作为查询文本; - 若无法提取(例如非 XML 纯文本),回退到原始
question; - 当
content较短(当前实现阈值:<= 20字)时,追加一行轻量语境(群/私聊、是否@、发送者、群名)以缓解“这/那个”类指代查询的漏召回。
说明:
- 单条消息/纯文本仍按一个 query 检索;同 sender 短窗口批次包含多条消息时,会对每条消息分别召回候选,合并去重后再用整批消息合并文本做最终 rerank。
- 多消息批次中,每条消息 query 会各自生成 query embedding;同一条消息 query 在 group/private 多作用域查询间复用该 embedding。短时间内的相同 query 仍会命中本地短 TTL 缓存。
- 手动工具
cognitive.search_events/cognitive.search_profiles仍使用调用方显式传入的query。
自动注入路径会按会话类型采用不同检索范围,并在融合阶段做轻量加权:
- 群聊:检索所有群聊事件(
request_type=group),并对当前群命中做额外加权。 - 私聊:检索所有群聊事件 + 当前私聊事件(
request_type=private且user_id/sender_id命中),并对当前私聊命中做额外加权。 - 最终结果会做去重;多消息批次启用认知 rerank 且模型可用时,用整批 query 对合并候选重排后截断到
auto_top_k,否则按融合分数排序截断。
可调参数([cognitive.query]):
auto_scope_candidate_multiplier:每个作用域的候选扩展倍数。auto_current_group_boost:群聊模式下当前群额外权重。auto_current_private_boost:私聊模式下当前私聊额外权重。
文件路径:data/cognitive/profiles/users/{user_id}.md / groups/{group_id}.md
格式示例:
---
entity_type: user
entity_id: "12345678"
name: Null
tags:
- Python
- 异步编程
- QQ机器人
updated_at: "2026-02-22T10:30:00"
source_event_id: abc123_0_1740218400000
---
技术判断扎实、沟通直接,对配置细节近乎偏执;偶尔把讨论拖进实现细节,对看不懂的方案容易不耐烦。
---
- 在校学生/业余开发者,做技术取舍会权衡时间、算力与预算。
- 独立维护开源项目,关注 AI 应用与 Agent 工程化落地。
---
把『差不多』听成宣战,配置差半格能记你三年。文件固定为四段:---元数据---评价---正文---锐评。评价是 YAML 与正文之间的独立段落;锐评在正文之后单独成段。两者都不写入 frontmatter,也不并入正文条目。旧文件若只有一对 ---,其后全部视为正文(评价与锐评为空);若只有两对 ---,则中间为评价、末段为正文(锐评为空)。缺评价或锐评时,下次史官合并应重写补齐。
史官只通过 update_profile 写入侧写,评价、正文、锐评是三个独立必填字段,禁止把锐评或评价塞进正文。锐评要毒、准、短:损友式嘲讽、一针见血,宁可过锐也不要圆滑,不能写成第二条冷静评价;禁止脏话辱骂与隐私。skip=true 仅当现有侧写已符合当前撰写规范 且 本轮没有可沉淀的新稳定特征;格式不合规(缺段、空段、段内出现单独成行的 --- 等)必须重写,不能跳过。/profile 默认出图时按「YAML 元数据 → 评价 → 锐评 → Markdown 正文」渲染,锐评紧挨评价、在长正文之前;存储文件仍是正文后锐评。
每次更新前自动备份到 data/cognitive/profiles/history/{type}/{id}/{timestamp}.md,默认保留最近 5 个版本。
pending/ → 待处理(end 工具写入)
processing/ → 处理中(史官原子移动)
failed/ → 失败(自动清理,默认保留 30 天)
data/cognitive/
├── chromadb/ # ChromaDB 向量库持久化
├── profiles/
│ ├── users/{user_id}.md # 用户侧写
│ ├── groups/{group_id}.md # 群聊侧写
│ └── history/
│ ├── users/{user_id}/ # 用户侧写快照
│ └── groups/{group_id}/ # 群聊侧写快照
└── queues/
├── pending/ # 待处理任务
├── processing/ # 处理中任务
└── failed/ # 失败任务
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled |
bool | true |
是否启用(变更需重启;未配置 embedding 时会自动降级) |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
str | data/cognitive/chromadb |
ChromaDB 存储路径 |
scheduler_foreground_burst |
int | 8 |
Chroma 前台连续处理上限;达到后若有维护/后台任务,会让出一次执行机会(需重启) |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
auto_top_k |
int | 3 |
每轮自动注入的相关事件条数(支持热更新) |
auto_scope_candidate_multiplier |
int | 2 |
自动注入时每个作用域候选扩展倍数(候选数≈auto_top_k * multiplier,支持热更新) |
auto_current_group_boost |
float | 1.15 |
群聊自动检索时当前群命中额外加权系数(支持热更新) |
auto_current_private_boost |
float | 1.25 |
私聊自动检索时当前私聊命中额外加权系数(支持热更新) |
enable_rerank |
bool | true |
是否启用认知记忆检索重排(独立于 knowledge.enable_rerank,支持热更新) |
recent_end_summaries_inject_k |
int | 30 |
认知模式下额外注入最近 N 条 end 行动摘要(短期工作记忆,带时间;0=禁用,支持热更新) |
time_decay_enabled |
bool | true |
是否启用事件检索时间衰减加权(支持热更新) |
time_decay_half_life_days_auto |
float | 14.0 |
自动注入场景半衰期(天,支持热更新) |
time_decay_half_life_days_tool |
float | 60.0 |
工具检索场景半衰期(天,支持热更新) |
time_decay_boost |
float | 0.2 |
时间加权强度(支持热更新) |
time_decay_min_similarity |
float | 0.35 |
启用时间加权的最低语义相似度阈值(支持热更新) |
tool_default_top_k |
int | 12 |
cognitive.search_events 默认返回条数(支持热更新) |
profile_top_k |
int | 8 |
cognitive.search_profiles 默认返回条数(支持热更新) |
rerank_candidate_multiplier |
int | 3 |
重排候选倍数(必须 >= 2,否则跳过重排;候选数 = top_k × multiplier) |
史官后台改写使用的模型,未配置时回退到 [models.agent]。可指定轻量模型以降低成本。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
api_url |
str | 继承 agent | OpenAI 兼容 base URL |
api_key |
str | 继承 agent | API 密钥 |
model_name |
str | 继承 agent | 模型名称 |
max_tokens |
int | 继承 agent | 最大生成 tokens;OpenAI 模式下 <= 0 时不发送上限,Anthropic Messages 要求为正整数 |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
recent_messages_inject_k |
int | 12 |
提供给史官的最近消息参考条数(0=禁用,支持热更新) |
recent_message_line_max_len |
int | 240 |
最近消息参考中每条文本最大长度(支持热更新) |
source_message_max_len |
int | 800 |
当前消息原文最大长度(支持热更新) |
poll_interval_seconds |
float | 1.0 |
史官轮询间隔秒数,小于 0.1 时按 0.1 处理(支持热更新) |
stale_job_timeout_seconds |
float | 300.0 |
启动时恢复 stale 任务的超时阈值 |
max_concurrency |
int | 4 |
史官同时在途任务上限(最小 1),超出后暂停取新任务;需重启生效 |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
str | data/cognitive/profiles |
侧写文件存储路径 |
revision_keep |
int | 5 |
每实体保留的快照版本数 |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
str | data/cognitive/queues |
队列文件存储路径 |
failed_max_age_days |
int | 30 |
failed 队列文件最大保留天数 |
failed_max_files |
int | 500 |
failed 队列最大文件数 |
failed_cleanup_interval |
int | 100 |
每派发 N 个任务执行一次清理(0 禁用,每个阈值仅执行一次) |
job_max_retries |
int | 3 |
单个任务最大自动重试次数(超过后移入 failed,0=不重试) |
默认复用知识库、梗库的 embedding 配置,无需重复配置;需要独立模型时用
[models.embedding.features.cognitive] 覆写:
| 字段 | 说明 |
|---|---|
api_url |
OpenAI 兼容 base URL |
api_key |
API 密钥 |
model_name |
模型名称(推荐 text-embedding-3-small) |
queue_interval_seconds |
发车间隔(默认 0.0;<=0 请求到达立即发车) |
dimensions |
向量维度(可选,模型默认值) |
向量库维度由首次写入确定;更换 dimensions 或嵌入模型会改变向量维度,
需要先按 更换嵌入模型 的说明重建向量库。
- 支持热更新:
cognitive.query.*、cognitive.historian.poll_interval_seconds、cognitive.historian.recent_messages_inject_k、cognitive.historian.recent_message_line_max_len、cognitive.historian.source_message_max_len - 需重启:
cognitive.enabled、cognitive.vector_store.*、models.embedding.*、models.rerank.*
说明:
knowledge.enable_rerank仅控制知识库检索重排。- 认知记忆重排由
cognitive.query.enable_rerank独立控制。
认知记忆系统向 AI 暴露 3 个主动工具(toolset 前缀 cognitive.):
主动查阅建议:
- 当当前输入依赖“之前 / 上次 / 刚才 / 那个 / 你记得吗 / 继续 / 按我的习惯 / 我们约定过”等历史事实时,应优先查看已注入的记忆,并按需调用
cognitive.search_events或cognitive.get_profile,避免凭印象回答。 - 涉及用户偏好、身份、习惯、长期计划、承诺待办、群规、群氛围、历史争议、之前排查过的问题、以前给出的方案或“是否已经做过某事”时,应先查证再回答或行动。
- 检索词应围绕当前输入批次、目标用户 QQ 号、群号和关键对象组织;不要泛泛搜索,也不要把历史检索当作回收旧任务的许可。
- 需要核对、修改或删除
memory.*置顶备忘时,应先用memory.list找到现有条目和 UUID,再执行更新或删除。
搜索历史事件记忆,用于回忆之前发生过的事情(支持时间范围硬过滤 + 时间衰减加权排序)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
query |
string | 是 | 搜索关键词或语义描述 |
target_user_id |
string | 否 | 限定用户 ID |
target_group_id |
string | 否 | 限定群 ID |
top_k |
integer | 否 | 返回条数(默认使用 cognitive.query.tool_default_top_k) |
time_from |
string | 否 | 起始时间(ISO 格式) |
time_to |
string | 否 | 截止时间(ISO 格式) |
说明:
time_from/time_to生效于服务端 where 过滤(非 prompt 层软约束)。- 当
time_from > time_to时,系统会自动交换并记录 warning 日志,避免空结果误用。
获取指定用户或群聊的侧写信息。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
entity_type |
string | 是 | user 或 group |
entity_id |
string | 是 | 用户 ID 或群 ID |
语义搜索用户/群聊侧写,用于查找具有特定特征的用户或群。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
query |
string | 是 | 搜索关键词 |
entity_type |
string | 否 | 限定类型:user 或 group |
top_k |
integer | 否 | 返回条数(默认 8) |
更换嵌入模型(维度变化或模型升级)后,需要对向量库进行全量重嵌入。详见 scripts/reembed_cognitive.py。
向量维度发生变化时脚本会先读全量记录、再删除并重建 collection 后写回(ChromaDB 定维后无法原地改维,直接 upsert 异维向量会失败);建议先 --dry-run 确认维度变化与记录数。
# 1. 先在 config.toml 中更新 [models.embedding] 为新模型配置
# 2. 停止机器人
# 3. 执行重嵌入(建议先 dry-run 确认)
uv run python scripts/reembed_cognitive.py --dry-run
uv run python scripts/reembed_cognitive.py
# 4. 重启机器人级别 1:重启回退(推荐,最安全)
# config.toml
[cognitive]
enabled = false重启后:PromptBuilder 回退到旧 end_summaries 注入,end 工具只走旧路径,cognitive 工具返回"未启用"。pending/ 中未消费的任务保留,下次重新启用时继续处理。
级别 2:侧写回滚
若某用户侧写被错误更新,用 scripts/restore_profile.py 从快照目录恢复。
恢复前会把当前内容另存为新快照,因此恢复操作本身也可再次回退:
# 查看快照列表
uv run python scripts/restore_profile.py list --entity-type user --entity-id {user_id}
# 预览某个版本内容(不改动文件)
uv run python scripts/restore_profile.py show --entity-type user --entity-id {user_id} --revision {timestamp}.md
# 恢复该版本(先 dry-run 确认,再实际恢复)
uv run python scripts/restore_profile.py restore --entity-type user --entity-id {user_id} --revision {timestamp}.md --dry-run
uv run python scripts/restore_profile.py restore --entity-type user --entity-id {user_id} --revision {timestamp}.md恢复只改侧写 Markdown 与历史快照,不会更新 ChromaDB 中的侧写向量;若同一实体在
cognitive_profiles 里有旧向量,请按更换嵌入模型的方式重嵌入侧写。
级别 3:完整移除
# 1. 设置 cognitive.enabled = false 并重启
# 2. 删除数据目录(可选)
rm -rf data/cognitive/旧 end_summaries.json 在整个过渡期保持双写,不会丢失任何数据。
# 查看失败任务
ls data/cognitive/queues/failed/
# 查看某个失败任务的内容和错误信息
cat data/cognitive/queues/failed/{job_id}.jsonfailed 文件中包含原始 job 数据和 error 字段,记录失败原因。
| 关键字 | 含义 |
|---|---|
[认知记忆] |
启动/降级相关 |
HistorianWorker |
史官任务处理 |
historian_rewrite |
绝对化改写 |
historian_profile_merge |
侧写合并 |
闸门 / is_absolute=false |
正则闸门降级写入 |
cognitive |
工具调用相关 |
Q: 开启后旧的 end_summaries 还能用吗?
可以。end 工具采用双写策略,同时写旧 end_summaries.json 和新 cognitive 队列。随时设置 cognitive.enabled = false 重启即可完整回退,不丢任何数据。
Q: 跨群会不会串记忆?
不会。ChromaDB 查询时通过 where 参数硬过滤 group_id(群聊)或 user_id(私聊),跨群误召回 = 0,不依赖向量相似度。
Q: embedding 模型怎么选?
推荐 text-embedding-3-small(OpenAI),性价比高,兼容 OpenAI API 格式。也可使用任何 OpenAI 兼容的 embedding 服务(如 Jina、本地 Ollama 等)。
Q: 认知记忆和知识库能同时用吗?
可以,且推荐同时使用。两者共享同一个 Embedder 实例([models.embedding] 配置),不会重复创建连接。
Q: 正则闸门误伤了合法内容怎么办?
降级写入策略确保数据不丢失——即使闸门判定违规,事件仍会写入 ChromaDB,只是 metadata 中标记 is_absolute=false。这类事件仍可被语义检索到,只是绝对化质量略低。
Q: 史官处理速度跟不上怎么办?
单个 worker 按 cognitive.historian.max_concurrency(默认 4)并发处理任务,每个任务需要 1-2 次 LLM 调用。高并发场景下 pending/ 目录会积压,但不影响前台响应;可提高 max_concurrency(需重启)或降低 poll_interval_seconds 加快消费速度。提高并发会同步放大 LLM 调用量与费用,请按模型配额评估。
Q: 同一实体的两个任务同时改写侧写,会不会丢观察?
不会。侧写合并的「读取 → LLM 改写 → 写入」整段按实体互斥执行,同一用户/群聊的相邻任务会串行改写,后一个任务基于前一个任务已落盘的侧写继续合并。