Skip to main content

ChannelMagiBridge

ChannelMagiBridge 是 Channel 系统与 Agent 处理层之间的桥梁,负责将 Channel 消息转换为 MagiIncomingMessage 格式,提交给 MagiService 处理,并将响应路由回原始 Channel。

源码位置: packages/desktop/app/main/services/agent-core/magi/ChannelMagiBridge.ts

数据流

sequenceDiagram
participant Router as ChannelMessageRouter
participant Registry as ChannelPluginRegistry
participant Bridge as ChannelMagiBridge
participant Magi as MagiService
participant Plugin as Channel Plugin

Registry->>Bridge: emit('routeToAgent', payload)
Note over Bridge: 转换 ChannelMessage → MagiIncomingMessage

Bridge->>Magi: handleMessage(incoming)

alt Agent 生成中间消息
Magi->>Bridge: onIntermediateMessage(text)
Bridge->>Router: sendResponse(channelId, chatId, text, type)
Router->>Plugin: sendMessage(chatId, chunk)
end

Magi-->>Bridge: { response, mode }

alt 有最终回复且未发送中间消息
Bridge->>Router: sendResponse(channelId, chatId, response, type)
Router->>Plugin: sendMessage(chatId, chunk)
end

Note over Bridge: 如果已发送中间消息,跳过最终回复<br/>(避免重复发送)

生命周期

class ChannelMagiBridge {
constructor(
registry: ChannelPluginRegistry,
channelRouter: ChannelMessageRouter,
magiService: MagiService,
logger: LoggerService,
)

/** 开始监听 'routeToAgent' 事件 */
start(): void

/** 停止监听并清理 */
stop(): void

/** 是否正在运行 */
isActive(): boolean
}

start() 应在 MagiService.start() 之后调用,确保 Agent 服务已就绪。

Channel Source 映射

Bridge 将 Channel 插件类型映射为 MagiMessageSource

Channel Plugin TypeMagiMessageSource
discorddiscord
telegramtelegram
slackslack
qqbotqqbot
whatsappwhatsapp
emailemail
wechatwechat
signalsignal
lineline
其他/未映射api(回退值)

MagiMessageSource 影响 Agent 的行为和上下文,例如不同来源可能触发不同的提示词策略。

RouteToAgentPayload

ChannelMessageRouter 通过 registry.emit('routeToAgent', payload) 发射的事件载荷:

interface RouteToAgentPayload {
/** 格式化后的 prompt(私信为原文,群组为 XML) */
prompt: string;
/** 触发回复的原始消息 */
sourceMessage: ChannelMessage;
/** 所有相关消息(缓冲 + 触发消息) */
allMessages: ChannelMessage[];
/** 插件返回的 Channel 专属 system prompt */
channelSystemPrompt?: string;
/** 是否为一对一对话(私信/DM) */
isDirectConversation: boolean;
/** 用户权限信息 */
channelUserPermissions?: {
canUseTool: boolean;
requireConfirmation: boolean;
};
}

MagiIncomingMessage 构建

Bridge 将 RouteToAgentPayload 转换为 MagiIncomingMessage

const incoming: MagiIncomingMessage = {
content: prompt, // 群组:XML 格式;私信:原文
source: CHANNEL_SOURCE_MAP[channelType], // 消息来源映射
channelId: sourceMessage.channelId, // Channel 实例 ID
chatId: sourceMessage.chatId, // 聊天/频道 ID
userId: sourceMessage.senderId, // 平台用户 ID
messageId: sourceMessage.id, // 平台消息 ID
timestamp: Date.now(), // 处理时间戳
channelSystemPrompt, // 插件专属 system prompt
isDirectConversation, // 是否一对一
attachments, // 附件列表
channelUserPermissions, // 用户权限
onIntermediateMessage, // 中间消息回调
};

附件转发

Bridge 从 allMessages 中收集所有附件并转发给 Agent:

const attachments: MagiAttachment[] = [];
for (const msg of payload.allMessages) {
if (msg.attachments) {
for (const att of msg.attachments) {
if (!att.url && !att.localPath) continue; // 跳过无源附件
attachments.push({
type: att.type,
url: att.url,
localPath: att.localPath,
name: att.name || `attachment_${attachments.length + 1}`,
size: att.size,
});
}
}
}

附件来自缓冲区中的所有消息(不仅是触发消息),确保 Agent 能看到群组对话中分享的图片和文件。

中间消息处理

当 Agent 在处理过程中产生中间输出(如逐步推理、工具调用结果),Bridge 通过 onIntermediateMessage 回调实时转发到 Channel:

onIntermediateMessage: async (text: string) => {
intermediatesSent = true;
await this.channelRouter.sendResponse(
sourceMessage.channelId,
sourceMessage.chatId,
text,
sourceMessage.channelType,
);
}

去重逻辑: 如果已经通过中间消息发送了回复(intermediatesSent === true),Bridge 会跳过最终的 result.response 发送,避免重复。

错误处理

当消息处理失败时,Bridge 会向原始 Channel 发送错误反馈:

const errorText = `[Elftia Error] ${error.message}`;
await this.channelRouter.sendResponse(
sourceMessage.channelId,
sourceMessage.chatId,
errorText,
sourceMessage.channelType,
);

如果发送错误反馈本身也失败,仅记录日志,不再重试。

下一步