---
doc_id: tenant-assistant-implementation-spec
title: 助手 TenantAssistant 實現規格說明書
description: Aile TenantAssistant 模型、觸發管線、Webhook 分發、回覆管理、批量任務、過期任務與扣點規格，供開發團隊參考。
slug: /specifications/tenant-assistant-implementation-spec
product: Aile
category: specification
audience:
  - developer
  - architect
  - qa
visibility: public
status: published
version: 1.0.0
owner: aile-platform
updated_at: 2026-06-13
tags:
  - Aile
  - TenantAssistant
  - Webhook
  - AI
  - 規格說明書
rendered_html: /rendered/specifications/tenant-assistant-implementation-spec/
download: true
sidebar_position: 10
---

# 助手（TenantAssistant）實現規格說明書

**版本：** v1.0  
**最後更新：** 2026-06-13  
**服務範圍：** `aile-service-tenant`、`aile-service-room`、`aile-service-admin`  

---

## 1. 概述

Aile 助手系統是跨三個微服務實現的 AI 輔助會話引擎。它支援租戶級別的 AI 助手配置、消息觸發、Webhook 分發和回覆管理。

### 1.1 架構分佈

| 層級 | 服務 | 職責 |
|------|------|------|
| **觸發管線** | `aile-service-room` | 攔截每條服務號消息 → 判斷是否觸發助手 → PubSub 發布 |
| **領域核心** | `aile-service-tenant` | TenantAssistantModel CRUD、狀態生命週期、Webhook 分發、房間綁定 |
| **管理視圖** | `aile-service-admin` | 管理後台助手列表、訂單管理（透過 Feign 讀取 tenant 服務） |

### 1.2 核心設計原則

- 助手是**服務號級別**資源（歸屬於 serviceNumberId）
- 助手觸發管線在**消息寫入後**非同步執行
- 助手回應模式支援三種：`Qa`（問答回覆）、`CollectOnly`（僅收集）、`Batch`（批量觸發）

---

## 2. TenantAssistantModel（助手領域模型）

### 2.1 資料結構

MongoDB 集合：`aile.tenant.assistant`

```java
// aile-api/aile-tenant-api/.../model/assistant/TenantAssistantModel.java
@Document("aile.tenant.assistant")
@CompoundIndexes({
    @CompoundIndex(name = "assistantId_1", def = "{'assistantId': 1}", unique = true, sparse = true),
    @CompoundIndex(name = "tenantId_1_accountId_1_updateTime_1", 
        def = "{'tenantId': 1, 'accountId': 1, 'updateTime': -1}"),
    @CompoundIndex(name = "tenantId_1_accountId_1_status_1", 
        def = "{'tenantId': 1, 'accountId': 1, 'status': 1}")
})
public class TenantAssistantModel extends BaseModel {
    String assistantId;                        // 助手業務 ID，格式：Asst_xxx
    String accountId;                          // 建立者帳號 ID
    String tenantId;                           // 租戶 ID
    String serviceNumberId;                    // 服務號 ID
    String name;                              // 助手名稱
    String description;                       // 助手描述
    TenantAssistantStatus status;             // Enable / Disable
    TenantAssistantResponseMode responseMode; // Qa / CollectOnly / Batch
    TenantAssistantPaymentStatus paymentStatus; // Unpaid / Paid
    TenantAssistantSource source;            // 助手來源
    // Webhook 配置
    String webhookUrl;                        // 外部助手回調地址
    String webhookSecret;                     // 外部助手密鑰
    // 過濾配置
    TenantAssistantMessageType messageType;   // 處理的消息類型
    TenantAssistantSenderTypeEnum senderType; // 發送者類型過濾
    // Room 綁定
    List<String> roomIds;                     // 綁定的聊天室 ID 列表
    // Reply 排除
    TenantAssistantReplyExcludeTypeEnum replyExcludeType; // 回覆排除類型
}
```

### 2.2 狀態機

```
Enable ←──→ Disable
```

助手的狀態為簡單的二元開關，不支援複雜生命週期。

### 2.3 回應模式

| 模式 | 值 | 說明 |
|------|-----|------|
| 問答回覆 | `Qa` | 標準 AI 問答模式，每次消息觸發後等待外部助手回覆 |
| 僅收集 | `CollectOnly` | 僅收集消息內容，不產生回覆 |
| 批量觸發 | `Batch` | 批量觸發模式，定時收集多條消息後統一處理 |

### 2.4 支付狀態

| 狀態 | 說明 |
|------|------|
| `Unpaid` | 未支付，助手不可用 |
| `Paid` | 已支付，助手可用 |

---

## 3. 觸發管線（Room 服務）

### 3.1 觸發入口

位置：`aile-service/aile-service-room/.../message/service/impl/MessageServiceImpl.java`

每條服務號消息寫入後，`MessageServiceImpl` 會調用 `triggerAssistantIfNeeded()`：

```
MessageServiceImpl.saveMessage()
  │
  ├── 消息寫入 Elasticsearch
  ├── 房間成員通知
  └── triggerAssistantIfNeeded(message, room)
        │
        ├── 判斷房間類型是否為 services（只有 services 房間觸發助手）
        ├── 查詢該服務號的啟用助手列表（透過 TenantAssistantFeign）
        ├── 組裝 AssistantTriggerCommandDto
        └── AssistantTriggerPublishService.publish(command)
              │
              └── PubSub → assistantTriggerChannel
```

### 3.2 觸發命令

```java
// aile-api/aile-room-api/.../dto/assistant/AssistantTriggerCommandDto.java
public class AssistantTriggerCommandDto {
    MessageVO message;           // 原始消息
    RoomModel room;              // 聊天室資訊
    String sessionId;            // 當前會話 ID
    List<String> assistantIds;   // 候選助手 ID 列表
}
```

### 3.3 PubSub 監聽器

位置：`aile-service/aile-service-room/.../listener/PubSubAssistantTriggerListener.java`

```
PubSubAssistantTriggerListener (inputChannel = "assistantTriggerChannel")
  │
  └── AssistantTriggerOrchestrator.orchestrate(command)
```

### 3.4 觸發編排器

位置：`aile-service/aile-service-room/.../service/assistant/impl/AssistantTriggerOrchestratorImpl.java`

```java
public class AssistantTriggerOrchestratorImpl implements AssistantTriggerOrchestrator {
    // 核心編排邏輯：
    // 1. 逐一處理每個候選助手
    // 2. 檢查助手狀態（Enable / Disable）
    // 3. 檢查支付狀態（Paid / Unpaid）
    // 4. 檢查房間綁定（若配置了 roomIds）
    // 5. 依回應模式分流處理：
    //    - Qa: 組裝 Webhook 事件 → publish
    //    - CollectOnly: 記錄收集 → 不觸發 Webhook
    //    - Batch: 加入批量佇列 → 等待定時任務處理
    // 6. 非 Qa 模式：建立 AssistantReplyDispatch 記錄
}
```

---

## 4. Webhook 分發（Tenant 服務）

### 4.1 分發流程

```
AssistantTriggerOrchestrator (room 服務)
  │
  ├── 組裝 TenantAssistantWebhookEventDto
  ├── PubSub → assistantWebhookChannel
  │
  └── PubSubTenantAssistantWebhookListener (tenant 服務)
        │
        └── TenantAssistantWebhookDispatchService.dispatch(event)
              │
              ├── 查詢 TenantAssistantModel 取得 webhookUrl + webhookSecret
              ├── 構建 HTTP 請求（HMAC 簽名可選）
              ├── POST → 外部助手平台
              └── 接收回覆 → AssistantReplyDispatchService
```

### 4.2 Webhook 事件格式

```java
// aile-api/aile-tenant-api/.../dto/assistant/TenantAssistantWebhookEventDto.java
public class TenantAssistantWebhookEventDto {
    String assistantId;                          // 助手 ID
    String sessionId;                            // 會話 ID
    String roomId;                               // 聊天室 ID
    String serviceNumberId;                      // 服務號 ID
    TenantAssistantMessageType messageType;       // 消息類型
    List<TenantAssistantWebhookMessageDto> messages; // 消息列表
}
```

### 4.3 PubSub 監聽器

位置：`aile-service/aile-service-tenant/.../listener/PubSubTenantAssistantWebhookListener.java`

```java
@Component
public class PubSubTenantAssistantWebhookListener extends AbstractPubSubConsumer {
    @ServiceActivator(inputChannel = "assistantWebhookChannel")
    public void receiveMessage(...) {
        // 解析 OpDetail → TenantAssistantWebhookDispatchDto
        // 調用 TenantAssistantWebhookDispatchService
    }
}
```

---

## 5. 回覆管理（Room 服務）

### 5.1 回覆分發服務

位置：`aile-service/aile-service-room/.../service/assistant/AssistantReplyDispatchService.java`

```
外部助手平台回覆
  │
  └── AssistantReplyDispatchController (接收回覆)
        │
        └── AssistantReplyDispatchService.receiveReply()
              │
              ├── 建立/更新 AssistantReplyDispatchModel 記錄
              ├── Token 校驗（透過 AssistantDispatchTokenService）
              └── 寫入房間消息
```

### 5.2 AssistantReplyDispatchModel

記錄每次助手回覆的狀態：

| 狀態 | 說明 |
|------|------|
| `Pending` | 等待回覆 |
| `Replied` | 已回覆 |
| `Failed` | 回覆失敗 |
| `Timeout` | 回覆超時 |

### 5.3 Token 機制

位置：`aile-service/aile-service-room/.../service/assistant/AssistantDispatchTokenService.java`

每次觸發助手時生成一個 dispatch token，用於驗證外部回覆的合法性：

- 生成：`AssistantDispatchTokenService.generateToken(assistantId, sessionId)`
- 驗證：`AssistantDispatchTokenService.validateToken(token)`
- 儲存：Redis（有過期時間）

---

## 6. 批量任務

### 6.1 OnceAssistantBatchTask

位置：`aile-service/aile-service-room/.../task/OnceAssistantBatchTask.java`

用於 Batch 模式的定時批量處理：

```
定時觸發（透過 OnceScheduleManager）
  │
  └── OnceAssistantBatchTask.execute()
        │
        ├── 從 Redis 讀取批量收集的消息
        ├── 組裝批量 Webhook 事件
        └── 推送至外部助手平台
```

### 6.2 批量狀態管理

位置：`aile-service/aile-service-room/.../service/assistant/model/AssistantBatchStateData.java`

```java
public class AssistantBatchStateData {
    String assistantId;
    String serviceNumberId;
    List<String> messageIds;     // 收集的消息
    Long firstMessageTime;       // 第一批消息時間
    Long lastMessageTime;        // 最後一批消息時間
    int retryCount;              // 重試次數
}
```

Redis Key 管理：`AssistantBatchStateRedisUtil` / `AssistantBatchRedisKey`

---

## 7. 助手過期任務

### 7.1 TenantAssistantExpireTask

位置：`aile-service/aile-service-tenant/.../task/TenantAssistantExpireTask.java`

定時掃描過期的助手（基於訂單到期時間），自動停用：

```
定時觸發
  │
  └── TenantAssistantExpireTask.execute()
        │
        ├── 查詢 paymentStatus=Paid 且訂單已過期的助手
        ├── 更新 paymentStatus → Unpaid
        └── 更新 status → Disable
```

---

## 8. 管理後台視圖

### 8.1 AdminAssistantController

位置：`aile-service/aile-service-admin/.../controller/AdminAssistantController.java`

提供管理後台的助手列表和操作視圖：

| 功能 | 說明 |
|------|------|
| 助手列表 | 透過 `TenantAssistantFeign` 查詢 tenant 服務 |
| 助手詳情 | 包含訂單資訊（透過 `TenantOrderFeign`） |
| 狀態管理 | 啟用/停用助手 |
| 訂單管理 | 查詢關聯訂單 |

---

## 9. 助手 CRUD API（Tenant 服務）

### 9.1 TenantAssistantController

路由：`/assistant/`

| 方法 | 路徑 | 說明 |
|------|------|------|
| `POST` | `/assistant/create` | 建立助手 |
| `POST` | `/assistant/save` | 儲存/更新助手 |
| `POST` | `/assistant/status` | 變更狀態（Enable/Disable） |
| `POST` | `/assistant/query` | 分頁查詢助手列表 |
| `GET` | `/assistant/detail?assistantId=` | 查詢助手詳情 |
| `POST` | `/assistant/room-bind` | 綁定/解綁聊天室 |

### 9.2 建立助手請求示例

```json
{
  "tenantId": "T001",
  "serviceNumberId": "SN001",
  "name": "客服助手",
  "description": "自動回覆常見問題",
  "responseMode": "Qa",
  "webhookUrl": "https://ai.example.com/webhook",
  "webhookSecret": "secret_key",
  "messageType": "Text",
  "senderType": "Customer"
}
```

---

## 10. 回覆 API

### 10.1 TenantAssistantReplyController

路由：`/assistant/reply/`

| 方法 | 路徑 | 說明 |
|------|------|------|
| `POST` | `/assistant/reply/dispatch` | 外部助手平台推送回覆 |
| `GET` | `/assistant/reply/status?dispatchId=` | 查詢回覆狀態 |

---

## 11. 配額與扣點

### 11.1 配額檢查

助手觸發時會進行配額檢查（透過 `TenantQuotaAccessGuardService`）：

- 檢查租戶的助手使用配額是否充足
- 檢查帳號的試用次數是否耗盡（`AccountBizTrialFeign`）
- 配額不足時拒絕觸發，不推送 Webhook

### 11.2 用量記錄

- 每次成功觸發助手後記錄用量（`TenantQuotaUsageService`）
- 配額消耗支援按月/按年統計

---

## 12. 關鍵檔案索引

| 層級 | 檔案 | 說明 |
|------|------|------|
| API Model | `aile-api/aile-tenant-api/.../model/assistant/TenantAssistantModel.java` | 助手持久化模型 |
| API Enum | `aile-api/aile-tenant-api/.../enums/assistant/TenantAssistantStatus.java` | 狀態枚舉 |
| API Enum | `aile-api/aile-tenant-api/.../enums/assistant/TenantAssistantResponseMode.java` | 回應模式枚舉 |
| API Enum | `aile-api/aile-tenant-api/.../enums/assistant/TenantAssistantPaymentStatus.java` | 支付狀態枚舉 |
| API Enum | `aile-api/aile-tenant-api/.../enums/assistant/TenantAssistantMessageType.java` | 消息類型枚舉 |
| API Enum | `aile-api/aile-tenant-api/.../enums/assistant/TenantAssistantSenderTypeEnum.java` | 發送者類型枚舉 |
| API Enum | `aile-api/aile-tenant-api/.../enums/assistant/TenantAssistantSource.java` | 助手來源枚舉 |
| API DTO | `aile-api/aile-room-api/.../dto/assistant/AssistantTriggerCommandDto.java` | 觸發命令 DTO |
| Room 觸發 | `aile-service/aile-service-room/.../message/service/impl/MessageServiceImpl.java` | 消息攔截 + triggerAssistantIfNeeded |
| Room 觸發 | `aile-service/aile-service-room/.../service/assistant/impl/AssistantTriggerOrchestratorImpl.java` | 觸發編排器 |
| Room 觸發 | `aile-service/aile-service-room/.../listener/PubSubAssistantTriggerListener.java` | PubSub 觸發監聽器 |
| Room 觸發 | `aile-service/aile-service-room/.../service/assistant/AssistantTriggerPublishService.java` | 觸發發布服務 |
| Room 回覆 | `aile-service/aile-service-room/.../service/assistant/impl/AssistantReplyDispatchServiceImpl.java` | 回覆分發服務 |
| Room 回覆 | `aile-service/aile-service-room/.../controller/AssistantReplyDispatchController.java` | 回覆接收 API |
| Room Token | `aile-service/aile-service-room/.../service/assistant/impl/AssistantDispatchTokenServiceImpl.java` | Token 服務 |
| Room Batch | `aile-service/aile-service-room/.../task/OnceAssistantBatchTask.java` | 批量任務 |
| Room Batch | `aile-service/aile-service-room/.../service/assistant/model/AssistantBatchStateData.java` | 批量狀態 |
| Tenant 領域 | `aile-service/aile-service-tenant/.../service/assistant/impl/TenantAssistantServiceImpl.java` | 助手 CRUD 服務 |
| Tenant 領域 | `aile-service/aile-service-tenant/.../controller/assistant/TenantAssistantController.java` | 助手 API |
| Tenant 領域 | `aile-service/aile-service-tenant/.../controller/assistant/TenantAssistantReplyController.java` | 回覆 API |
| Tenant Webhook | `aile-service/aile-service-tenant/.../listener/PubSubTenantAssistantWebhookListener.java` | Webhook 監聽器 |
| Tenant Webhook | `aile-service/aile-service-tenant/.../service/assistant/impl/TenantAssistantWebhookDispatchServiceImpl.java` | Webhook 分發服務 |
| Tenant 後台 | `aile-service/aile-service-tenant/.../task/TenantAssistantExpireTask.java` | 過期停用任務 |
| Admin 視圖 | `aile-service/aile-service-admin/.../controller/AdminAssistantController.java` | 管理後台 API |
| Admin 視圖 | `aile-service/aile-service-admin/.../service/impl/AdminAssistantServiceImpl.java` | 管理後台服務 |
| 配額 | `aile-service/aile-service-tenant/.../service/quota/impl/TenantQuotaAccessGuardServiceImpl.java` | 配額守衛 |
| 配額 | `aile-service/aile-service-tenant/.../service/quota/impl/TenantQuotaUsageServiceImpl.java` | 用量服務 |
