> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt # Signal **添加于:** `@mastra/core@1.39.0` > **Beta:** 此功能处于 beta 阶段。在 API 稳定之前,可能会在不提升主版本号的情况下发生破坏性变更。 Signal 是一种通过 thread 与 Agent 交互的方式。无需每次交互都从 `agent.stream()` 开始,你可以订阅 thread,然后发送消息或 Signal。Mastra 会在 thread 空闲时唤醒 Agent,将输入放入正在运行的 Agent 循环,或将输入排队到下一轮。 使用消息 API 处理用户输入。使用 `sendSignal()` 处理较低层级的系统上下文,例如后台任务通知、策略提醒或 processor 生成的上下文。 > **📹 观看:** 观看 [Mastra Signal 概览](https://www.youtube.com/watch?v=7It2y89TVP4),了解 Signal 如何唤醒和引导长时间运行的 Agent。 ## 何时使用 Signal 当 Agent thread 需要原始 `stream()` 调用之外的新输入或上下文时,请使用 Signal。用户在运行活跃期间发送后续消息、后台系统需要向 thread 添加上下文,或外部事件应唤醒、更新或通知 Agent 时,Signal 都很有用。 用户输入请使用 `sendMessage()` 和 `queueMessage()`。较低层级的系统上下文请使用 `sendSignal()`。持久化状态通道请使用 `sendStateSignal()`;外部事件需要创建持久化通知 inbox 记录时,请使用 `sendNotificationSignal()`。 ## 快速开始 创建 Agent,订阅 thread,然后向该 thread 发送消息。当消息唤醒 Agent 或进入正在运行的循环时,订阅会接收活跃 stream。 ```typescript import { Agent } from '@mastra/core/agent' const agent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'Help the user compare options.', model: 'openai/gpt-5.6-sol', }) const thread = { resourceId: 'user_123', threadId: 'thread_456', } const subscription = await agent.subscribeToThread(thread) await agent.sendMessage('Compare that with the previous option.', thread) for await (const chunk of subscription.stream) { console.log(chunk) } ``` 当 thread 有正在运行的 Agent stream 时,`sendMessage()` 会成为该 Agent 循环中的新输入。当 thread 空闲时,Mastra 会以该消息作为首个输入启动 stream。 ## 消息输入 ### 立即发送消息 当用户希望活跃 Agent 立即看到消息时,请使用 `sendMessage()`。 ```typescript agent.sendMessage( { contents: 'Use the latest customer note too.', attributes: { name: 'Jane', sentFrom: 'slack' }, }, { resourceId: 'user_123', threadId: 'thread_456', }, ) ``` 模型会以 XML 包装的用户输入形式接收带属性的消息: ```xml Use the latest customer note too. ``` 没有属性的消息会作为纯用户输入发送。 ### 将消息排队到下一轮 当用户发送后续消息,但当前模型调用应先完成时,请使用 `queueMessage()`。Mastra 会等待活跃运行完成,然后在同一 thread 上启动新运行。 ```typescript agent.queueMessage('Also check whether the tests need updates.', { resourceId: 'user_123', threadId: 'thread_456', }) ``` 当 thread 空闲时,`queueMessage()` 会立即启动运行。当 thread 活跃时,它会在活跃运行完成后启动新运行,以保持轮次顺序。 ## Signal 上下文 ### 控制低层级 Signal 行为 需要发送系统生成的上下文而不是用户输入时,请使用 `sendSignal()`。对于外部事件,使用 `type: 'notification'`。默认情况下,Mastra 会将 Signal 传递到活跃运行并唤醒空闲 thread。使用 `ifActive.behavior` 和 `ifIdle.behavior` 可更改此行为。 ```typescript const result = agent.sendSignal( { type: 'notification', contents: 'GitHub CI failed on PR #123: 3 tests failed.', }, { resourceId: 'user_123', threadId: 'thread_456', ifIdle: { behavior: 'persist', }, }, ) await result.persisted ``` 当空闲唤醒 stream 需要模型设置、Tool 或运行时上下文等选项时,请传入 `ifIdle.streamOptions`。`ifActive`、`ifIdle`、分支属性和 `streamOptions` 请参阅 [`Agent.sendSignal()` 参考](https://mastra.zisheng.pro/reference/agents/agent)。 ### 发送通知上下文 Signal 具有语义 `type` 和面向 LLM 的 `tagName`。使用 `type` 描述 Signal 类别;使用 `tagName` 控制模型看到的 XML 标签。 对于外部事件,使用 `type: 'notification'`。Reactive Signal 保留给 processor 或运行时生成的上下文,例如策略指导、后台任务结果和自动加载的指令。 ```typescript agent.sendSignal( { type: 'notification', contents: 'PR #123 has a new review comment from User X about the API surface.', attributes: { source: 'github', pr: '123', }, }, { resourceId: 'user_123', threadId: 'thread_456', }, ) ``` 模型会按如下形式接收 Signal 上下文: ```xml PR #123 has a new review comment from User X about the API surface. ``` 使用符合 XML 规范的 `tagName` 和属性名称。它们可以包含字母、数字、下划线、句点和连字符,并且必须以字母或下划线开头。 #### 存储支持 通知 inbox 存储可用于支持更丰富 memory 和 Signal Workflow 的 storage adapter:[libSQL](https://mastra.zisheng.pro/reference/storage/libsql)、[PostgreSQL](https://mastra.zisheng.pro/reference/storage/postgresql) 和 [MongoDB](https://mastra.zisheng.pro/reference/storage/mongodb)。这些 adapter 通过 `getStore('notifications')` 公开通知记录。 ### 发送 processor 上下文 Processor 可以在运行期间发送 Reactive Signal。Processor 应检查聊天历史,对特定触发条件作出反应,并避免多次发送相同上下文。 以下示例演示一个 processor:Tool 调用读取 `AGENTS.md` 文件后,该 processor 会注入 `AGENTS.md` 指令。 ```typescript import type { Processor, ProcessInputStepArgs } from '@mastra/core/processors' export const agentsMdReminderProcessor: Processor = { id: 'agents-md-reminder', async processInputStep({ messageList, sendSignal }: ProcessInputStepArgs) { const messages = messageList.get.all.db() const agentsMdPath = findAgentsMdPathFromToolCalls(messages) if (!agentsMdPath || hasAlreadySentAgentsMdReminder(messages, agentsMdPath)) { return messageList } await sendSignal?.({ type: 'reactive', contents: readAgentsMdInstructions(agentsMdPath), attributes: { type: 'dynamic-agents-md', path: agentsMdPath, }, metadata: { path: agentsMdPath, }, }) return messageList }, } ``` Reactive Signal 默认为 `tagName: 'system-reminder'`,因此模型会按如下形式接收此上下文: ```xml $agentsMdFileContents ``` 等待 `sendSignal()` 可在订阅的 thread 活跃时保持 stream echo 顺序。 ### 条件属性 使用 `ifActive.attributes` 和 `ifIdle.attributes`,根据 Agent 在传递时处于活跃还是空闲状态,为输入标记上下文。顶层 `attributes` 始终应用;接受输入时,Mastra 会将所选分支的 `attributes` 合并进去。分支专用属性请参阅 [`Agent.sendMessage()` 参考](https://mastra.zisheng.pro/reference/agents/agent)和 [`Agent.sendSignal()` 参考](https://mastra.zisheng.pro/reference/agents/agent)。 ## 状态与通知 Signal ### 状态 Signal 状态 Signal 会公开命名的、thread 范围的上下文通道。可用于随时间变化的持久化上下文,例如浏览器状态、编辑器状态或后台 watcher 结果。 当外部 producer 检测到状态变化时,使用 `sendStateSignal()`。每个状态 Signal 都会标识状态通道、由 producer 管理的 cache key,以及更新是 snapshot 还是 delta。 ```typescript await agent.sendStateSignal( { id: 'browser', mode: 'snapshot', cacheKey: 'browser:https://example.com:3-tabs', contents: 'Browser is open. Active tab URL: https://example.com. 3 open tabs.', value: { activeUrl: 'https://example.com', tabCount: 3, open: true, }, }, { resourceId: 'user_123', threadId: 'thread_456', }, ) ``` Mastra 接受状态 Signal 后,会在 thread 上存储紧凑的跟踪元数据。如果在该状态仍为当前状态时,producer 再次发送相同的 `cacheKey` 和 mode,Mastra 会跳过重复项。 当 processor 管理状态通道时,使用 `computeStateSignal()`。Mastra 会在每个模型输入步骤中,于 `processInputStep()` 之后调用一次。状态 Signal 字段和返回值请参阅 [`Agent.sendStateSignal()` 参考](https://mastra.zisheng.pro/reference/agents/agent)。 ```typescript import type { ComputeStateSignalArgs, Processor } from '@mastra/core/processors' export const browserStateProcessor: Processor = { id: 'browser-state', stateId: 'browser', computeStateSignal(args: ComputeStateSignalArgs) { const browser = readCurrentBrowserState() const previous = readMostRecentBrowserState(args.activeStateSignals) const changed = previous ? diffBrowserState(previous, browser) : browser const shouldRefreshSnapshot = Boolean(args.lastSnapshot && !args.contextWindow.hasSnapshot) if (previous && Object.keys(changed).length === 0 && !shouldRefreshSnapshot) { return } const isDelta = Boolean(previous && !shouldRefreshSnapshot) return { mode: isDelta ? 'delta' : 'snapshot', cacheKey: stableBrowserStateCacheKey(browser), contents: isDelta ? describeBrowserDelta(changed) : describeBrowserSnapshot(browser), value: browser, ...(isDelta ? { delta: changed } : {}), } }, } ``` Mastra 会将 `lastSnapshot` 和 `deltasSinceSnapshot` 传入 `computeStateSignal()`。当当前消息列表不包含最新 snapshot 时,它会从消息历史解析这些值。Processor 仍负责合并和 diff 逻辑。 `contextWindow.hasSnapshot` 会告诉 processor,活跃消息窗口是否已包含该状态通道的 snapshot。如果为 `false`,请返回新的 `snapshot`,这样即使较旧状态消息已从上下文窗口裁剪,模型仍能看到当前状态。 内置浏览器上下文 processor 使用 `browser` id,以 snapshot 和 delta 模式发出状态。 ### 通知 Signal 通知 Signal 表示 GitHub 活动、电子邮件、Slack mention、CI 状态、incident、录制内容或私信等外部事件。当事件应创建持久化 inbox 记录时,请使用 `agent.sendNotificationSignal()`。 通知传递分为两个阶段。Ingress 阶段,`agent.sendNotificationSignal()` 存储通知记录并解析 Agent 的传递策略。Dispatch 阶段,Mastra 消费到期记录并发出完整通知或摘要 Signal。 默认传递策略会感知优先级。紧急通知会立即传递,较低优先级通知则可能批量汇总为摘要,或等到 thread 空闲。通知字段请参阅 [`Agent.sendNotificationSignal()` 参考](https://mastra.zisheng.pro/reference/agents/agent),`notifications.deliveryPolicy` 配置请参阅 [`Agent` 构造函数参考](https://mastra.zisheng.pro/reference/agents/agent),inbox Tool 操作请参阅 [`createNotificationInboxTool()` 参考](https://mastra.zisheng.pro/reference/signals/create-notification-inbox-tool)。 ```typescript await agent.sendNotificationSignal( { source: 'github', kind: 'ci-status', priority: 'high', summary: 'CI failed on main: 3 tests failed.', payload: { repository: 'acme/app', branch: 'main', }, dedupeKey: 'github:acme/app:main:ci', }, { resourceId: 'user_123', threadId: 'thread_456', }, ) ``` 模型会以如下上下文形式接收完整通知: ```xml CI failed on main: 3 tests failed. ``` 通知摘要会告诉模型,inbox 中有记录正在等待: ```xml github: 3, email: 5, slack: 2 ``` Mastra 发出摘要时,会清除每条已汇总记录的 `summaryAt` 并设置 `summarySignalId`。记录保持 pending 且可读取。Mastra 发出完整通知时,会设置 `deliveredSignalId` 并将记录标记为 `delivered`。如果 inbox Tool 先读取通知,它可以注入完整通知 Signal 并将记录标记为 `seen`,以防止重复完整传递。 当部分通知应等待其他 dispatch 窗口或摘要汇总时,请在 Agent 上配置传递策略。当延迟通知和摘要汇总应自动传递时,请在 Mastra 层级启用定时 dispatch。`notifications.deliveryPolicy` 请参阅 [`Agent` 构造函数参考](https://mastra.zisheng.pro/reference/agents/agent),运行时通知 dispatch 配置请参阅 [`Mastra` 类参考](https://mastra.zisheng.pro/reference/core/mastra-class)。 #### 通知 inbox Tool 使用 `createNotificationInboxTool()` 为 Agent 提供一个 inbox 操作 Tool,而不是多个 CRUD Tool。当 Agent 在收到 `` Signal 后需要摘要背后的完整记录时,请使用 `read`。通知内容以 Signal 传递,而不是作为普通 Tool 输出。设置示例、输入 schema 和 action 行为请参阅 [`createNotificationInboxTool()` 参考](https://mastra.zisheng.pro/reference/signals/create-notification-inbox-tool)。 `sendNotificationSignal()` 需要支持 `notifications` 的 storage domain。仅当较低层级的通知形状上下文应绕过 inbox 存储时,才使用 `sendSignal({ type: 'notification' })`。 ## 分布式与 serverless 部署 Signal 通过 pub/sub 后端协调运行。当 Signal 到达实现了 `LeaseProvider` 的后端时,Mastra 会获取目标 thread 的 lease,确保同一时间只有一个进程拥有该对话;随后唤醒 Agent,或将输入路由到正在运行的循环。没有 lease 的后端会回退到始终授予所有权的 no-op,这适合单进程,但不适合跨实例。 默认的内存 pub/sub 无法跨越实例边界。在 Vercel 等 serverless 平台或任何多实例部署中,后续 Signal 可能被路由到不同于正在运行 Agent 的实例。 如果没有共享 pub/sub,该实例无法访问活跃运行,只能自行启动一个运行,使原运行不受影响,并导致 thread 被处理两次。 在 `Mastra` 实例上配置由 Redis Streams 支持的共享 pub/sub,使 lease 和 Signal 能够跨实例协调: ```typescript import { Mastra } from '@mastra/core' import { RedisStreamsPubSub } from '@mastra/redis-streams' export const mastra = new Mastra({ agents: { agent }, pubsub: new RedisStreamsPubSub({ url: process.env.REDIS_URL, keyPrefix: 'mastra:my-app', }), }) ``` `RedisStreamsPubSub` 同时实现事件传递约定和分布式 lease,因此一个后端即可处理跨实例 Signal 传递和 lease 所有权。Vercel 托管的 Redis 集成和 Upstash Redis 都很合适。有关何时需要分布式 pub/sub,请参阅 [PubSub 指南](https://mastra.zisheng.pro/docs/server/pubsub)和 [`RedisStreamsPubSub` 参考](https://mastra.zisheng.pro/reference/pubsub/redis-streams)。 ## 兼容性与 API ### 兼容性 Mastra 仍接受 `type: 'user-message'` 和 `type: 'system-reminder'` 等旧版 Signal payload,并在内部将其规范化为新的类别和标签结构: - `type: 'user-message'`:规范化为 `type: 'user'` 和 `tagName: 'user'` - `type: 'system-reminder'`:规范化为 `type: 'reactive'` 和 `tagName: 'system-reminder'` 已存储的现有 Signal 行和旧客户端会继续通过兼容层加载。Server 支持时,新客户端会调用消息路由;检测到旧版 server 时,React 的 thread Signal 路径会回退到旧版 `/signals` 路由。完整消息、Signal 和订阅类型请参阅 [Agent Signal 参考](https://mastra.zisheng.pro/reference/agents/agent)。 ### 批准 Tool 调用 订阅运行因 Tool 审批暂停时,请使用订阅原生方法批准或拒绝 Tool 调用。恢复后的 chunk 会通过现有 thread 订阅到达。请求和响应结构请参阅 [`client.getAgent().sendToolApproval()` 参考](https://mastra.zisheng.pro/reference/client-js/agents)和 [server Agent 路由](https://mastra.zisheng.pro/reference/server/routes)。 ### 使用 HTTP 路由 如果通过 HTTP 直接调用 Mastra,请使用 `POST /api/agents/:agentId/send-message` 发送即时消息,使用 `POST /api/agents/:agentId/queue-message` 发送下一轮消息。订阅原生 Tool 审批请使用 `POST /api/agents/:agentId/send-tool-approval`。请求和响应 schema 请参阅 [Server 路由参考](https://mastra.zisheng.pro/reference/server/routes)。 ### 使用客户端 SDK JavaScript 客户端公开 thread Signal API。 发送 thread 输入前使用 `subscribeToThread()`,使客户端可以呈现接收输入或因输入而唤醒的 stream。 ```typescript const agent = client.getAgent('supportAgent') const subscription = await agent.subscribeToThread({ resourceId: 'user_123', threadId: 'thread_456', }) await agent.sendMessage({ message: 'Show the shorter version.', resourceId: 'user_123', threadId: 'thread_456', }) await subscription.processDataStream({ onChunk: chunk => { console.log(chunk) }, reconnect: true, }) ``` 长时间订阅请使用 `reconnect: true`。重连选项请参阅 [`client.getAgent().subscribeToThread()` 参考](https://mastra.zisheng.pro/reference/client-js/agents)。 ### 保持自定义 SSE 订阅活跃 如果为 thread 订阅公开自己的 Server-Sent Events(SSE)endpoint,请在 stream 空闲时定期发送 heartbeat frame。这样可以防止浏览器、proxy 和 load balancer 在下一个 Signal 或模型 chunk 到达前关闭连接。 以下示例每 25 秒发送一条 SSE 注释: ```typescript const heartbeat = setInterval(() => { controller.enqueue(encoder.encode(': keep-alive\n\n')) }, 25_000) request.signal.addEventListener('abort', () => { clearInterval(heartbeat) }) ``` 将 heartbeat 与客户端重连逻辑结合使用。Heartbeat 可减少空闲断开,而重连可在网络或运行时仍关闭 stream 时进行恢复。 ## 相关内容 - [`Agent.sendMessage()`](https://mastra.zisheng.pro/reference/agents/agent) - [`Agent.queueMessage()`](https://mastra.zisheng.pro/reference/agents/agent) - [`Agent.sendSignal()`](https://mastra.zisheng.pro/reference/agents/agent) - [`Agent.sendStateSignal()`](https://mastra.zisheng.pro/reference/agents/agent) - [`Agent.subscribeToThread()`](https://mastra.zisheng.pro/reference/agents/agent) - [`createNotificationInboxTool()`](https://mastra.zisheng.pro/reference/signals/create-notification-inbox-tool) - [`client.getAgent().sendMessage()`](https://mastra.zisheng.pro/reference/client-js/agents) - [`client.getAgent().queueMessage()`](https://mastra.zisheng.pro/reference/client-js/agents) - [`client.getAgent().sendSignal()`](https://mastra.zisheng.pro/reference/client-js/agents) - [Server Agent 路由](https://mastra.zisheng.pro/reference/server/routes) - [`client.getAgent().subscribeToThread()`](https://mastra.zisheng.pro/reference/client-js/agents) - [`client.getAgent().sendToolApproval()`](https://mastra.zisheng.pro/reference/client-js/agents) - [`RedisStreamsPubSub`](https://mastra.zisheng.pro/reference/pubsub/redis-streams) - 📹 [Mastra Signal workshop](https://www.youtube.com/watch?v=KLg6uFKz9aw\&t=3020s)