文档定位:对
yolo-data指标问数平台的「记忆能力」做标准化分层重构设计。 背景:用户反馈当前记忆模块「架构有些简单,而且不能很好地融入智能体的上下文中」。 目标:对照业界标准记忆分层(系统提示词 / 对话滑动窗口 / 临时工作记忆 / 长期记忆 / 共享组织记忆 / 元记忆检索),把现有散落模块归位、补齐缺口、给出分期迁移路径与配置项,便于后续调整。 范围:本文档只定设计与契约,不改动任何业务代码。 涉及实现时以本文件为蓝图,落到src/对应模块。 关联文档:docs/architecture.md(总体架构,第 11 节「多轮会话与上下文决策」)、src/userMemory.js、src/memory.js、src/agent.js、src/queryIntent.js、src/feedback.js。
平台已经具备记忆能力的雏形,但呈「多模块各自为政、未形成分层」的状态:
buildSystemPrompt)、组织记忆(指标/行策略/技能/工作区产物)、长期用户记忆(userMemory)、纠错记忆(feedbackHints)、滑动窗口(SessionMemoryStore)、指代解析(resolveConversationContext)六类记忆混在一个单体 system prompt 与一段固定 20 条窗口里,没有分层预算、没有统一的读写/注入契约。建议目标态:把记忆明确拆成 L0 系统提示词层 → L1 共享组织记忆层 → L2 长期用户记忆层 → L3 对话滑动窗口层 → L4 临时工作记忆层 → L5 元记忆与检索层 六层,并由一组横切治理(捕获/整合/压缩遗忘/检索/注入/度量)统一调度。详见 §2–§4,迁移路径见 §6。
| 关注点 | 现有模块 / 文件 | 关键事实(含行号证据) |
|---|---|---|
| 系统提示词(单体) | src/agent.js buildSystemPrompt() |
1386 起组装;把主题提示词、指标行、行级策略、技能指令、feedbackHints、业务数据集、workspaceArtifacts 全部拼进一个 system message |
| 消息最终组装 | src/agent.js harness 调用 |
5178–5203:[{role:'system',content:systemPrompt}, ...history, {role:'user',content:normalizedQuestion}] |
| 纠错记忆注入 | src/agent.js:2370 + src/feedback.js |
feedbackHints = this.feedback?.promptHints(theme.id, 8);取最近 8 条 correct:false 反馈,作为文本塞进 system prompt(feedback.js:65 promptHints) |
| 会话滑动窗口 | src/memory.js SessionMemoryStore |
contextLimit=20(来自 chatMemoryMessageLimit)固定窗口;buildModelContext()(66 行)返回 [memoryMessage, ...history] |
| 长期用户记忆存储 | src/userMemory.js UserMemoryStore(208) |
三表:user_memory_documents(摘要+正文、版本化、可回滚)、user_memory_notes(追加式、去重、PENDING/MERGED/ARCHIVED)、user_memory_versions(版本快照) |
| 长期记忆注入 | src/userMemory.js buildContextMessage()(632) |
只注入 summary 作为 system 消息;正文经 search_user_memory 工具按需检索 |
| 预算常量 | src/userMemory.js |
SUMMARY_BUDGET=1200 / CONTENT_BUDGET=8000 / MAX_PENDING_NOTES=400;MEMORY_KINDS = BUSINESS_KNOWLEDGE/CALIBER/TERM/TIME_RANGE/PREFERENCE |
| 写入-合并-压缩 | src/userMemoryDistiller.js |
三通道:抽取(append-only 笔记)、合并(整体重写文档+版本+1)、压缩(超长正文去重);ingest() 由 memory.js distillAfterTurn()(78 行)每轮后置触发;阈值 autoConsolidateAt=8、compactRatio=0.8、冷却 10min |
| 按需检索工具 | src/userMemoryTools.js |
search_user_memory:无 query 返回索引,有 query 关键词行检索;scope ∈ {all,global,theme} |
| 指代/上下文解析 | src/queryIntent.js resolveConversationContext()(147) |
判定 NEW_TOPIC / FOLLOW_UP / INDEPENDENT;遇到指代/结果加工,把最近 1–3 条用户消息拼进 question 字符串(203、213 行) |
| 组织记忆-工作区产物 | src/agent.js workspaceArtifacts(1435) |
历史查询结果的 artifact(列、样例、能力、指纹)拼进 system prompt |
| 组织记忆-指标/技能 | src/agent.js buildSystemPrompt |
指标行、行级策略、技能指令均来自主题与作用域配置 |
事实归纳:平台已有「长期语义记忆 + 滑动窗口 + 指代解析 + 纠错记忆 + 工作区产物」,但:
对照 MemGPT / Mem0 / Zep 等业界分层,结合本平台「确定性优先、口径治理」的硬约束,定义六层 + 横切治理:
┌──────────────────────────────────────────────────────────────┐
│ L0 系统提示词层 (Foundation / System Prompt) │ ← 静态基座:人格、全局铁律、不可协商规则
│ · 平台契约 10 条、派生指标规则、语义分类法(buildSemantic…)│
├──────────────────────────────────────────────────────────────┤
│ L1 共享组织记忆层 (Shared Org Memory) │ ← 跨用户/跨会话可复用
│ · 指标定义与业务口径、行级策略、技能目录、工作区产物(artifact)│
├──────────────────────────────────────────────────────────────┤
│ L2 长期用户记忆层 (Long-term User Memory) │ ← 语义记忆 + 程序记忆
│ · 用户手册(摘要+正文, 版本化)、feedbackHints(纠错/程序记忆) │
├──────────────────────────────────────────────────────────────┤
│ L3 对话滑动窗口层 (Conversation Sliding Window) │ ← 本会话最近 K 轮
│ · SessionMemoryStore, contextLimit=20, 分级淘汰/递归压缩 │
├──────────────────────────────────────────────────────────────┤
│ L4 临时工作记忆层 (Working / Temporary Memory) │ ← 本轮/当前子任务存活
│ · 指代消解结果、澄清槽位、当前子目标、半成契约、scratchpad │
├──────────────────────────────────────────────────────────────┤
│ L5 元记忆与检索层 (Meta-memory & Retrieval) │ ← 不持有内容,只负责"何时取哪层"
│ · 检索编排(前置注入 vs 按需工具)、索引、命中账本 │
└──────────────────────────────────────────────────────────────┘
横切治理(贯穿 L1–L5):捕获 → 整合 → 压缩遗忘 → 检索 → 注入 → 度量
分层原则(务必遵守,避免破坏平台命根「确定性」):
| 现有模块 | 当前归属(隐含) | 目标层 | 缺口 / 动作 |
|---|---|---|---|
buildSystemPrompt 平台契约 10 条 + 派生规则 + 语义分类法 |
混在单体 prompt | L0 | 抽离为「不可协商基座」,与 L1/L2 解耦,独立 token 预算 |
| 指标行 / 行级策略 / 技能指令 | 混在单体 prompt | L1 | 标记为「组织记忆」,与用户记忆隔离;增加检索入口 |
workspaceArtifacts |
混在单体 prompt | L1 | 已属组织记忆;补「artifact 检索/复用策略」到 L5 |
userMemory 文档(摘要+正文) |
摘要注入 + 工具检索 | L2(语义) | 见 §5.1:摘要→前置注入保证;正文检索编排归 L5 |
feedbackHints(纠错) |
文本塞进 prompt | L2(程序记忆) | 与用户手册分离但同层;可进版本化「程序记忆区」 |
SessionMemoryStore 20 条窗口 |
独立 | L3 | 见 §5.4:分级淘汰 + 窗口递归压缩(summary of older turns) |
resolveConversationContext 指代拼接 |
改 question 字符串 | L4 | 见 §5.2:改为结构化 working memory,不污染 question |
search_user_memory 工具 |
独立 | L5 | 升级为「检索编排」:前置注入(取数前) + 按需工具(推理中) 双通道 |
UserMemoryDistiller 抽取/合并/压缩 |
独立 | 横切(捕获/整合/压缩遗忘) | 保留;补「全局记忆预算」触发点 |
| 指标/口径知识库(语义治理) | 散落 | L1(可检索) | 已有 semantic-domain-governance,接入 L5 检索 |
buildSystemPrompt 头部)。{role:'system'}(agent.js:5180)。buildSystemPrompt。workspace_artifacts 表。indicatorLines/rowPolicyLines/skillLines/workspaceLines)。resolveArtifactReusePolicy)。user_memory_documents(摘要+正文、版本、可回滚)、user_memory_notes、user_memory_versions;纠错来自 qa_feedback 经 FeedbackService.promptHints。distillAfterTurn → UserMemoryDistiller.ingest → 抽取→合并→压缩。buildModelContext 首条 system 消息(memory.js:73-74);feedbackHints 进 L0 段。chat_sessions / chat_messages。buildModelContext 取最近 contextLimit=20 条。...history)。resolveConversationContext 填充;推理中由工具/子步骤更新。question 字符串(queryIntent.js:203/213),无结构化承载。mcp_query_contracts 式的检索缓存可参考);检索本身走确定性匹配。search_user_memory 单一按需工具。UserMemoryDistiller.extractNotes / extractExplicitNotes(显式「记住…」确定性落笔记)。consolidate() 整体重写 + 版本+1;无变化不产版本。compact() 超长正文去重;minKeepRatio=0.35 防误删;冷却 10min。buildContextMessage 只注入摘要(~1200 字预算),正文靠模型主动调用 search_user_memory。取数所需的具体口径若只在正文、且模型没调工具,就会「记了但用不上」。normalizedQuestion 对用户记忆做确定性检索(同 store.search 关键词匹配),把 Top-N 命中片段直接前置注入 L2 段;search_user_memory 工具保留为推理中补充检索。检索仍走确定性匹配,不破坏口径治理。resolveConversationContext 把历史用户消息拼进 question 字符串(203/213 行)。这污染了「用户原始问题」,也承载不了中间态(半成契约、子目标、澄清槽位)。mode / inheritedUserMessages / 澄清槽位 / 当前子目标 作为独立上下文段注入,question 保持用户原话。resolveConversationContext 输出继续作为 L4 的填充源,但不再改写 question。contextLimit=20 写死,无递归压缩;长会话早期指代/澄清一旦滑出窗口即丢失,且 §5.2 的拼接法在窗口边界更脆弱。SUMMARY_BUDGET/CONTENT_BUDGET;L0/L1/L3/L4 无预算,也无「记忆命中率/召回」度量,无法判断投入产出。原则:Phase 0 不改行为、只定契约与命名;后续逐步增强,每期可独立回退。
| 阶段 | 目标 | 主要动作 | 风险/回退 |
|---|---|---|---|
| Phase 0 | 分层归位、命名收敛 | 在 buildSystemPrompt/buildModelContext 增加「层」注释与分段常量;配置项集中到 config;本文档落地为契约 |
零行为变化,纯重构可秒回退 |
| Phase 1 | 全局记忆预算 + L5 前置注入 | 新增记忆预算账本;search_user_memory 升级为「前置注入 + 按需工具」双通道(确定性检索,不引入向量) |
若命中率下降,关前置注入开关回退到纯工具模式 |
| Phase 2 | L4 工作记忆结构化 + L3 分级淘汰 | resolveConversationContext 输出注入 L4 段、不再拼接 question;L3 加滚动摘要 |
question 拼接回退开关;窗口摘要可关闭 |
| Phase 3 | 情节记忆 + L1 检索化 | 历史会话/查询作为 episodic 可检索;指标/artifact 接入 L5 检索,超量按相关性截断 | 不接入即维持现状全常驻 |
建议落地顺序:Phase 0 → Phase 1(性价比最高,直接解决「融入上下文」主因)→ Phase 2 → Phase 3。
chatMemoryMessageLimit → L3 contextLimit(当前 20)。userMemory 预算:SUMMARY_BUDGET=1200 / CONTENT_BUDGET=8000 / MAX_PENDING_NOTES=400。autoConsolidateAt=8、compactRatio=0.8、minKeepRatio=0.35、冷却 10min。memory.layers.L0/L1/L2/L3/L4.budget:每层 token 配额(默认 L0 固定、L2 摘要 1200、L1 按主题、L3 20 条、L4 小)。memory.retrieval.preInject.enabled:L5 前置注入开关(默认关,Phase 1 打开)。memory.retrieval.preInject.topN:前置注入命中条数(默认 6)。memory.window.rollingSummary.enabled:L3 滚动摘要开关(Phase 2)。memory.window.recentKeep:L3 原文保留条数(默认 8,其余摘要化)。feedbackHints 命中后下一轮是否仍错(程序记忆有效性)。preInject.enabled 开关,关掉即退回纯工具模式。legacyQuestionFlatten 开关,逐步切流。chat_sessions/chat_messages,还是新建 qa_episodes 表?docs/architecture.md 第 11 节,保持总体架构与记忆设计的术语一致?用户目标:记忆必须对提问真正生效且正确;冗余层可去。本次实现最小高杠杆改造,已落地并验证。
src/agent.js
resolveMemoryPreInject({userId,themeId,question}):问数前用当前补全指代后的问题调 userMemories.search()(确定性关键词匹配),取 Top-N 命中并按 maxChars 截断。buildSystemPrompt 新增段落「本轮命中的用户长期记忆」,把命中片段直接前置注入 system prompt;附治理约束(记忆仅为默认口径参考,冲突以平台规则为准)。run 流程在 feedbackHints 之后计算,buildSystemPrompt 时传入。src/config.js userMemory.retrieval.preInject = { enabled, topN=6, maxChars=1200 },支持环境变量 USER_MEMORY_PREINJECT_* 覆盖;application.js 把 config 注入 agent。src/userMemory.js buildContextMessage 改为说明「相关细节已前置注入,search_user_memory 改为按需深挖工具」,消除重复提示。src/http/routes/memory.js 新增 GET /api/memories/search(鉴权+作用域收敛,admin 空 userId 跨全部用户检索)。public/js/pages/memory.js 新增「记忆命中预览」面板:输入问题即预览将被前置注入的命中片段,直接验证记忆是否生效。summaryBudget,而非删除。search_user_memory 工具:保留。前置注入覆盖常见情况,工具作为「超出 Top-N 的按需深挖」兜底,移除会降低鲁棒性。node --check 全过;lint 0 error(5 文件经 --allow-growth 显式承认:agent 5761→5809、memory 页 611→689、routes/memory 503→534、application 441→442、config 133→143)。/api/health=200,无启动报错。/api/memories/search?q=ZZZ_TEST_CALIBER 返回精确命中片段(excerpt:"- ZZZ_TEST_CALIBER 本部门默认按含税口径看销售额", score:3)→清理后检索归零。证明 search 与将被注入的内容一致。设计基线:基于 origin/v1.0.1 分支源码实地核对(2026-10-16)。后续调整请以本文件 §2–§7 为契约,实现改动登记到对应 src/ 模块并回写本节「待确认」清单。