Skip to content

10. 多渠道支持

本章定位

在实际的应用开发中,个人助理往往需要支持多种接入渠道(例如网页端、微信、Telegram 机器人或桌面插件),使用户在不同终端下都能与其交互。

但是,在我们的 L1(Harness 101)阶段中,我们将贯彻“只做接口设计预留,不编写任何具体渠道实现”的原则

原因在于:

  • 我们当前的首要任务是确保核心 Harness 包(@hachimi/core)中的逻辑与状态机足够干净稳定。
  • 多渠道接入属于最外围的“网络输入层”,如果过早将复杂的第三方 Webhook 轮询、Tauri IPC 消息分发等细节引入核心,会极大干扰我们对 Agent 原理的研究。
  • 保持项目体量轻量,便于学习和本地调试。

在本章中,我们将简要说明如何通过协议解耦为多渠道做扩展预留。


核心解耦原则:核心不绑定渠道

多渠道架构设计的核心思想是:Harness 核心代码绝不直接依赖任何第三方交互平台协议。

text
┌────────────────────────────────────────────────────────┐
│                        外部渠道层                       │
│    CLI      Desktop App    REST API Server   Telegram  │
└─────┬────────────┬────────────────┬──────────────┬─────┘
      │            │                │              │
      └────────────┼────────────────┴──────────────┘
                   │ 1. 各个渠道适配器拦截请求
                   │    封装为统一的 IncomingMessage

┌────────────────────────────────────────────────────────┐
│                    Harness 核心包                      │
│             (Agent / Memory / Session)                 │
└──────────────────────────┬─────────────────────────────┘
                           │ 2. 核心计算完毕
                           │    吐出统一的 OutgoingMessage

┌────────────────────────────────────────────────────────┐
│                      渠道转换输出                      │
│  stdout       Tauri IPC        SSE Stream      Webhook │
└────────────────────────────────────────────────────────┘

Harness 核心本身扮演一个纯净的数据转换层:

  • 接收统一的格式输入。
  • 返回统一的格式响应。
  • 具体的协议转化和通信,完全交由外围的独立包(Channel Adapters)自行实现。

统一输入输出接口

我们在 packages/core/src/types/index.ts 中前瞻性地留下了这两个数据契约:

ts
/**
 * 渠道输入定义
 */
export interface IncomingMessage {
  sessionId?: SessionId; 
  userId: UserId;        
  channel: ChannelType;  // 枚举值,标注消息的来路
  content: string;       
  attachments?: Array<{ type: string; url?: string; data?: Buffer }>; 
  metadata?: Record<string, unknown>; 
}

/**
 * 渠道输出定义
 */
export interface OutgoingMessage {
  sessionId: SessionId;
  content: string;       
  toolCalls?: ToolCall[];
  metadata?: Record<string, unknown>;
}

在 L1 的 CLI 环境中,我们在 scripts/chat.ts 里所做的事其实就是直接扮演了这个适配器的角色:读取控制台文本,包装送入 agent.run(),拿到文本后再用 console.log 打印出来。


代码目录中的占位预留

为了展示如何在 Monorepo 中规划多渠道结构,我们在工作区中进行了物理目录分层:

  • packages/channels/cli/packages/channels/api/(预留占位)
  • apps/cli/ (当前已实现的 CLI 客户端)
  • apps/server/ (预留用于托管 REST API 或 Express Bot Webhook 的服务器端应用)

通过这种多包机制,后续如果你想要增加 Telegram 支持,只需在 packages/channels/telegram 下编写适配代码,完全不需要改动 packages/core


本章总结

本章我们:

  • 阐述了 Core 与 Channels 分离的解耦理念。
  • 了解了 IncomingMessageOutgoingMessage 数据传输契约。
  • 规划了 Monorepo 下渠道相关的多包结构。