10 KiB
10 KiB
工程師交接說明
後臺方案更換建議:Streamlit
若未來認為 Rocket.Chat 部署過重或整合不便,可轉用 Streamlit 開發自定義管理後台。此方案能針對 Open WebUI 的資料結構進行 100% 客製化。
核心優勢
- 極簡開發:純 Python 即可構建前端,約 200 行程式碼可完成完整後台。
- 超輕量級:單一容器(~100MB 記憶體),無需額外資料庫(直接讀取現有 Postgres)。
- 完全客製:可完美呈現「用戶 → 對話 → 訊息」的三層結構,不受限於聊天軟體的頻道邏輯。
- 部署簡單:標準 Docker image
python:3.11-slim+pip install streamlit。
實作架構
- 前端:Streamlit Web App (Port 8501)
- 後端邏輯:直連 PostgreSQL
reply_queue表格 - 功能:自動刷新、用戶篩選、歷史回覆查詢
程式碼範例 (admin.py)
import streamlit as st
import psycopg2
import pandas as pd
# 自動刷新設定 (每 5 秒)
from streamlit_autorefresh import st_autorefresh
st_autorefresh(interval=5000, key="msg_refresh")
st.set_page_config(layout="wide", page_title="TobiichiGPT Admin")
# 1. 連接資料庫
conn = psycopg2.connect("postgresql://user:pass@postgres:5432/tobiichiGPT")
# 2. 側邊欄:用戶列表
st.sidebar.title("用戶列表")
users = pd.read_sql("SELECT DISTINCT user_id FROM reply_queue", conn)
selected_user = st.sidebar.radio("選擇用戶", users['user_id'])
# 3. 主畫面:顯示該用戶的對話
if selected_user:
st.header(f"用戶: {selected_user}")
# 撈取該用戶訊息
msgs = pd.read_sql(
f"SELECT * FROM reply_queue WHERE user_id='{selected_user}' ORDER BY created_at DESC",
conn
)
for _, row in msgs.iterrows():
with st.expander(f"對話 {row['chat_id']} ({row['status']})", expanded=True):
st.info(f"用戶: {row['user_message']}")
if row['status'] == 'pending':
with st.form(key=f"form_{row['id']}"):
reply = st.text_area("回覆內容")
if st.form_submit_button("送出"):
# 更新資料庫
cur = conn.cursor()
cur.execute(
"UPDATE reply_queue SET admin_reply=%s, status='replied' WHERE id=%s",
(reply, row['id'])
)
conn.commit()
st.success("已回覆")
st.rerun()
else:
st.success(f"管理員: {row['admin_reply']}")
部署配置 (Docker)
Dockerfile:
FROM python:3.11-slim
WORKDIR /app
RUN pip install streamlit psycopg2-binary pandas streamlit-autorefresh
COPY admin.py .
CMD ["streamlit", "run", "admin.py", "--server.port=8501", "--server.address=0.0.0.0"]
docker-compose.yml:
admin-ui:
build: ./admin-ui
ports: ["8501:8501"]
environment:
- DB_HOST=postgres
- DB_PASSWORD=${DB_PASSWORD}
networks:
- tobiichiGPT-network
與 Rocket.Chat/Chatwoot比較
| 特性 | Streamlit (自建) | Rocket.Chat | Chatwoot |
|---|---|---|---|
| 對應 Open WebUI 結構 | ⭐⭐⭐⭐⭐ (完全貼合) | ⭐⭐⭐ (需用頻道模擬) | ⭐ (結構扁平) |
| 即時性 | ⭐⭐ (輪詢刷新) | ⭐⭐⭐⭐⭐ (WebSocket) | ⭐⭐⭐⭐⭐ (WebSocket) |
| 手機 App | ⭐ (網頁版) | ⭐⭐⭐⭐⭐ (原生 App) | ⭐⭐⭐⭐⭐ (原生 App) |
| 資源消耗 | 低 (~100MB) | 中 (~500MB) | 高 (~1GB) |
| 適用場景 | 單人/少數管理員,追求輕量與精準管理 | 多人團隊協作,需要 App 通知 | 專業客服團隊 |
建議切換時機
若遇到以下情況,建議切換至 Streamlit 方案:
- Rocket.Chat 的頻道/執行緒管理變得混亂,難以追蹤用戶對話。
- 伺服器資源不足,無法負擔 Rocket.Chat + MongoDB。
- 需要針對特定業務邏輯(如:查看用戶餘額、審核特定關鍵字)進行客製化開發。
Rocket.Chat 實作紀錄
實作日期
2026年2月1日
系統架構
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Open WebUI │────▶│ API 轉接層 │────▶│ Rocket.Chat │
│ (Port 10060) │ │ (Port 18000) │ │ (Port 13000) │
│ 用戶前端 │◀────│ FastAPI │◀────│ 管理員後台 │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌─────────────────┐
│ PostgreSQL │ │ MongoDB │
│ (Port 5432) │ │ (Port 27017) │
└──────────────────┘ └─────────────────┘
工作流程
- 用戶發送訊息 → Open WebUI 呼叫
/v1/chat/completions - API 轉接層接收 → 提取用戶資訊 (user_name, chat_id)
- 創建 Rocket.Chat 頻道 → 頻道名稱:
{用戶名}-{對話ID前8位} - 發送用戶訊息 → 顯示格式:
💬 用戶名: 訊息內容 - 輪詢等待回覆 → 每 2 秒檢查是否有管理員新訊息
- 回傳給用戶 → 以 OpenAI 格式回傳管理員的回覆
已完成的修改
1. docker-compose.yml
新增服務:
mongo: MongoDB 7.0 (Rocket.Chat 8.0.1 需要 7.0+)mongo-init-replica: 初始化 MongoDB Replica Setrocketchat: Rocket.Chat 8.0.1
修改服務:
openwebui: 新增ENABLE_FORWARD_USER_INFO_HEADERS=True環境變數
關鍵配置:
mongo:
image: mongo:7.0 # 從 5.0 升級,因為 Rocket.Chat 8.0.1 不支援 5.0
command: mongod --oplogSize 128 --replSet rs0
rocketchat:
image: registry.rocket.chat/rocketchat/rocket.chat:latest
ports:
- "13000:3000"
environment:
MONGO_URL: mongodb://mongo:27017/rocketchat?replicaSet=rs0
MONGO_OPLOG_URL: mongodb://mongo:27017/local?replicaSet=rs0
openwebui:
environment:
- ENABLE_FORWARD_USER_INFO_HEADERS=True # 轉發用戶資訊到 API
2. api/server.py
新增 imports:
import json
import hashlib
核心函數重寫:
| 函數名稱 | 功能說明 |
|---|---|
rocketchat_login() |
使用帳密登入 Rocket.Chat,取得 Auth Token |
get_or_create_chat_channel() |
為每個對話創建專屬頻道 (不使用 Thread) |
send_user_message() |
發送用戶訊息到頻道,回傳訊息 ID |
wait_for_admin_reply() |
輪詢等待管理員回覆,過濾機器人訊息 |
Headers 提取邏輯:
# Open WebUI 轉發的 Headers
user_id = headers_dict.get("x-openwebui-user-id")
chat_id = headers_dict.get("x-openwebui-chat-id")
user_name = headers_dict.get("x-openwebui-user-name")
user_email = headers_dict.get("x-openwebui-user-email")
系統任務過濾:
# 跳過 Open WebUI 的標題/標籤/後續問題生成請求
if user_message.strip().startswith("### Task:"):
return {"choices": [{"message": {"content": "{}"}}], ...}
遇到的問題與解決方案
問題 1: MongoDB 版本不相容
- 錯誤:
MongoServerSelectionError - 原因: Rocket.Chat 8.0.1 需要 MongoDB 7.0+,但配置的是 5.0
- 解決: 將
mongo:5.0升級為mongo:7.0
問題 2: postMessage 返回 400 錯誤
- 錯誤:
alias參數需要特殊權限 - 原因: Rocket.Chat API 的
alias欄位需要 bot 權限 - 解決: 移除
alias參數,改用訊息內文標註用戶名
問題 3: Open WebUI 不發送用戶資訊 Headers
- 錯誤:
user_name始終為None - 原因: Open WebUI 預設不轉發用戶資訊
- 解決: 設置環境變數
ENABLE_FORWARD_USER_INFO_HEADERS=True
問題 4: 奇怪的系統訊息被發送到 Rocket.Chat
- 錯誤:
### Task: Generate a concise title...等訊息出現 - 原因: Open WebUI 會額外發送標題/標籤/後續問題生成請求
- 解決: 過濾以
### Task:開頭的訊息,直接回傳空 JSON
問題 5: 重複回傳相同的管理員回覆
- 錯誤: 每次用戶訊息都收到同一個回覆
- 原因: Thread 架構複雜,難以正確追蹤新回覆
- 解決: 改用簡化架構 (每個對話一個 Channel,不用 Thread)
最終架構決策
放棄 Thread 架構,改用 Channel 架構:
| 項目 | Thread 架構 (放棄) | Channel 架構 (採用) |
|---|---|---|
| 結構 | 用戶→頻道→多個Thread | 對話→專屬頻道 |
| 追蹤 | 需要追蹤 Thread ID | 只需追蹤訊息 ID |
| 複雜度 | 高 | 低 |
| 可讀性 | 中 (Thread 內對話) | 高 (直接看頻道) |
頻道命名規則:
{用戶名小寫}-{chat_id前8位}
例: ckliu-67b4341e
待辦事項
- 實作管理員回覆後的通知機制
- 處理超時後的重試邏輯
- 新增管理員身份驗證 (目前使用單一 admin 帳號)
- 考慮切換到 Streamlit 方案 (更輕量、完全客製化)
相關檔案
tobiichiGPT/
├── docker-compose.yml # 容器編排配置
├── api/
│ ├── server.py # API 轉接層主程式
│ └── requirements.txt # Python 依賴
└── exchange.md # 本文件
環境變數清單
| 變數名稱 | 用途 | 預設值 |
|---|---|---|
ROCKETCHAT_URL |
Rocket.Chat API 位址 | http://rocketchat:3000 |
ROCKETCHAT_USER |
登入帳號 | admin |
ROCKETCHAT_PASSWORD |
登入密碼 | admin |
DB_HOST |
PostgreSQL 主機 | postgres |
DB_PASSWORD |
資料庫密碼 | (必填) |
測試指令
# 重建並重啟 API 容器
cd /mnt/data/External/tobiichiGPT
sudo docker-compose up -d --build api
# 查看 API 日誌
sudo docker logs tobiichiGPT-api --tail 100
# 重啟 Open WebUI (套用 Headers 設定)
sudo docker-compose up -d openwebui