# 私聊拟人性增强分阶段计划 ## 总体目标 - 在尽量保留当前 maibot 主干能力与最近更新的前提下,增强私聊中的连续感、活人感与主动性。 - 优先修复“私聊对象信息容易遗忘”“聊天断开后像失忆”“上下文窗口过于保守”这三类核心问题。 - 再逐步增加“定时跟进”“主动私聊”“关系阶段推进”等能力。 - 落地时坚持“平行新增、契约尽量不变”: - 不破坏现有 `reply` 语义 - 新能力优先通过新工具、新配置、新任务表、新 metadata 增量扩展 - 尽量不改现有 WebUI 聊天消息结构与事件结构 ## 借鉴来源 ### 1. `MoFox-Core` - 主要借鉴: - 私聊对象关系信息常态注入 - 主动前护栏判断 - `WAITING / IDLE` 状态机思路 - 发完后等待回复、超时再判断是否继续发言的活人感逻辑 - 不直接照搬: - AFC / KFC 全套 runtime - 强耦合插件系统 - 其整套数据库结构 ### 2. `Muice-Chatbot` - 主要借鉴: - 轻量主动开场/问候思路 - 随机主动话题池思路 - 不直接照搬: - 到点直接发送固定文本 - 过于依赖固定 prompt 的主动对话实现 ## 当前问题 - 私聊对话者信息经常会被忘记,应该被常态化注入。 - 当前上下文管理偏保守,没充分发挥大上下文模型的能力。 - 私聊 runtime 重建后不会自动回灌最近历史,聊天断开一阵子后容易出现“失忆”。 - 人物信息、关系信息、共同经历主要依赖临时检索,缺少稳定默认上下文。 - planner 查到的信息不能稳定传递到 replyer,导致回复阶段再次遗忘。 ## 当前准备增加的能力 - 定时跟进机制:允许 LLM 在当前私聊中创建未来跟进任务,到点后重新唤醒 planner 判断要不要主动开口。 - 主动私聊发送工具:新增不依赖 `msg_id` 的 `send_private_message`,作为 `replyer` 的新发送出口,让主动聊天不再伪装成回复旧消息。 - 到点前护栏机制:加入静默时段、最小主动间隔、可选每日上限,避免技术上能发但体验上打扰。 - 阶段以及主线机制:允许你在私聊中像玩 galgame 一样,和小麦进行逐步推进的互动。 - 为后续私聊状态机预留扩展:后面再补“发完等回复、超时再判断”的活人感机制。 ## 兼容性原则 ### 前后端契约 - 现有 `reply` 工具保持原语义、原参数、原发送形态不变。 - 新增 `send_private_message` 与 `schedule_private_followup` 时,采用与现有 builtin tool 平行的注册、执行、记录方式。 - 现有聊天消息 `WsMessage / ChatMessage / MessageSegment` 契约尽量不变: - 主动私聊发出的消息仍然是普通 bot 可见消息 - 不新增专用 `MessageSegment.type` 作为前提 - 现有工具记录结构尽量不变: - 继续复用现有 tool record / tool result 链路 - 新增内容尽量放在 `tool_name`、`tool_data`、metadata 中 ### 配置与 WebUI - 配置面优先平行新增独立配置块,默认值完整,保证旧配置不改也能运行。 - WebUI V1 不应成为主阻塞项: - 新功能优先复用现有聊天渲染与工具渲染 - 后续再考虑做任务管理页、状态展示页等增强 UI --- ## 阶段 0:开发期稳定化 ### 本阶段要解决的问题 - 当前准备进入连续多阶段改造,如果开发过程中误触自动更新,容易导致本体与 WebUI 版本漂移。 - 一旦前后端版本不一致,后续上下文、工具、调度器相关改造会很难排障。 - private-main 开发期间需要一个尽量稳定、可复现的运行基线。 ### 本阶段要增加/修改的点 - `☐` 断开 WebUI 自动更新检测 - `☐` 断开本体自动更新检测 - `☐` 避免开发期自动拉取或自动提示更新 - `☐` 保留显式手动恢复/更新路径 ### 如何做 - 找到当前 WebUI 与本体的更新检查入口: - 启动时自动检查 - 定时轮询检查 - 页面内自动提示 - 开发期优先做“禁用自动检查”,而不是修改版本号或伪造远端结果。 - 尽量采用可逆方案: - 配置开关 - 环境变量 - 显式开发模式判断 - 如果本体和 WebUI 的更新逻辑是分开的,也应分别断开,避免只关一边。 ### 借鉴来源 - 不依赖 `MoFox-Core` 或 `Muice-Chatbot`。 - 这是当前 private-main 连续改造前的工程防护阶段。 ### 验收标准 - 启动本体时,不再主动发起自动更新检查。 - 启动 WebUI 时,不再自动弹更新提示或自动轮询更新状态。 - 本体日志和 WebUI 调试输出中,不再出现自动更新任务被触发的痕迹。 - 若后续需要更新,仍存在显式、可控、可手动触发的更新路径。 --- ## 阶段 1:修复私聊遗忘问题 ### 本阶段要解决的问题 - 私聊对象信息经常被忘记。 - 聊天断开后再回来,像从空白状态重新开始。 - planner 查到的信息不稳定,replyer 容易再次遗忘。 ### 本阶段要增加/修改的点 - `☐` 私聊对象信息常态注入 - `☐` runtime 重建时自动回灌最近历史 - `☐` planner 到 replyer 的信息继承增强 - `☐` 上下文选择策略从“纯按条数裁切”调整为“更适合大上下文模型”的策略 ### 本阶段目标预算 - 以“大上下文真正可用,但不盲目塞满”为原则,先把私聊 planner 的目标输入预算定在 **256K token 级别**。 - 不建议把整整 256K 都让历史消息吃满,应预留系统提示、工具定义、注入块、输出空间。 - 第一版建议按下面的思路分配: - 总目标预算:`256K` - 预留给系统提示 / 工具定义 / injected reminders / 输出余量:`32K ~ 64K` - 可用于“历史消息 + 人物信息块 + 共同事件块”的主预算:`160K ~ 220K` - replyer 阶段不必与 planner 一样大,可以显著更小: - 优先依赖 planner 已整理出的参考信息 - replyer 可先控制在 `32K ~ 64K` 级别 ### 本阶段要先定下的几个原则 - **历史回灌** 不是滑动窗口本身,而是滑动窗口的前置步骤。 - **token 滑窗** 不应只做“超了就 pop”,而应做“固定高优先级块保底 + 历史候选池按 token 预算裁切”。 - **人物信息块、当前约定、最近共同事件** 的优先级应高于普通旧聊天。 - **planner 与 replyer 的上下文策略不必完全一样**: - planner 负责大窗理解 - replyer 负责在较小上下文内稳定生成 - **必须保留降级路径**: - 即使 tokenizer 暂时不可用,也能退回近似估算模式 - 但默认目标是本地 token 预算裁切,而不是继续硬按条数 ### 如何做 - 为当前私聊补一个轻量信息聚合入口: - 优先从现有 `person_info` - 再从 A_memorix 的人物画像/关系证据中补充 - 统一格式化为稳定上下文块,默认注入 planner - 在 runtime 重建时,按 `session_id` 从消息库回灌最近一段真实聊天历史,而不是从空 `_chat_history` 开始。 - replyer 生成阶段不再过度依赖临时工具结果,而是显式承接 planner 已整理出的稳定参考信息。 - 调整上下文窗口策略: - 不再只用固定条数近似控制 - 引入更大的私聊历史回放范围 - 优先保留“人物关系/最近共同事件/当前约定”这类高价值上下文 ### 1.1 runtime 历史重建 - 触发时机: - 私聊 runtime 首次创建时 - 私聊 runtime 被回收后再次恢复时 - 定时跟进任务唤醒私聊 runtime 时 - 基本流程: 1. 根据 `session_id` 查询最近一段真实消息 2. 将消息重新构造成 Maisaka 使用的 `LLMContextMessage` 3. 写回 `_chat_history` 4. 再叠加本轮新收到的消息与 injected reminders - 第一版建议: - 先取最近 `200 ~ 1000` 条私聊真实消息作为“候选历史池” - 不是全部送给模型,而是交给后续 token 滑窗裁切 - 注意: - 历史重建应优先复用现有消息入库格式和 `SessionBackedMessage` 构建逻辑 - 不要单独造一套“历史专用消息对象” ### 1.2 本地 tokenizer / token 预算器 - 当前项目里已有 token 使用统计,但那是模型返回 usage 后的结果,不足以做请求前选窗。 - 因此本阶段需要新增一个“请求前 token 估算器”抽象层。 - 目标不是一开始追求 100% 精确,而是先把“按 token 预算裁切”跑通。 - 建议方案: - 新增统一的 `context_token_counter` / `prompt_token_estimator` - 输入为即将送给模型的消息列表或其序列化结果 - 输出为估算 token 数 - tokenizer 选择建议: - 优先选择与当前 OpenAI-compatible 请求栈兼容的本地 tokenizer 方案 - 若误差验证可接受,可直接作为私聊上下文预算器 - 若误差过大,再考虑换成更贴近目标模型的 tokenizer - 降级方案: - tokenizer 初始化失败时,可暂时退回“字符数 / 中文字数近似预算” - 但只作为 fallback,不作为主路径 ### 1.3 token 滑动窗口 - 目标不是“把 256K 塞满”,而是“尽量稳定地用到 256K 级别能力”。 - 建议采用“两层预算”: - 第一层:固定保底块 - 第二层:历史滑窗块 - 固定保底块建议包含: - 系统提示 - 当前私聊对象信息块 - 当前约定 / 当前跟进原因 - 最近共同事件摘要 - 必要的工具定义 - 历史滑窗块建议逻辑: 1. 从最近消息开始向前累加 2. 每加入一条,重新累加 token 3. 超预算则停止 4. 保证最近几轮真实对话优先进入窗口 - 不建议简单地“全拼后从头一直 pop 到合法”作为唯一策略。 - 更推荐: - 先固定高优先级块 - 再对普通历史做从近到远的预算填充 ### 1.4 高优先级上下文保底 - 下面这些信息不应与普通旧聊天放在同一优先级: - 当前私聊对象是谁 - 你和对方目前是什么关系 - 近期共同事件 - 最近承诺/约定 - 当前定时跟进原因 - 这些内容即使在历史极长的情况下,也应尽量保底进入窗口。 - 这部分内容应从: - `person_info` - A_memorix 的人物画像 / 关系证据 / episode - 当前会话内最近承诺 统一汇总出来 ### 1.5 planner → replyer 信息继承 - 目前 replyer 容易再次遗忘,核心原因之一是它并不稳定承接 planner 已经查到和整理过的信息。 - 第一版建议: - planner 输出时显式生成“稳定参考信息块” - replyer 直接消费这块,而不是只依赖再看一遍历史 - 这样即使 replyer 窗口比 planner 小很多,也能保持人物信息连续性。 ### 1.6 配置建议 - 本阶段建议新增但保持默认兼容的配置项,例如: - `chat.private_context_rebuild_enabled` - `chat.private_context_rebuild_recent_limit` - `chat.private_context_token_budget` - `chat.private_context_reserved_tokens` - `chat.private_context_inject_relation_info` - `chat.private_context_recent_events_limit` - `chat.replyer_context_token_budget` - 默认值应尽量保守,避免对现有用户造成突然的延迟或成本上升。 ### 借鉴来源 - `MoFox-Core` - 借鉴其“关系信息 + 印象 + 偏好 +聊天流印象”统一拼装进私聊上下文的思路 - `Muice-Chatbot` - 基本不借鉴实现,只参考“最近历史 + 旧记忆回注入”的轻量骨架 ### 验收标准 - 同一个私聊会话在沉默一段时间后再次收到消息,bot 不应明显忘记对方是谁。 - 重建后的 runtime 能带上最近一段真实聊天历史,而不是只看最后一条消息。 - 私聊中不需要用户重复提醒,bot 也能较稳定记住: - 对方称呼 - 近期正在聊的事 - 最近约定/承诺 - 在日志或调试信息里,能看到“重建历史条数”“对象信息注入块”的明确痕迹。 - 能在日志或调试信息里看到: - 候选历史条数 - 最终入窗条数 - 估算 token 数 - 被预算裁掉的原因 - 在高上下文测试下,planner 能稳定工作在 `256K` 级别预算附近,而不是仍然被 60 条历史限制住。 - replyer 即使使用更小窗口,也不应明显丢失 planner 已确认的人物信息。 --- ## 阶段 2:定时跟进 V1 ### 本阶段要解决的问题 - 当前私聊没有“我之后再来找你”的能力。 - 即使用户和 bot 约好了未来某个时间点,系统也无法主动在那时重新思考。 - 需要主动发言时,只能强行伪装成 `reply(msg_id=...)`,不自然。 - 如果直接让 planner 裸写 `message_text` 并发送,会让主动私聊和普通回复走成两套文案体系。 ### 本阶段要增加/修改的点 - `☐` 新 builtin tool:`schedule_private_followup` - `☐` 新 builtin tool:`send_private_message` - `☐` 私聊定时跟进任务表 - `☐` 后台调度器 - `☐` runtime 的“带 reminder 唤醒 planner”入口 ### 如何做 - `schedule_private_followup` - 只允许在当前私聊里创建未来任务 - 记录触发时间、跟进原因、当时承诺话术 - 后台调度器 - 轮询 `pending` 任务 - 到点后 claim 任务并唤醒当前私聊 runtime - runtime 新入口 - 以 `` 形式注入“这不是用户新消息,而是一次到点跟进触发” - planner 基于当前上下文重新判断要不要发 - `send_private_message` - 不直接裸发 `message_text` - 而是作为 `replyer` 的新发送语义出口 - 复用现有人设、表达习惯、replyer 文案生成链路 - 最终仍复用现有 `send_service` / Platform IO / 写库 / 历史同步 ### 借鉴来源 - `MoFox-Core` - 借鉴“主动思考不是直接发消息,而是先重进上下文再判断”的理念 - `Muice-Chatbot` - 明确不采用其“到点直接发固定文本”的路径 - 当前 maibot 架构 - 延续“planner 负责想、replyer 负责写”的职责分离,不让主动私聊成为例外 ### 验收标准 - 私聊中可以成功创建一条未来跟进任务。 - 到点后系统会唤醒 planner,而不是直接发写死文本。 - planner 可以根据当前情况选择: - 主动发一条消息 - 什么都不发,直接结束 - 再次约下一次跟进 - 主动发出的消息是普通 bot 消息,不带旧消息引用。 - 主动发出的文本风格应与普通 `reply()` 路径保持一致,不应明显像另一套文案系统。 - 发送成功后仍会进入: - 现有消息存储 - Maisaka 历史同步 - memory automation ### `send_private_message` 设计细化 #### 工具职责 - 在当前私聊会话中主动发出一条可见消息。 - 不依赖 `msg_id`。 - 不默认带引用回复。 - 语义上是“主动开口”,但文案生成仍应走 `replyer`,而不是绕开 replyer 直接裸发。 #### 参数设计建议 - 不建议把主参数设计成裸 `message_text`。 - 更推荐让 planner 提供“为什么说”,让 replyer 负责“具体怎么说”。 - 第一版建议参数: - `proactive_reason` - 类型:`string` - 含义:这次主动发言的直接理由,作为 replyer 的“最新推理” - `reference_info` - 类型:`string` - 含义:本轮主动发言依赖的事实性参考信息 - `trigger_source` - 类型:`string` - 含义:触发来源,例如 `scheduled_followup` - `assistant_commitment_text` - 类型:`string` - 含义:若这次主动发言是在延续先前承诺,可作为风格一致性参考 #### 实现建议 - `send_private_message` 应复用当前 `reply()` 已有的 replyer 生成链路。 - 推荐执行路径: 1. planner 调用 `send_private_message` 2. tool 内部获取当前会话 replyer 3. 以“主动发言场景”调用 `generate_reply_with_context(...)` 4. 此时 `reply_message=None` 5. `reply_reason=proactive_reason` 6. `reference_info` 中整理注入 `reference_info + assistant_commitment_text + trigger_source` 7. replyer 生成文本后,再复用现有发送链路发出 #### 返回建议 - 成功时至少返回: - `session_id` - `generated_message_text` - `sent_message_id` - `maisaka_source_kind` - 失败时返回明确原因: - 非私聊 - replyer 生成失败 - 发送失败 --- ## 阶段 3:主动私聊护栏与轻量主动能力 ### 本阶段要解决的问题 - 即使有定时跟进,系统也缺少“主动前的工程护栏”。 - 后续如果要做更常态化的主动私聊,没有最小打扰控制会很容易打扰用户。 ### 本阶段要增加/修改的点 - `☐` 静默时段 - `☐` 最小主动间隔 - `☐` 可选每日上限 - `☐` skip reason 与 guardrail snapshot - `☐` 轻量主动开场/问候 fallback ### 如何做 - 在“任务到期”与“真正唤醒 planner”之间插入规则层: - 私聊校验 - 状态校验 - 静默时段判断 - 最小主动间隔判断 - 可选每日上限判断 - 对被跳过的任务记录: - `skip_reason` - `guardrail_snapshot_json` - 为未来“非约定型主动私聊”预留轻量主动开场能力: - 轻问候 - 轻话题冒泡 - 但此时仍不做完整主动聊天系统 ### 借鉴来源 - `MoFox-Core` - 重点借鉴其主动思考调度器中的工程护栏思路 - `Muice-Chatbot` - 借鉴其轻量问候/轻话题池思路,仅作为 fallback ### 验收标准 - 明显不适合打扰的时段,任务会被安全跳过或延后,而不是强行唤醒 planner。 - 日志中能明确看到“为什么没发”的 skip reason。 - 即使以后加入更强主动私聊,这一层规则也能直接复用。 --- ## 阶段 4:私聊状态机活人感增强 ### 本阶段要解决的问题 - 当前主动私聊即使能发,也缺少“发完之后等你回复”的真实感。 - 系统无法区分“及时回”“晚回”“一直没回”。 ### 本阶段要增加/修改的点 - `☐` `WAITING / IDLE` 私聊状态机 - `☐` 主动发送后的等待态 - `☐` 超时后再判断是否继续等、追问或闭嘴 - `☐` 连续超时计数与打扰保护 ### 如何做 - 在阶段 2 的基础上,扩展主动发送后的状态记录: - 最近一次主动发送时间 - 是否正在等待回复 - 等待配置 - 连续超时计数 - 收到用户消息时区分: - `reply_in_time` - `reply_late` - `new_message` - 等待超时后,再重新进行一轮轻量判断: - 继续等 - 轻追问 - 结束等待,不再打扰 ### 借鉴来源 - `MoFox-Core` - 这是最核心的借鉴来源,重点是其 `WAITING / IDLE` 与超时后再思考的设计 - `Muice-Chatbot` - 基本不借鉴 ### 验收标准 - 主动发言后,系统能进入等待态,而不是把这件事完全忘掉。 - 用户及时回复和很久后才回复时,系统的反应能出现可区分差异。 - 多次未回复后,系统会自动降低继续打扰的倾向。 --- ## 阶段 5:关系阶段与主线推进 ### 本阶段要解决的问题 - 当前即使记住了人、能主动聊,也还没有真正的关系推进结构。 - 缺少“朋友 → 更亲密阶段”的可持续演进逻辑。 ### 本阶段要增加/修改的点 - `☐` 关系阶段状态 - `☐` 阶段推进条件 - `☐` 近期主线事件/共同事件摘要 - `☐` 更适合私聊恋爱化推进的上下文注入 ### 如何做 - 在现有 `person_info` / A_memorix 证据层之上,增加一层更明确的关系状态表达。 - 阶段推进仍然尽量保持轻规则,不急着做全硬编码 romance engine。 - 优先做: - 阶段状态 - 最近共同事件摘要 - 对阶段敏感的 prompt 注入 - 后续再看是否要加入更强规则。 ### 借鉴来源 - `MoFox-Core` - 借鉴其“关系分数 + 关系阶段 + 印象文案”的分层思路 - 不直接采用其当前阶段命名作为最终产品逻辑 - `Muice-Chatbot` - 不足以支撑这一阶段 ### 验收标准 - 私聊中的称呼、关心方式、主动发言方式,会随关系阶段出现可感知变化。 - 最近共同事件能够被作为关系推进依据,而不是每次都从零开始。 - 系统在长期私聊中呈现出更稳定的关系连续性,而不是只有短期记忆连续性。 --- ## 配置与 WebUI 影响评估 ### 配置面 - 配置面预计是“小改”,不是大改。 - 建议新增独立配置块,例如: - `private_followup.enabled` - `private_followup.scheduler_interval_seconds` - `private_followup.quiet_hours_start` - `private_followup.quiet_hours_end` - `private_followup.min_interval_between_proactive_seconds` - `private_followup.max_daily_followups_per_session` - `private_followup.inject_relation_info` - `private_followup.recent_shared_events_limit` - 原则: - 默认值完整 - 老配置不动也能跑 - 优先改模板并递增版本号 ### WebUI 面 - V1 到 V3 原则上不需要大改聊天协议。 - 聊天渲染层: - `send_private_message` 发送的仍然是普通 bot 消息 - 不要求新增 `MessageSegment.type` - 工具渲染层: - 新工具复用现有 tool record 展示链路 - 前端最多看到新的 `tool_name` - 可选增强: - 定时跟进任务列表 - 任务取消/重试管理页 - 主动护栏状态可视化 - 私聊状态机状态展示 ## 分阶段验收顺序建议 - 第 0 阶段先稳定开发环境,避免自动更新打断改造过程。 - 第一阶段先解决遗忘与历史重建,不急着上主动聊天。 - 第二阶段打通定时跟进 V1。 - 第三阶段补主动护栏与轻量主动能力。 - 第四阶段再做 `WAITING / IDLE` 状态机。 - 第五阶段最后做关系阶段与主线推进。 ## 当前最推荐的开发顺序 1. 断开 WebUI / 本体自动更新检测 2. 私聊对象信息常态注入 3. runtime 历史重建 4. planner → replyer 信息继承增强 5. `schedule_private_followup` 6. `send_private_message` 7. `send_private_message` 的 replyer 驱动实现 8. 后台调度器与 runtime 唤醒入口 9. 到点前护栏 10. `WAITING / IDLE` 扩展 11. 阶段/主线推进