Files

335 lines
23 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ------------------------------------------------------------------
Natural Memory v2 — 站点数据层
所有数字均直接摘自工程内的正式报告,src 字段标注出处文件。
本文件不包含任何推算、外推或未测量的数字。
------------------------------------------------------------------ */
window.NM_DATA = {
meta: {
name: "Natural Memory v2",
version: "v2",
base: "Qwen3.5-4B",
date: "2026-09",
repo: "H:\\Memory",
disclaimer:
"本文只描述当前实现已经完成并实际测量的内容。它不把系统描述成通用智能,也不把实验室结果表述成线上服务承诺。"
},
/* ---------- 一句话定位 ---------- */
positioning: {
headline: "模型重启之后,它仍然记得。",
latin: "A memory layer inside the model.",
body:
"Natural Memory v2 是接在本地 Qwen3.5-4B 上的一个模型内记忆层:把适合长期复用的个人事实、项目事实和短对话片段写进带地址的记忆记录," +
"当前问题只读取少量相关记录,再把这些记录作为模型内部前缀交给 Qwen 生成答案。",
problem:
"要解决的具体问题:模型重启后不携带历史聊天记录,仍能访问已经保存的个人或项目事实,同时不把全部历史转换成 GPU 上的长 KV Cache。"
},
/* ---------- 记忆与 KV 的分工 ---------- */
division: [
{ k: "最近对话", v: "Qwen 热 KV", note: "顺序关系仍由普通上下文负责" },
{ k: "长期个人/项目事实", v: "Natural Memory 记录", note: "事实、版本、来源、短证据、语义地址" },
{ k: "当前问题", v: "有界路由与 Top-K 读取", note: "不做全量 slot 注意力" },
{ k: "原始持久化状态", v: "嵌入式 memory safetensors", note: "无 SQLite、无磁盘分页" }
],
/* ---------- 读取路径(可交互的分步) ---------- */
readPath: [
{ id: "q", label: "用户问题", detail: "普通自然语言,不要求 /remember 之类的显式指令。", io: "输入 · 一句口语化的问题" },
{ id: "hs", label: "Qwen hidden state", detail: "由模型隐状态生成紧凑查询地址,而不是独立的外部 embedding 服务。", io: "问题 → 紧凑查询地址(单条 batch 编码,避免显存瞬时峰值)" },
{ id: "lsh", label: "LSH / 地址粗索引", detail: "精确桶 + Hamming-1/2 探针,把候选收敛到少量页面。", io: "查询地址 → 候选页(批次 A 粗索引 730 个桶,最近一次粗筛返回 24 个候选)" },
{ id: "pg", label: "候选页", detail: "页容量 32 条记录;默认最多读取 4 页。", io: "候选页 → 少量页面(本次 723 条 active records 分布在 23 个页面上)" },
{ id: "rr", label: "页内记录重排", detail: "记录级重排由独立的 text_retriever 完成(6 张量、1,573,377 参数)。", io: "页内记录 → 有序候选(text_retriever 自身 Top-1 只有 23.20%,是当前主要瓶颈)" },
{ id: "tk", label: "Top-K 记录", detail: "正式脏数据测试中的读取上限为 2 条。", io: "有序候选 → 少量证据(目标召回:批次 A 166/246,批次 B 228/246)" },
{ id: "inj", label: "证据前缀注入 Qwen", detail: "命中的短证据被提升到 GPU,作为模型内部前缀。", io: "证据 → 模型内部前缀(批次 B 96.97% 的题实际注入,平均 410.34 token)" },
{ id: "gen", label: "普通生成", detail: "记忆主体留在进程内存,只有热点记录进入有界 GPU cache。", io: "前缀 + 问题 → 回答(读取额外耗时:批次 A 61.10 ms → 批次 B 26.35 ms)" }
],
readPathCost: { value: 61.1, unit: "ms", label: "读取路径额外耗时", src: "对外技术总结 §4" },
/* ---------- 记忆记录解剖 ---------- */
recordFields: [
{ f: "raw_text", d: "原始短文本" },
{ f: "semantic_addr", d: "语义地址" },
{ f: "entity", d: "实体" },
{ f: "attribute", d: "属性" },
{ f: "value", d: "值" },
{ f: "timestamp", d: "时间" },
{ f: "source", d: "来源" },
{ f: "confidence", d: "置信度" },
{ f: "importance", d: "重要性" },
{ f: "access_count", d: "访问次数" },
{ f: "status", d: "状态" },
{ f: "supersedes", d: "版本关系" },
{ f: "related_ids", d: "关联记录" },
{ f: "audit", d: "审计信息" }
],
states: [
{ s: "active", d: "当前有效事实,参与正常读取", tone: "ok" },
{ s: "superseded", d: "同一实体+属性出现新值,旧值退役", tone: "mute" },
{ s: "quarantined", d: "低置信度写入,需显式批准", tone: "warn" },
{ s: "retracted", d: "已撤回,但不从审计中消失", tone: "bad" }
],
/* ---------- 主结果:正式脏数据迁移测试 ---------- */
dirty: {
title: "正式脏数据迁移测试",
src: "对外技术总结 §3",
subtitle: "真实工程源码 / 文档 + 本项目对话中用户明确说过的短事实",
corpus: [
{ k: "真实源码与文档", v: "65 个文件 · 1,110,524 字节 · 620 个分片" },
{ k: "用户对话事实", v: "15 条记录" },
{ k: "由记录派生的问题", v: "44 题(38 可回答 / 6 未知)" },
{ k: "来自源码与文档", v: "220 题" },
{ k: "题目总数", v: "264 题 · 246 可回答 · 18 未知" }
],
protocol: "Qwen3.5-4B · 4-bit NF4 · greedy 解码 · 最大新生成 64 token · 进程显存上限 10 GiB · 读取上限 2 条记录 · 同一 tokenizer",
batchNote: "同一题集(264 题 / 246 可回答 / 18 未知)、同一协议、同一仓库,先后跑了两个独立批次。批次配置不同,因此两个批次的分数不可合并、不可平均,只能并列读。",
headline: [
{ metric: "可回答正确率", base: 1.22, a: 62.60, b: 82.52, baseRaw: "3/246", aRaw: "154/246", bRaw: "203/246" },
{ metric: "目标记忆召回率", base: 0.00, a: 67.48, b: 92.68, baseRaw: "0/246", aRaw: "166/246", bRaw: "228/246" },
{ metric: "总体正确率", base: 7.58, a: 64.39, b: 82.95, baseRaw: "20/264", aRaw: "170/264", bRaw: "219/264" },
{ metric: "未知拒答正确率", base: 94.44, a: 88.89, b: 88.89, baseRaw: "17/18", aRaw: "16/18", bRaw: "16/18" }
],
batches: [
{
id: "A", label: "批次 A", date: "2026-09-05 23:22", file: "dirty_real_corpus_compare_4b.json",
tag: "对外技术总结采用",
reader: 61.10, promptTokens: 641.42, prefixRate: 90.91, prefixTokens: 610.67,
latencyBase: 2650.4, latencyNm: 2718.9, decodeBase: 24.06, decodeNm: 21.68,
allocNm: 3.134, resvNm: 5.021, records: 723, pages: 23, cacheTokens: 15885
},
{
id: "B", label: "批次 B", date: "2026-09-06 11:36", file: "dirty_real_corpus_compare_4b_router_final.json",
tag: "接入记忆路由器后",
reader: 26.35, promptTokens: 441.19, prefixRate: 96.97, prefixTokens: 410.34,
latencyBase: 2854.9, latencyNm: 2878.3, decodeBase: 22.34, decodeNm: 20.68,
allocNm: 3.134, resvNm: 5.180, records: 738, pages: 24, cacheTokens: 17990
}
],
categories: [
{ k: "symbol_navigation", label: "符号定位(函数在哪个文件)", n: 192, base: "0/192", a: "129/192", b: "181/192" },
{ k: "unknown_refusal", label: "未知拒答", n: 18, base: "17/18", a: "16/18", b: "16/18" },
{ k: "constraint", label: "约束类问题", n: 15, base: "0/15", a: "5/15", b: "6/15" },
{ k: "paraphrase", label: "自然改写", n: 9, base: "0/9", a: "5/9", b: "5/9" },
{ k: "cross_session", label: "跨会话", n: 6, base: "1/6", a: "5/6", b: "5/6" },
{ k: "temporal_conflict", label: "时间冲突目录查询", n: 4, base: "0/4", a: "4/4", b: "4/4" },
{ k: "reasoning", label: "推理", n: 4, base: "0/4", a: "2/4", b: "0/4" },
{ k: "write_safety", label: "写入安全解释", n: 2, base: "0/2", a: "2/2", b: "0/2" },
{ k: "baseline_limitation", label: "基线局限说明", n: 2, base: "2/2", a: "2/2", b: "2/2" },
{ k: "operational_architecture", label: "操作性架构", n: 2, base: "0/2", a: "0/2", b: "0/2" },
{ k: "architecture_tradeoff", label: "架构取舍", n: 2, base: "0/2", a: "0/2", b: "0/2" },
{ k: "memory_tiers", label: "记忆分层", n: 2, base: "0/2", a: "0/2", b: "0/2" },
{ k: "benchmark_protocol", label: "基准协议", n: 2, base: "0/2", a: "0/2", b: "0/2" },
{ k: "control_path", label: "控制路径", n: 2, base: "0/2", a: "0/2", b: "0/2" },
{ k: "router_path", label: "路由器路径", n: 2, base: "0/2", a: "0/2", b: "0/2" }
],
categoryNote: "类别题量差异极大:符号定位 192 题,而 11 个类别各只有 2 题——2 题的类别里错 1 题就是 50 个百分点,不构成结论。批次 B 也并非全面提升:符号定位从 129 升到 181,但写入安全从 2/2 掉到 0/2、推理从 2/4 掉到 0/4。",
domains: [
{ domain: "用户对话事实", n: 38, base: 2.63, baseRaw: "1/38", a: 55.26, aRaw: "21/38", b: 52.63, bRaw: "20/38" },
{ domain: "真实源码/文档", n: 208, base: 0.96, baseRaw: "2/208", a: 63.94, aRaw: "133/208", b: 87.98, bRaw: "183/208" }
],
gap: {
recallA: 67.48, recallARaw: "166/246",
answerA: 62.60, answerARaw: "154/246",
recallB: 92.68, recallBRaw: "228/246",
answerB: 82.52, answerBRaw: "203/246",
note: "目标记忆召回与最终回答正确必须分开报告——检索准确率不等于回答准确率。批次 A 的召回 67.48%、回答 62.60%(差 4.88pp);批次 B 召回 92.68%、回答 82.52%(差 10.16pp):路由器把该查的记忆查回来了,但证据到答案的闭环仍有丢失。"
}
},
/* ---------- 实测样例(逐字取自评测原始结果) ---------- */
examples: {
title: "实测样例",
src: "dirty_real_corpus_compare_4b_router_final.json(批次 B 逐条记录)",
subtitle: "下面五条题目与回答全部逐字取自评测原始结果,未经改写或润色。包含两条我们判错的样例。",
note:
"判分规则:可回答题要求 expected_anchors 里的锚点全部出现在回答中(去空白、转小写后做子串匹配);未知题则看模型是否拒答。" +
"输出上限为 64 token,标注「达上限截断」的回答是被硬截断的原文,不是完整句——我们保留原样,不做补全。" +
"另外:第 1 条命中的记忆记录里写的是「Natural Memory v1」,那是这条记录写入时的项目名,记录内容按原样保留、未做改写;" +
"第 5 条则是证据明明已经召回、回答却没能把锚点说全,因此判为未命中——这类「检索对、答案错」正是当前最主要的失分来源。"
},
/* ---------- Stage 5 结构化工程基准 ---------- */
stage5: {
title: "Stage 5 结构化工程基准",
src: "对外技术总结 §5",
subtitle: "与脏数据迁移集不是同一题集,不能合并成一个总分",
sets: [
{
name: "通用个人事实",
n: 107,
rows: [
{ sys: "原版 Qwen3.5-4B", acc: 3.74, raw: "4/107", lat: 1233.2, tok: 23.51, abstain: "21/21" },
{ sys: "强 RAG 基线", acc: 99.07, raw: "106/107", lat: 885.8, tok: 21.67, abstain: "20/21" },
{ sys: "Natural Memory v2", acc: 100.00, raw: "107/107", lat: 994.3, tok: 21.70, abstain: "21/21" }
]
},
{
name: "项目库",
n: 64,
rows: [
{ sys: "原版 Qwen3.5-4B", acc: 70.31, raw: "45/64", lat: 1351.5, tok: 23.70, abstain: null },
{ sys: "强 RAG 基线", acc: 100.00, raw: "64/64", lat: 1395.3, tok: 22.94, abstain: null },
{ sys: "Natural Memory v2", acc: 100.00, raw: "64/64", lat: 1463.2, tok: 21.85, abstain: null }
]
}
],
note: "强 RAG 使用本地 CPU bert-base-chinese 向量、稀疏特征和固定规则重排。它不是训练好的公开 cross-encoder,因此可以作为更强的本地基线,但不能代表所有生产 RAG 服务。"
},
/* ---------- 代价账本 ---------- */
cost: {
title: "代价账本",
src: "对外技术总结 §4",
subtitle: "以下数字来自同一次正式脏数据测试。显存字段是每次请求结束后的 allocated/reserved 快照,不是 torch.cuda.max_memory_allocated() 记录的严格峰值。",
rows: [
{ k: "平均总延迟", base: "2650.4 ms", nm2: "2718.9 ms", delta: "+2.58%", tone: "warn" },
{ k: "平均解码速度", base: "24.06 tok/s", nm2: "21.68 tok/s", delta: "-9.90%", tone: "bad" },
{ k: "平均输入 token", base: "30.8", nm2: "641.4", delta: "证据前缀增加", tone: "mute" },
{ k: "读取额外耗时", base: "无", nm2: "61.1 ms", delta: "新增路径", tone: "mute" },
{ k: "allocated 快照峰值", base: "3.084 GiB", nm2: "3.134 GiB", delta: "+0.050 GiB", tone: "ok" },
{ k: "reserved 快照峰值", base: "3.234 GiB", nm2: "5.021 GiB", delta: "+1.787 GiB", tone: "warn" }
],
vram: { card: 12, allocBase: 3.084, allocNm2: 3.134, resvBase: 3.234, resvNm2: 5.021, unit: "GiB" },
quote: "这个代价来自 Qwen 查询编码、稀疏路由、证据前缀和热点缓存,而不是免费获得的能力。"
},
/* ---------- 容量与存储 ---------- */
capacity: {
title: "容量与存储",
src: "对外技术总结 §4",
stats: [
{ k: "active records", v: "723", d: "本次运行实际装载" },
{ k: "pages", v: "23", d: "页容量 32 records/page" },
{ k: "设计容量", v: "1,048,576", d: "地址空间容量,非已装入数量" },
{ k: "GPU cache 上限", v: "256 records", d: "且 131,072 token" },
{ k: "实际 GPU cache token", v: "15,885", d: "" },
{ k: "GPU cache fallback", v: "0", d: "分配失败 0" },
{ k: "cold page", v: "0", d: "本次为 embedded / process-RAM 模式" }
],
storage: {
mode: "embedded weight-shard",
text: "记忆快照、路由器参数、页面元数据和短文本证据写入模型包的 memory safetensors 切片。启动时加载到进程内存,命中的热点记录才进入有界 GPU cache。",
tradeoff: "当前默认路径不使用 SQLite,也不使用磁盘分页。该设计减少了运行时依赖,但意味着模型包会随持久化记忆增长而变大,且需要重新保存权重切片才能固化更新。"
},
grid: { pagesTotal: 32768, perPage: 32, usedPages: 23 }
},
/* ---------- 记忆路由器工程线 ---------- */
router: {
title: "记忆路由器工程线",
src: "PRODUCTION_ROUTER.md · router_scorecard_final.md · V6_FINAL_REPORT.md · ZERO_OVERLAP_FINDINGS.md · WRITE_PATH_FIX.md",
subtitle: "128 维路由器替换原 512 维方案。以下为工程报告中的实测值。",
spec: [
{ k: "参数量", v: "2,037,774" },
{ k: "张量数", v: "16 (float32)" },
{ k: "地址字节", v: "512 B" },
{ k: "结构", v: "dim=128 · heads=8 · hidden=2560 · max_hops=3" },
{ k: "权重摘要", v: "sha256 69f8295e…d189deb · 8,156,921 B" },
{ k: "冻结评测集", v: "21,920 episode / 10 类" }
],
metrics: [
{ k: "Top-1 命中", base: "41.12%", nm2: "94.14%", note: "REPLAY-128" },
{ k: "Recall@1 / @3 / @5", base: "37.03 / 46.73 / 48.91", nm2: "88.58 / 96.48 / 97.15", note: "" },
{ k: "MRR / nDCG@3", base: "47.69 / 44.22", nm2: "95.77 / 95.37", note: "" },
{ k: "多跳证据全中 (Top-3)", base: "43.43%", nm2: "96.22%", note: "" },
{ k: "hop 正确率", base: "73.23%", nm2: "100.00%", note: "欠预测率 4.68% → 0.00%" },
{ k: "未知拒答率", base: "0.00%", nm2: "100.00%", note: "门槛 0.30–0.80 每点均为 100%" },
{ k: "单查询延迟", base: "1.0995 ms", nm2: "0.9663 ms", note: "路由 QPS 909.5 → 1034.8" },
{ k: "batch256 吞吐", base: "207,287", nm2: "242,759", note: "texts/s 口径" },
{ k: "零重叠改写 Top-1", base: "18.40%", nm2: "59.60%", note: "+41.20pp,随机基线 4.17%" },
{ k: "写入路径存活记录", base: "12/20", nm2: "20/20", note: "retract 16 → 0;e2e 37.50% → 68.75%" },
{ k: "逐轴判定", base: "—", nm2: "99 通过 / 11 未通过", note: "128 维两条线 22/22 全方位超越" }
],
caveats: [
"端到端答案不由路由器决定:93.75% 的读取仍走旧版 16 槽注入路径,地址命中 0/16,记录级排序由 text_retriever 决定(其 Top-1 仅 23.20%)。路由器 +41.20pp 不传递到最终答案。",
"512 维方案「全部指标不劣于基线」在数学上不可达(地址存储 ×4 与 batch-256 吞吐两条独立障碍);XL 架构准确率最高,但吞吐真实低 26–52%。",
"拒答机制只在域内成立:跨域 A 段可回答 0.00%,集成门已回退为 gate=false;开放词表属性匹配仅 49.20% / AUC 0.6560。",
"属性头词表固定 24 类闭集:训练后新写入的属性会被判为 NONE 而误拒。",
"同一指标在多份报告中的延迟口径不一致(1.0995→0.9663、0.9549→0.9169 等),本页只列其中一套。",
"长上下文:8K 可跑,16K / 32K OOM。不得据此宣称百万级 token 上下文。"
]
},
/* ---------- 边界:不能宣称的内容 ---------- */
limits: {
title: "我们不宣称的内容",
subtitle: "以下说法目前没有足够证据支持。把它们放在这里,是因为它们和上面的数字一样重要。",
claims: [
"Natural Memory 已经等价于百万 token 的完整 KV",
"Natural Memory 已经击败生产级 embedding + reranker RAG",
"任何自然语言表达都能稳定召回正确记忆",
"记忆写入永远不会出错",
"一百万条记录已经完成端到端验证",
"当前 4B 结果可以直接外推到 14B、32B 或更大模型",
"reserved VRAM 就是实际模型峰值显存",
"本次用户事实测试代表大规模真实用户群体"
],
limitations: [
{ t: "训练数据仍不够自然", d: "路由器主体训练仍来自本地构造的实体—属性—值事实和 hard negatives。自然改写仍是人工编写的测试变体,不是大规模脱敏真人会话。" },
{ t: "真实仓库的语义索引覆盖有限", d: "620 个真实分片全部进库,但为控制 GPU 压力,只有 96 个符号目标和 8 个操作性事实建立了 Qwen 语义地址。这不等于整个仓库都被高质量索引。" },
{ t: "开放式解释弱于明确定位", d: "「某个函数在哪个文件」明显好于「结合多个文件解释系统为什么这样设计」。后者需要多跳证据合并,仍会出现证据已召回但回答不完整。" },
{ t: "速度仍有真实代价", d: "平均延迟高约 2.58%,解码速度低约 9.90%,输入前缀显著变长。" },
{ t: "生产级基线还不完整", d: "本次对照聚焦原版 Qwen 无记忆基线;要作服务经济学结论,还需加入强 embedding 检索器、cross-encoder、滑动窗口和分页 KV。" }
],
quotes: [
{ q: "Memory Slot 不模拟完整 KV。它只接管 KV 中最昂贵、最适合长期保存、最容易重复利用的部分。", s: "README_NATURAL_MEMORY_V2.md" },
{ q: "这些限制是设计边界,不是用一个「无限上下文」数字掩盖的未验证假设。", s: "README_NATURAL_MEMORY_V2.md" },
{ q: "任何排序器都救不回一条已经不存在的记录。", s: "WRITE_PATH_FIX.md" },
{ q: "这不是调参空间不够,而是这个信号根本不存在。", s: "ABSTENTION_BREAKTHROUGH.md" }
]
},
/* ---------- 已解决的问题 ---------- */
solved: [
"普通自然语言触发自动记忆读取,不要求用户输入 /remember",
"模型重启后不回放历史聊天,只加载已保存的 memory shard",
"旧值和新值保留版本关系,旧值不会继续作为 active 事实参与正常读取",
"记忆读取器、地址索引和证据注入位于模型适配架构内部",
"记忆主体留在进程内存,热点记录使用有界 GPU cache",
"当前问题只读取 Top-K 记录,不对全量 slot 做注意力",
"低置信度写入、撤回和审计状态有独立表示",
"在真实工程源码定位任务上,相比无记忆 Qwen 有明显的可回答性提升"
],
/* ---------- 路线图 ---------- */
roadmap: [
"使用有用户同意的脱敏对话,增加自然省略、指代、错别字、时间间隔、互相矛盾的旧说法和多轮项目协作样本",
"重新训练路由器,重点覆盖「同一事实的多种问法」和「相似但错误的记录」",
"对「检索正确但回答错误」的样本单独训练证据使用与拒答策略",
"将真实仓库的语义地址覆盖从目标记录扩展到完整索引,仍保持单条/小批次编码以保护显存",
"加入多跳证据合并和当前版本优先规则",
"将写入、冲突解决和摘要从生成热路径移到回合边界或后台线程",
"把显存测试改为记录真正的 CUDA high-water mark,并分别报告 live allocated、reserved 和 cache payload",
"在同一协议下补齐强 RAG、滑动窗口和分页 KV 的成本—质量曲线"
],
/* ---------- 复现 ---------- */
reproduce: {
cwd: "H:\\Memory",
python: "C:\\Users\\Administrator\\miniconda3\\envs\\LLM\\python.exe",
cmd: [
"& C:\\Users\\Administrator\\miniconda3\\envs\\LLM\\python.exe `",
" -m dynamic_memory_lab.benchmark_dirty_real_corpus_4b `",
" --project-records 8192 `",
" --project-targets 96 `",
" --max-new-tokens 64 `",
" --encode-batch-size 1 `",
" --skip-chunk-rag `",
" --output H:\\Memory\\dynamic_memory_lab\\dirty_real_corpus_compare_4b.json"
],
files: [
{ k: "原始结果", v: "H:\\Memory\\dynamic_memory_lab\\dirty_real_corpus_compare_4b.json" },
{ k: "评测脚本", v: "H:\\Memory\\dynamic_memory_lab\\benchmark_dirty_real_corpus_4b.py" },
{ k: "对外技术说明", v: "H:\\Memory\\dynamic_memory_lab\\Natural_Memory_v2_对外技术总结.md" }
]
},
/* ---------- 结论 ---------- */
conclusion:
"Natural Memory v2 当前已经不是只有构造数据上的演示:在真实工程源码、文档和本项目用户要求组成的脏数据迁移测试中," +
"它显著提高了窗口外事实的可回答性,并在 10 GiB GPU 进程上完成了 723 条 active records 的读取运行。" +
"同时,62.60% 的可回答正确率、88.89% 的未知拒答正确率和约 10% 的解码速度下降说明它仍处于研究原型到工程验证之间。"
};