# 企微客服记忆与上下文架构 ## 1. 目标 客服回复不能把无限增长的 Claude Code Session 当作客户长期记忆。本方案借鉴 Hermes Agent 的分层记忆、按需召回、冻结核心快照和 Session 搜索思想,同时保持企微客户数据本地优先、证据可追溯和人工审核边界。 设计目标: - Workbench DB 是客户事实与消息的唯一权威来源; - Claude Code Session 只保存短期推理轨迹和审阅记录; - 每轮上下文有明确容量上限,不随客户生命周期无限增长; - 明确事实与模型推断分开保存; - 所有客户记忆必须能追溯到原始消息; - 记忆故障不阻塞草稿生成和人工处理。 ## 2. 分层模型 | 层级 | 数据源 | 生命周期 | 注入方式 | | --- | --- | --- | --- | | 企业人格与规则 | `knowledge/*.md`、运行时 system prompt | 项目级 | 每轮固定注入 | | 结构化业务状态 | profiles、tasks、alerts、recommendations | 客户级 | 每轮注入当前状态 | | 核心长期记忆 | `customer_memory_items` / snapshots | 客户级 | 限长快照注入 | | 情景记忆 | `messages` | 客户级完整历史 | 相关时按需召回 | | 工作记忆 | 最近消息 | 当前任务 | 近期窗口注入 | | Session 轨迹 | Claude Code JSONL | 单个 Epoch | 仅用于推理连续性和审阅 | 项目级固定上下文由 `QIWEI_AGENT_CONTEXT_FILES` 控制,默认加载 `personality.md`、`context.md`、`rules.md`,总字符数受 `QIWEI_AGENT_CONTEXT_CHAR_LIMIT` 限制。上下文文件可使用独占一行的 `@context relative.md#标题` 引用同一知识库内的片段;解析基于已索引 Markdown,不允许越过知识库目录读取任意文件。 ## 3. 记忆类型和证据 `customer_memory_items.type`: - `fact`:客户明确表达且可核验的事实; - `preference`:明确偏好,例如“更喜欢地铁附近”; - `constraint`:明确排除项,例如“不考虑顶楼”; - `event`:具有后续价值的历史事件; - `hypothesis`:模型推断,只能用于询问确认,不能作为已知事实。 每条记忆包含 `source_message_ids_json`、`confidence`、`importance`、`status`、`direction` 和 `created_by`。当前自动写入仅接受: 1. 现有 grounding 规则确认的 `profileUpdates`; 2. 从当前客户原话中通过明确句式提取的偏好、约束和称呼; 3. 通过提示注入与凭证模式扫描的安全内容。 人工确认的记忆允许没有客户原消息 ID,但必须标记 `created_by=human` 并写入审计。人工修改结构化客户主档时,对应的稳定记忆键 `profile:` 同步更新;清空字段会把旧记忆标记为 `superseded`,避免画像与长期记忆互相冲突。 ### 3.1 生命周期治理 后台治理接口支持以下操作,默认不在客服工作台提供可视化入口: | 操作 | 结果 | | --- | --- | | 添加 | 创建高重要性的人工确认事实 | | 编辑 | 覆盖内容并标记 `created_by=human` | | 确认 | 将 `hypothesis` 转为 `fact`,置信度设为 1 | | 拒绝 | 状态改为 `rejected`,不再进入上下文 | | 过期 | `expires_at` 到期后自动改为 `superseded` | | 遗忘 | 物理删除记忆条目并重建快照 | 每次治理操作均生成新快照(内容未变化时复用原版本)并写入审计。记忆遗忘与原始消息保留是两个独立策略;如需满足客户完整删除请求,还必须同时执行消息和客户主档的数据删除流程。 ## 4. 上下文组装 上下文由 `AgentContextBuilder` 统一构建,模型客户端不再直接拼接画像、记忆和历史消息。Builder 对指令、结构化状态和权威会话分别分配字符预算,并在总预算不足时从较早历史开始裁剪,保证最新客户消息保留。 每轮生成顺序: 1. 企业客服身份、规则和安全边界; 2. 当前客户核心记忆快照; 3. 与最新消息相关的较早历史片段; 4. 当前画像、未完成待办、预警和房源反馈; 5. 近期有效会话和最新客户消息; 6. 结构化输出约束。 默认限制: | 配置 | 默认值 | 说明 | | --- | ---: | --- | | `QIWEI_AGENT_MEMORY_CORE_CHAR_LIMIT` | 4000 | 核心记忆字符上限;运行时允许配置 500–8000 | | `QIWEI_AGENT_MEMORY_RECALL_LIMIT` | 6 | 单轮相关历史上限 | | `QIWEI_AGENT_MEMORY_RECENT_MESSAGES` | 12 | 不参与远期召回的近期消息数 | | `QIWEI_AGENT_MEMORY_HISTORY_SCAN_LIMIT` | 240 | 本地相关性扫描范围 | 相关历史以字符二元组、英文词和数字重叠计算本地相关性。该实现不发送额外外部请求,后续可替换为本地 embedding 或 provider hybrid search。 ## 5. Claude Session Epoch Session 不再永久续接。达到以下任一条件时创建新 Epoch: - `CLAUDE_CODE_SESSION_MAX_TURNS`,默认 12 次生成; - `CLAUDE_CODE_SESSION_MAX_AGE_MS`,默认 24 小时; - Session 无效或单次预算超限。 新 Epoch 记录 `parentSessionId`、`memoryVersion`、开始时间和轮次。当前映射保留最近 50 个已关闭 Epoch 的元数据。客户长期记忆不依赖 Session,因此轮换不会丢失画像、偏好、约束、任务或完整消息。 ## 6. 故障与安全边界 - 记忆召回或写入失败只记录 `memory_recall_failed` / `memory_capture_failed` 审计,不阻塞草稿; - 召回的历史消息被标记为客户或客服原话,不作为系统指令; - `hypothesis` 不进入已确认核心事实; - 客户数据按企微账号数据库和 conversation ID 隔离; - Claude Code 保持 `--bare` 和只读工具,避免隐式 auto-memory 造成不可审计写入; - 自动发送仍由白名单、会话模式、置信度和 `requiresHuman` 共同控制。 ## 7. Provider 扩展 后续外部记忆提供者统一实现: ```js prefetch(context) capture(turn) search(query, scope) conclude(conversationId) getProfile(conversationId) forget(memoryId) ``` 当前仅使用 Workbench SQLite 本地记忆,不接入外部记忆 provider。Provider 接口保留为未来扩展边界,但不包含云端实现或客户数据外发逻辑。 ## 8. 验证 `node scripts/agent-console-smoke-test.js` 覆盖: - 明确事实、偏好和约束写入; - 注入/凭证模式不进入记忆; - 较早相关历史按需召回; - 核心快照容量限制; - Epoch 按轮次轮换并保留父 Session 和历史元数据; - 原有审核、白名单、发送幂等和 grounding 行为不回归。 - 人工新增、编辑、确认、拒绝和遗忘; - 临时记忆到期退出 active 快照; - 既有画像和明确客户原话幂等回填; - 人工清空画像字段后稳定记忆失效。 ## 8.1 实施状态 | 阶段 | 状态 | 说明 | | --- | --- | --- | | Memory Store 与数据表 | 已完成 | 本地 SQLite、快照、治理 API;按产品决定不提供 Dashboard 记忆面板 | | `AgentContextBuilder` | 已完成 | Claude Code 与其他 provider 统一使用 Builder | | 本地检索与预算 | 已完成 | 核心记忆、相关历史、项目上下文和总 prompt 均有限额 | | 异步提取、证据、冲突与过期 | 已完成 | 持久化异步提取、重试恢复、证据闸门、冲突修订历史和过期均已实现 | | Session Epoch | 已完成 | 按轮次/时长轮换并保留父子关系和审计元数据 | | 外部记忆 provider | 不实施 | 当前本地记忆已满足客服需求,不引入额外云服务、成本和隐私边界 | ## 9. Dashboard 离线可用性 长期记忆和上下文治理依赖本地 Workbench DB,不应被 Fmode 登录状态探测阻塞。Dashboard 的「智能会话」页面因此采用以下启动顺序: 1. 立即按当前 origin 中保存的账号绑定加载本地会话、记忆、任务和审计; 2. 通过 `/api/status?fast=true` 读取登录和订阅状态缓存,前端探测上限为 2.5 秒; 3. 在后台补全服务端账号绑定,并更新在线状态; 4. 上游不可达时保留本地工作台能力,仅禁用依赖真实企微连接的收发操作。 浏览器账号列表按 origin 隔离。正式 Dashboard 固定使用 `http://127.0.0.1:4320/`;其他临时端口拥有独立的 `localStorage`,不能据此判断正式账号已退出登录。