Natural Memory Agent Lab

为 Natural Memory v2(Qwen3.5-4B + 模型内记忆层) 量身定制的测试 Agent 框架。 基座是 Pi Agent Harness(MIT)——用它的 pi-agent-core(带状态管理与事件流的 agent runtime)与 pi-ai(统一多 provider LLM API), 而不是自己重写一个 agent 循环。

为什么用 Pi 而不是别的

理由 事实
provider 接线是现成的 pi-ai 已有 openai-completions 适配器,provider 配置原生支持 baseUrl;本机 DSH 的 Grok/GPT provider 就是这么接的。不需要改 Pi 的代码
插桩是一等公民 agent.subscribe(event => …) 提供 `agent_start / turn_start / message_* / tool_execution_start
多会话天然支持 stateful agent + 独立 SQLite session backend;本框架每个会话新建 Agent(空上下文),而模型记忆持续存在——这正是被测能力
许可 MIT(© 2025 Mario Zechner),改/fork 无约束

架构:进程与语言双重隔离

┌──────────────────────────────┐         HTTP (OpenAI protocol)        ┌────────────────────────────┐
│ model_server/server.py       │ ◄───────────────────────────────────► │ agent/  (Node + Bun)       │
│ 【LLM conda 环境】            │   /v1/chat/completions (tools)        │  pi-agent-core + pi-ai     │
│  torch 2.9/cu128 原样不动     │   /v1/memory/*  /v1/diagnostics       │  自己的 node_modules       │
│  复用 natural_memory_service  │                                       │  工具 / 场景 / 记录          │
└──────────────────────────────┘                                       └────────────────────────────┘

模型侧留在原有的 LLM conda 环境(torch 2.9 + bitsandbytes 不能被 agent 依赖污染), Agent 侧是独立的 Node 工程——两边只通过 HTTP 说话。

完整接管 NM2:观测面 + 控制面 + 路由即工具

项目的 natural_memory_service.py 只给了一个单条消息的 /v1/chat 和少量记录管理接口。 要「接管」NM2,agent 需要三样它给不了的东西,本 server 全部补齐:

1. 观测面(NM2 内部其实早就记录了,只是没人暴露)

暴露出来的状态 含义
v2_last_decisions 每轮路由决策的全部字段:need_memory、页/记录 id、页/记录分数、hop_count、hop_trace、stop_reason、confidence、score_margin、evidence_score
text_read_seconds 路由+重排耗时(与解码耗时分离,便于成本归因)
text_prefix_tokens / text_prefix_used 实际注入的前缀 token 数
auto_memory_probability / auto_memory_forget_probability 自动记忆策略头的写入/遗忘概率
context_compaction KV 自动压缩报告
v2_no_evidence 路由器是否判定无证据

每次调用后 server 会 snapshot 这些状态并保留逐轮 trace 历史(GET /v1/nm2/trace)。

2. 控制面(15 个可运行时改写的旋钮 + 治理操作 + 用户隔离)

类别 接口
旋钮读写 GET/POST /v1/nm2/config —— top_k_pages top_k_records max_hops hot_pages read_threshold write_threshold min_read_margin require_evidence auto_memory_threshold auto_forget_threshold gpu_cache_records gpu_cache_tokens context_chunk_tokens kv_budget_tokens kv_keep_recent_tokens(白名单 + 类型/范围校验,拒绝非法键)
记录治理 POST /v1/nm2/write(可信显式写入,带 entity/attribute/value/importance/confidence)、/correct(生成新版本)、/retract、/approve(quarantine → active,此前模型没有公开包装)、GET /v1/nm2/records、/audit、/export
会话/用户隔离 POST /v1/nm2/session —— save/load/list/delete/reset,走 MemoryOSV2.export_payload() / from_payload(),完整搬移记录、页、quarantine、检索阈值与 KV 预算(阈值随会话走,不随进程走)
KV 预算 POST /v1/nm2/compact —— 把长上下文压缩成 NM2 记录并返回热窗口报告
请求级覆盖 chat 请求体里的 nm2: { top_k_pages, top_k_records, max_hops, read_threshold, ... },只在该次请求内生效(进入前保存旧值、退出时恢复)

3. 路由即工具:POST /v1/nm2/probe

只跑「粗索引 → 候选页 → 页内重排 → Top-K」这条有界路径,不生成任何 token,直接返回: 决策(need/hop/stop_reason/margin/evidence)、命中的记录及其分数、以及 encode_seconds / route_seconds 分解。 这让「这里记忆会给我什么」变成 agent 可调用、harness 可评分的一等操作。

Agent 侧对应的工具

工具 action 用途
nm2_read probe / search / trace / audit / stats 探测路由、检索记录、回看逐轮决策与延迟
nm2_write write / correct / retract / approve 记忆治理
nm2_admin config_get / config_set / compact / session / reset 调参、压缩、切换用户

只用三个工具而不是二十个:4B 模型对「少量工具 + 明确 action 枚举」的可靠性远高于长工具表。

仍然保留的(原有能力,未删)

  • 用模型自己的 chat template 渲染完整消息列表,原生支持 Qwen 的 <tool_call> / <tool_response>,是真正的 function calling;
  • 保持项目记忆语义:用户轮触发写入、生成只读、reset token 生效、写入自动持久化;
  • memory_mode = on | read_only | off,即无记忆对照组的开关。

Agent 侧做了什么

文件 作用
src/provider.ts 把本机模型注册成 pi-ai 的一个 provider(createProvider + OpenAICompletions)
src/memory.ts 模型 server 的记忆/诊断客户端 + MemoryRecorder(逐轮记录,忠实于「每次用户消息 = 一条记录」而不是内部循环次数)
src/tools.ts 工具集:run_shell / read_file / write_file / http_get / notes / memory_admin
src/agent.ts createTestAgent():Pi Agent + 事件插桩 + ask() 一次用户往返
src/run_scenario.ts 多会话场景编排、双组(记忆开/关)、断言评分、写出 JSONL 轨迹与 summary.json
scenarios/*.json 场景套件

两个刻意的设计

  1. notes 工具(文件便签)是故意的。 对照组若没有任何外部记忆,比较的就不是「模型内记忆 vs 无记忆」而是 「有记忆 vs 什么都没有」。两组都给同一份文件便签,才能真正隔离被测变量。
  2. memory_admin 把记忆治理变成可评测的 Agent 能力(列出/审计/撤回),而不是看不见的旁路。

场景套件

场景 考的是什么
cross_session_recall 会话 1 写入 → 会话 2/3(全新上下文、无历史文本)询问
conflict_update 同属性旧值→新值,跨会话必须返回新值且旧值不得活跃
unknown_abstention 从未提及的属性必须明确说不知道,不许编造或硬套相似记忆
multi_hop_chain 项目→负责人→工号,两跳串联
forget_retraction 明确要求遗忘后,跨会话不得再使用该事实

运行

# 1) 启动模型 server(在 LLM conda 环境,占用 GPU)
$env:PYTHONPATH = 'H:\Memory'
& C:\Users\Administrator\miniconda3\envs\LLM\python.exe H:\Memory\agent_lab\model_server\server.py `
    --model-path qwen3_5_4b_natural_memory_v2 --port 8766

# 2) 安装 agent 侧依赖(独立于模型环境)
cd H:\Memory\agent_lab\agent
bun install
bun run typecheck

# 3) 跑单个场景(双组对照)
$env:NATURAL_MEMORY_API_KEY = 'local'
bun run scenario --scenario scenarios/cross_session_recall.json --arm both

# 4) 跑全套
bun run suite

产物:runs/<timestamp>/<scenario>.<arm>.jsonl(逐轮轨迹:用户消息、助手回答、工具调用与结果、记忆诊断) 与 runs/<timestamp>/summary.json(每场景每组的断言通过率、活跃记录数、每轮耗时)。

当前状态(重要,勿误读)

  • 模型 server:已成功启动过一次({"event":"ready", ...},memory_mode: on),但因用户正在满载训练路由器、 需要让出 GPU,在 tool call 往返验证之前就主动停掉了。
  • Agent 侧:代码按 [email protected] 与 pi-agent-core 的实测 API 面编写(createProvider / Model 字段 / AgentTool / 事件名均已核对本机安装与官方 README),但尚未 bun install、尚未 typecheck、尚未真正跑过。 首次运行预计需要少量类型修正。
  • 因此:目前没有任何关于该模型 Agent 能力的实测结论。

后续可加

  • pass^k 式重复采样稳定性(参考 τ-bench)
  • 用户模拟器(多轮协商类场景)
  • 记忆容量压力场景(逼近 max_pages / 淘汰行为)
  • 多用户隔离场景(两个 user id 交叉询问,验证互不污染)
  • 工具输出是否被误写入长期记忆(记忆污染)
S
Description
Natural Memory Agent Lab:模型服务 + TypeScript agent + 记忆能力场景评测(冲突更新、跨会话回忆、遗忘撤回、多跳、未知拒答)
Readme
216 KiB
Languages
TypeScript 50.3%
Python 49.7%