Files
gochat/docs/architecture/message-lifecycle-and-ai-flow.md
T
Rogee 1446cfe701 feat(channels): 社交渠道 OAuth 改进 + 集成应用重命名 + AI 流程文档
- Facebook/Instagram/TikTok 渠道 OAuth 授权流程改进
- 新增 oauth/credentialstore 包
- 集成应用 OpenAI 重命名为 OpenAI 兼容 (migration 061)
- 消息生命周期与 AI 流程架构文档
- inbox 管理界面 i18n 更新
2026-08-01 19:38:27 +08:00

524 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GoChat 消息生命周期与 AI 自动回复流程
> 本文档描述从渠道消息入站、人工/AI 回复、消息打标签到 AI 自动触发的完整运行流程。
> 所有文件路径相对于仓库根目录,行号截至 2026-07-30。
---
## 一、整体架构概览
```
┌─────────────────────────────────────────────────────────────────────┐
│ 外部渠道 │
│ Facebook │ Instagram │ TikTok │ Telegram │ LINE │ Email │ Web Widget │ API
└─────┬──────────┬──────────┬──────────┬─────────┬───────┬──────┬──────┘
│ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ Webhook 路由层 (router.go) │
│ /webhooks/facebook /webhooks/instagram /webhooks/tiktok ... │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Channel Provider 层 (插件接口) │
│ FacebookProvider InstagramProvider TikTokProvider TelegramProvider│
│ IncomingMessage() 解析渠道原始 payload → channel.IncomingMessage │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ IncomingPersister (持久化层) │
│ 1. resolveOrCreateContactInbox — 查找/创建 Contact + ContactInbox │
│ 2. resolveOrCreateConversation — 查找/创建 Conversation │
│ 3. createMessage — 持久化 Message │
│ 4. dispatchIncomingEvents — 广播 channel events │
└──────────────────────────────┬──────────────────────────────────────┘
│ DispatchAsync
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Dispatcher (事件分发总线) │
│ channel.Dispatcher → 广播 ChannelEvent 给所有注册的 EventListener │
│ │
│ 注册的 Listener: │
│ ├── AutoReplyListener — AI 自动回复 │
│ ├── AutomationRuleListener— 自动化规则(标签/分配/状态变更) │
│ ├── ActionCableListener — WebSocket 推送到前端 │
│ ├── NotificationListener — 创建通知 │
│ ├── AgentBotRuleListener — Agent Bot 规则 │
│ └── AutoAssignmentListener— 自动分配 │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 二、入站消息流(Inbound)
### 2.1 Webhook 路由注册
路由在 `backend/internal/router/router.go` 中注册:
- **Facebook**: `POST /webhooks/facebook` → `FacebookWebhookHandler.HandleWebhook`
- **Instagram**: `POST /webhooks/instagram` → `InstagramWebhookHandler`
- **TikTok**: `POST /webhooks/tiktok` → `TikTokWebhookHandler`
- **Telegram**: `POST /webhooks/telegram/:inbox_id` → `TelegramWebhookHandler`
- **LINE**: `POST /webhooks/line/:inbox_id` → `LineWebhookHandler`
- **Email**: IMAP 轮询 + ActionMailbox 端点
- **Web Widget**: WebSocket 实时消息(不走 webhook)
- **API Channel**: `POST /webhooks/api/:inbox_id` → 外部系统主动推送
### 2.2 Webhook Handler → Provider → IncomingMessage
以 Facebook 为例:
```
FacebookWebhookHandler.HandleWebhook (handler/webhook/facebook_webhook.go)
→ 解析 Facebook webhook payload (entry[].messaging[])
→ 遍历每条 messaging event
→ 调用 FacebookProvider.IncomingMessage() (channel/facebook/provider.go)
→ 解析 sender_id, recipient_id, message text, attachments
→ 返回 channel.IncomingMessage{
SourceID: "facebook_msg_xxx",
ConversationID: senderPSID,
Content: messageText,
ContentType: channel.ContentText,
Attachments: []Attachment{...},
ChannelType: channel.ChannelFacebook,
}
```
其他渠道同理,每个 Provider 实现 `IncomingMessage()` 方法将渠道原始数据归一化为 `channel.IncomingMessage` 结构。
### 2.3 IncomingPersister 持久化
`backend/internal/handler/webhook/incoming_persister.go:74`
```
IncomingPersister.PersistIncoming(ctx, inbox, msg)
│
├── 若有 WorkerPool → enqueueIncomingMessagePersist (异步队列)
│ └── providerIncomingMessagePersistJob (incoming_persister_jobs.go:24)
│ └── performPersistIncoming (异步执行)
│
└── performPersistIncoming(ctx, inbox, msg) — 同步路径
│
├── 1. resolveOrCreateContactInbox (L451)
│ ├── 按 inbox_id + source_id 查找 ContactInbox
│ ├── 找到 → 返回关联的 Contact(更新 name 等)
│ └── 未找到 → 创建 Contact + ContactInbox
│
├── 2. resolveOrCreateConversation (L504)
│ ├── 查找 account_id+inbox_id+contact_id+status=open 的最近会话
│ ├── 找到 → 更新 last_activity_at, last_message_at
│ └── 未找到 → 创建新 Conversation (status=open)
│
├── 3. createMessage (L545)
│ ├── 构建 model.Message{SenderType:"Contact", MessageType:"incoming"}
│ ├── 处理 in_reply_to(按 source_id 查找被回复的消息)
│ ├── 处理 attachments
│ └── messageRepo.Create() 持久化到 messages 表
│
└── 4. dispatchIncomingEvents (L383)
├── EventContactCreated (仅新联系人)
├── EventConversationCreated + EventConversationOpened (仅新会话)
├── EventConversationUpdated (已有会话)
├── EventMessageCreated
└── EventMessageIncoming
└── dispatcher.DispatchAsync() → 广播给所有 Listener
```
### 2.4 WebSocket 实时推送
```
ActionCableListener.OnEvent (internal/wsevent/bridge_listener.go:51)
├── 接收 EventMessageCreated / EventMessageIncoming
├── 构建 WebSocket payload (message JSON + conversation meta)
└── wsHub.Broadcast() → 推送到该 account 所有在线 agent 前端
└── 前端 Vue SPA WebSocket 连接接收 → Vuex/Pinia store 更新 → UI 刷新
```
### 2.5 通知创建
```
NotificationListener.OnEvent
├── 接收 EventMessageIncoming
├── 检查 inbox 的通知设置
├── 创建 Notification 记录 (notification 表)
├── 推送 Push notification (PushDeliveryService)
│ └── SendPushNotification (push_delivery_service.go:63)
└── 发送邮件通知(如配置)
```
---
## 三、出站回复流(Outbound)
### 3.1 前端发起
```
Agent 在 ReplyBox 输入消息 → 点击发送
│
├── frontend/app/javascript/dashboard/api/inbox/message.js:56
│ MessageApi.create({ conversationId, message, ... })
│ → POST /api/v1/accounts/:id/conversations/:conversation_id/messages
│
└── 前端 Vuex store: conversations/actions.js
createPendingMessageAndSend → MessageApi.create()
```
### 3.2 后端处理
```
MessageHandler.Create (handler/api/v1/message_handler.go)
→ 解析请求 body (content, private, files, echo_id, ...)
→ 调用 MessageService.Create()
│
│ backend/internal/service/message_service.go
│
├── 构建 model.Message{MessageType:"outgoing", SenderType:"User"}
├── messageRepo.Create() — 持久化消息
├── 更新 conversation.last_activity_at, last_message_at
├── dispatchMessageEvent(EventMessageCreated + EventMessageOutgoing)
│ └── dispatcher.Dispatch() → 广播给 Listener
│ ├── ActionCableListener → WebSocket 推送 (agent 端即时看到)
│ ├── AutomationRuleListener → 触发自动化规则
│ └── 其他 Listener
│
└── 投递到渠道 (MessageDeliveryWorker)
│
├── 若有 WorkerPool → EnqueueSendReply (异步队列)
│ └── message_delivery_worker.go: CreateOutgoing → SendReply
│
└── SendReply(messageID)
├── 加载 message + conversation + inbox + contact
├── 根据 inbox.ChannelType 获取对应 Provider
├── 调用 Provider.SendMessage(ctx, inbox, message, contact)
│ ├── Facebook: POST graph.facebook.com/me/messages
│ ├── Instagram: POST graph.facebook.com/me/messages (IGID)
│ ├── TikTok: POST business-api.tiktok.com/...
│ ├── Telegram: POST api.telegram.org/bot.../sendMessage
│ ├── Email: SMTP 发送
│ ├── Web Widget: WebSocket 推送到 widget 端
│ └── API: POST 到 inbox.webhook_url
├── 更新 message.Status = "sent" / "delivered" / "failed"
└── dispatchMessageEvent(EventMessageStatusUpdated)
```
### 3.3 消息状态回执
渠道 webhook 回调消息已读/已送达状态:
```
FacebookWebhookHandler → 解析 delivery/read event
→ IncomingPersister.dispatchMessageStatusEvent()
→ 更新 message.Status
→ dispatcher.Dispatch(EventMessageStatusUpdated)
→ ActionCableListener → WebSocket 推送状态更新到前端
```
---
## 四、标签系统
### 4.1 数据模型(双路径并存)
GoChat 标签系统存在两条并行路径:
**路径 A — Tag 关联表(结构化)**
- **Tag** (`backend/internal/model/tag.go:12`): 表名 `tags`,字段 `ID, AccountID, Name(账号内唯一), Color, Description, ShowOnSidebar`
- 唯一索引:`idx_tag_account_name`(AccountID + Name)
- **ConversationLabel** (`backend/internal/model/conversation_label.go:11`): 表名 `conversation_labels`,字段 `ID, ConversationID, TagID, AccountID`
- 唯一索引:`idx_conv_label_tag`(ConversationID + TagID),防止重复关联
- Repository: `TagRepo` (`repository/tag_repo.go`) — `DeleteWithAssociations` 删除 Tag 时同步清理关联行和 `conversations.labels` 文本字段
**路径 B — Conversation.Labels 文本字段(遗留)**
- `Conversation.Labels string` (`model/conversation.go:30`) — 逗号分隔字符串,如 `"billing,urgent"`
- 辅助函数: `mergeConversationLabels()` / `splitConversationLabels()` (`conversation_maintenance_worker.go:726,755`)
- `TagRepo.RenameConversationLabelText` (`repository/tag_repo.go:70`) — Tag 重命名时同步更新 `conversations.labels` 中的文本
两条路径在自动化规则和手动操作中同时维护。
### 4.2 标签 CRUD API
```
LabelHandler (handler/api/v1/label_handler.go)
├── GET /api/v1/accounts/:id/labels — 列出标签
├── POST /api/v1/accounts/:id/labels — 创建标签
├── PATCH /api/v1/accounts/:id/labels/:id — 更新标签
└── DELETE /api/v1/accounts/:id/labels/:id — 删除标签
│
└── LabelService (service/label_service.go)
└── LabelRepo (repository/label_repo.go)
```
### 4.3 会话标签操作
```
ConversationHandler (handler/api/v1/conversation_handler.go)
├── POST /conversations/:id/labels — 给会话打标签
└── POST /conversations/:id/labels — 移除标签
│
└── ConversationService.ToggleLabels()
├── 更新 conversation.labels 字段 (mergeConversationLabels)
├── dispatchConversationEventWithData(EventConversationLabelsUpdated)
└── ActionCableListener → WebSocket 推送标签变更到前端
```
### 4.4 自动化规则中的标签
```
AutomationRule (model/automation_rule.go)
└── AutomationAction.ActionType = "add_label"
└── ActionParams = {"labels": ["billing", "urgent"]}
AutomationRuleListener.OnEvent (automation/listener.go)
├── 接收 EventMessageCreated / EventConversationCreated
├── ConditionsFilterService 检查规则条件是否匹配
├── 匹配 → ActionService 执行动作
│ └── add_label → 更新 conversation.labels
└── dispatch EventConversationLabelsUpdated
```
### 4.5 前端标签管理
```
LabelBox.vue (dashboard/routes/dashboard/conversation/labels/LabelBox.vue)
├── 显示当前会话所有标签
├── 添加/移除标签 → API 调用
└── useConversationLabels composable
```
---
## 五、AI 自动回复
### 5.1 架构概览
```
入站消息 EventMessageCreated
│
▼
┌────────────────────────────────┐
│ AutoReplyListener │
│ (service/auto_reply_listener.go)│
└───────────────┬────────────────┘
│
┌─────────▼──────────┐
│ EvaluateRules() │
│ (auto_reply_rule │
│ _service.go) │
└─────────┬──────────┘
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
Static Mode LLM Mode Mixed Mode
(固定文本回复) (LLM 生成回复) (LLM + 固定文本)
│ │ │
│ ┌──────▼──────┐ │
│ │ LLM Provider │ │
│ │ (RAG 知识库) │ │
│ └──────┬──────┘ │
│ │ │
└───────────────┼───────────────┘
▼
MessageService.Create()
(outgoing message)
│
▼
MessageDeliveryWorker
→ 渠道发送
```
### 5.2 AutoReplyListener 触发链路
`backend/internal/service/auto_reply_listener.go`
```
AutoReplyListener.OnEvent(ctx, event) (L62)
│
├── 过滤:仅处理 EventMessageCreated / EventMessageIncoming
├── 过滤:仅处理 sender_type = "Contact" 的入站消息
├── 加载 Conversation(检查是否已 resolved → 跳过)
├── fetchRecentMessages — 取最近 10 条消息作为上下文 (L205)
│
├── 构建 AutoReplyEvaluationContext {
│ AccountID, InboxID, ConversationID,
│ MessageContent, PreviousMessages,
│ ConversationStatus, Language
│ }
│
├── AutoReplyRuleService.EvaluateRules(ctx, evalCtx) (auto_reply_rule_service.go)
│ ├── 按 inbox_id 查找 active 状态的 auto-reply rules
│ ├── 逐条评估 conditions(关键词匹配 / 正则 / 消息长度 / 时间窗口)
│ ├── 按 priority 排序,返回第一个匹配的 rule
│ └── 根据 rule.Mode 生成回复内容:
│ ├── Static: 直接返回 rule.ResponseText
│ ├── LLM: 调用 LLM Provider 生成回复(可带 RAG 检索)
│ └── Mixed: LLM 生成 + 固定文本拼接
│
├── 检查 OneTimeOnly — 该规则是否已对此会话触发过 (L225)
│
├── 若有 DelaySeconds → 异步 goroutine 延迟发送 (L137)
│
└── sendAutoReply(ctx, event, conversation, result) (L151)
├── 解析 inbox 关联的 AgentBot 作为 sender
├── 构建 CreateMessageRequest{
│ Content: replyContent,
│ MessageType: "outgoing",
│ SenderType: "agent_bot",
│ }
└── MessageService.Create() → 消息持久化 + 事件分发
└── → MessageDeliveryWorker → 渠道发送
```
### 5.3 Auto-Reply Rule 数据模型
`backend/internal/model/captain_auto_reply_rule.go`
```
CaptainAutoReplyRule {
AccountID
AssistantID — 关联 Captain Assistant
InboxID — 绑定到特定 inbox
Name, Description
Status — draft / active / archived
Mode — static / llm / mixed
Priority — 数字越小优先级越高
Conditions — JSON: 关键词/正则/消息属性条件
ResponseText — static/mixed 模式的固定回复文本
LLMPromptOverride — LLM 模式的自定义 prompt
DelaySeconds — 延迟发送秒数
OneTimeOnly — 是否只触发一次
}
```
### 5.4 Captain Assistant
Captain 是 GoChat 的 AI 助手框架,提供 LLM 能力:
```
CaptainAssistant (model/captain_assistant.go)
├── 关联一个 LLM Provider (OpenAI / DeepSeek / 自定义)
├── 关联 RAG 文档库 (CaptainDocument)
├── 关联 Scenario(场景化 prompt 模板)
└── 关联 Auto-Reply Rules
CaptainAssistantHandler (handler/api/v1/captain_assistant_handler.go)
├── CRUD assistant
├── 测试 assistant (发送测试消息)
└── 生成回复 (直接调用 LLM)
CaptainConversationHandler (handler/api/v1/captain_conversation_handler.go)
├── AI 参与会话 — Captain 读取会话上下文 + RAG 检索 → 生成建议回复
└── 也可直接发送 AI 回复到会话
```
### 5.5 Copilot(Agent 辅助)
Copilot 是给人工 agent 用的 AI 辅助工具,不会自动发送回复:
```
CopilotHandler (handler/api/v1/copilot_handler.go)
├── POST /copilot/suggest — 根据会话上下文生成建议回复
├── POST /copilot/rephrase — 改写 agent 输入的文本
└── POST /copilot/summarize — 总结会话
CopilotContainer.vue (前端侧边栏)
├── Agent 在侧边栏与 Copilot 交互
├── Copilot 调用 LLM + RAG 生成建议
└── Agent 确认后手动发送(不自动发送)
```
### 5.6 RAG 知识检索
```
RAGHandler (handler/api/v1/rag_handler.go)
├── 上传文档 → 向量化 → 存入 pgvector
├── 检索:query 向量化 → pgvector 相似度搜索 → 返回 top-k 文档片段
└── 在 LLM 调用时注入检索到的上下文
流程:
用户消息 → 向量化 → pgvector 检索相关文档 → 注入 LLM prompt → 生成回复
```
---
## 六、完整消息生命周期
```
客户 GoChat 后端 Agent 前端
│ │ │
│ 1. 发送消息 │ │
│ ──────────────────────► │ │
│ 2. Webhook 接收 │
│ 3. Provider 解析 │
│ 4. IncomingPersister 持久化 │
│ 5. Dispatcher 广播事件 │
│ │ 6. WebSocket 推送 │
│ │ ────────────────────────────► │
│ │ 7. Agent 看到消息 │
│ │ │
│ 8. AutoReplyListener 检查 │
│ 9. 匹配规则 → AI 生成回复 │
│ 10. MessageService.Create() │
│ 11. MessageDeliveryWorker │
│ 12. 渠道发送回复 │ │
│ ◄────────────────────── │ │
│ │ 13. WebSocket 推送 AI 回复 │
│ │ ────────────────────────────► │
│ │ 14. Agent 看到 AI 回复 │
│ │ │
│ │ 15. (可选) Agent 手动回复 │
│ │ ◄──────────────────────────── │
│ 16. MessageService.Create() │
│ 17. Provider.SendMessage() │
│ 18. 渠道发送回复 │ │
│ ◄────────────────────── │ │
│ │ │
│ │ 19. AutomationRuleListener │
│ │ → 匹配规则 → 自动打标签 │
│ │ → 自动分配 agent │
│ │ → 自动变更状态 │
```
---
## 七、关键文件索引
| 组件 | 文件路径 | 关键函数/行号 |
|---|---|---|
| Webhook 路由 | `backend/internal/router/router.go` | webhook 路由注册 |
| Facebook Webhook | `backend/internal/handler/webhook/facebook_webhook.go` | `HandleWebhook` |
| TikTok Webhook | `backend/internal/handler/webhook/tiktok_webhook.go` | `HandleWebhook` |
| LINE Webhook | `backend/internal/handler/webhook/line_webhook.go` | `HandleWebhook` |
| IncomingPersister | `backend/internal/handler/webhook/incoming_persister.go` | `PersistIncoming` L74 |
| 持久化 Job | `backend/internal/handler/webhook/incoming_persister_jobs.go` | `providerIncomingMessagePersistJob` L24 |
| Dispatcher | `backend/internal/channel/dispatcher.go` | `Dispatch` L85, `DispatchAsync` L110 |
| Channel Event | `backend/internal/channel/event.go` | `ChannelEvent` 结构体, EventType 常量 |
| Facebook Provider | `backend/internal/channel/facebook/provider.go` | `IncomingMessage`, `SendMessage` L352 |
| Instagram Provider | `backend/internal/channel/facebook/instagram_provider.go` | `IncomingMessage`, `SendMessage` L364 |
| Message Service | `backend/internal/service/message_service.go` | `Create`, `dispatchMessageEvent` L68 |
| Message Handler | `backend/internal/handler/api/v1/message_handler.go` | `Create` |
| Message Delivery Worker | `backend/internal/service/message_delivery_worker.go` | `EnqueueSendReply`, `SendReply` |
| AutoReply Listener | `backend/internal/service/auto_reply_listener.go` | `OnEvent` L62, `sendAutoReply` L151 |
| AutoReply Rule Service | `backend/internal/service/auto_reply_rule_service.go` | `EvaluateRules` |
| AutoReply Rule Model | `backend/internal/model/captain_auto_reply_rule.go` | `CaptainAutoReplyRule` |
| Captain Assistant Handler | `backend/internal/handler/api/v1/captain_assistant_handler.go` | CRUD + 测试 |
| Captain Conversation Handler | `backend/internal/handler/api/v1/captain_conversation_handler.go` | AI 会话参与 |
| Copilot Handler | `backend/internal/handler/api/v1/copilot_handler.go` | suggest/rephrase/summarize |
| RAG Handler | `backend/internal/handler/api/v1/rag_handler.go` | 文档上传/检索 |
| Automation Rule Listener | `backend/internal/automation/listener.go` | `OnEvent` |
| Automation Action | `backend/internal/model/automation_action.go` | `AutomationAction` L10 |
| Tag Model | `backend/internal/model/tag.go` | `Tag` L12 |
| ConversationLabel Model | `backend/internal/model/conversation_label.go` | `ConversationLabel` L11 |
| Tag Repo | `backend/internal/repository/tag_repo.go` | `DeleteWithAssociations` L54, `RenameConversationLabelText` L70 |
| ConversationLabel Repo | `backend/internal/repository/conversation_label_repo.go` | CRUD |
| WebSocket Hub | `backend/internal/ws/hub.go` | `Broadcast` |
| WS Event Bridge | `backend/internal/wsevent/bridge_listener.go` | `ActionCableListener.OnEvent` L51 |
| Inbox Serializer | `backend/internal/handler/api/v1/inbox_serializer.go` | `serializeInbox` L21 |
| Notification Service | `backend/internal/service/notification_delivery_service.go` | 通知投递 |
| Push Delivery | `backend/internal/service/push_delivery_service.go` | `SendPushNotification` L63 |
| 前端 Message API | `frontend/app/javascript/dashboard/api/inbox/message.js` | `create` L56 |
| 前端 ReplyBox | `frontend/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue` | 消息输入框 |
| 前端 Copilot | `frontend/app/javascript/dashboard/components/copilot/CopilotContainer.vue` | Copilot 侧边栏 |