> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt # 多用户 Thread 多个用户可以共享同一个 Mastra Thread,每个用户都有自己的姓名和职能角色。你可以在消息正文中携带发言者身份,让 Agent 在读取同一个共享 Thread 时区分不同用户。 ## 何时使用多用户 Thread 当多人通过同一个 Agent 围绕同一主题协作时,请使用多用户 Thread: - 包含编辑者、审阅者和批准者的协作文档 - 由一个助手服务多位参与者的群聊 - 不同角色拥有不同权限的多利益相关方审阅 ## 让所有参与者共享一个 `resourceId` 一个 Thread 只属于一个 `resourceId`,因此共享 Thread 的所有参与者都需要传入相同的值。不要使用用户 ID(单用户应用的默认做法),而应根据对话本身确定 `resourceId`,例如共享文档使用 `doc_${docId}`,群聊使用 `room_${roomId}`。所有人都指向同一个 `resourceId` 后,就会读写同一份历史。 ## 为每条用户消息标记发言者身份 模型需要知道每一轮是谁在发言。消息正文是唯一会保留在历史中并重新进入上下文的位置,因此请用一个简短的 `` 标签包裹每条用户消息,并在其中包含发言者的 ID、姓名和角色。该标签会始终附着在消息上,因此召回先前轮次时,模型仍能看到每句话是谁说的。 使用一个简短的辅助函数构建该标签。下面的示例展示了一种实现方式;请将其复制到你的项目中,并根据用户数据结构进行调整: ```typescript export type Speaker = { id: string name: string role: string } function escapeAttr(value: string) { return value .replace(/&/g, '&') .replace(/"/g, '"') .replace(//g, '>') } export function asUserTurn(speaker: Speaker, text: string) { const id = escapeAttr(speaker.id) const name = escapeAttr(speaker.name) const role = escapeAttr(speaker.role) return { role: 'user' as const, content: ` ${text} `, } } ``` 在 Agent 的指令中教它如何读取 `` 标签。Agent 必须配置 `memory`,才能在调用时传入 `thread` 和 `resource`: ```typescript import { Agent } from '@mastra/core/agent' import { Memory } from '@mastra/memory' import { LibSQLStore } from '@mastra/libsql' const memory = new Memory({ storage: new LibSQLStore({ id: 'collab-storage', url: 'file:./collab.db' }), options: { lastMessages: 20, }, }) export const collabAgent = new Agent({ id: 'collab', name: 'CollabAgent', model: 'openai/gpt-5-mini', memory, instructions: ` You are a collaborative document assistant. Multiple users talk to you in the SAME thread. Every user message is wrapped in a tag carrying the user's identity: ...message text... Rules: 1. Address users by their author_name. 2. Respect functional_role: editors propose changes, reviewers approve. 3. When attributing past statements, read author_name from the surrounding tag. 4. Do not echo the tags back at users. `.trim(), }) ``` 使用包裹后的消息调用 Agent。每位参与者都共享相同的 `thread` 和 `resource`: ```typescript import { asUserTurn } from './identity' const docResourceId = 'doc_42' const docThreadId = 'doc_42' const alice = { id: 'u_alice', name: 'Alice', role: 'editor' } const bob = { id: 'u_bob', name: 'Bob', role: 'reviewer' } await collabAgent.generate([asUserTurn(alice, 'My favorite color is teal.')], { memory: { thread: docThreadId, resource: docResourceId }, }) await collabAgent.generate([asUserTurn(bob, 'I want QA sign-off before publish.')], { memory: { thread: docThreadId, resource: docResourceId }, }) ``` `` 标签会保留在消息正文中,因此在后续轮次召回历史时,模型仍然能看到每句话是谁说的。 ## 与 Memory 层结合使用 用户标记模式可以与每个 Memory 层组合使用。请根据对话需要记住每位用户相关事实的时长选择对应层: - **短对话**(单个会话或规模足够小、能够装入 `lastMessages` 的 Thread),或者需要逐字记录谁说了什么时:只使用[消息历史](#message-history-alone)即可。历史中的用户标签已经足够,无需额外的 Memory 层。 - **长期运行的 Thread**(对话规模超过 `lastMessages`,而且每位用户的事实需要在历史淘汰后继续保留):使用 [Observational Memory](#with-observational-memory-recommended)。 - **需要结构化参与者列表,或者你的 Storage Adapter 不支持 OM**(OM 需要 LibSQL、PG 或 MongoDB):使用 [Working Memory](#with-working-memory)。 建议使用 Observational Memory 或 Working Memory 二者之一,因为它们覆盖的需求存在重叠。同时运行两者会增加延迟和 token 成本,却没有太大收益。 ### 仅使用消息历史 对于短对话或需要逐字记录谁说了什么的场景,历史中的用户标签已经足够。`lastMessages` 会将先前轮次带回上下文,同时保留其归属信息: ```typescript import { Memory } from '@mastra/memory' import { LibSQLStore } from '@mastra/libsql' const memory = new Memory({ storage: new LibSQLStore({ id: 'collab-storage', url: 'file:./collab.db' }), options: { lastMessages: 20, }, }) ``` 模型会从当前消息的 `` 标签,以及通过 `lastMessages` 带回的先前已标记消息中读取身份。 ### 使用 Observational Memory(推荐) [Observational Memory](https://mastra.zisheng.pro/docs/memory/observational-memory)(OM)会将每位用户的事实提取到后台日志中,而不会占用 Agent 的 Tool 预算。默认 Observer 模型能够直接读取 `` 标签,并生成类似 `Alice stated her favorite color is teal.` 和 `Bob asked for QA sign-off before publish.` 的归属记录。 如果你的 Storage 支持 OM,在多用户 Thread 中应优先使用 OM,而不是 Working Memory。OM 会自动提取事实,可以扩展到任意数量的参与者,也不需要维护模板。不做任何覆盖即可启用: ```typescript import { Memory } from '@mastra/memory' import { LibSQLStore } from '@mastra/libsql' const memory = new Memory({ storage: new LibSQLStore({ id: 'collab-storage', url: 'file:./collab.db' }), options: { lastMessages: 20, observationalMemory: true, }, }) ``` OM 需要使用支持它的 Storage Adapter:`@mastra/libsql`、`@mastra/pg`、`@mastra/mongodb` 或 `@mastra/oracledb`。 > **备注:** 如果将 Observer 切换到能力较弱的模型后,发现事实都被归为笼统的 `User`,请使用 [`observation.instruction`](https://mastra.zisheng.pro/reference/memory/observational-memory) 教 Observer 读取 `` 标签。 ### 使用 Working Memory 当 OM 不可用时(例如 Storage Adapter 不支持 OM),或者需要 Agent 在每一轮都能读写结构化且确定的参与者列表时,请使用 Working Memory。 默认的 [Working Memory](https://mastra.zisheng.pro/docs/memory/working-memory) 模板假定每个 Thread 只有一个用户(“First Name”“Last Name”等)。对于多用户 Thread,请提供一个包含参与者列表的模板: ```typescript import { Memory } from '@mastra/memory' import { LibSQLStore } from '@mastra/libsql' const memory = new Memory({ storage: new LibSQLStore({ id: 'collab-storage', url: 'file:./collab.db' }), options: { lastMessages: 20, workingMemory: { enabled: true, scope: 'thread', template: `# Document Collaboration State ## Participants ## Open Questions ## Decisions `, }, }, }) ``` 设置 `scope: 'thread'`,让参与者列表属于文档,而不是属于某个用户。再添加一条指令,要求 Agent 每当新的 `author_id` 出现在 `` 中时,就将新参与者追加到列表中。 有关模板的更多信息,请参阅[自定义模板](https://mastra.zisheng.pro/docs/memory/working-memory)。 ## 安全性 请从经过身份验证的请求上下文中设置 `speaker`,绝不要从请求正文中设置。如果客户端可以自行选择 `author_id`,某个用户就可能冒充另一个用户。请使用 [Request Context](https://mastra.zisheng.pro/docs/server/request-context) 从身份验证层读取已验证用户,并在调用 Agent 之前在服务端构建 `` 标签。 ## 相关内容 - [Working Memory](https://mastra.zisheng.pro/docs/memory/working-memory) - [Observational Memory](https://mastra.zisheng.pro/docs/memory/observational-memory) - [在 Agent 之间共享 Memory](https://mastra.zisheng.pro/docs/memory/overview) - [`Memory` 参考文档](https://mastra.zisheng.pro/reference/memory/memory-class)