news 2026/9/15 0:59:07

SurfSense 团队记忆协议(Team Memory Protocol)深度解析:多智能体系统中团队持久记忆的写入策略与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SurfSense 团队记忆协议(Team Memory Protocol)深度解析:多智能体系统中团队持久记忆的写入策略与实现

SurfSense 团队记忆协议(Team Memory Protocol)深度解析:多智能体系统中团队持久记忆的写入策略与实现

【免费下载链接】SurfSenseOpen-source NotebookLM alternative. Research the open web with live data(Reddit, YT, IG, TikTok, Indeed, Google Search, Maps etc) through one platform, API or MCP server. Join our Discord: https://discord.gg/ejRNvftDp9项目地址: https://gitcode.com/GitHub_Trending/su/SurfSense

SurfSense 是一个开源的 NotebookLM 替代方案,其核心是运行在surfsense_backend中基于 LangGraph 构建的多智能体聊天架构。在系统提示词中,<memory_protocol>的 team 变体(team.md)定义了一套完整的团队级长期记忆写入规范:主智能体如何识别、判定并持久化关于团队本身的持久事实——决策、约定、架构笔记、流程与关键事实。读完本文你将掌握:团队记忆的判定标准、update_memory工具的正确调用时机与参数契约、heading-based Markdown 记忆格式规范、软/硬容量限制的处理策略,以及这套协议从系统提示词组装、每轮记忆注入、工具执行到后端校验的完整落地链路与源码证据。

一、协议概述:团队记忆是什么

team.md定义的是workspace(工作空间)维度的共享长期记忆。与用户个人记忆(private.md)相对,团队记忆存储的是关于这个团队/工作空间的持久事实,而不是关于某个具体用户的个人信息。

协议的核心判定逻辑可以浓缩为一句话:

在理解每一条用户消息之后,检查它是否揭示了关于团队的持久事实——决策(decisions)、约定(conventions)、架构笔记(architecture notes)、流程(processes)或关键事实(key facts)。

这意味着团队记忆关注的是团队成员之间共享、跨会话仍然有意义的信息,例如:

  • 团队决定了每周一开例会(应归入## Product Decisions);
  • 项目约定使用某种数据库或技术栈(应归入## Engineering Conventions);
  • 办公室地址等团队级事实(应归入## Project Facts)。

反过来,协议明确排除的类别是瞬时聊天噪音(ephemeral chat noise),包括:

  • 一次性问答(one-off Q/A);
  • 问候语(greetings);
  • 会话物流(session logistics,例如"我们这周先把文档整理完"这类临时安排)。

这些内容不入库,避免记忆文档被噪声污染。

与用户个人记忆(private.md)的边界

SurfSense 将记忆按可见性划分为两类,二者的协议模板并列存放在同一目录下:

维度团队记忆(team)用户个人记忆(private)
协议文件team.mdprivate.md
作用域Workspace(工作空间)共享单个用户
记录对象决策、约定、架构笔记、流程、关键事实角色、兴趣、偏好、项目、背景、长期指示
禁止项个人资料、个人偏好、仅针对个人的长期指示无(本身就是个人维度)
建议 heading## Product Decisions## Engineering Conventions## Project Facts## Open Questions## Facts## Preferences## Instructions

这条边界在代码层面被严格强制执行:update_memory工具说明明确要求 "NEVER store personal memory in team memory",而后端校验服务validate_memory_scope(见下文"后端校验"一节)会在落库前拒绝在团队记忆中新增个人类 heading,从提示词到运行时形成双重防线。

二、触发时机与调用姿势:alongside,而不是 defer

协议对调用时机的规定非常明确:

If yes, callupdate_memoryalongsideyour normal response — don't defer it to a later turn.

也就是说,主智能体一旦判定消息中蕴含持久团队事实,应当在同一轮次内与正常回复并行调用update_memory,而不是推迟到后续轮次。这一"同步写入"约束避免了两个工程问题:

  1. 上下文漂移导致记忆丢失:推迟意味着后续轮次的模型可能不再持有这条信息,记忆写入就落空了;
  2. 重复扫描成本:每轮都做一次"是否值得记忆"的判定,保证记忆写入与对话进程同步推进,而不是事后回溯。

团队记忆工具的 Prompt 契约

update_memory的 team 变体提示词位于 prompts/tools/update_memory/team/description.md,它定义了完整的工具语义:

  • 职责:维护当前 workspace 的共享长期记忆文档;
  • 当前记忆展示:已有记忆会以<team_memory>块注入系统提示词,其中携带用量与上限(usage vs limit);
  • 触发条件:团队成员要求记住/忘记某事,或对话中浮现持久的团队决策、约定、架构笔记、流程或关键事实;
  • 禁止项:绝不把个人记忆写入团队记忆(个人简介、个人偏好、仅针对个人的长期指示);
  • 跳过项:瞬时聊天噪音(一次性问答、问候、会话物流);
  • 参数契约updated_memory参数必须是完整的替换版 Markdown(FULL replacement),需要做合并与策展(merge and curate),而不是只追加新条目;
  • 格式要求:heading-based Markdown,新条目形如- YYYY-MM-DD: text,并保持在<team_memory>中显示的容量限制内;
  • 兼容性:若旧记忆存在(YYYY-MM-DD) [fact]形式的遗留标记,须保留其中的信息,但新保存的内容统一写成新格式;
  • 禁止创建个人 heading:如## Preferences## Instructions## Personal Notes## Personal Instructions
  • 裁剪优先级:决策/约定(decisions/conventions)> 关键事实(key facts)> 当前优先级(current priorities)。

示例佐证

prompts/tools/update_memory/team/example.md 给出了两条最直接的调用示例:

user: "Let's remember that we decided to do weekly standup meetings on Mondays" → update_memory(updated_memory="...\n\n## Product Decisions\n- 2025-03-15: Weekly standup meetings happen on Mondays\n...") user: "Our office is in downtown Seattle, 5th floor" → update_memory(updated_memory="...\n\n## Project Facts\n- 2025-03-15: Office location is downtown Seattle, 5th floor\n...")

注意两条示例的共同点:日期由模型填写为记录当天2025-03-15),内容是对团队事实的中性陈述,heading 使用协议推荐值。这正是YYYY-MM-DD: text规范在真实调用中的标准形态——新增条目时传...省略号表示"保留既有内容",实际调用时是完整文档的替换。

三、记忆格式规范:heading-based Markdown

协议规定了记忆文档的存储格式,这是团队记忆能否被稳定读写、校验和渲染的基础。

3.1 结构约定

  • 顶层结构:使用##级别的 heading 分节,推荐## Product Decisions## Engineering Conventions## Project Facts## Open Questions
  • 条目格式- YYYY-MM-DD: text,例如- 2025-03-15: Weekly standup meetings happen on Mondays
  • 禁止项:不得创建## Preferences## Instructions等个人类 heading(团队记忆只能容纳团队级信息)。

3.2 遗留格式迁移

协议明确要求:如果旧记忆中存在(YYYY-MM-DD) [fact]这类遗留标记(legacy markers),需要保留其中的信息、改写为新格式——新保存的内容全部使用 heading-based 格式,遗留标记中的信息被并入对应章节,而不是简单丢弃。

3.3 解析与渲染的源码实现

记忆文档模型在 surfsense_backend/app/services/memory/document.py 中实现。它刻意只解析 SurfSense 记忆的小型 Markdown 契约:##分节 + 带日期的列表项。关键行为包括:

  • parse_bullet_line同时识别标准格式- 2025-03-15: text,要求body[10:12] == ": "且日期可被date.fromisoformat解析)和遗留格式- (2025-03-15) [fact] text);
  • 无法解析为记忆条目的未知行会被保留为MemoryRawLine,避免用户手工编辑的内容在写入时丢失;
  • render_memory_document在渲染时会自动规范化:遗留格式子弹统一输出为- YYYY-MM-DD: text
  • extract_headings通过normalize_heading做归一化(小写、非字母数字字符折叠为空格),用于后续 heading 校验。

这也解释了协议中"write new saves in the heading-based format"的深层原因:即使历史遗留数据存在,save_memory在保存时也会经render_memory_document将文档规范化输出,新写入的内容天然符合协议格式。

3.4 预算控制:软/硬容量限制

<team_memory>注入块会携带chars(当前字符数)与limit(上限)。具体数值定义在 surfsense_backend/app/services/memory/validation.py:

  • MEMORY_SOFT_LIMIT = 18_000:软上限。超过后提示模型在下次update_memory时合并重复项、移除过期条目、缩短描述;
  • MEMORY_HARD_LIMIT = 25_000:硬上限。超过后写入失败,返回错误消息要求压缩。

协议中的 "Stay within the budget shown in<team_memory>" 指向的正是这套限制机制——模型需要基于注入块中的实时用量自行判断是直接追加,还是先策展合并再写入。

四、从协议到运行时:完整落地链路

team.md并非孤立文档,它是 SurfSense 多智能体系统提示词的组成部分,被"组装 → 注入 → 执行 → 校验"四个环节调用。理解这条链路,才能明白协议规则是如何被真正执行到位的。

4.1 组装:visibility 感知的协议注入

系统提示词由 compose.py 组装,<memory_protocol>一节位于默认流程中<tools>之后。组装逻辑实现在 sections/memory_protocol.py:

def build_memory_protocol_section(*, visibility: ChatVisibility) -> str: variant = "team" if visibility == ChatVisibility.SEARCH_SPACE else "private" fragment = read_prompt_md(f"memory_protocol/{variant}.md") return f"\n{fragment}\n" if fragment else ""

即:当线程可见性为SEARCH_SPACE(共享/工作空间线程)时注入team.md,否则注入private.md。同理,update_memory工具说明也按可见性选择变体——_MEMORY_VARIANT_TOOLS集合中的工具会从tools/update_memory/{team|private}/子目录读取 description 与 example(见 tool_instruction_block.py)。也就是说,同样的对话,在私人线程里走用户记忆协议,在共享线程里自动切换为团队记忆协议。

4.2 注入:MemoryInjectionMiddleware 每轮注入

团队记忆的实际注入由 middleware/memory/middleware.py 中的MemoryInjectionMiddleware完成。它在每个 turn 的abefore_agent阶段执行:

  • 仅处理以HumanMessage结尾的轮次;
  • 根据ChatVisibility决定 scope:SEARCH_SPACE→ team,否则 → user;
  • Workspace.shared_memory_md字段读取团队记忆(_load_team_memory),组装为如下注入块:
<team_memory chars="..." limit="25000"> ... </team_memory>
  • 当字符数超过MEMORY_SOFT_LIMIT(18000)时,额外注入<memory_warning>块,明确要求模型在下次调用时"合并重复项、移除过期条目、缩短描述"后再新增内容;
  • 记忆块以SystemMessage形式插入消息序列(位于第二条消息位置),保证模型每次推理都能看到最新记忆。

这意味着<team_memory>中的 usage vs limit 是实时的——模型据此判断当前预算空间,决定是直接追加还是需要先策展。

4.3 执行:update_memory 工具的两条实现路径

update_memory工具在代码库中有两个等价实现:

  1. 主智能体直调main_agent/tools/update_memory.py中的create_update_team_memory_tool(workspace_id, ...)。它刻意使用一次性短生命周期数据库会话(fresh short-lived session per call),避免 compiled-agent 缓存持有过期请求级会话(见update_memory.py的 docstring);
  2. 记忆子智能体subagents/builtins/memory/tools/update_memory.py中的create_update_team_memory_tool,服务于专门的 memory 子智能体,供 supervisor 委托记忆操作(其系统提示词见 subagents/builtins/memory/system_prompt.md)。

两者最终都调用app.services.memory.service.save_memory(scope=MemoryScope.TEAM, target_id=workspace_id, ...),将内容写入Workspace.shared_memory_md字段(见_set_memory)。注意MemoryScope枚举定义了USER = "user"TEAM = "team"两个值:团队记忆的 target 是 workspace_id,用户记忆的 target 是 user_id,两者在数据库层面分属Workspace.shared_memory_mdUser.memory_md两个字段。

五、后端校验:什么能写进团队记忆

协议中的规则并非只停留在提示词层面——save_memory会执行一系列校验,违反规则将导致写入失败并返回错误。全部校验定义在 surfsense_backend/app/services/memory/validation.py。

5.1 校验清单

校验器规则违反后果
validate_memory_size长度 ≤ 25000 字符error:要求合并相关条目、移除过期条目、缩短描述
validate_heading_sanity超过 40 字符的长内容必须至少含一个##headingerror:"Memory must be markdown with at least one ## heading."
validate_memory_scope团队记忆不得引入个人 heading(preferencesinstructionspersonal notespersonal instructionserror:"Team memory cannot introduce personal headings..."
validate_diff检测被删除的分节、显著缩水(新内容 < 原内容 40% 时提示可能的数据丢失)warning
validate_bullet_format所有-开头的行都应是标准或遗留格式warning:"Non-standard memory bullet: ..."
soft_limit_warning长度 > 18000 时提示合并warning

此外还有两个值得注意的预处理行为(实现在 service.py):

  • preamble 剥离strip_preamble_to_first_heading会丢弃模型输出中第一个##heading 之前的所有前言文本——即使模型在updated_memory中夹带了"好的,以下是更新:"之类的开场白,也不会污染存储内容;
  • NO_UPDATE 哨兵:当内容为NO_UPDATE/NO CHANGE等哨兵字符串时,直接返回status="no_op",不产生任何写入。

5.2 作用域校验的细节:grandfather 机制

validate_memory_scope有一个值得注意的设计:历史遗留的个人 heading 被允许保留(grandfathered),但新增的一律拒绝。实现逻辑是:

  • 计算新内容中的_FORBIDDEN_TEAM_HEADINGS(即preferencesinstructionspersonal notespersonal instructions)与旧记忆 heading 集合的交集;
  • grandfathered(新旧同时存在)→ 只产生 warning,提示尽快合并到团队安全 heading;
  • introduced(只在新内容中出现)→ 直接 error,拒绝写入。

这保证了协议中"不要创建个人 heading"的规则在历史数据迁移场景下不会破坏既有内容——老数据可以继续存在并逐步收敛,但新数据必须守规矩。

5.3 超限自动重写(forced rewrite)

当内容超过硬上限(25000 字符)且调用时提供了llm参数,save_memory会调用forced_rewrite(rewrite.py),用一次聚焦的 LLM 调用把记忆压缩到限制以内;当重写成功且结果更短时,使用重写结果,并附带 notice:"Memory was automatically rewritten to fit within limits."。这为协议中的 "Stay within the budget" 提供了自动兜底,模型无需总是手动压缩。

5.4 测试佐证

上述校验行为有完整的单元测试覆盖,见 tests/unit/agents/new_chat/tools/test_update_memory_scope.py 与 tests/unit/services/test_memory_service.py。其中可直接验证的关键行为包括:

  • test_validate_memory_scope_rejects_new_personal_heading_in_team:在团队记忆中新增## Preferences返回 error;
  • test_validate_memory_scope_allows_old_marker_payload_in_team_scope:遗留(2026-04-10) [pref]标记可以被读取、不报错;
  • test_save_memory_blocks_new_personal_heading_in_team_before_commit:含个人 heading 的团队写入不触发 commit,shared_memory_md保持为空——校验发生在落库之前
  • test_save_memory_allows_grandfathered_personal_heading_in_team:旧记忆已含## Preferences时再次写入成功,但产生 warning;
  • test_save_memory_normalizes_legacy_marker_bullets:遗留标记子弹在保存时被规范化为标准 heading-based 格式(## Memory\n- 2026-04-10: Legacy fact is preserved);
  • test_save_memory_strips_preamble_before_heading:模型输出中首个##heading 之前的 preamble 被剥离,只保留## Facts\n- 2026-04-10: Likes cats

六、记忆子智能体:委托路径的补充约束

除主智能体直接调用工具外,SurfSense 还提供了专门的 memory 子智能体,其系统提示词(subagents/builtins/memory/system_prompt.md)从操作者视角复述并强化了team.md的原则:

  • 目标:用update_memory持久化 durable 的偏好/事实/指示,同时避免瞬态或不安全的存储;
  • 可见性:记忆是 workspace 作用域的,不得假设跨 workspace 可见
  • 工具策略:只保存对未来有价值的持久信息;不存瞬时聊天;不存 secrets(除非被明确指示);意图不明时返回status=blocked并附缺失的意图信号字段;
  • 输出契约:仅返回一个 JSON 对象,包含status(success/partial/blocked/error)、action_summaryevidence(含memory_updated布尔值与memory_category语义分类)、next_stepmissing_fieldsassumptions。其中明确注明:memory_category只是供 supervisor 日志使用的语义分类,不是存储格式,不得强制在保存内容中插入[fact|preference|instruction]标记。

这从侧面解释了为什么team.md中 "do not create personal headings" 的约束会传导到保存内容的实际格式:即使子智能体在内部做了偏好/事实的语义分类,持久化的文档本身仍必须遵循 heading-based 的团队格式,分类只存在于日志层面。

七、总结:团队记忆协议的关键设计要点

回顾整个协议与其代码实现,可以提炼出以下设计要点,它们共同保证了团队记忆在 SurfSense 多智能体系统中的可靠性:

  1. 判定先行、同步写入:每条用户消息先判定是否蕴含团队级持久事实,是则同轮并行调用update_memory,绝不 defer 到后续轮次;
  2. 格式统一、自动规范化:heading-based Markdown(##分节 +- YYYY-MM-DD: text),遗留(YYYY-MM-DD) [fact]标记保留信息但由render_memory_document统一规范化;
  3. 边界刚性、双保险:团队记忆禁止个人 heading,提示词层由description.md约束,运行时层由validate_memory_scope强制(新增即拒、遗留可迁移);
  4. 预算显式、软硬分层<team_memory>实时携带字符数与上限(软 18000 / 硬 25000),超软限注入<memory_warning>,超硬限触发 forced rewrite 自动重写或直接写入失败;
  5. 整链落地、可测试:协议 → 组装(visibility 感知)→ 注入(每轮中间件)→ 执行(主智能体/子智能体双实现)→ 校验(服务层),每一环都有源码与单元测试可查证。

如需深入探索,建议继续阅读以下仓库文件:

  • 协议本体:team.md、private.md
  • 工具说明与示例:update_memory/team/description.md、update_memory/team/example.md
  • 运行时实现:middleware.py、main_agent/tools/update_memory.py
  • 存储与校验服务:service.py、validation.py、document.py、rewrite.py
  • 测试证据:test_update_memory_scope.py、test_memory_service.py

【免费下载链接】SurfSenseOpen-source NotebookLM alternative. Research the open web with live data(Reddit, YT, IG, TikTok, Indeed, Google Search, Maps etc) through one platform, API or MCP server. Join our Discord: https://discord.gg/ejRNvftDp9项目地址: https://gitcode.com/GitHub_Trending/su/SurfSense

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 0:57:14

MySQL 5.7驱动的C++ MMORPG服务端搭建与协议调试指南

简介&#xff1a;本资源为《龙族》网络游戏原始服务端与客户端插件的完整源代码集合&#xff0c;面向游戏开发学习者、逆向研究者及模组开发者&#xff0c;助力理解经典MMORPG底层架构与核心机制。压缩包含791个文件&#xff0c;主体为387个C头文件&#xff08;.h&#xff09;与…

作者头像 李华
网站建设 2026/9/15 0:55:57

K12教育外呼策略优化:从30%到58%接通率的实战方法

1. 项目背景与核心价值在教育行业摸爬滚打多年&#xff0c;我深刻体会到招生季外呼工作的重要性。每年开学季&#xff0c;我们团队要处理上万通电话&#xff0c;但平均接通率长期徘徊在30%左右。直到我们系统性优化了外呼策略&#xff0c;这个数字才提升到58%&#xff0c;直接带…

作者头像 李华
网站建设 2026/9/15 0:53:56

Python+Django就业岗位推荐系统设计与实现,从推荐算法到避坑指南

基于PythonDjango就业岗位推荐系统&#xff0c;我从零搭了一整套&#xff0c;聊聊设计与避坑每年这个时候&#xff0c;总有读者跑来问我&#xff1a;准备做毕设/课设&#xff0c;题目是“就业岗位推荐系统”&#xff0c;用PythonDjango能不能做&#xff1f;需要哪些功能才显得完…

作者头像 李华
网站建设 2026/9/15 0:53:17

人才交流活动复盘:协同创新与技术成果转化的实战路径

1. 活动背景与参与定位&#xff1a;一次跨区域人才协作的典型样本今年春天&#xff0c;我们团队作为外部技术观察方&#xff0c;受主办方邀请参与了一场以“协同创新”为主题的年度人才交流活动。这类活动在各地并不少见&#xff0c;名字五花八门&#xff0c;有的叫“人才日”&…

作者头像 李华
网站建设 2026/9/15 0:49:54

用HTML/CSS/JS构建响应式卡片墙:数据驱动与懒加载的摸鱼入口页

简介&#xff1a;这是一个结合HTML、CSS与JavaScript的响应式可过滤作品集页面&#xff0c;面向希望学习前端布局与交互的开发者&#xff0c;用来展示100个小游戏和实用工具。页面通过HTML搭建语义化卡片模块&#xff0c;利用CSS3媒体查询适配不同屏幕&#xff0c;借助JavaScri…

作者头像 李华
网站建设 2026/9/15 0:49:01

三相PWM整流器FCS-MPC仿真:从离散模型到调参实战

三相PWM整流器在能量回馈、有源前端、电动汽车V2G这些场景里越来越常见&#xff0c;说白了它就是一块能"反过来发电"的整流器&#xff0c;把交流侧单位功率因数可控地变成直流母线电压。我最早接这个方向&#xff0c;是因为一台要求频繁加减载、母线电压波动要小于5%…

作者头像 李华