頻道
新增於: @mastra/core@1.22.0
Channels 將 Agent 連接至訊息平台。請透過 Agent constructor 的 channels property 設定。傳入的物件是 ChannelConfig。有關概念及平台設定指引,請參閱 Channels 概覽。
使用範例使用範例 的直接連結
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:
slack、discord)。如要使用預設值,可直接傳入 Adapter;如要自訂各 adapter 的選項,則傳入 ChannelAdapterConfig 物件。handlers?:
inlineMedia?:
inlineLinks?:
tools?:
getTools() 是否傳回 channel 專用 Tools(add_reaction、remove_reaction)。對於不支援 function calling 的模型,請設為 false。Channel Tools 絕不會自動加入 Agent;請透過 tools: { ...channels.getTools() } 明確傳入。state?:
MastraStateAdapter。Channels 必須先設定 storage。userName?:
name;如未設定名稱,則為 'Mastra'。threadContext?:
maxMessages 控制首次被提及時擷取多少則近期平台訊息(設為 0 可停用;只適用於非私訊 thread)。addSystemMessage: false 會略過內建 system message,該訊息原本會告知 Agent 請求來自哪個 channel/平台。chatOptions?:
dedupeTtlMs、fallbackStreamingPlaceholderText、lockScope 及 messageHistory 等進階設定。resolveResourceId?:
resourceId 擁有 channel thread 的 resource-level memory。只會在建立新 thread 時執行;重用的 thread 會保留已儲存的擁有者,且不會呼叫 hook。傳回 ctx.defaultResourceId(${platform}:${message.author.userId})即可保留內建行為。resolveThreadId?:
resolveResourceId 之後執行,context 會包含已解析的擁有者,而且只會在建立新 thread 時執行;重用的 thread 會保留已儲存的 id,且不會呼叫 hook。傳回的 id 在整個 memory store 中必須是唯一;如有衝突,系統會改用產生的 id。傳回 ctx.defaultThreadId(隨機 UUID)即可保留內建行為。waitUntil?:
waitUntil function。Vercel 必須使用此 function,讓背景 Agent run 在 webhook 傳回 200 後仍可繼續。在 Vercel 上,請傳入來自 @vercel/functions 的 waitUntil。系統會從 request context 自動偵測 Cloudflare Workers 及 Netlify Functions。AWS Lambda 會自然等待 event loop 清空,因此不需要 waitUntil。resolveWaitUntil?:
waitUntil 位於 Hono request context、但內建 helper 未有涵蓋的 runtime resolver。解析次序:獨立的 waitUntil → resolveWaitUntil(c) → 預設值(Cloudflare Workers、Netlify)。各 adapter 選項各 adapter 選項 的直接連結
將 adapter 包裝在 ChannelAdapterConfig 物件內,以設定各 adapter 的選項:
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:
gateway?:
false。cards?:
toolDisplay。未設定 toolDisplay 時,cards: true 對應至 toolDisplay: "cards",而 cards: false 對應至 toolDisplay: "text"。IDE 會以刪除線標示此欄位;runtime 行為維持不變。cors?:
formatError?:
formatToolCall?:
toolDisplay。設定後會以 ToolDisplayFn 執行,只會在 result/error event 觸發;running 及 approval event 不會呈現。在 type level 上與 toolDisplay 互斥。streaming?:
true;其他 adapter 預設為 false。textFormat?:
'markdown'(預設值)會以 markdown 發佈回覆:原生支援 markdown 呈現的 adapter(Slack)會直接呈現,其他 adapter 則會轉換成平台格式。'plain' 會以純文字原樣發佈回覆,為被提示輸出 Slack mrkdwn 等平台格式的 Agent 還原採用 markdown 前的行為。只適用於最終回覆文字;Tool card、錯誤訊息及 tripwire 通知不受影響。無論此設定為何,原生串流一律使用 markdown。toolDisplay?:
"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 呈現。typingStatus?:
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 使用預設值。Tool 顯示模式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 行為。
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 項目無法包含互動式按鈕。
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
可以回復使用內建預設值。
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<void>:包裝或取代預設 handler
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?:
onMention?:
onSubscribedMessage?:
ChannelHandler function signature:
type ChannelHandler = (
thread: Thread,
message: Message,
defaultHandler: (thread: Thread, message: Message) => Promise<void>,
ctx: ChannelHandlerContext,
) => Promise<void>
type ChannelHandlerContext = {
mastra?: Mastra
requestContext: RequestContext
}
ctx.mastra 是已解析的 mastra instance,因此無需傳入外部 accessor,handler 亦可存取 storage 或其他已註冊 primitive:
onDirectMessage: async (thread, message, defaultHandler, ctx) => {
const store = await ctx.mastra?.getStorage()?.getStore('memory')
await defaultHandler(thread, message)
}
ctx.requestContext 是此訊息即將啟動的 run 所使用的 RequestContext,每則訊息都會重新建立。請在呼叫 defaultHandler 前寫入;該值會連同 Mastra 隨後加入的 channel 項目傳至 run:
onDirectMessage: async (thread, message, defaultHandler, ctx) => {
ctx.requestContext.set('locale', 'en-GB')
await defaultHandler(thread, message)
}
透過此方式,run 從 request context 讀取的任何資料都可按訊息決定,例如平台傳送者對應至哪位使用者。
Resource ID 解析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 即可回復使用內建行為。
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:
slack、discord)。thread:
thread.isDM 區分私訊與群組/channel thread。message:
message.author.userId 是操作者/傳送者,不一定是 memory 擁有者。defaultResourceId:
${platform}:${message.author.userId})。傳回此值可保留目前行為。Thread ID 解析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 即可保留內建行為。
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:
slack、discord)。thread:
thread.isDM 區分私訊與群組/channel thread。message:
resourceId:
resourceId(在 resolveResourceId 後)。defaultThreadId:
Inline 媒體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 連結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 視為影片內容。
type InlineLinkEntry =
| string // Domain pattern (HEAD determines mime type)
| { match: string; mimeType: string } // Domain + forced mime type (skips HEAD)
相關內容相關內容 的直接連結
- Channels 概覽:概念、快速入門及平台設定
- Agent class:Constructor 參數及 method
- Chat SDK adapters:Adapter 設定及平台設定