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 Type | MagiMessageSource |
|---|---|
discord | discord |
telegram | telegram |
slack | slack |
qqbot | qqbot |
whatsapp | whatsapp |
email | email |
wechat | wechat |
signal | signal |
line | line |
| 其他/未映射 | 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,
);
如果发送错误反馈本身也失败,仅记录日志,不再重试。
下一步
- 消息路由与安全管线 — 消息到达 Bridge 之前的处理流程
- 编写 Channel 插件 — 从零编写自定义 Channel 插件