> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-HK/llms.txt # Signals **新增於:** `@mastra/core@1.39.0` > **Beta:** 此功能目前處於 beta 階段。在 API 穩定之前,即使沒有提升主要版本,也可能出現破壞性變更。 Signals 是透過 thread 與 Agent 互動的一種方式。你毋須每次互動都呼叫 `agent.stream()`,而可以訂閱 thread,然後傳送訊息或 signal。Mastra 會在 Agent 閒置時喚醒它、將輸入加入正在運行的 Agent 迴圈,或把輸入排入下一個 turn 的佇列。 使用訊息 API 傳送由用戶撰寫的輸入。至於背景任務通知、政策提示或 processor 產生的上下文等較底層系統上下文,則使用 `sendSignal()`。 > **📹 觀看影片:** 觀看 [Mastra signals 概覽](https://www.youtube.com/watch?v=7It2y89TVP4),了解 signals 如何喚醒及引導長時間運行的 Agent。 ## 何時使用 signals 當 Agent thread 需要接收原來 `stream()` 呼叫以外的新輸入或上下文時,便可使用 signals。以下情況特別適合使用:運行期間用戶傳送後續訊息、背景系統需要向 thread 加入上下文,或外部事件需要喚醒、更新或通知 Agent。 由用戶撰寫的輸入使用 `sendMessage()` 和 `queueMessage()`;較底層的系統上下文使用 `sendSignal()`;持久狀態通道使用 `sendStateSignal()`;若外部事件需要建立持久的通知收件箱記錄,則使用 `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. ``` 沒有屬性的訊息會以純文字用戶輸入傳送。 ### 將訊息排入下一個 turn 若用戶傳送後續訊息,但目前的模型呼叫應先完成,請使用 `queueMessage()`。Mastra 會等待目前的運行完成,然後在同一個 thread 上開始新的運行。 ```typescript agent.queueMessage('Also check whether the tests need updates.', { resourceId: 'user_123', threadId: 'thread_456', }) ``` 當 thread 閒置時,`queueMessage()` 會立即開始運行。當 thread 活躍時,它會在目前的運行完成後開始新的運行,以保留 turn 的順序。 ## Signal 上下文 ### 控制底層 signal 行為 需要傳送由系統產生的上下文,而非由用戶撰寫的輸入時,請使用 `sendSignal()`。外部事件請使用 `type: 'notification'`。Mastra 預設會把 signals 傳送至活躍的運行,並喚醒閒置的 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 需要模型設定、Tools 或運行時上下文等選項,請傳入 `ifIdle.streamOptions`。有關 `ifActive`、`ifIdle`、分支屬性及 `streamOptions`,請參閱 [`Agent.sendSignal()` 參考資料](https://mastra.zisheng.pro/zh-HK/reference/agents/agent)。 ### 傳送通知上下文 Signals 具有語義上的 `type` 和面向 LLM 的 `tagName`。使用 `type` 描述 signal 類別,並使用 `tagName` 控制模型所看到的 XML 標籤。 外部事件請使用 `type: 'notification'`。Reactive signals 預留給 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` 和屬性名稱。它們可以包含字母、數字、底線、句點及連字號,並且必須以字母或底線開頭。 #### 儲存支援 支援較豐富 Memory 及 signal Workflow 的儲存介接器可使用通知收件箱儲存:[libSQL](https://mastra.zisheng.pro/zh-HK/reference/storage/libsql)、[PostgreSQL](https://mastra.zisheng.pro/zh-HK/reference/storage/postgresql) 和 [MongoDB](https://mastra.zisheng.pro/zh-HK/reference/storage/mongodb)。這些介接器透過 `getStore('notifications')` 提供通知記錄。 ### 傳送 processor 上下文 Processors 可以在運行期間傳送 reactive signals。Processor 應檢查聊天記錄、回應特定觸發條件,並避免重複傳送相同的上下文。 以下範例示範一個 processor:當 Tool 呼叫讀取 `AGENTS.md` 檔案後,它會注入 `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 signals 預設使用 `tagName: 'system-reminder'`,因此模型會收到如下形式的上下文: ```xml $agentsMdFileContents ``` 當訂閱的 thread 處於活躍狀態時,等待 `sendSignal()` 完成可保留 stream 回應的順序。 ### 條件式屬性 使用 `ifActive.attributes` 和 `ifIdle.attributes`,可根據傳送時 Agent 處於活躍還是閒置狀態,為輸入加上相應的上下文標籤。最上層的 `attributes` 一律套用;當輸入獲接受時,Mastra 會把所選分支的 `attributes` 合併其中。有關分支專用屬性,請參閱 [`Agent.sendMessage()` 參考資料](https://mastra.zisheng.pro/zh-HK/reference/agents/agent)和 [`Agent.sendSignal()` 參考資料](https://mastra.zisheng.pro/zh-HK/reference/agents/agent)。 ## 狀態及通知 signals ### 狀態 signals 狀態 signals 提供具名且限定於 thread 範圍的上下文通道。它們適用於會隨時間改變的持久上下文,例如瀏覽器狀態、編輯器狀態或背景監察器的結果。 當外部產生者偵測到狀態變更時,請使用 `sendStateSignal()`。每個狀態 signal 都會識別狀態通道、由產生者擁有的快取鍵,以及該更新屬於快照還是差異。 ```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 上儲存精簡的追蹤 metadata。如果產生者在該狀態仍然有效時再次傳送相同的 `cacheKey` 和模式,Mastra 便會略過重複項目。 當 processor 擁有狀態通道時,請使用 `computeStateSignal()`。Mastra 會在 `processInputStep()` 之後,每個模型輸入步驟呼叫它一次。有關狀態 signal 欄位及傳回值,請參閱 [`Agent.sendStateSignal()` 參考資料](https://mastra.zisheng.pro/zh-HK/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()`。當目前的訊息清單不包含最新快照時,它會從訊息記錄解析兩者;合併和比較差異的邏輯仍由 processor 負責。 `contextWindow.hasSnapshot` 會告知 processor,活躍的訊息視窗是否已包含此狀態通道的快照。如果值為 `false`,請傳回新的 `snapshot`,以便即使較舊的狀態訊息已從上下文視窗裁走,模型仍可看到目前狀態。 內置瀏覽器上下文 processor 會以 `browser` id 發出狀態,並使用快照及差異模式。 ### 通知 signals 通知 signals 代表 GitHub 活動、電郵、Slack 提及、CI 狀態、事故、錄音或直接訊息等外部事件。若事件應建立持久的收件箱記錄,請使用 `agent.sendNotificationSignal()`。 通知傳送分為兩個階段。在接收階段,`agent.sendNotificationSignal()` 會儲存通知記錄,並解析 Agent 的傳送政策。在派送階段,Mastra 會處理到期記錄,並發出完整通知或摘要 signals。 預設的傳送政策會考慮優先級。緊急通知會立即傳送;優先級較低的通知則可能彙整成摘要,或等待 thread 閒置。有關通知欄位,請參閱 [`Agent.sendNotificationSignal()` 參考資料](https://mastra.zisheng.pro/zh-HK/reference/agents/agent);有關 `notifications.deliveryPolicy` 設定,請參閱 [`Agent` 建構函式參考資料](https://mastra.zisheng.pro/zh-HK/reference/agents/agent);有關收件箱 Tool 操作,請參閱 [`createNotificationInboxTool()` 參考資料](https://mastra.zisheng.pro/zh-HK/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. ``` 通知摘要會告知模型,收件箱中有記錄等待處理: ```xml github: 3, email: 5, slack: 2 ``` Mastra 發出摘要時,會清除每筆已摘要記錄的 `summaryAt`,並設定 `summarySignalId`。記錄會維持待處理且可讀取的狀態。Mastra 發出完整通知時,會設定 `deliveredSignalId`,並把記錄標記為 `delivered`。如果收件箱 Tool 先讀取通知,它可以注入完整通知 signal,並把記錄標記為 `seen`,以避免重複傳送完整通知。 若部分通知應等待另一個派送時段或摘要彙整,請在 Agent 上設定傳送政策。若要自動傳送延後的通知及摘要彙整,請在 Mastra 層級啟用排程派送。有關 `notifications.deliveryPolicy`,請參閱 [`Agent` 建構函式參考資料](https://mastra.zisheng.pro/zh-HK/reference/agents/agent);有關運行時通知派送設定,請參閱 [`Mastra` 類別參考資料](https://mastra.zisheng.pro/zh-HK/reference/core/mastra-class)。 #### 通知收件箱 Tool 使用 `createNotificationInboxTool()`,可讓 Agent 透過一個 Tool 執行收件箱操作,毋須使用多個 CRUD Tools。當 Agent 收到 `` signal 後,需要取得摘要背後的完整記錄時,請使用 `read`。通知內容會以 signals 而非一般 Tool 輸出的形式傳送。有關設定範例、輸入 schema 及操作行為,請參閱 [`createNotificationInboxTool()` 參考資料](https://mastra.zisheng.pro/zh-HK/reference/signals/create-notification-inbox-tool)。 `sendNotificationSignal()` 需要支援 `notifications` 的儲存 domain。只有較底層、具通知形式但應繞過收件箱儲存的上下文,才使用 `sendSignal({ type: 'notification' })`。 ## 分散式及 serverless 部署 Signals 透過 pub/sub backend 協調運行。當 signal 到達實作了 `LeaseProvider` 的 backend 時,Mastra 會取得目標 thread 的租約,確保一次只有一個程序擁有對話,然後喚醒 Agent 或把輸入傳送到正在運行的迴圈。沒有租約功能的 backend 會退回至一律授予擁有權的 no-op;這適用於單一程序,但不適用於跨執行個體的環境。 預設的記憶體內 pub/sub 無法跨越執行個體邊界。在 Vercel 等 serverless 平台或任何多執行個體部署中,後續 signal 可能被傳送至另一個執行個體,而非正在運行 Agent 的執行個體。 如果沒有共用 pub/sub,該執行個體便無法連接活躍的運行,並會自行開始另一個運行,令原來的運行不受影響,卻使 thread 被處理兩次。 請在 `Mastra` 執行個體上設定由 Redis Streams 支援的共用 pub/sub,讓租約及 signals 可跨執行個體協調: ```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` 同時實作事件傳送合約及分散式租約,因此單一 backend 即可處理跨執行個體的 signal 傳送及租約擁有權。Vercel 的受管理 Redis 整合及 Upstash Redis 均十分合適。如要進一步了解何時需要分散式 pub/sub,請參閱 [PubSub 指南](https://mastra.zisheng.pro/zh-HK/docs/server/pubsub)及 [`RedisStreamsPubSub` 參考資料](https://mastra.zisheng.pro/zh-HK/reference/pubsub/redis-streams)。 ## 相容性及 API ### 相容性 Mastra 仍接受舊版 signal payload,例如 `type: 'user-message'` 和 `type: 'system-reminder'`。它會在內部把它們正規化為新的類別及標籤形式: - `type: 'user-message'`:正規化為 `type: 'user'` 和 `tagName: 'user'` - `type: 'system-reminder'`:正規化為 `type: 'reactive'` 和 `tagName: 'system-reminder'` 現有已儲存的 signal 資料列及較舊的 client 會繼續透過相容層載入。當伺服器支援時,新 client 會呼叫訊息 routes;React 的 thread signal 路徑偵測到較舊的伺服器時,則會退回舊版 `/signals` route。有關完整的訊息、signal 及訂閱類型,請參閱 [Agent signals 參考資料](https://mastra.zisheng.pro/zh-HK/reference/agents/agent)。 ### 核准 Tool 呼叫 當訂閱的運行暫停以等待 Tool 核准時,請使用訂閱原生的方法核准或拒絕 Tool 呼叫。恢復後的 chunks 會透過現有的 thread 訂閱送達。有關請求及回應格式,請參閱 [`client.getAgent().sendToolApproval()` 參考資料](https://mastra.zisheng.pro/zh-HK/reference/client-js/agents)和[伺服器 Agent routes](https://mastra.zisheng.pro/zh-HK/reference/server/routes)。 ### 使用 HTTP routes 如果你直接透過 HTTP 呼叫 Mastra,立即傳送訊息請使用 `POST /api/agents/:agentId/send-message`,把訊息排入下一個 turn 則使用 `POST /api/agents/:agentId/queue-message`。訂閱原生的 Tool 核准請使用 `POST /api/agents/:agentId/send-tool-approval`。有關請求及回應 schema,請參閱[伺服器 routes 參考資料](https://mastra.zisheng.pro/zh-HK/reference/server/routes)。 ### 使用 client SDK JavaScript client 提供 thread signal API。 傳送 thread 輸入之前,請先使用 `subscribeToThread()`,讓 client 可以呈現接收該輸入或因應輸入而被喚醒的 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/zh-HK/reference/client-js/agents)。 ### 保持自訂 SSE 訂閱連線 如果你自行提供 Server-Sent Events (SSE) endpoint 用於 thread 訂閱,請在 stream 閒置時定期傳送 heartbeat frames。這可避免瀏覽器、proxies 及負載平衡器在下一個 signal 或模型 chunk 到達前關閉連線。 以下範例每 25 秒傳送一次 SSE 註解: ```typescript const heartbeat = setInterval(() => { controller.enqueue(encoder.encode(': keep-alive\n\n')) }, 25_000) request.signal.addEventListener('abort', () => { clearInterval(heartbeat) }) ``` 請把 heartbeats 與 client 端的重新連線邏輯配合使用。Heartbeats 可減少閒置中斷,而當網絡或運行時仍然關閉 stream 時,重新連線則可恢復連接。 ## 相關內容 - [`Agent.sendMessage()`](https://mastra.zisheng.pro/zh-HK/reference/agents/agent) - [`Agent.queueMessage()`](https://mastra.zisheng.pro/zh-HK/reference/agents/agent) - [`Agent.sendSignal()`](https://mastra.zisheng.pro/zh-HK/reference/agents/agent) - [`Agent.sendStateSignal()`](https://mastra.zisheng.pro/zh-HK/reference/agents/agent) - [`Agent.subscribeToThread()`](https://mastra.zisheng.pro/zh-HK/reference/agents/agent) - [`createNotificationInboxTool()`](https://mastra.zisheng.pro/zh-HK/reference/signals/create-notification-inbox-tool) - [`client.getAgent().sendMessage()`](https://mastra.zisheng.pro/zh-HK/reference/client-js/agents) - [`client.getAgent().queueMessage()`](https://mastra.zisheng.pro/zh-HK/reference/client-js/agents) - [`client.getAgent().sendSignal()`](https://mastra.zisheng.pro/zh-HK/reference/client-js/agents) - [伺服器 Agent routes](https://mastra.zisheng.pro/zh-HK/reference/server/routes) - [`client.getAgent().subscribeToThread()`](https://mastra.zisheng.pro/zh-HK/reference/client-js/agents) - [`client.getAgent().sendToolApproval()`](https://mastra.zisheng.pro/zh-HK/reference/client-js/agents) - [`RedisStreamsPubSub`](https://mastra.zisheng.pro/zh-HK/reference/pubsub/redis-streams) - 📹 [Mastra signals 工作坊](https://www.youtube.com/watch?v=KLg6uFKz9aw\&t=3020s)