> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-HK/llms.txt # 頻道 **新增於:** `@mastra/core@1.22.0` Channels 將 Agent 連接至訊息平台。請透過 `Agent` constructor 的 `channels` property 設定。傳入的物件是 `ChannelConfig`。有關概念及平台設定指引,請參閱 [Channels 概覽](https://mastra.zisheng.pro/zh-HK/docs/capabilities/channels/overview)。 ## 使用範例 ```typescript import { Agent } from '@mastra/core/agent' import { createSlackAdapter } from '@chat-adapter/slack' import { createDiscordAdapter } from '@chat-adapter/discord' export const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'You are a helpful support assistant.', model: 'openai/gpt-5.6-sol', channels: { adapters: { slack: createSlackAdapter(), discord: createDiscordAdapter(), }, }, }) ``` ## 參數 `channels` property 接受包含以下欄位的 `ChannelConfig` 物件: **adapters** (`Record`): 以名稱作為 key 的平台 adapter(例如 slack、discord)。如要使用預設值,可直接傳入 Adapter;如要自訂各 adapter 的選項,則傳入 ChannelAdapterConfig 物件。 **handlers** (`ChannelHandlers`): 覆寫私訊、提及及已訂閱 thread 的預設訊息 handler。 **inlineMedia** (`string[] | ((mimeType: string) => boolean)`): 控制哪些附件類型會以檔案部分傳送至模型。不相符的類型會以文字摘要描述。接受 mime type glob 陣列或 predicate function。預設值符合主要視覺模型支援的格式。 (Default: `['image/png', 'image/jpeg', 'image/webp', 'application/pdf']`) **inlineLinks** (`InlineLinkEntry[]`): 將訊息文字中的 URL 提升為檔案部分,讓模型可以處理連結內容。每個項目會比對一個 domain。預設停用。 **tools** (`boolean`): getTools() 是否傳回 channel 專用 Tools(add\_reaction、remove\_reaction)。對於不支援 function calling 的模型,請設為 false。Channel Tools 絕不會自動加入 Agent;請透過 tools: { ...channels.getTools() } 明確傳入。 (Default: `true`) **state** (`StateAdapter`): 用於訂閱及去重的 state adapter。預設使用由 Mastra instance storage 支援的 MastraStateAdapter。Channels 必須先設定 storage。 (Default: `MastraStateAdapter(來自 Mastra storage)`) **userName** (`string`): 平台訊息中顯示的 bot 名稱。預設為 Agent 的 name;如未設定名稱,則為 'Mastra'。 (Default: `` Agent 的 `name` ``) **threadContext** (`{ maxMessages?: number; addSystemMessage?: boolean }`): Agent 如何取得目前 thread 的 context。maxMessages 控制首次被提及時擷取多少則近期平台訊息(設為 0 可停用;只適用於非私訊 thread)。addSystemMessage: false 會略過內建 system message,該訊息原本會告知 Agent 請求來自哪個 channel/平台。 (Default: `{ maxMessages: 10, addSystemMessage: true }`) **chatOptions** (`Omit`): 直接傳入 Chat SDK 的額外選項。適用於 dedupeTtlMs、fallbackStreamingPlaceholderText、lockScope 及 messageHistory 等進階設定。 **resolveResourceId** (`(ctx: ResolveResourceIdContext) => string | Promise`): 獨立於訊息傳送者,決定哪個 resourceId 擁有 channel thread 的 resource-level memory。只會在建立新 thread 時執行;重用的 thread 會保留已儲存的擁有者,且不會呼叫 hook。傳回 ctx.defaultResourceId(${platform}:${message.author.userId})即可保留內建行為。 **resolveThreadId** (`(ctx: ResolveThreadIdContext) => string | Promise`): 決定 channel thread 的內部 Mastra thread id。此項會在 resolveResourceId 之後執行,context 會包含已解析的擁有者,而且只會在建立新 thread 時執行;重用的 thread 會保留已儲存的 id,且不會呼叫 hook。傳回的 id 在整個 memory store 中必須是唯一;如有衝突,系統會改用產生的 id。傳回 ctx.defaultThreadId(隨機 UUID)即可保留內建行為。 **waitUntil** (`(promise: Promise) => void`): 平台的 waitUntil function。Vercel 必須使用此 function,讓背景 Agent run 在 webhook 傳回 200 後仍可繼續。在 Vercel 上,請傳入來自 @vercel/functions 的 waitUntil。系統會從 request context 自動偵測 Cloudflare Workers 及 Netlify Functions。AWS Lambda 會自然等待 event loop 清空,因此不需要 waitUntil。 **resolveWaitUntil** (`(c: Context) => ((promise: Promise) => void) | undefined`): 適用於 waitUntil 位於 Hono request context、但內建 helper 未有涵蓋的 runtime resolver。解析次序:獨立的 waitUntil → resolveWaitUntil(c) → 預設值(Cloudflare Workers、Netlify)。 ## 各 adapter 選項 將 adapter 包裝在 `ChannelAdapterConfig` 物件內,以設定各 adapter 的選項: ```typescript import { Agent } from '@mastra/core/agent' import { createDiscordAdapter } from '@chat-adapter/discord' import { createSlackAdapter } from '@chat-adapter/slack' const agent = new Agent({ id: 'example', name: 'Example', instructions: '...', model: 'openai/gpt-5.6-sol', channels: { adapters: { discord: { adapter: createDiscordAdapter(), toolDisplay: 'text', cors: { origin: ['https://customer-saas.example'], credentials: true, }, gateway: false, }, slack: createSlackAdapter(), // Plain adapter uses defaults }, }, }) ``` **adapter** (`Adapter`): 此平台的 Chat SDK adapter instance。 **gateway** (`boolean`): 啟動持續運作的 Gateway WebSocket listener,以接收私訊、@提及及 reaction。只需 webhook 互動的 serverless 部署可設為 false。 (Default: `true`) **cards** (`boolean`): \*\*已棄用\*\* — 請改用 toolDisplay。未設定 toolDisplay 時,cards: true 對應至 toolDisplay: "cards",而 cards: false 對應至 toolDisplay: "text"。IDE 會以刪除線標示此欄位;runtime 行為維持不變。 **cors** (`CorsOptions`): 此 adapter webhook route 的 CORS 設定。需要跨來源 credential 的瀏覽器 channel adapter 可使用此選項。 **formatError** (`(error: Error) => PostableMessage`): 覆寫錯誤在聊天中的呈現方式。請傳回易於理解的訊息,而非公開原始錯誤。 (Default: `"❌ 錯誤:"`) **formatToolCall** (`(args: { toolName: string; args: unknown; result: unknown; isError: boolean }) => PostableMessage | null`): \*\*已棄用\*\* — 請改用 function 形式的 toolDisplay。設定後會以 ToolDisplayFn 執行,只會在 result/error event 觸發;running 及 approval event 不會呈現。在 type level 上與 toolDisplay 互斥。 **streaming** (`boolean | { updateIntervalMs?: number }`): Agent 產生文字 delta 時即串流至 channel,而非先緩衝再於每個步驟發佈一次。底層 adapter 必須支援發佈及編輯串流。Slack 預設為 true;其他 adapter 預設為 false。 (Default: `false(Slack 為 true)`) **textFormat** (`'markdown' | 'plain'`): Agent 最終回覆文字使用的格式。'markdown'(預設值)會以 markdown 發佈回覆:原生支援 markdown 呈現的 adapter(Slack)會直接呈現,其他 adapter 則會轉換成平台格式。'plain' 會以純文字原樣發佈回覆,為被提示輸出 Slack mrkdwn 等平台格式的 Agent 還原採用 markdown 前的行為。只適用於最終回覆文字;Tool card、錯誤訊息及 tripwire 通知不受影響。無論此設定為何,原生串流一律使用 markdown。 (Default: `'markdown'`) **toolDisplay** (`'cards' | 'text' | 'timeline' | 'grouped' | 'hidden' | ToolDisplayFn`): 控制 Tool call 在 channel 中的呈現方式。"cards" 會以豐富的 Block Kit,為每個 Tool 發佈執行中/結果 card。"text" 會以純文字發佈相同的生命週期(不使用 Block Kit)。"timeline" 及 "grouped" 會將 Tool 狀態以 inline task\_update chunk 串流(需要 streaming: true;目前只支援 Slack,其他 adapter 可能會呈現 placeholder)。"hidden" 會靜默執行 Tools。傳入 function 可自行呈現 Tool event;傳回 { kind: "post", message } 以獨立發佈/編輯、傳回 { kind: "stream", chunk } 以推送至串流 widget,或傳回 undefined 以略過該 event。當 chunk 只應套用於使用中的串流 session 時,請在 stream result 加入 openIfEmpty: false。無論採用哪種模式,核准/拒絕提示一律以獨立 card 呈現。 (Default: `'cards'(Slack 為 'grouped')`) **typingStatus** (`boolean | ((chunk: AgentChunkType, ctx: TypingStatusContext) => string | false | null | undefined | void)`): 控制平台的輸入狀態指示器。true 使用內建預設值(文字為 is typing…、Tool call 為 is calling {tool}…、Tool call 核准為 is waiting for approval…)。false 會完全隱藏輸入狀態;當即時串流 widget(例如 Slack 中的 toolDisplay: "grouped")已顯示進度時尤其適用。傳入 function 可為每個 chunk 設定自訂狀態文字;傳回字串以設定狀態,或傳回 false/null/undefined 以保持不變。可配合從 @mastra/core/channels export 的 defaultTypingStatus,讓未處理的 chunk 使用預設值。 (Default: `true`) ## Tool 顯示模式 `toolDisplay` 控制 Tool call 在聊天中的呈現方式。預設的 `'cards'` 會為每個 Tool 發佈「執行中……」card,再以結果編輯該 card,與舊版本的 行為一致。`'text'` 採用相同生命週期,但不使用豐富的 Block Kit, 適合無法妥善呈現 card 的平台。 `'timeline'` 及 `'grouped'` 會把 Tool 狀態連同 Agent 文字,以 inline `task_update` chunk 串流。這些模式需要 `streaming: true`,並依賴聊天 adapter 呈現 chunk。Slack 原生支援兩者;其他 adapter 在加入支援前 可能只會呈現 placeholder。如停用 `streaming`,channel 會記錄一次警告, 並回復使用 `'cards'`。 `'hidden'` 會靜默執行 Tools。只有輸入狀態會顯示工作正在進行。 向 `toolDisplay` 傳入 function,即可完全自訂呈現方式。該 function 會接收 `ToolDisplayEvent`(`running`/`result`/`error`/`approval`) 及 `ToolDisplayContext`(`{ mode, platform }`);傳回 `{ kind: 'post', message }` 以獨立發佈/編輯、傳回 `{ kind: 'stream', chunk }` 以推送至 使用中的串流 widget,或傳回 `undefined` 以略過該 event。 預設情況下,如沒有使用中的串流 session,stream result 會開啟一個 session。 當 chunk 只適用於現有 session 時,請設定 `openIfEmpty: false`。如沒有使用中的 session,Mastra 會略過該 chunk。靜態 channels 會忽略此選項,並維持現有的 純文字 fallback 行為。 ```typescript toolDisplay: event => { if (event.kind !== 'running') return undefined return { kind: 'stream', chunk: { type: 'task_update', id: event.toolCallId, title: event.displayName, status: 'in_progress', }, openIfEmpty: false, } } ``` 無論採用哪種模式,核准/拒絕提示(`requireApproval`)一律以獨立 card 呈現,因為 inline task 項目無法包含互動式按鈕。 ```typescript import { Agent } from '@mastra/core/agent' import { createSlackAdapter } from '@chat-adapter/slack' const agent = new Agent({ id: 'streaming-agent', name: 'Streaming Agent', instructions: '...', model: 'openai/gpt-5.6-sol', channels: { adapters: { slack: { adapter: createSlackAdapter(), streaming: true, // already the Slack default toolDisplay: 'timeline', }, }, }, }) ``` ## 自訂輸入狀態 向 `typingStatus` 傳入 function,以自訂狀態文字。每個 stream chunk 都會 呼叫該 function 一次;傳回字串以設定狀態,或傳回 `false`/`null`/ `undefined` 以保持目前狀態不變。傳回值會去重,因此平台只會在狀態改變時 收到呼叫。 `defaultTypingStatus` 從 `@mastra/core/channels` export,讓未處理的 chunk 可以回復使用內建預設值。 ```typescript import { Agent } from '@mastra/core/agent' import { defaultTypingStatus } from '@mastra/core/channels' import { createDiscordAdapter } from '@chat-adapter/discord' const agent = new Agent({ id: 'custom-typing-agent', name: 'Custom Typing Agent', instructions: '...', model: 'openai/gpt-5.6-sol', channels: { adapters: { discord: { adapter: createDiscordAdapter(), typingStatus: (chunk, ctx) => { if (chunk.type === 'tool-call' && chunk.payload.toolName === 'searchDocs') { return 'is searching docs…' } return defaultTypingStatus(chunk, ctx) }, }, }, }, }) ``` ## 處理器 覆寫內建 event handler。每個 handler 可以是: - **省略**:使用預設 Mastra handler(透過 Agent 路由訊息並發佈回覆) - **`false`**:完全停用 handler - **Function** `(thread, message, defaultHandler) => Promise`:包裝或取代預設 handler ```typescript import { Agent } from '@mastra/core/agent' import { createSlackAdapter } from '@chat-adapter/slack' const agent = new Agent({ id: 'custom-handler-agent', name: 'Custom Handler Agent', instructions: '...', model: 'openai/gpt-5.6-sol', channels: { adapters: { slack: createSlackAdapter(), }, handlers: { onMention: async (thread, message, defaultHandler) => { console.log('Received mention:', message.text) await defaultHandler(thread, message) }, onDirectMessage: false, }, }, }) ``` **onDirectMessage** (`ChannelHandler | false`): 當 bot 收到私訊時呼叫。 **onMention** (`ChannelHandler | false`): 當 bot 在 channel 或 thread 中被 @提及時呼叫。 **onSubscribedMessage** (`ChannelHandler | false`): 收到 Agent 已訂閱 thread 中的訊息時呼叫。 `ChannelHandler` function signature: ```typescript type ChannelHandler = ( thread: Thread, message: Message, defaultHandler: (thread: Thread, message: Message) => Promise, ctx: ChannelHandlerContext, ) => Promise type ChannelHandlerContext = { mastra?: Mastra requestContext: RequestContext } ``` `ctx.mastra` 是已解析的 `mastra` instance,因此無需傳入外部 accessor,handler 亦可存取 storage 或其他已註冊 primitive: ```typescript onDirectMessage: async (thread, message, defaultHandler, ctx) => { const store = await ctx.mastra?.getStorage()?.getStore('memory') await defaultHandler(thread, message) } ``` `ctx.requestContext` 是此訊息即將啟動的 run 所使用的 [`RequestContext`](https://mastra.zisheng.pro/zh-HK/docs/server/request-context),每則訊息都會重新建立。請在呼叫 `defaultHandler` 前寫入;該值會連同 Mastra 隨後加入的 channel 項目傳至 run: ```typescript onDirectMessage: async (thread, message, defaultHandler, ctx) => { ctx.requestContext.set('locale', 'en-GB') await defaultHandler(thread, message) } ``` 透過此方式,run 從 request context 讀取的任何資料都可按訊息決定,例如平台傳送者對應至哪位使用者。 ## Resource ID 解析 Channel thread 的 memory `resourceId` 預設為 `${platform}:${message.author.userId}`。傳送者擁有該 memory,而範圍按平台劃分。對於使用共享身分的應用程式(例如單一登入(SSO)),這會拆分 memory:同一使用者在飛書私訊中會取得 `feishu:user_123`,但在網頁上則是 `user_123`。 傳入 `resolveResourceId`,即可獨立於傳送者決定 memory 擁有權。此 function 只會在建立新 thread 時執行。重用的 thread 會保留已儲存的 `resourceId`,且不會呼叫 hook,因此現有對話不依賴 resolver 是否可用。傳回 `ctx.defaultResourceId` 即可回復使用內建行為。 ```typescript import { Agent } from '@mastra/core/agent' import { createSlackAdapter } from '@chat-adapter/slack' const agent = new Agent({ id: 'sso-agent', name: 'SSO Agent', instructions: '...', model: 'openai/gpt-5.6-sol', channels: { adapters: { slack: createSlackAdapter(), }, resolveResourceId: async ({ thread, message }) => { // DM: share resource-level memory with the web app by using the bare SSO id if (thread.isDM) { return await resolveSsoUserId(message) } // Group chat: the conversation owns the memory; the sender stays the actor return thread.channelId }, }, }) ``` 傳入該 function 的 `ResolveResourceIdContext`: **platform** (`string`): 平台名稱(例如 slack、discord)。 **thread** (`Thread`): 訊息送達的 channel thread。使用 thread.isDM 區分私訊與群組/channel thread。 **message** (`Message`): 傳入的訊息。message.author.userId 是操作者/傳送者,不一定是 memory 擁有者。 **defaultResourceId** (`string`): 內建預設值(${platform}:${message.author.userId})。傳回此值可保留目前行為。 ## Thread ID 解析 新的 channel thread 預設會取得隨機 UUID,作為內部 Mastra thread id。傳入 `resolveThreadId` 可自行選擇 id;例如,讓 thread 採用其所屬 session 的相同 id,以配合應用程式為自行建立的 thread 命名的方式。 Hook 會在 `resolveResourceId` 後執行,因此 context 中可取得已解析的擁有者。與 `resolveResourceId` 一樣,此 hook 只會在建立新 thread 時執行;重用的 thread 會保留已儲存的 id,且不會呼叫 hook。傳回的 id 在整個 memory store 中必須是唯一。如該 id 已屬於現有 thread,Mastra 會記錄警告並改用產生的 id,確保不會覆寫現有 thread。傳回 `ctx.defaultThreadId` 即可保留內建行為。 ```typescript import { Agent } from '@mastra/core/agent' import { createSlackAdapter } from '@chat-adapter/slack' const agent = new Agent({ id: 'session-agent', name: 'Session Agent', instructions: '...', model: 'openai/gpt-5.6-sol', channels: { adapters: { slack: createSlackAdapter(), }, // Owner: a session id resolved from the sender's linked account resolveResourceId: async ctx => resolveSessionId(ctx), // Thread id: align with the session id so app URLs that address // threads by session id resolve channel-created threads too resolveThreadId: ({ resourceId, defaultThreadId }) => { return isSessionId(resourceId) ? resourceId : defaultThreadId }, }, }) ``` 傳入該 function 的 `ResolveThreadIdContext`: **platform** (`string`): 平台名稱(例如 slack、discord)。 **thread** (`Thread`): 訊息送達的 channel thread。使用 thread.isDM 區分私訊與群組/channel thread。 **message** (`Message`): 傳入的訊息。 **resourceId** (`string`): 新 thread 將會所屬的已解析 memory resourceId(在 resolveResourceId 後)。 **defaultThreadId** (`string`): 內建預設值(隨機 UUID)。傳回此值可保留目前行為。 ## Inline 媒體 控制哪些附件類型(圖片、影片、PDF 等)會以檔案部分傳送至模型。不相符的類型會以文字摘要描述,讓 Agent 得知該檔案,同時避免拒絕不支援類型的模型發生錯誤。 預設值(`['image/png', 'image/jpeg', 'image/webp', 'application/pdf']`)符合主要視覺模型支援的格式。覆寫 `inlineMedia` 可擴充清單(例如 `['image/*', 'audio/*']`),或以 predicate function 完全取代清單。 支援的 glob pattern: | Pattern | 相符類型 | | ----------------- | ---------------------------------- | | `image/*` | 所有圖片類型(`image/png`、`image/jpeg` 等) | | `video/*` | 所有影片類型 | | `*` 或 `*/*` | 所有類型 | | `application/pdf` | 完全相符的類型 | 對於使用私人 CDN 的平台(例如 Slack),Chat SDK 會使用已驗證的 credential 擷取附件。對於使用公開 CDN 的平台(例如 Discord),URL 會直接傳送至模型。 ## Inline 連結 將訊息文字中的 URL 提升為檔案部分,讓模型可以處理連結內容,而非只看到原始 URL 文字。每個項目可以是字串(domain pattern),或包含強制 mime type 的物件。 **字串項目**會比對 domain,並執行 HEAD request 以偵測 Content-Type。系統會按 `inlineMedia` 檢查解析出的類型,只有相符類型才會成為檔案部分。 **物件項目**會比對 domain 並強制指定 mime type,略過 HEAD request 及 `inlineMedia` 檢查。這適合 YouTube 等網站:HEAD request 會傳回 `text/html`,但模型會將 URL 視為影片內容。 ```typescript type InlineLinkEntry = | string // Domain pattern (HEAD determines mime type) | { match: string; mimeType: string } // Domain + forced mime type (skips HEAD) ``` ## 相關內容 - [Channels 概覽](https://mastra.zisheng.pro/zh-HK/docs/capabilities/channels/overview):概念、快速入門及平台設定 - [Agent class](https://mastra.zisheng.pro/zh-HK/reference/agents/agent):Constructor 參數及 method - [Chat SDK adapters](https://chat-sdk.dev/adapters):Adapter 設定及平台設定