欢迎光临
我们一直在努力

【Harness:落地实战】22、Gateway架构:多平台消息接入的统一Harness设计——打造企业级AI Agent消息网关

Gateway架构:多平台消息接入的统一Harness设计——打造企业级AI Agent消息网关

关键词:Gateway网关、多平台接入、消息路由、Harness架构、企业微信、Telegram机器人、Discord、Slack、飞书 摘要:在AI Agent系统中,如何优雅地处理来自Telegram、Discord、Slack、飞书、企业微信等不同平台的消息,并统一接入Harness引擎?本文提出“Gateway网关层”设计模式,全面解析消息路由、身份识别、格式转换、协议适配等核心职责。通过Go语言实战,演示多平台SDK集成、连接池管理、限流重试、状态同步等关键技术,最终构建一个生产级的统一消息网关。全文包含大量Mermaid架构图与代码片段,助力读者构建跨渠道智能助手。


📑 目录

  • 为什么需要Gateway架构?
  • Gateway在Harness系统中的位置
  • Gateway核心职责详解
    • 3.1 消息路由
    • 3.2 身份识别与统一用户映射
    • 3.3 格式转换
    • 3.4 协议适配
  • 多平台接入设计要点
    • 4.1 跨渠道身份一致性
    • 4.2 限流与重试策略
    • 4.3 连接池管理
  • 平台集成实战
    • 5.1 Telegram Bot接入
    • 5.2 Discord Bot接入
    • 5.3 Slack Bot接入
    • 5.4 飞书机器人接入
    • 5.5 企业微信接入
  • Gateway与Harness核心引擎的交互
    • 6.1 消息处理流程:解析→路由→执行→回复
    • 6.2 异步与同步模式选择
  • 统一推送与多平台状态同步
    • 7.1 消息已读回执同步
    • 7.2 对话状态跨渠道漂移
  • 实战案例:构建一个跨平台客服机器人
  • 总结与最佳实践

  • 1. 为什么需要Gateway架构?

    现代AI Agent系统往往需要同时服务多个消息平台:员工用企业微信、客户用Telegram、社区用户用Discord、合作伙伴用Slack……如果每个平台单独对接Harness引擎,会造成以下问题:

    • 代码重复:每个平台都要实现消息接收、解析、回复发送、错误重试等逻辑。
    • 逻辑耦合:业务逻辑(意图识别、工具调用)与平台特性(消息格式、速率限制)混在一起,难以维护。
    • 状态孤岛:用户在不同平台上的对话历史无法共享,体验割裂。
    • 扩展困难:新增一个平台需要改动Harness核心代码。

    Gateway(网关)层作为前置适配层,将所有平台的消息统一转换成内部标准格式,再交给Harness引擎处理;引擎的回复再通过网关分发回原平台。这种架构类似于微服务中的API网关模式。

    下面我们通过一张Mermaid图展示Gateway在整个系统中的位置。


    2. Gateway在Harness系统中的位置

    #mermaid-svg-DfM7aEmmXSmOvGSK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DfM7aEmmXSmOvGSK .error-icon{fill:#552222;}#mermaid-svg-DfM7aEmmXSmOvGSK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DfM7aEmmXSmOvGSK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DfM7aEmmXSmOvGSK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DfM7aEmmXSmOvGSK .marker.cross{stroke:#333333;}#mermaid-svg-DfM7aEmmXSmOvGSK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DfM7aEmmXSmOvGSK p{margin:0;}#mermaid-svg-DfM7aEmmXSmOvGSK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-DfM7aEmmXSmOvGSK .cluster-label text{fill:#333;}#mermaid-svg-DfM7aEmmXSmOvGSK .cluster-label span{color:#333;}#mermaid-svg-DfM7aEmmXSmOvGSK .cluster-label span p{background-color:transparent;}#mermaid-svg-DfM7aEmmXSmOvGSK .label text,#mermaid-svg-DfM7aEmmXSmOvGSK span{fill:#333;color:#333;}#mermaid-svg-DfM7aEmmXSmOvGSK .node rect,#mermaid-svg-DfM7aEmmXSmOvGSK .node circle,#mermaid-svg-DfM7aEmmXSmOvGSK .node ellipse,#mermaid-svg-DfM7aEmmXSmOvGSK .node polygon,#mermaid-svg-DfM7aEmmXSmOvGSK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DfM7aEmmXSmOvGSK .rough-node .label text,#mermaid-svg-DfM7aEmmXSmOvGSK .node .label text,#mermaid-svg-DfM7aEmmXSmOvGSK .image-shape .label,#mermaid-svg-DfM7aEmmXSmOvGSK .icon-shape .label{text-anchor:middle;}#mermaid-svg-DfM7aEmmXSmOvGSK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DfM7aEmmXSmOvGSK .rough-node .label,#mermaid-svg-DfM7aEmmXSmOvGSK .node .label,#mermaid-svg-DfM7aEmmXSmOvGSK .image-shape .label,#mermaid-svg-DfM7aEmmXSmOvGSK .icon-shape .label{text-align:center;}#mermaid-svg-DfM7aEmmXSmOvGSK .node.clickable{cursor:pointer;}#mermaid-svg-DfM7aEmmXSmOvGSK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DfM7aEmmXSmOvGSK .arrowheadPath{fill:#333333;}#mermaid-svg-DfM7aEmmXSmOvGSK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DfM7aEmmXSmOvGSK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DfM7aEmmXSmOvGSK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DfM7aEmmXSmOvGSK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DfM7aEmmXSmOvGSK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DfM7aEmmXSmOvGSK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DfM7aEmmXSmOvGSK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DfM7aEmmXSmOvGSK .cluster text{fill:#333;}#mermaid-svg-DfM7aEmmXSmOvGSK .cluster span{color:#333;}#mermaid-svg-DfM7aEmmXSmOvGSK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DfM7aEmmXSmOvGSK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DfM7aEmmXSmOvGSK rect.text{fill:none;stroke-width:0;}#mermaid-svg-DfM7aEmmXSmOvGSK .icon-shape,#mermaid-svg-DfM7aEmmXSmOvGSK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DfM7aEmmXSmOvGSK .icon-shape p,#mermaid-svg-DfM7aEmmXSmOvGSK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DfM7aEmmXSmOvGSK .icon-shape .label rect,#mermaid-svg-DfM7aEmmXSmOvGSK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DfM7aEmmXSmOvGSK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DfM7aEmmXSmOvGSK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DfM7aEmmXSmOvGSK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    存储层

    核心层

    Gateway 网关层

    外部消息平台

    Telegram

    Discord

    Slack

    飞书

    企业微信

    多平台适配器

    消息路由器

    格式转换器

    身份识别

    Harness 引擎ReAct循环/工具/LLM

    Redis会话/限流

    数据库用户映射

    各模块职责:

    • 适配器:负责监听各平台的消息事件,调用平台SDK收发消息。
    • 格式转换器:将平台特有的消息结构(Telegram的Message、企业微信的TextMessage)统一为内部GatewayMessage。
    • 身份识别:根据平台类型+平台用户ID,映射到内部统一的UserID(例如telegram:123456 -> user_10086)。
    • 消息路由器:根据用户ID、会话ID或意图,将消息分发到对应的Harness实例或会话。

    3. Gateway核心职责详解

    3.1 消息路由

    消息路由决定了一条消息应该交给哪个Harness处理。常见策略:

    • 单例模式:所有消息路由到同一个Harness引擎(适合个人助理)。
    • 会话粘滞:同一会话(如Telegram的chat_id)的消息始终发往同一个Harness实例。
    • 意图分流:技术问题发往技术助手,销售问题发往销售助手。

    路由表通常存储在Redis中:

    type Router struct {
    backend map[string]HarnessClient // sessionID -> Harness地址
    }

    func (r *Router) Route(sessionID string) HarnessClient {
    if client, ok := r.backend[sessionID]; ok {
    return client
    }
    // 分配新实例
    client := r.selectLeastLoaded()
    r.backend[sessionID] = client
    return client
    }

    3.2 身份识别与统一用户映射

    不同平台用户标识不同:Telegram用user.id,Discord用user.id(但guild不同),企业微信用UserID。Gateway需要维护一个映射表:

    type IdentityResolver struct {
    db *sql.DB
    }

    func (ir *IdentityResolver) Resolve(platform string, platformUserID string) (internalUserID string, err error) {
    row := ir.db.QueryRow("SELECT internal_user_id FROM user_mapping WHERE platform=? AND platform_user_id=?", platform, platformUserID)
    if err == sql.ErrNoRows {
    // 创建新用户
    internalUserID = generateUUID()
    ir.db.Exec("INSERT INTO user_mapping VALUES (?,?,?)", internalUserID, platform, platformUserID)
    return internalUserID, nil
    }
    return
    }

    3.3 格式转换

    内部统一消息格式示例:

    type GatewayMessage struct {
    ID string // 全局唯一ID
    Platform string // telegram, discord, slack, feishu, wecom
    ChannelID string // 会话ID(群或私聊)
    Sender User // 发送者信息
    Content string // 文本内容
    RawData interface{} // 原始消息(用于扩展)
    Attachments []Attachment // 图片、文件等
    Timestamp time.Time
    }

    type User struct {
    InternalID string // 内部统一ID
    PlatformID string // 平台原始ID
    DisplayName string
    }

    转换器实现:

    func ConvertTelegram(update *tgbotapi.Update) *GatewayMessage {
    msg := update.Message
    return &GatewayMessage{
    ID: fmt.Sprintf("tg_%d", msg.MessageID),
    Platform: "telegram",
    ChannelID: strconv.FormatInt(msg.Chat.ID, 10),
    Sender: User{
    PlatformID: strconv.Itoa(msg.From.ID),
    DisplayName: msg.From.FirstName + " " + msg.From.LastName,
    },
    Content: msg.Text,
    RawData: msg,
    Timestamp: time.Unix(int64(msg.Date), 0),
    }
    }

    3.4 协议适配

    不同平台的通信协议差异:

    • Telegram:长轮询或Webhook(HTTP回调)。
    • Discord:Gateway WebSocket(需要维持连接)。
    • Slack:RTM API或Event Subscription(HTTP)。
    • 飞书/企业微信:HTTP回调(需验证签名)。

    Gateway适配器负责启动相应的监听器,并统一转换为内部事件流。


    4. 多平台接入设计要点

    4.1 跨渠道身份一致性

    用户可能同时在Telegram和企业微信上与机器人对话。如何识别为同一用户?可以采用绑定码方式:用户在任一平台发送绑定指令,机器人返回一次性绑定码,用户在其他平台输入该码完成关联。关联信息存储后,后续所有消息都映射到同一internalUserID。

    WX

    机器人

    Gateway

    Telegram用户

    WX

    机器人

    Gateway

    Telegram用户

    #mermaid-svg-uLccotOmzMXZ6BYv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uLccotOmzMXZ6BYv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uLccotOmzMXZ6BYv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uLccotOmzMXZ6BYv .error-icon{fill:#552222;}#mermaid-svg-uLccotOmzMXZ6BYv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uLccotOmzMXZ6BYv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uLccotOmzMXZ6BYv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uLccotOmzMXZ6BYv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uLccotOmzMXZ6BYv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uLccotOmzMXZ6BYv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uLccotOmzMXZ6BYv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uLccotOmzMXZ6BYv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uLccotOmzMXZ6BYv .marker.cross{stroke:#333333;}#mermaid-svg-uLccotOmzMXZ6BYv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uLccotOmzMXZ6BYv p{margin:0;}#mermaid-svg-uLccotOmzMXZ6BYv .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uLccotOmzMXZ6BYv text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-uLccotOmzMXZ6BYv .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uLccotOmzMXZ6BYv .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-uLccotOmzMXZ6BYv .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-uLccotOmzMXZ6BYv .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-uLccotOmzMXZ6BYv #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-uLccotOmzMXZ6BYv .sequenceNumber{fill:white;}#mermaid-svg-uLccotOmzMXZ6BYv #sequencenumber{fill:#333;}#mermaid-svg-uLccotOmzMXZ6BYv #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-uLccotOmzMXZ6BYv .messageText{fill:#333;stroke:none;}#mermaid-svg-uLccotOmzMXZ6BYv .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uLccotOmzMXZ6BYv .labelText,#mermaid-svg-uLccotOmzMXZ6BYv .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-uLccotOmzMXZ6BYv .loopText,#mermaid-svg-uLccotOmzMXZ6BYv .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-uLccotOmzMXZ6BYv .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uLccotOmzMXZ6BYv .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-uLccotOmzMXZ6BYv .noteText,#mermaid-svg-uLccotOmzMXZ6BYv .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-uLccotOmzMXZ6BYv .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uLccotOmzMXZ6BYv .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uLccotOmzMXZ6BYv .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uLccotOmzMXZ6BYv .actorPopupMenu{position:absolute;}#mermaid-svg-uLccotOmzMXZ6BYv .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-uLccotOmzMXZ6BYv .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uLccotOmzMXZ6BYv .actor-man circle,#mermaid-svg-uLccotOmzMXZ6BYv line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-uLccotOmzMXZ6BYv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    /bind

    生成绑定码 "ABC123"

    请在微信输入 ABC123

    ABC123

    验证绑定码

    关联两个平台的用户ID

    绑定成功

    4.2 限流与重试策略

    每个平台都有API速率限制。Gateway应实现:

    • 令牌桶/漏桶限制发送频率。
    • 指数退避重试:当发送失败时,重试3次,间隔1s、2s、4s。
    • 死信队列:超过重试次数的消息存入队列,人工介入。

    type RateLimiter struct {
    limiter *rate.Limiter // golang.org/x/time/rate
    }

    func (rl *RateLimiter) SendWithRetry(sendFunc func() error) error {
    err := rl.limiter.Wait(context.Background())
    if err != nil {
    return err
    }
    for i := 0; i < 3; i++ {
    err = sendFunc()
    if err == nil {
    return nil
    }
    time.Sleep(time.Duration(math.Pow(2, float64(i))) * time.Second)
    }
    return fmt.Errorf("max retries exceeded: %v", err)
    }

    4.3 连接池管理

    对于WebSocket长连接(Discord、Slack RTM),需要管理连接生命周期、心跳、断线重连。建议每个平台一个连接池,按guild或channel复用连接。

    type DiscordConnectionPool struct {
    conns map[string]*discordgo.Session // key: guildID
    mu sync.RWMutex
    }

    func (p *DiscordConnectionPool) Get(guildID string) *discordgo.Session {
    p.mu.RLock()
    defer p.mu.RUnlock()
    return p.conns[guildID]
    }


    5. 平台集成实战

    以下使用Go语言演示各平台接入代码片段(仅核心部分)。

    5.1 Telegram Bot接入

    使用go-telegram-bot-api库,Webhook模式。

    func StartTelegramBot(token string, gw *Gateway) {
    bot, err := tgbotapi.NewBotAPI(token)
    if err != nil {
    panic(err)
    }
    updateChan := bot.ListenForWebhook("/webhook/telegram")
    go func() {
    for update := range updateChan {
    msg := ConvertTelegram(&update)
    gw.OnMessage(msg)
    }
    }()
    }

    5.2 Discord Bot接入

    使用discordgo库,Gateway Intents。

    func StartDiscordBot(token string, gw *Gateway) {
    dg, err := discordgo.New("Bot " + token)
    if err != nil {
    panic(err)
    }
    dg.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
    if m.Author.ID == s.State.User.ID { return }
    msg := ConvertDiscord(m)
    gw.OnMessage(msg)
    })
    dg.Open()
    }

    5.3 Slack Bot接入

    使用slack-go/slack,Socket Mode或Event API。

    func StartSlackBot(token, appToken string, gw *Gateway) {
    api := slack.New(token, slack.OptionDebug(true))
    client := slack.NewSocketModeClient(slack.SocketModeOption{
    Client: api,
    AppToken: appToken,
    })
    client.AddHandler(socketmode.EventHandler(func(evt *socketmode.Event, client *socketmode.Client) {
    if evt.Type == socketmode.EventTypeEventsAPI {
    eventsAPI, _ := evt.Data.(slackevents.EventsAPIEvent)
    if innerEvent := eventsAPI.InnerEvent; innerEvent.Data != nil {
    switch msg := innerEvent.Data.(type) {
    case *slackevents.MessageEvent:
    gm := ConvertSlack(msg)
    gw.OnMessage(gm)
    }
    }
    }
    }))
    client.Run()
    }

    5.4 飞书机器人接入

    飞书使用HTTP回调,需要验证签名。Gateway实现一个HTTP Handler。

    func FeishuWebhookHandler(w http.ResponseWriter, r *http.Request) {
    // 验证签名(略)
    var req struct {
    Header struct {
    EventID string `json:"event_id"`
    } `json:"header"`
    Event struct {
    Message struct {
    MessageID string `json:"message_id"`
    Content string `json:"content"` // JSON格式,需解析
    SenderID string `json:"sender_id"`
    CreateTime string `json:"create_time"`
    } `json:"message"`
    } `json:"event"`
    }
    json.NewDecoder(r.Body).Decode(&req)
    // 解析Content中的text
    var contentMap map[string]string
    json.Unmarshal([]byte(req.Event.Message.Content), &contentMap)
    gm := &GatewayMessage{
    Platform: "feishu",
    ChannelID: req.Event.Message.MessageID,
    Content: contentMap["text"],
    }
    gw.OnMessage(gm)
    }

    5.5 企业微信接入

    类似飞书,需要验证URL、解密消息。

    func WecomWebhookHandler(w http.ResponseWriter, r *http.Request) {
    // 使用企业微信提供的加解密库(略)
    msg := ParseWecomMessage(r)
    gm := &GatewayMessage{
    Platform: "wecom",
    ChannelID: msg.ChatID,
    Sender: User{PlatformID: msg.FromUserID},
    Content: msg.Text.Content,
    }
    gw.OnMessage(gm)
    }


    6. Gateway与Harness核心引擎的交互

    6.1 消息处理流程:解析→路由→执行→回复

    完整的数据流如下:

    工具

    大模型

    Harness引擎

    Router

    Converter

    Adapter

    消息平台

    工具

    大模型

    Harness引擎

    Router

    Converter

    Adapter

    消息平台

    #mermaid-svg-t1xZNFLhhypLjesl{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t1xZNFLhhypLjesl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t1xZNFLhhypLjesl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t1xZNFLhhypLjesl .error-icon{fill:#552222;}#mermaid-svg-t1xZNFLhhypLjesl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t1xZNFLhhypLjesl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t1xZNFLhhypLjesl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t1xZNFLhhypLjesl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t1xZNFLhhypLjesl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t1xZNFLhhypLjesl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t1xZNFLhhypLjesl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t1xZNFLhhypLjesl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t1xZNFLhhypLjesl .marker.cross{stroke:#333333;}#mermaid-svg-t1xZNFLhhypLjesl svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t1xZNFLhhypLjesl p{margin:0;}#mermaid-svg-t1xZNFLhhypLjesl .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-t1xZNFLhhypLjesl text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-t1xZNFLhhypLjesl .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-t1xZNFLhhypLjesl .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-t1xZNFLhhypLjesl .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-t1xZNFLhhypLjesl .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-t1xZNFLhhypLjesl #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-t1xZNFLhhypLjesl .sequenceNumber{fill:white;}#mermaid-svg-t1xZNFLhhypLjesl #sequencenumber{fill:#333;}#mermaid-svg-t1xZNFLhhypLjesl #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-t1xZNFLhhypLjesl .messageText{fill:#333;stroke:none;}#mermaid-svg-t1xZNFLhhypLjesl .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-t1xZNFLhhypLjesl .labelText,#mermaid-svg-t1xZNFLhhypLjesl .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-t1xZNFLhhypLjesl .loopText,#mermaid-svg-t1xZNFLhhypLjesl .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-t1xZNFLhhypLjesl .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-t1xZNFLhhypLjesl .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-t1xZNFLhhypLjesl .noteText,#mermaid-svg-t1xZNFLhhypLjesl .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-t1xZNFLhhypLjesl .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-t1xZNFLhhypLjesl .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-t1xZNFLhhypLjesl .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-t1xZNFLhhypLjesl .actorPopupMenu{position:absolute;}#mermaid-svg-t1xZNFLhhypLjesl .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-t1xZNFLhhypLjesl .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-t1xZNFLhhypLjesl .actor-man circle,#mermaid-svg-t1xZNFLhhypLjesl line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-t1xZNFLhhypLjesl :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    alt

    [有工具调用]

    原始消息(Webhook/WebSocket)

    原始对象

    转换为GatewayMessage

    统一消息

    识别用户,查找会话

    转发内部请求

    调用模型推理

    响应(含工具调用或最终答案)

    执行工具

    结果

    再次调用(ReAct循环)

    最终回复内容

    回复+原始平台信息

    发送消息(调用平台API)

    6.2 异步与同步模式选择

    • 同步模式:Gateway等待Harness返回结果后再回复平台(适用于Webhook有超时限制,如企业微信5秒)。对于长耗时任务,应先回复“正在处理中”,然后异步完成后再推送结果。
    • 异步模式:Gateway收到消息后立即返回200,将消息放入队列,由Worker处理,通过平台API主动推送回复。

    推荐实现:

    func (gw *Gateway) OnMessage(msg *GatewayMessage) {
    // 快速回复“正在思考”
    gw.SendTyping(msg.ChannelID)

    // 异步处理
    go func() {
    resp := gw.harness.Process(msg)
    gw.SendMessage(msg.ChannelID, resp)
    }()
    }


    7. 统一推送与多平台状态同步

    7.1 消息已读回执同步

    当用户在一个平台阅读了消息,Gateway可以同步到其他平台(如企业微信已读状态→Telegram)。实现需要平台支持(Telegram不支持已读回执,企业微信支持)。

    7.2 对话状态跨渠道漂移

    用户可能在手机上(Telegram)开始对话,然后切换到电脑(企业微信)继续。Gateway需要共享Harness的会话状态。解决方案:

    • 将Harness会话存储在Redis中,以internalUserID为键。
    • Gateway在不同平台的消息到达时,加载同一个会话。

    type SessionStore struct {
    redis *redis.Client
    }

    func (s *SessionStore) Get(userID string) *HarnessSession {
    data, _ := s.redis.Get("session:" + userID).Bytes()
    var sess HarnessSession
    json.Unmarshal(data, &sess)
    return &sess
    }

    状态同步流程:

    #mermaid-svg-HUGWmG5lHhlOor6c{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HUGWmG5lHhlOor6c .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HUGWmG5lHhlOor6c .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HUGWmG5lHhlOor6c .error-icon{fill:#552222;}#mermaid-svg-HUGWmG5lHhlOor6c .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HUGWmG5lHhlOor6c .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HUGWmG5lHhlOor6c .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HUGWmG5lHhlOor6c .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HUGWmG5lHhlOor6c .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HUGWmG5lHhlOor6c .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HUGWmG5lHhlOor6c .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HUGWmG5lHhlOor6c .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HUGWmG5lHhlOor6c .marker.cross{stroke:#333333;}#mermaid-svg-HUGWmG5lHhlOor6c svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HUGWmG5lHhlOor6c p{margin:0;}#mermaid-svg-HUGWmG5lHhlOor6c .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-HUGWmG5lHhlOor6c .cluster-label text{fill:#333;}#mermaid-svg-HUGWmG5lHhlOor6c .cluster-label span{color:#333;}#mermaid-svg-HUGWmG5lHhlOor6c .cluster-label span p{background-color:transparent;}#mermaid-svg-HUGWmG5lHhlOor6c .label text,#mermaid-svg-HUGWmG5lHhlOor6c span{fill:#333;color:#333;}#mermaid-svg-HUGWmG5lHhlOor6c .node rect,#mermaid-svg-HUGWmG5lHhlOor6c .node circle,#mermaid-svg-HUGWmG5lHhlOor6c .node ellipse,#mermaid-svg-HUGWmG5lHhlOor6c .node polygon,#mermaid-svg-HUGWmG5lHhlOor6c .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HUGWmG5lHhlOor6c .rough-node .label text,#mermaid-svg-HUGWmG5lHhlOor6c .node .label text,#mermaid-svg-HUGWmG5lHhlOor6c .image-shape .label,#mermaid-svg-HUGWmG5lHhlOor6c .icon-shape .label{text-anchor:middle;}#mermaid-svg-HUGWmG5lHhlOor6c .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HUGWmG5lHhlOor6c .rough-node .label,#mermaid-svg-HUGWmG5lHhlOor6c .node .label,#mermaid-svg-HUGWmG5lHhlOor6c .image-shape .label,#mermaid-svg-HUGWmG5lHhlOor6c .icon-shape .label{text-align:center;}#mermaid-svg-HUGWmG5lHhlOor6c .node.clickable{cursor:pointer;}#mermaid-svg-HUGWmG5lHhlOor6c .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HUGWmG5lHhlOor6c .arrowheadPath{fill:#333333;}#mermaid-svg-HUGWmG5lHhlOor6c .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HUGWmG5lHhlOor6c .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HUGWmG5lHhlOor6c .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HUGWmG5lHhlOor6c .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HUGWmG5lHhlOor6c .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HUGWmG5lHhlOor6c .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HUGWmG5lHhlOor6c .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HUGWmG5lHhlOor6c .cluster text{fill:#333;}#mermaid-svg-HUGWmG5lHhlOor6c .cluster span{color:#333;}#mermaid-svg-HUGWmG5lHhlOor6c div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HUGWmG5lHhlOor6c .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HUGWmG5lHhlOor6c rect.text{fill:none;stroke-width:0;}#mermaid-svg-HUGWmG5lHhlOor6c .icon-shape,#mermaid-svg-HUGWmG5lHhlOor6c .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HUGWmG5lHhlOor6c .icon-shape p,#mermaid-svg-HUGWmG5lHhlOor6c .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HUGWmG5lHhlOor6c .icon-shape .label rect,#mermaid-svg-HUGWmG5lHhlOor6c .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HUGWmG5lHhlOor6c .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HUGWmG5lHhlOor6c .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HUGWmG5lHhlOor6c :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Telegram消息

    GW

    企业微信消息

    Redis会话

    Harness


    8. 实战案例:构建一个跨平台客服机器人

    需求:一个电商客服机器人,同时接入Telegram(海外客户)、企业微信(国内员工)、飞书(内部团队)。功能包括查订单、转人工、FAQ。

    实现步骤:

  • 搭建Gateway:按照上述各平台集成代码,启动三个适配器。
  • 实现身份映射:用户首次在任一平台输入手机号绑定,Gateway将手机号与内部ID关联。
  • Harness引擎:使用第20篇中的tiny-claw引擎,配置订单查询工具、转人工工具。
  • 统一推送:当订单状态变更时,主动推送给用户的所有绑定平台(例如同时发送Telegram和企业微信)。
  • 人工坐席桥接:转人工时,Gateway将不同平台的消息统一转发到企业微信群,坐席回复后回传。
  • 效果演示(Mermaid流程图):

    #mermaid-svg-Zl09TqGAM7yxzzPp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Zl09TqGAM7yxzzPp .error-icon{fill:#552222;}#mermaid-svg-Zl09TqGAM7yxzzPp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Zl09TqGAM7yxzzPp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Zl09TqGAM7yxzzPp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Zl09TqGAM7yxzzPp .marker.cross{stroke:#333333;}#mermaid-svg-Zl09TqGAM7yxzzPp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Zl09TqGAM7yxzzPp p{margin:0;}#mermaid-svg-Zl09TqGAM7yxzzPp .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Zl09TqGAM7yxzzPp .cluster-label text{fill:#333;}#mermaid-svg-Zl09TqGAM7yxzzPp .cluster-label span{color:#333;}#mermaid-svg-Zl09TqGAM7yxzzPp .cluster-label span p{background-color:transparent;}#mermaid-svg-Zl09TqGAM7yxzzPp .label text,#mermaid-svg-Zl09TqGAM7yxzzPp span{fill:#333;color:#333;}#mermaid-svg-Zl09TqGAM7yxzzPp .node rect,#mermaid-svg-Zl09TqGAM7yxzzPp .node circle,#mermaid-svg-Zl09TqGAM7yxzzPp .node ellipse,#mermaid-svg-Zl09TqGAM7yxzzPp .node polygon,#mermaid-svg-Zl09TqGAM7yxzzPp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Zl09TqGAM7yxzzPp .rough-node .label text,#mermaid-svg-Zl09TqGAM7yxzzPp .node .label text,#mermaid-svg-Zl09TqGAM7yxzzPp .image-shape .label,#mermaid-svg-Zl09TqGAM7yxzzPp .icon-shape .label{text-anchor:middle;}#mermaid-svg-Zl09TqGAM7yxzzPp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Zl09TqGAM7yxzzPp .rough-node .label,#mermaid-svg-Zl09TqGAM7yxzzPp .node .label,#mermaid-svg-Zl09TqGAM7yxzzPp .image-shape .label,#mermaid-svg-Zl09TqGAM7yxzzPp .icon-shape .label{text-align:center;}#mermaid-svg-Zl09TqGAM7yxzzPp .node.clickable{cursor:pointer;}#mermaid-svg-Zl09TqGAM7yxzzPp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Zl09TqGAM7yxzzPp .arrowheadPath{fill:#333333;}#mermaid-svg-Zl09TqGAM7yxzzPp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Zl09TqGAM7yxzzPp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Zl09TqGAM7yxzzPp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Zl09TqGAM7yxzzPp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Zl09TqGAM7yxzzPp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Zl09TqGAM7yxzzPp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Zl09TqGAM7yxzzPp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Zl09TqGAM7yxzzPp .cluster text{fill:#333;}#mermaid-svg-Zl09TqGAM7yxzzPp .cluster span{color:#333;}#mermaid-svg-Zl09TqGAM7yxzzPp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Zl09TqGAM7yxzzPp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Zl09TqGAM7yxzzPp rect.text{fill:none;stroke-width:0;}#mermaid-svg-Zl09TqGAM7yxzzPp .icon-shape,#mermaid-svg-Zl09TqGAM7yxzzPp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Zl09TqGAM7yxzzPp .icon-shape p,#mermaid-svg-Zl09TqGAM7yxzzPp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Zl09TqGAM7yxzzPp .icon-shape .label rect,#mermaid-svg-Zl09TqGAM7yxzzPp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Zl09TqGAM7yxzzPp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Zl09TqGAM7yxzzPp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Zl09TqGAM7yxzzPp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Telegram

    内部消息

    查订单

    回复

    Telegram

    无法处理

    企业微信

    用户

    GW

    Harness

    OrderAPI

    人工坐席通道

    Staff


    9. 总结与最佳实践

    本文详细阐述了Gateway架构在多平台消息接入中的价值与实现。核心要点总结如下:

    职责实现技术
    消息路由 会话粘滞、Redis存储映射、一致性哈希
    身份识别 独立用户映射表、绑定码机制
    格式转换 内部标准GatewayMessage结构
    协议适配 各平台SDK + Webhook/WebSocket管理器
    限流重试 令牌桶 + 指数退避 + 死信队列
    连接池管理 并发安全的map存储Session,心跳维护
    状态同步 Redis集中会话存储,跨平台加载

    最佳实践建议:

    • 统一错误处理:各平台错误码统一转换为内部错误码,便于监控。
    • 可观测性:为每个消息注入trace_id,串联Gateway→Harness→LLM全链路日志。
    • 配置化接入:使用YAML配置文件定义各平台的token、回调地址,支持热加载。
    • 安全防护:验证平台回调签名,防止伪造消息。

    下一步扩展:

    • 支持语音消息(Telegram语音转文字)。
    • 支持富媒体卡片(统一的消息卡片格式,各平台渲染适配)。
    • 接入物联网设备(MQTT协议)作为新平台。

    Gateway架构让AI Agent能够无感知地服务于所有用户,无论他们身在哪个聊天工具。希望本文能为你的跨平台机器人开发提供清晰的方向。

    如果觉得本文对你有帮助,欢迎点赞、收藏、评论交流!你的支持是我持续分享的动力。


    📚 参考资料

    • Telegram Bot API
    • Discord Developer Portal
    • Slack API
    • 飞书开放平台
    • 企业微信开发者中心
    • 《微服务架构设计模式》- Chris Richardson
    赞(0)
    未经允许不得转载:171主机测评 » 【Harness:落地实战】22、Gateway架构:多平台消息接入的统一Harness设计——打造企业级AI Agent消息网关
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址