Chuyển đến nội dung chính

レッスン 18: チャット チャネル — マルチプラットフォーム メッセージング

チャネル アダプター パターン: Web チャット、テレグラム、Slack、Discord、Zalo、Facebook Messenger、LINE、WhatsApp。統一されたメッセージ形式、チャネル固有の機能、Webhook 処理。

🧠 AI と ML — レッスン 17 レッスン 18: チャット チャネル — マルチプラットフォーム メッセージング

AIエージェントプラットフォームをゼロから構築 — xClawとの実戦

パート 6: マルチテナント、RBAC、チャネル

xdev.asia

はじめに

AI エージェントは、ユーザーが使い慣れたプラットフォームからアクセスできる場合にのみ役立ちます。チャット チャネルを使用すると、エージェントを Telegram、Slack、Discord、およびその他の多くのプラットフォームに展開でき、すべて同じエージェント コアを使用できます。


1. チャネルアダプターインターフェース

// packages/channels/src/types.ts
export interface ChannelAdapter {
  readonly channelType: string;

  // Initialize channel (set up webhooks, etc.)
  initialize(config: ChannelConfig): Promise<void>;

  // Send message to channel
  sendMessage(channelId: string, message: ChannelMessage): Promise<void>;

  // Parse incoming webhook to unified format
  parseIncoming(rawPayload: unknown): IncomingMessage;

  // Cleanup
  shutdown(): Promise<void>;
}

export interface ChannelMessage {
  text: string;
  markdown?: string;
  images?: string[];
  buttons?: { label: string; action: string }[];
  metadata?: Record<string, unknown>;
}

export interface IncomingMessage {
  channelType: string;
  channelId: string;        // channel/group ID
  userId: string;           // user ID on the platform
  userName: string;
  text: string;
  images?: string[];
  replyToMessageId?: string;
  raw: unknown;             // Original payload
}

export interface ChannelConfig {
  token: string;
  webhookUrl?: string;
  additionalConfig?: Record<string, unknown>;
}

2. 電報チャネル

// packages/channels/telegram/src/telegram-adapter.ts
export class TelegramAdapter implements ChannelAdapter {
  readonly channelType = 'telegram';
  private bot: TelegramBot;

  async initialize(config: ChannelConfig) {
    this.bot = new TelegramBot(config.token);

    if (config.webhookUrl) {
      await this.bot.setWebhook(config.webhookUrl);
    }
  }

  parseIncoming(payload: unknown): IncomingMessage {
    const update = payload as TelegramUpdate;
    const msg = update.message!;

    return {
      channelType: 'telegram',
      channelId: String(msg.chat.id),
      userId: String(msg.from!.id),
      userName: msg.from!.first_name,
      text: msg.text || '',
      images: msg.photo?.map(p => p.file_id),
      replyToMessageId: msg.reply_to_message
        ? String(msg.reply_to_message.message_id)
        : undefined,
      raw: payload,
    };
  }

  async sendMessage(chatId: string, message: ChannelMessage) {
    if (message.markdown) {
      await this.bot.sendMessage(chatId, message.markdown, {
        parse_mode: 'MarkdownV2',
      });
    } else {
      await this.bot.sendMessage(chatId, message.text);
    }

    // Send images if any
    if (message.images) {
      for (const image of message.images) {
        await this.bot.sendPhoto(chatId, image);
      }
    }
  }

  async shutdown() {
    await this.bot.deleteWebhook();
  }
}

3. チャネルルーター

// packages/gateway/src/channels/channel-router.ts
export class ChannelRouter {
  private channels = new Map<string, ChannelAdapter>();
  private agent: Agent;

  register(adapter: ChannelAdapter) {
    this.channels.set(adapter.channelType, adapter);
  }

  // Webhook handler — receives messages from all channels
  async handleWebhook(channelType: string, payload: unknown): Promise<void> {
    const adapter = this.channels.get(channelType);
    if (!adapter) throw new Error(`Unknown channel: ${channelType}`);

    // Parse to unified format
    const incoming = adapter.parseIncoming(payload);

    // Find or create user mapping
    const user = await this.resolveUser(incoming);

    // Chat with agent
    const response = await this.agent.chat(
      incoming.text,
      {
        tenantId: user.tenantId,
        userId: user.id,
        sessionId: `${channelType}:${incoming.channelId}`,
      },
    );

    // Send response back through the same channel
    await adapter.sendMessage(incoming.channelId, {
      text: response,
      markdown: response,
    });
  }
}

// Webhook routes
app.post('/webhooks/telegram', async (c) => {
  const payload = await c.req.json();
  await channelRouter.handleWebhook('telegram', payload);
  return c.json({ ok: true });
});

app.post('/webhooks/slack', async (c) => {
  const payload = await c.req.json();
  await channelRouter.handleWebhook('slack', payload);
  return c.json({ ok: true });
});

4. サポートされているチャンネル

チャンネル特長アダプター
ウェブチャットフルリッチな UI、ストリーミング、ファイルアップロード組み込みの React アプリ
電報マークダウン、インラインボタン、グループボット API + Webhook
スラックスレッド、リアクション、スラッシュコマンドイベントAPI
ディスコード埋め込み、スレッド、スラッシュ コマンドDiscord.js
ザロベトナム語メッセージング、OA APIZalo OA API
Facebook メッセンジャークイック返信、テンプレートグラフAPI
LINEリッチメニュー、フレックスメッセージメッセージング API
WhatsAppテンプレート、メディア、ビジネス APIクラウドAPI

5. まとめ

  • アダプター パターン — すべてのプラットフォーム向けの統一インターフェイス
  • チャネルルーター — Webhookゲートウェイ→解析→agent.chat()→応答の送信
  • ユーザー マッピング — 外部プラットフォーム ユーザー → 内部ユーザー
  • 1 つのエージェント、多数のチャネル — 同じ AI、異なるフロントエンド

次の記事: React フロントエンド — Web チャット インターフェイスを構築します。