Files
tobiichiGPT/exchange.md
2026-02-01 08:15:45 +08:00

10 KiB
Raw Permalink Blame History

工程師交接說明

後臺方案更換建議Streamlit

若未來認為 Rocket.Chat 部署過重或整合不便,可轉用 Streamlit 開發自定義管理後台。此方案能針對 Open WebUI 的資料結構進行 100% 客製化。

核心優勢

  1. 極簡開發:純 Python 即可構建前端,約 200 行程式碼可完成完整後台。
  2. 超輕量級:單一容器(~100MB 記憶體),無需額外資料庫(直接讀取現有 Postgres
  3. 完全客製:可完美呈現「用戶 → 對話 → 訊息」的三層結構,不受限於聊天軟體的頻道邏輯。
  4. 部署簡單:標準 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 方案:

  1. Rocket.Chat 的頻道/執行緒管理變得混亂,難以追蹤用戶對話。
  2. 伺服器資源不足,無法負擔 Rocket.Chat + MongoDB。
  3. 需要針對特定業務邏輯(如:查看用戶餘額、審核特定關鍵字)進行客製化開發。

Rocket.Chat 實作紀錄

實作日期

2026年2月1日

系統架構

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   Open WebUI    │────▶│   API 轉接層     │────▶│  Rocket.Chat    │
│   (Port 10060)  │     │   (Port 18000)   │     │  (Port 13000)   │
│   用戶前端      │◀────│   FastAPI        │◀────│  管理員後台     │
└─────────────────┘     └──────────────────┘     └─────────────────┘
                               │                        │
                               ▼                        ▼
                        ┌──────────────────┐     ┌─────────────────┐
                        │   PostgreSQL     │     │    MongoDB      │
                        │   (Port 5432)    │     │   (Port 27017)  │
                        └──────────────────┘     └─────────────────┘

工作流程

  1. 用戶發送訊息 → Open WebUI 呼叫 /v1/chat/completions
  2. API 轉接層接收 → 提取用戶資訊 (user_name, chat_id)
  3. 創建 Rocket.Chat 頻道 → 頻道名稱: {用戶名}-{對話ID前8位}
  4. 發送用戶訊息 → 顯示格式: 💬 用戶名: 訊息內容
  5. 輪詢等待回覆 → 每 2 秒檢查是否有管理員新訊息
  6. 回傳給用戶 → 以 OpenAI 格式回傳管理員的回覆

已完成的修改

1. docker-compose.yml

新增服務:

  • mongo: MongoDB 7.0 (Rocket.Chat 8.0.1 需要 7.0+)
  • mongo-init-replica: 初始化 MongoDB Replica Set
  • rocketchat: 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