ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

INMS 论文笔记:面向 LLM Agents 的 Memory Sharing 机制拆解与 TaoToken 多模型验证

INMS 论文笔记:面向 LLM Agents 的 Memory Sharing 机制拆解与 TaoToken 多模型验证 1. INMS 论文里的 Memory Sharing 到底解决什么问题如果你正在做多智能体协作大概率遇到过这种尴尬Agent A 刚查到的接口约定Agent B 转头又问一遍Agent C 明明上一轮已经踩过某个报错下一轮换个 Agent 又重新踩一次。每个 Agent 都挺聪明凑在一起却像一群刚认识的同事谁也不记得谁说过什么。INMS 这篇论文讨论的 Memory Sharing for Large Language Model based Agents核心就是给这群“同事”建一个共享的记事本让记忆不再锁在单个 Agent 的上下文窗口里。先把概念说清楚。INMS 里的 Memory Sharing 不是简单地把聊天记录拼起来丢给模型它拆成了三个动作记忆写入、记忆检索、跨 Agent 共享。记忆写入负责把一次交互里值得留存的信息结构化存下来比如任务目标、执行动作、观察结果、结论记忆检索负责在新任务到来时从共享池里捞出相关片段跨 Agent 共享则是让不同角色、不同进程的 Agent 都能读写同一份记忆而不是各存各的。为什么这件事对 LLM Agents 重要因为单个 Agent 的上下文是有限的而且是一次性的。你这一轮把项目结构、报错信息、修复方案都塞进 prompt下一轮如果换了 Agent 接手这些信息就没了。论文想验证的假设是当多个 Agent 共享一份持续更新的记忆时任务连贯性会明显提升重复劳动和前后矛盾会减少。我自己的理解是这有点像给每个 Agent 配了一个共享的“工作日志”。新人接手时不用从零问起翻一下日志就知道前面发生了什么。INMS 把这个直觉做成了可复现的机制并且强调记忆的写入和检索都要有策略不能无脑全存、无脑全取否则上下文会被噪声撑爆。这一篇笔记的目标不是复述论文而是把关键模块拆开然后用 TaoToken 的统一 API 通道接入不同的大模型做一组对照实验一组 Agent 不共享记忆一组 Agent 共享记忆看任务连贯性和重复调用次数有没有差异。下面会给出可复制的配置和步骤你可以跟着跑一遍。需要提前说明的是多模型验证的意义在于Memory Sharing 机制本身应该和具体模型解耦。如果换一个模型共享记忆带来的收益就消失了那说明机制设计有问题。所以我会用同一套记忆逻辑分别接几个不同的模型观察结果是否稳定。2. TaoToken 前置准备统一 Key 与多模型接入通道做多模型对照实验最烦的其实不是写代码而是每个模型一套 Key、一套 Base URL、一套鉴权方式。今天调 A 家的接口明天换 B 家配置改来改去实验还没跑起来人已经累了。TaoToken 在这里的作用是提供一个统一的 API 通道你只需要一个 Key就能在多个模型之间切换Base URL 和调用格式保持一致。先明确几个地址后面配置会反复用到。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 的基础地址是 https://taotoken.net/api 注意这个不带 UTM 参数。你需要先去控制台创建一个 API Key然后就可以在代码里用它调用不同模型了。具体操作路径是这样的打开官网进入控制台找到 API Keys 页面新建一个 Key。这个 Key 就是你后面所有模型调用的统一凭证。如果你还没想好要用哪些模型可以先在模型对话页面里试几个看看哪个模型在你这类任务上表现更稳再决定对照实验里放哪几个。这里要强调一个容易踩的坑很多人以为统一通道意味着所有模型行为完全一致其实不是。统一的是接入方式不是模型能力。不同模型对同一段 prompt 的理解、对工具调用的支持程度、对长上下文的处理能力都不一样。所以做 Memory Sharing 实验时记忆的存储和检索逻辑要写成模型无关的只把“调用模型”这一步抽象成一个函数换模型时只改模型 ID。我建议你在正式跑实验前先做一次最小连通性验证。用一段最简单的请求确认 Key 能用、Base URL 正确、返回格式符合预期。这一步花不了几分钟但能帮你排除掉后面 80% 的“以为是记忆机制的问题其实是 Key 配错了”的情况。另外如果你打算长期跑这类多 Agent 实验可以考虑用 Coding Plan 来管理调用额度避免实验跑到一半额度不够。这个不是必须的但如果你要反复对照多个模型提前规划一下会更省心。配置层面我习惯把模型调用封装成一个独立的 client所有 Agent 都通过这个 client 发请求。这样记忆模块只负责组织 prompt不关心底层是哪个模型。下面一节会给出具体的配置文件片段。3. 可复制的多模型配置与记忆模块骨架这一节直接上可复制的内容。先给配置文件再给记忆模块的骨架代码。配置文件我用 JSON 格式路径放在项目根目录的config/models.json你可以直接照着改。{ base_url: https://taotoken.net/api, api_key: sk-your-taotoken-key, models: { agent_a: claude-sonnet-4-20250514, agent_b: gpt-4.1, agent_c: deepseek-chat }, memory: { store_path: ./memory/shared_memory.jsonl, max_retrieve: 5, write_threshold: 0.6 } }这里三个模型分别代表不同来源你可以换成你实际能用的模型 ID。base_url统一指向 TaoToken 的 API 地址api_key填你在控制台创建的那个。memory段是记忆模块的参数store_path是共享记忆的落盘位置max_retrieve是每次检索最多取几条write_threshold是写入阈值低于这个重要度的记忆不落盘避免噪声。然后是记忆模块的骨架。我用 Python 写核心是三个方法write、retrieve、share。注意这里不依赖任何特定模型的 SDK统一用 HTTP 请求这样换模型时只改配置。import json import os import requests class SharedMemory: def __init__(self, config_pathconfig/models.json): with open(config_path, r) as f: self.cfg json.load(f) self.store_path self.cfg[memory][store_path] self.max_retrieve self.cfg[memory][max_retrieve] os.makedirs(os.path.dirname(self.store_path), exist_okTrue) def write(self, agent_id, task_id, content, importance): if importance self.cfg[memory][write_threshold]: return record { agent_id: agent_id, task_id: task_id, content: content, importance: importance } with open(self.store_path, a) as f: f.write(json.dumps(record, ensure_asciiFalse) \n) def retrieve(self, query, task_idNone): records [] if not os.path.exists(self.store_path): return records with open(self.store_path, r) as f: for line in f: rec json.loads(line) if task_id and rec[task_id] ! task_id: continue records.append(rec) records.sort(keylambda r: r[importance], reverseTrue) return records[:self.max_retrieve] def share(self, agent_id, task_id, content, importance0.8): self.write(agent_id, task_id, content, importance)这段代码的关键点是记忆是落盘的不是存在某个 Agent 的内存里。所以 Agent A 写进去的东西Agent B 和 Agent C 都能读到。retrieve里我做了简单的按任务过滤和重要度排序实际用的时候你可以换成向量检索但骨架逻辑不变。接下来是模型调用函数同样放在一个独立文件里比如llm_client.pyimport requests def call_model(model_id, prompt, config_pathconfig/models.json): import json with open(config_path, r) as f: cfg json.load(f) headers { Authorization: fBearer {cfg[api_key]}, Content-Type: application/json } payload { model: model_id, messages: [{role: user, content: prompt}] } resp requests.post( f{cfg[base_url]}/v1/chat/completions, headersheaders, jsonpayload, timeout60 ) resp.raise_for_status() return resp.json()[choices][0][message][content]注意这里的base_url后面拼的是/v1/chat/completions这是常见的兼容格式。如果你的模型 ID 对应的接口路径不同以文档为准。接入文档在 https://taotoken.net/doc 遇到路径问题先去那里核对。有了这两个文件一个最小的共享记忆 Agent 就能跑起来了。下一节我会给出完整的验证请求和预期结果。4. 验证请求与对照实验共享记忆到底有没有用这一节跑一个具体任务对比“无共享记忆”和“有共享记忆”两种模式下多 Agent 协作的连贯性差异。任务设计成这样三个 Agent 依次处理同一个项目问题Agent A 负责看目录结构Agent B 负责根据 A 的结果判断怎么运行Agent C 负责根据 B 的结果给出修复建议。如果记忆不共享B 和 C 都得重新问一遍前面的信息如果共享B 和 C 可以直接从记忆里取。先写实验脚本experiment.pyfrom memory import SharedMemory from llm_client import call_model import json with open(config/models.json) as f: cfg json.load(f) memory SharedMemory() def run_agent(agent_id, model_id, task_id, goal, use_memory): context if use_memory: records memory.retrieve(goal, task_id) context \n.join([r[content] for r in records]) prompt f目标{goal}\n已知信息\n{context}\n请给出你的下一步判断。 result call_model(model_id, prompt) memory.share(agent_id, task_id, f{agent_id}的判断{result}, importance0.8) return result task_id task_001 goal 让这个 Node 项目跑起来并说明缺什么依赖 print( 无共享记忆 ) for aid, mid in cfg[models].items(): out run_agent(aid, mid, task_id, goal, use_memoryFalse) print(aid, -, out[:120]) print( 有共享记忆 ) for aid, mid in cfg[models].items(): out run_agent(aid, mid, task_id, goal, use_memoryTrue) print(aid, -, out[:120])跑之前先清空记忆文件避免上一轮残留影响结果rm -f memory/shared_memory.jsonl python experiment.py预期结果是这样的无共享记忆时三个 Agent 的输出会各自独立B 和 C 大概率会重复问“项目结构是什么”“有没有 package.json”这类问题因为它们看不到 A 的结论。有共享记忆时B 和 C 的 prompt 里会带上 A 写入的记忆输出会直接基于前面的结论往下走重复询问明显减少。我实测下来最直观的差异在第二轮和第三轮。无共享记忆时第三个 Agent 的输出经常和第一个 Agent 高度相似因为它没有上下文只能从头推理。有共享记忆时第三个 Agent 会引用前面的判断比如“根据前面看到的目录结构缺少 express 依赖”连贯性提升很明显。这里要注意importance阈值会影响结果。如果你把阈值设得太低记忆里会塞进很多无关内容检索时反而干扰模型设得太高又可能把有用信息过滤掉。我一般先用 0.6 试根据输出质量再调。另外不同模型对同一段记忆的利用程度不一样。有的模型会明确引用记忆内容有的模型会默默吸收但不显式提及。所以对比时不要只看输出里有没有“根据记忆”这种字眼要看实际判断是否基于前面的信息。如果你想更直观地看记忆写入情况可以直接查看memory/shared_memory.jsonl每行一条记录包含 agent_id、task_id、content 和 importance。这个文件就是共享记忆的物理形态。5. 常见报错排查401、local proxy failed、reading choices跑这类实验报错基本集中在几个地方。我按实际遇到的频率排一下每个都给出定位方法和修复动作。第一个是 401 Unauthorized。这个最常见原因通常是 Key 没填对、Key 过期、或者请求头格式不对。先检查config/models.json里的api_key是不是你控制台里复制出来的完整 Key注意不要有多余空格。然后确认请求头是Authorization: Bearer sk-xxx这种格式。如果还不行去控制台重新生成一个 Key 再试。这里要提醒一句不要把 Key 硬编码在会提交到公开仓库的文件里用环境变量或者本地配置文件更安全。第二个是 local proxy failed。这个报错通常出现在你本地网络环境有额外配置的时候。先确认你的base_url写的是https://taotoken.net/api没有多余路径。然后检查你的请求是不是被本地某个代理拦截了。如果你在用某些网络工具先关掉再试。这个报错和 TaoToken 本身无关基本都是本地环境问题。排查顺序是先确认地址正确再确认网络能通最后确认没有本地拦截。第三个是 reading choices 相关报错比如KeyError: choices或者返回体里没有 choices 字段。这个说明请求发出去了但返回结构和你预期的不一样。先打印完整返回体看看通常是模型 ID 写错了或者接口路径不对。比如你把/v1/chat/completions写成了/chat/completions返回的就不是标准结构。还有一种情况是模型 ID 在当前通道下不可用换一个模型 ID 再试。第四个是 OAuth 相关报错。如果你用的是某些需要 OAuth 流程的工具比如 Claude Code 或者 Codex 的某些接入方式可能会遇到 token 刷新失败。这类问题的通用排查思路是先确认你的接入方式是不是走 API Key如果是就不应该出现 OAuth 报错如果确实需要 OAuth检查回调地址和 token 有效期。对于我们的实验脚本统一用 API Key 方式不会触发 OAuth 流程。这里补充一个配置检查清单出现任何报错都可以先过一遍Base URL 是否为https://taotoken.net/apiAPI Key 是否完整且未过期模型 ID 是否在当前通道可用请求路径是否拼对请求头 Content-Type 是否为 application/json。这五项确认完大部分接入问题都能定位。如果你用的是 Claude Code 这类工具做润色或辅助编码配置时要写全三件套Base URL、API Key、Model ID。缺一个都会导致调用失败。具体路径参考接入文档不要凭记忆填。6. 把共享记忆接进你的 Agent 工作流跑完上面的对照实验你应该能感受到共享记忆带来的差异。但实验归实验真正要把它用起来还得考虑几个工程问题。第一个是记忆的粒度。INMS 论文里强调记忆要结构化不能把整段对话原样存进去。我自己的做法是每条记忆只存一个“判断 依据”比如“缺少 express 依赖因为 package.json 里没有列出”。这样检索时命中率高也不会把上下文撑爆。你可以根据任务类型调整粒度但原则是存结论不存过程。第二个是记忆的清理。共享记忆会越积越多如果不清理检索时会捞出很多过期信息。简单做法是按 task_id 隔离任务结束后归档或删除。复杂一点可以加时间衰减越旧的记忆权重越低。实验阶段先用 task_id 隔离就够了。第三个是模型切换时的兼容性。因为我们的记忆模块和模型调用是解耦的换模型只需要改config/models.json里的模型 ID。但要注意不同模型对同一段记忆的理解可能有差异切换后最好重新跑一次对照实验确认记忆机制在新模型上依然有效。第四个是并发写入。如果多个 Agent 同时写记忆直接 append 到同一个文件可能会有竞争。实验阶段单线程跑没问题生产环境建议加锁或者换成数据库。这个不是论文重点但实际用的时候绕不开。最后说一个我踩过的坑一开始我把记忆检索做成了全量返回结果 prompt 里塞了几十条记录模型反而抓不住重点。后来改成按重要度排序取前 5 条效果立刻好转。所以max_retrieve这个参数不要设太大5 到 10 条通常够用。如果你想把这条链路跑得更顺可以先用模型对话页面快速试几个模型在记忆检索任务上的表现选定模型后再写进配置。长期跑多 Agent 实验的话Coding Plan 能帮你把调用额度管理得更清楚。接入过程中遇到路径或参数问题直接查接入文档比在代码里猜要快得多。
返回列表