Files
natural-memory-agent-lab/PRODUCTION_TEST_REPORT.md
T

11 KiB
Raw Blame History

NM2 模型 + Agent 生产级测试报告

测试对象:H:\Memory\V2_dpskw\qwen3_5_4b_natural_memory_v2(NM2.0) 测试方式:真任务,不是合成场景;每条结论都配一台机器上可复现的客观基准 测试期:2026-09-15(Agent 侧来自本次会话的多轮实测) 被测设施:agent_lab/model_server/server.py(控制面)+ agent_lab/agent(Pi Agent,11 个工具)


0. 一句话结论

记忆这层已经站住了;挡在"每天都要用"前面的是模型侧的行为,不是记忆检索。

七个测试族里 六个可判定通过(各带基准),长时间稳定性因显卡被占用未测完。 过程中修掉 1 个模型包真 bug(属性抽取把盘符冒号当分隔符)、1 个沿用已久的结构性缺陷(更正无法版本化 Agent 写入的记录), 以及若干 harness 缺陷;并记录了我自己三次方法错误,供复核。


1. 测试方法(以及两次差点误判的地方)

  1. 先量基准,再判模型。所有数字(文件数、磁盘可用、文件首行)都在跑之前用 PowerShell 独立量出, 绝不用模型的输出去验证模型。
  2. 跨会话必须换进程。同一进程内的追问可能靠对话上下文答对,不能证明是记忆在起作用—— 凡是声称"记忆有效"的结论,都用全新进程、上下文为空复现过。
  3. 测延迟前必须查显卡。我的设施和用户的评测共用一块 RTX 5070。 第十一轮长跑里,每轮生成从 12.7 秒"涨到"137.6 秒,而 prompt 词元只涨 1.7 倍—— 比例不对,去查显卡才发现是用户自己的 locomo_nm21_e2e 评测占满 100%。 那是争用,不是会话退化。 差点当成性能衰减报出去。
  4. 测量 setup 必须复现真实时序。写入路径上"本轮的事实"在生成前还没进库, 我一度把两条记录都写进库再测,量到的是另一个问题。

2. 逐族结论

# 测试族 判定 客观基准 实测结果
1 真实文件理解 ✅ 通过 WRITE_PATH_FIX.md(4023 字符) 读完正确概括根因(confirmed_update 依赖学习打分 0.95、缺 (entity,attribute) 结构校验 → 误删)
2 真实命令与数据统计 ✅ 通过(修 harness 后) artifacts 顶层 46 个;V2_dpskw 递归 .py 133 个;C 盘可用 34.47 GB list_files→46 ✓、list_files{recursive}→133 ✓、disk_space→34.52 GB ✓
3 跨会话记忆支撑真活 ✅ 通过 新进程、上下文为空 召回命中(用记忆=要 · 命中 2 条 · 205 词元)→ 调工具 → 答对目录
4 更正必须生效 ✅ 通过(经三轮修复) 旧值应退位 改了:… → 旧记录 superseded、新进程答新路径;回归场景无污染
5 不知道时拒答 ✅ 通过 无 股票 →「我没有你购买的股票信息」;模型参数量 →「我不知道。没有证据表明…」
6 多步工具链 ✅ 通过(修 harness 后) 9 个 client-*.txt;首个 client-api-check.txt 首行 PASS /api/send 返回成功 两步全对,17.7 秒
7 长时间日用稳定性 ⚠️ 未测完 —— 24 轮只跑完 11 轮;已完成轮次全部答对,但耗时数据因显卡争用作废

3. 修掉的缺陷

3.1 模型包:属性抽取把盘符冒号当分隔符(真 bug)

infer_memory_metadata 的操作符列表含裸冒号:(?:…|是|为|叫|:|:|=)。 句子「我的评测报告放在 E:\deepseek\artifacts」用的是「放在」而非「是」,正则于是抓到盘符那个冒号:

属性='评测报告放在 E'   取值='\deepseek\artifacts'

后果:同一件事被归档在永远无法与 Agent 写的 评测报告路径 匹配的键下,旧值永远无法被取代。

修法:冒号前是单个 ASCII 字母时不作分隔符((?<![A-Za-z])[::])。CJK 后的冒号仍是正常分隔符。 修复后路径句返回 episode(宁可不给属性,也不给错属性)。

3.2 模型包:更正无法版本化「经接口写入」的记录(结构缺陷)

「措辞变了也要版本化」的补救路径按 slot_index 匹配继承对象; 而 Agent 经接口写入的记录 slot_index = -1(独立分页项,不占遗留槽位)。 凡是 Agent 自己写进记忆的事实,自动层永远无法把它版本化。 该段的注释本身写着它的来历(realistic 语料上 25/25 更新失败)——同一个 bug 类被修过一次,但修法绑死在槽位上。

修法:新增一条继承路径,但必须同时满足两道门:

门 理由
not candidate_conflict_key 解析器给出结构时一律不猜(原有代码就有这道门)
有身份记录自己的绝对分数 ≥ 5.0 实测两簇:真更新 8.591/8.6325/8.64,无关 ≤0.682/0.7421

为什么不用 score_margin(我第一版的做法):margin = top − second, 而自动层每一轮都会插一条近似重复的记录,把 margin 从 8.591 砸到 0.1523—— 那道门正好卡在它本该放行的那一类上。

3.3 我自己的三次方法错误(如实记录)

# 错误 后果 纠正
1 继承块漏了 not candidate_conflict_key 这道门 用读取器的"最近邻猜测"覆盖了解析器给出的正确结构,把 默认语言=日语 污染成 蓝鲸-47 已回退并补门;用仪表复现确认机理
2 标定正类用了「我的A改成 v2」 改成 本身会被结构解析,那批样本永远走不到继承块——population 标错了 按正确 population 重测,两簇间隔 0.74 vs 8.63
3 测延迟时未查显卡 把争用当成"会话退化"(12.7s→137.6s) 已作废该数据并记录纪律

3.4 harness 缺陷(Agent 侧)

缺陷 实测 修法
模型手写 PowerShell 必翻车 -eq 'C:' 比字符 → 成功但零输出 → 同一命令重发 10+ 次 → 会话被污染 新增窄接口工具 disk_space / list_files / peek_files,模型只填路径
工具输出只给裸文件名,逼模型自己拼路径 它拿到 9 个正确文件名后编了个 client-0001.txt list_files 每项返回完整路径
静默零输出无法与"还没查到"区分 模型陷入重试 零输出时明确解释"多半是过滤条件没匹配、不要再发同一条"
编造:问数量却跳过工具直接给数字 磁盘答 100 GB(真值 34.62);文件数答 0(真值 46) 编造检测 + 强制重问(问题问数量、零工具调用、回答含数字 → 追加一轮要求先调工具)
一次坏回合污染整段会话 失败命令的污染带进下一轮(99 秒) 熔断改同步 abort();会话卫生仍待查实(替换 agent.state.messages 实测未生效)
关掉记忆并不等于对照组 memory_mode=off 只关自动路径,显式工具调用照样能读写记忆 所有写入端点在该模式下 409 拒绝

4. 一个反直觉的结论

修好"编造"的不是检测器,而是窄接口工具。

加了 disk_space/list_files 之后,同一个跨会话场景(目录只存在于记忆里)它自己调了工具并答对 46 (此前答 0,一次工具没调)。合理解释:以前要么手写命令(易错)要么干脆不查;有了省事的正路,它就走正路。 检测/强制重问至今没在实战中触发过,属兜底。

推论:面对小模型,先把正确路径铺平,比教它不要犯错更有效。


5. 日用可用性判定

能力 判定
单步 + 窄接口工具 ✅ 可用(5–6 秒,答对)
多步链路(工具给完整路径) ✅ 可用(17.7 秒,两步都对)
跨会话记忆召回 ✅ 可用
拿不到就说拿不到 ✅ 可用
自然措辞的更正(「改了…」) ✅ 可用(经三轮修复后)
长时间(数十轮)稳定 ⚠️ 未验证(需独占显卡)
口语化、过短的更新(「我换到 X 了」) ❌ 路由弃权,自动层不版本化(路由自身的门)
自己拼路径 / 手写 PowerShell ❌ 不可用(已用完整路径与窄接口工具规避)

6. 按用户方针做的收敛

用户方针:「我希望更多配置由模型决定,外部不要传入那么多参数(必要的保留)」。 据此删掉了 Agent 面上一批覆盖模型已学判断的参数:nm2_write 的 importance/confidence (检查点本就有学到的 importance 头)、config_set 的 read_threshold/min_read_margin/auto_*_threshold/ max_hops/coverage_*/top_k_* 等。保留机器硬约束(KV 预算、缓存、分块)。 HTTP 的 /v1/nm2/config 仍保留全部旋钮——那是研究脚本在用的,收敛的是 Agent 能看到、能传的参数。


7. 未完成与复现

未完成

  1. 长时间稳定性:24 轮只跑完 11 轮,因用户评测占满显卡而中止;重跑前先查显卡。
  2. 更正修复的生产形态复跑(上轮同样被显卡占用打断)。
  3. 会话卫生:agent.state.messages 替换实测未生效(下一轮仍复读旧对话),需换一条能真正清尾的路子。
  4. 两代模型对照:全程只用 NM2.0,NM2.1 未做同一套生产测试(代码共用,但权重不同)。

复现

# 控制面(占卡;先确认显卡空闲!)
$env:PYTHONPATH='H:\Memory'
& C:\Users\Administrator\miniconda3\envs\LLM\python.exe H:\Memory\agent_lab\model_server\server.py `
    --model-path H:\Memory\V2_dpskw\qwen3_5_4b_natural_memory_v2 --port 8766 --no-auto-persist

# 控制面自检
& C:\Users\Administrator\miniconda3\envs\LLM\python.exe H:\Memory\agent_lab\model_server\selftest.py

# Agent 终端(真任务)
cd H:\Memory\agent_lab\agent
$env:NATURAL_MEMORY_API_KEY='local'
$env:NATURAL_MEMORY_BASE_URL='http://127.0.0.1:8766/v1'
bun run tui
# 脚本化多轮:bun run tui --session x --say '…' --say '…'

# CPU 测试(不占卡):92 项
cd H:\Memory
& C:\Users\Administrator\miniconda3\envs\LLM\python.exe -m unittest discover -s V2_dpskw\tests -t . -p 'test_*.py'

# margin / 分数判别力测量
& C:\Users\Administrator\miniconda3\envs\LLM\python.exe H:\Memory\agent_lab\measure_inherit_margin.py --rounds 3

逐轮原始记录

H:\Memory\agent_lab\RESULTS.md §6.10(第一轮到第十一轮),含每次的基准、命令、原始输出与当时的判断。


8. 结论

  • 记忆层可以用了:跨会话召回、更正生效、拒答、遗忘,都在真任务上验证过,且带了防误伤的回归检查。
  • 模型本身仍是瓶颈:它不会自己拼路径、写不对 PowerShell、会给没出处的数字。 这三条目前都是用工具设计绕过去的,不是修好了。
  • 一个可推广的经验:给小模型铺一条"正确且省事"的路(窄接口工具 + 可直接照抄的输出), 比反复叮嘱它别犯错有效得多——编造就是这么消失的。