客服回复不能把无限增长的 Claude Code Session 当作客户长期记忆。本方案借鉴 Hermes Agent 的分层记忆、按需召回、冻结核心快照和 Session 搜索思想,同时保持企微客户数据本地优先、证据可追溯和人工审核边界。
设计目标:
| 层级 | 数据源 | 生命周期 | 注入方式 |
|---|---|---|---|
| 企业人格与规则 | 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,不允许越过知识库目录读取任意文件。
customer_memory_items.type:
fact:客户明确表达且可核验的事实;preference:明确偏好,例如“更喜欢地铁附近”;constraint:明确排除项,例如“不考虑顶楼”;event:具有后续价值的历史事件;hypothesis:模型推断,只能用于询问确认,不能作为已知事实。每条记忆包含 source_message_ids_json、confidence、importance、status、direction 和 created_by。当前自动写入仅接受:
profileUpdates;人工确认的记忆允许没有客户原消息 ID,但必须标记 created_by=human 并写入审计。人工修改结构化客户主档时,对应的稳定记忆键 profile:<field> 同步更新;清空字段会把旧记忆标记为 superseded,避免画像与长期记忆互相冲突。
后台治理接口支持以下操作,默认不在客服工作台提供可视化入口:
| 操作 | 结果 |
|---|---|
| 添加 | 创建高重要性的人工确认事实 |
| 编辑 | 覆盖内容并标记 created_by=human |
| 确认 | 将 hypothesis 转为 fact,置信度设为 1 |
| 拒绝 | 状态改为 rejected,不再进入上下文 |
| 过期 | expires_at 到期后自动改为 superseded |
| 遗忘 | 物理删除记忆条目并重建快照 |
每次治理操作均生成新快照(内容未变化时复用原版本)并写入审计。记忆遗忘与原始消息保留是两个独立策略;如需满足客户完整删除请求,还必须同时执行消息和客户主档的数据删除流程。
上下文由 AgentContextBuilder 统一构建,模型客户端不再直接拼接画像、记忆和历史消息。Builder 对指令、结构化状态和权威会话分别分配字符预算,并在总预算不足时从较早历史开始裁剪,保证最新客户消息保留。
每轮生成顺序:
默认限制:
| 配置 | 默认值 | 说明 |
|---|---|---|
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。
Session 不再永久续接。达到以下任一条件时创建新 Epoch:
CLAUDE_CODE_SESSION_MAX_TURNS,默认 12 次生成;CLAUDE_CODE_SESSION_MAX_AGE_MS,默认 24 小时;新 Epoch 记录 parentSessionId、memoryVersion、开始时间和轮次。当前映射保留最近 50 个已关闭 Epoch 的元数据。客户长期记忆不依赖 Session,因此轮换不会丢失画像、偏好、约束、任务或完整消息。
memory_recall_failed / memory_capture_failed 审计,不阻塞草稿;hypothesis 不进入已确认核心事实;--bare 和只读工具,避免隐式 auto-memory 造成不可审计写入;requiresHuman 共同控制。后续外部记忆提供者统一实现:
prefetch(context)
capture(turn)
search(query, scope)
conclude(conversationId)
getProfile(conversationId)
forget(memoryId)
当前仅使用 Workbench SQLite 本地记忆,不接入外部记忆 provider。Provider 接口保留为未来扩展边界,但不包含云端实现或客户数据外发逻辑。
node scripts/agent-console-smoke-test.js 覆盖:
| 阶段 | 状态 | 说明 |
|---|---|---|
| Memory Store 与数据表 | 已完成 | 本地 SQLite、快照、治理 API;按产品决定不提供 Dashboard 记忆面板 |
AgentContextBuilder |
已完成 | Claude Code 与其他 provider 统一使用 Builder |
| 本地检索与预算 | 已完成 | 核心记忆、相关历史、项目上下文和总 prompt 均有限额 |
| 异步提取、证据、冲突与过期 | 已完成 | 持久化异步提取、重试恢复、证据闸门、冲突修订历史和过期均已实现 |
| Session Epoch | 已完成 | 按轮次/时长轮换并保留父子关系和审计元数据 |
| 外部记忆 provider | 不实施 | 当前本地记忆已满足客服需求,不引入额外云服务、成本和隐私边界 |
长期记忆和上下文治理依赖本地 Workbench DB,不应被 Fmode 登录状态探测阻塞。Dashboard 的「智能会话」页面因此采用以下启动顺序:
/api/status?fast=true 读取登录和订阅状态缓存,前端探测上限为 2.5 秒;浏览器账号列表按 origin 隔离。正式 Dashboard 固定使用 http://127.0.0.1:4320/;其他临时端口拥有独立的 localStorage,不能据此判断正式账号已退出登录。