Skip to content

Latest commit

 

History

History
547 lines (388 loc) · 28.6 KB

File metadata and controls

547 lines (388 loc) · 28.6 KB

认知记忆系统

概述

认知记忆系统是 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。


数据存储

事件记忆(ChromaDB events collection)

每条事件的 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)

史官任务载荷中的参考字段(非 events metadata)

字段 说明
source_message 当前触发消息原文(截断后)
recent_messages 最近消息参考列表(每条已裁剪)
force 来自 end.force 的强制标记;为 true 时,史官在绝对化正则闸门失败且无实体 ID 漂移时可跳过闸门直接入库

检索排序策略(events)

事件检索采用三段式排序,兼顾语义相关性、时间新近性与结果多样性:

  1. 语义召回(可选 rerank)得到候选集合;
  2. 在候选集合上按时间衰减加权重排;
  3. 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 为硬过滤条件,先过滤再排序。

MMR 去重

同一事实被反复观察时(如"用户喜欢 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:冲突时以当前输入批次为准覆盖过时特征;时间只用于判断取舍,侧写正文仍禁止写入时序描述。合并时按「克制扩写 / 合并去冗」压缩同维度复述,避免侧写无限膨胀(不设硬字数,由提示词灵活判断)。

ChromaDB 前后台调度

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 调用的时间。

自动注入场景的 Query 构造

每轮对话自动注入认知记忆(PromptBuilder -> cognitive.build_context)时,检索 query 的构造规则如下:

  1. 优先提取当前帧 <message><content>...</content></message> 的 content 作为查询文本;
  2. 若无法提取(例如非 XML 纯文本),回退到原始 question;
  3. 当 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:私聊模式下当前私聊额外权重。

用户/群侧写(Markdown + YAML Frontmatter)

文件路径: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/                  # 失败任务

配置参考

[cognitive] 配置项

字段 类型 默认值 说明
enabled bool true 是否启用(变更需重启;未配置 embedding 时会自动降级)

[cognitive.vector_store]

字段 类型 默认值 说明
path str data/cognitive/chromadb ChromaDB 存储路径
scheduler_foreground_burst int 8 Chroma 前台连续处理上限;达到后若有维护/后台任务,会让出一次执行机会(需重启)

[cognitive.query]

字段 类型 默认值 说明
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.historian](可选)

史官后台改写使用的模型,未配置时回退到 [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 要求为正整数

[cognitive.historian]

字段 类型 默认值 说明
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),超出后暂停取新任务;需重启生效

[cognitive.profile]

字段 类型 默认值 说明
path str data/cognitive/profiles 侧写文件存储路径
revision_keep int 5 每实体保留的快照版本数

[cognitive.queue]

字段 类型 默认值 说明
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=不重试)

[models.embedding](必须配置)

默认复用知识库、梗库的 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 工具

认知记忆系统向 AI 暴露 3 个主动工具(toolset 前缀 cognitive.):

主动查阅建议:

  • 当当前输入依赖“之前 / 上次 / 刚才 / 那个 / 你记得吗 / 继续 / 按我的习惯 / 我们约定过”等历史事实时,应优先查看已注入的记忆,并按需调用 cognitive.search_events 或 cognitive.get_profile,避免凭印象回答。
  • 涉及用户偏好、身份、习惯、长期计划、承诺待办、群规、群氛围、历史争议、之前排查过的问题、以前给出的方案或“是否已经做过某事”时,应先查证再回答或行动。
  • 检索词应围绕当前输入批次、目标用户 QQ 号、群号和关键对象组织;不要泛泛搜索,也不要把历史检索当作回收旧任务的许可。
  • 需要核对、修改或删除 memory.* 置顶备忘时,应先用 memory.list 找到现有条目和 UUID,再执行更新或删除。

cognitive.search_events

搜索历史事件记忆,用于回忆之前发生过的事情(支持时间范围硬过滤 + 时间衰减加权排序)。

参数 类型 必填 说明
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 日志,避免空结果误用。

cognitive.get_profile

获取指定用户或群聊的侧写信息。

参数 类型 必填 说明
entity_type string 是 user 或 group
entity_id string 是 用户 ID 或群 ID

cognitive.search_profiles

语义搜索用户/群聊侧写,用于查找具有特定特征的用户或群。

参数 类型 必填 说明
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 在整个过渡期保持双写,不会丢失任何数据。

failed 队列排查

# 查看失败任务
ls data/cognitive/queues/failed/

# 查看某个失败任务的内容和错误信息
cat data/cognitive/queues/failed/{job_id}.json

failed 文件中包含原始 job 数据和 error 字段,记录失败原因。

日志关键字

关键字 含义
[认知记忆] 启动/降级相关
HistorianWorker 史官任务处理
historian_rewrite 绝对化改写
historian_profile_merge 侧写合并
闸门 / is_absolute=false 正则闸门降级写入
cognitive 工具调用相关

FAQ

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 改写 → 写入」整段按实体互斥执行,同一用户/群聊的相邻任务会串行改写,后一个任务基于前一个任务已落盘的侧写继续合并。