| | --- |
| | title: 銀髮餐桌助手 |
| | emoji: 🥄 |
| | colorFrom: blue |
| | colorTo: green |
| | sdk: docker |
| | app_port: 7860 |
| | --- |
| | |
| | # 銀髮餐桌助手 - RAG 系統 |
| |
|
| | 本文件描述銀髮餐桌助手的 RAG(Retrieval-Augmented Generation)系統架構與實作細節。 |
| |
|
| | ## RAG 系統概述 |
| |
|
| | 銀髮餐桌助手採用 RAG 技術為台灣銀髮族(65歲以上)提供智能營養飲食諮詢服務。RAG 系統透過檢索相關知識庫文檔,增強 AI 回應的準確性和可靠性,確保所有營養建議基於權威來源。 |
| |
|
| | ### 核心功能 |
| |
|
| | - **知識檢索**:基於用戶問題從知識庫中檢索相關文檔 |
| | - **相似性搜索**:使用向量嵌入實現語義級相似性匹配 |
| | - **文檔增強生成**:將檢索到的文檔作為上下文,提升 AI 回應品質 |
| | - **個人化回應**:結合用戶健康檔案提供客製化建議 |
| |
|
| | ## 知識庫來源 |
| |
|
| | 知識庫內容托管在 **Hugging Face Dataset**: |
| | - **Dataset**: [pcreem/dietinstruction](https://huggingface.co/datasets/pcreem/dietinstruction) |
| | - 本地資料夾 `backend/data/` 用於存放下載的檔案 |
| | - 系統啟動時會自動從 Hugging Face 下載知識庫檔案 |
| |
|
| | ### 主要文檔 |
| |
|
| | | 文件 | 內容 | |
| | |------|------| |
| | | `每日飲食指南手冊.pdf` | 衛福部每日飲食指南 | |
| | | `高齡營養飲食質地衛教手冊.pdf` | 高齡者飲食質地指導 | |
| | | `慢性病飲食原則.md` | 糖尿病、高血壓、高脂血症等慢性病飲食指南 | |
| |
|
| | ### 文檔格式支援 |
| |
|
| | - **Markdown (.md)**:主要知識庫格式 |
| | - **PDF (.pdf)**:支援 PDF 文件載入 |
| |
|
| | ### 自動下載機制 |
| |
|
| | RAG 服務啟動時會檢查本地 `backend/data/` 資料夾: |
| | - 若資料夾不存在或為空,自動從 Hugging Face Dataset 下載知識庫 |
| | - 若已有檔案,則使用本地版本 |
| |
|
| | 所有文檔經過處理後存入向量資料庫,供相似性搜索使用。 |
| |
|
| | ## 系統架構流程圖 |
| |
|
| | ``` |
| | ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ |
| | │ User Query │────►│ Chat Service │────►│ RAG Service │ |
| | │ (問題輸入) │ │ (聊天服務) │ │ (RAG服務) │ |
| | └─────────────────┘ └────────┬────────┘ └────────┬────────┘ |
| | │ │ |
| | │ ▼ |
| | │ ┌─────────────────┐ |
| | │ │ Embeddings │ |
| | │ │ (向量化) │ |
| | │ └────────┬────────┘ |
| | │ │ |
| | │ ▼ |
| | │ ┌─────────────────┐ |
| | │ │ Supabase │ |
| | │ │ pgvector │ |
| | │ │ (向量資料庫) │ |
| | │ └────────┬────────┘ |
| | │ │ |
| | │ ▼ |
| | │ ┌─────────────────┐ |
| | │ │ Similarity │ |
| | │ │ Search │ |
| | │ │ (相似性搜索) │ |
| | │ └────────┬────────┘ |
| | │ │ |
| | │ ▼ |
| | │ ┌─────────────────┐ |
| | │ │ Relevant Docs │ |
| | │ │ (相關文檔) │ |
| | │ └────────┬────────┘ |
| | │ │ |
| | ▼ ▼ |
| | ┌─────────────────┐ ┌─────────────────┐ |
| | │ LLM Response │◄────│ Context │ |
| | │ (AI回應) │ │ Integration │ |
| | └─────────────────┘ └─────────────────┘ |
| | ``` |
| |
|
| | ## 核心組件說明 |
| |
|
| | ### RAG 服務 ([`rag.py`](backend/rag.py)) |
| |
|
| | RAG 服務是系統的核心模組,負責文檔處理和向量搜索。 |
| |
|
| | ```python |
| | class RAGService: |
| | """RAG service for document management and similarity search.""" |
| | |
| | def __init__(self): |
| | # 初始化 OpenAI 向量化模型 |
| | self.embeddings = OpenAIEmbeddings(...) |
| | # 初始化 Supabase 向量存儲 |
| | self.vector_store = SupabaseVectorStore(...) |
| | # 初始化文檔分塊器 |
| | self.text_splitter = RecursiveCharacterTextSplitter( |
| | chunk_size=1000, |
| | chunk_overlap=200 |
| | ) |
| | ``` |
| |
|
| | ### 聊天服務 ([`chat_service.py`](backend/chat_service.py)) |
| |
|
| | 聊天服務整合 RAG 能力,提供 AI 對話功能。 |
| |
|
| | ```python |
| | class ChatService: |
| | """Chat service for AI-powered conversations with RAG integration.""" |
| | |
| | def __init__(self): |
| | # 初始化 LLM 模型 |
| | self.llm = ChatOpenAI(...) |
| | # 整合 RAG 服務 |
| | self.rag_service = get_rag_service() |
| | ``` |
| |
|
| | ### 資料庫設置 ([`setup_rag_db.py`](backend/setup_rag_db.py)) |
| |
|
| | 設置 pgvector 擴展和文檔表結構。 |
| |
|
| | ## 文檔處理流程 |
| |
|
| | ### 流程步驟 |
| |
|
| | ``` |
| | ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ |
| | │ 載入文檔 │───►│ 文檔分塊 │───►│ 向量化 │───►│ 存入資料庫 │ |
| | │ Loader │ │ Chunker │ │ Embedding │ │ Storage │ |
| | └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ |
| | ``` |
| |
|
| | ### 1. 文檔載入 |
| |
|
| | 支援多種文檔載入器: |
| |
|
| | ```python |
| | # PDF 載入 |
| | loader = PyPDFLoader(str(file_path)) |
| | documents = loader.load() |
| | |
| | # Markdown 載入 |
| | loader = UnstructuredMarkdownLoader(str(file_path)) |
| | documents = loader.load() |
| | ``` |
| |
|
| | ### 2. 文檔分塊 |
| |
|
| | 使用 [`RecursiveCharacterTextSplitter`](backend/rag.py:66) 進行智慧分塊: |
| |
|
| | - **chunk_size**:1000 字元 |
| | - **chunk_overlap**:200 字元重疊 |
| | - **is_separator_regex**:false(使用純文字分隔) |
| |
|
| | 每個區塊包含以下元數據: |
| |
|
| | ```python |
| | chunk.metadata = { |
| | "source": "文件路徑", |
| | "file_name": "檔案名稱", |
| | "chunk_id": "唯一識別碼" |
| | } |
| | ``` |
| |
|
| | ### 3. 向量化 |
| |
|
| | 使用 OpenAI Embeddings 模型將文本轉換為向量: |
| |
|
| | ```python |
| | embeddings = OpenAIEmbeddings( |
| | model="azure-text-embedding-3-large", |
| | openai_api_key=api_key |
| | ) |
| | # 支援 LiteLLM 相容端點 |
| | ``` |
| |
|
| | 向量維度:**3072** |
| |
|
| | ### 4. 向量存儲 |
| |
|
| | 存入 Supabase pgvector 向量資料庫: |
| |
|
| | ```python |
| | vector_store = SupabaseVectorStore( |
| | client=supabase_client, |
| | embedding=embeddings, |
| | table_name="documents", |
| | query_name="match_documents" |
| | ) |
| | ``` |
| |
|
| | ## 相似性搜索 |
| |
|
| | ### 基本搜索 |
| |
|
| | ```python |
| | async def get_relevant_documents(self, query: str, k: int = 8) -> List[Document]: |
| | """執行相似性搜索檢索相關文檔""" |
| | results = await self.vector_store.asimilarity_search(query, k=k) |
| | return results |
| | ``` |
| |
|
| | ### 帶分數的搜索 |
| |
|
| | ```python |
| | async def get_relevant_documents_with_scores( |
| | self, |
| | query: str, |
| | k: int = 8, |
| | score_threshold: float = 0.7 |
| | ) -> List[Document]: |
| | """執行帶分數閾值的相似性搜索""" |
| | ``` |
| |
|
| | ### 分頁搜索 |
| |
|
| | ```python |
| | async def get_relevant_documents_paginated( |
| | self, |
| | query: str, |
| | page: int = 1, |
| | page_size: int = 10, |
| | score_threshold: Optional[float] = None |
| | ) -> Dict[str, Any]: |
| | """執行分頁相似性搜索""" |
| | ``` |
| |
|
| | ### 搜索結果快取 |
| |
|
| | 系統使用文檔快取機制(TTL: 30 分鐘)優化效能: |
| |
|
| | ```python |
| | @cache_result(document_cache, "rag_documents", ttl=1800) |
| | async def get_relevant_documents(self, query: str, k: int = 8): |
| | ... |
| | ``` |
| |
|
| | ## AI 對話整合 |
| |
|
| | ### 對話流程 |
| |
|
| | 1. 接收用戶問題 |
| | 2. 檢索相關文檔(k=6) |
| | 3. 獲取用戶健康檔案 |
| | 4. 格式化上下文資訊 |
| | 5. 組合系統提示詞 |
| | 6. 串流回應生成 |
| |
|
| | ### 系統提示詞 |
| |
|
| | ```python |
| | system_prompt = """你是「銀髮餐桌助手」,專為台灣銀髮族設計的AI營養飲食顧問助手。 |
| | |
| | 角色定位: |
| | - 溫暖、耐心、專業的營養飲食顧問 |
| | - 專門為台灣銀髮族(65歲以上)提供飲食建議 |
| | - 熟悉台灣在地食材、飲食文化和生活習慣 |
| | |
| | 核心原則: |
| | 1. 嚴格遵循台灣衛福部(MOHW)的營養指導原則 |
| | 2. 僅提供營養建議,絕不進行醫療診斷 |
| | 3. 針對銀髮族的特殊營養需求 |
| | 4. 考慮台灣在地飲食文化""" |
| | ``` |
| |
|
| | ### 上下文整合 |
| |
|
| | ```python |
| | def format_context_information(self, user_context, relevant_docs) -> str: |
| | """格式化用戶上下文和相關文檔""" |
| | # 組合用戶背景資訊 |
| | # 組合相關營養指南文檔 |
| | return formatted_context |
| | ``` |
| |
|
| | ## API 端點 |
| |
|
| | ### RAG 相關端點 |
| |
|
| | | 端點 | 方法 | 說明 | |
| | |------|------|------| |
| | | `/api/chat` | POST | AI 聊天諮詢(含 RAG) | |
| | | `/rag/load` | POST | 載入知識庫 | |
| | | `/rag/search` | GET | 相似性搜索 | |
| | | `/rag/count` | GET | 獲取文檔數量 | |
| | | `/rag/clear` | DELETE | 清除知識庫 | |
| | | `/` | GET | Gradio 聊天界面 | |
| |
|
| | ### 聊天請求範例 |
| |
|
| | ```bash |
| | curl -X POST "http://localhost:8000/api/chat" \ |
| | -H "Authorization: Bearer <token>" \ |
| | -H "Content-Type: application/json" \ |
| | -d '{ |
| | "message": "請問銀髮族應該如何補充蛋白質?", |
| | "profile_id": "uuid-string" |
| | }' |
| | ``` |
| |
|
| | ### 知識庫載入請求 |
| |
|
| | ```bash |
| | curl -X POST "http://localhost:8000/rag/load" \ |
| | -H "Authorization: Bearer <token>" |
| | ``` |
| |
|
| | ## 快速開始 |
| |
|
| | ### 前置要求 |
| |
|
| | - Python 3.8+ |
| | - PostgreSQL 13+(通過 Supabase,啟用 pgvector) |
| | - Supabase 專案 |
| | - OpenAI API Key 或 LiteLLM API Key |
| |
|
| | ### 環境變數配置 |
| |
|
| | 複製 `.env.example` 到 `.env` 並填入以下配置: |
| |
|
| | ```bash |
| | # Supabase Configuration |
| | SUPABASE_URL=your_supabase_project_url |
| | SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key |
| | |
| | # Database URL for pgvector (Supabase PostgreSQL) |
| | DATABASE_URL=postgresql+asyncpg://postgres:[YOUR_PASSWORD]@db.[YOUR_PROJECT_ID].supabase.co:5432/postgres |
| | |
| | # OpenAI Configuration (or LiteLLM) |
| | OPENAI_API_KEY=your_openai_api_key |
| | OPENAI_BASE_URL=your_openai_base_url # Optional, for LiteLLM/Azure |
| | |
| | # Or use LiteLLM directly |
| | LITELLM_API_KEY=your_litellm_api_key |
| | LITELLM_BASE_URL=https://your-litellm-endpoint/ |
| | |
| | # Server Configuration |
| | HOST=0.0.0.0 |
| | PORT=8000 |
| | ``` |
| |
|
| | ### 安裝依賴 |
| |
|
| | ```bash |
| | # 創建虛擬環境 |
| | python -m venv venv |
| | source venv/bin/activate # Linux/Mac |
| | # 或 venv\Scripts\activate # Windows |
| | |
| | # 安裝依賴 |
| | pip install -r requirements.txt |
| | ``` |
| |
|
| | ### 資料庫設置 |
| |
|
| | ```bash |
| | # 設置 pgvector 和文檔表 |
| | python setup_rag_db.py |
| | ``` |
| |
|
| | ### 啟動服務 |
| |
|
| | ```bash |
| | # 啟動 FastAPI 服務 |
| | python app.py |
| | |
| | # 或使用 uvicorn |
| | uvicorn app:app --reload --host 0.0.0.0 --port 8000 |
| | ``` |
| |
|
| | ### 載入知識庫 |
| |
|
| | 知識庫會在服務啟動時自動載入,或可手動觸發: |
| |
|
| | ```bash |
| | # 通過 API |
| | curl -X POST "http://localhost:8000/rag/load" |
| | |
| | # 或直接運行 |
| | python -m rag |
| | ``` |
| |
|
| | ### 測試 RAG 系統 |
| |
|
| | ```bash |
| | python -m rag |
| | python -m chat_service |
| | ``` |
| |
|
| | ### 訪問應用 |
| |
|
| | - **API 文檔**: http://localhost:8000/api/docs |
| | - **Gradio 聊天界面**: http://localhost:8000/ |
| | - **健康檢查**: http://localhost:8000/health |
| |
|
| | ## 故障排除 |
| |
|
| | ### 常見問題 |
| |
|
| | 1. **pgvector 擴展未啟用** |
| | ```sql |
| | CREATE EXTENSION IF NOT EXISTS vector; |
| | ``` |
| |
|
| | 2. **向量維度不匹配** |
| | - 確保 embedding 向量維度為 3072 |
| | - 檢查資料庫表結構:`embedding vector(3072)` |
| |
|
| | 3. **SupabaseVectorStore 兼容性問題** |
| | - 系統已內建手動 RPC 回退機制 |
| | - 使用 `match_documents` 函數直接執行相似性搜索 |
| |
|
| | 4. **知識庫載入失敗** |
| | - 檢查 Hugging Face Dataset 網路連接 |
| | - 確認 Dataset `pcreem/dietinstruction` 存在且有檔案 |
| | - 檢查 `backend/data/` 目錄存在且包含文件 |
| | - 確認文件格式為支援的類型(.md, .pdf, .txt) |
| |
|
| | ### 調試模式 |
| |
|
| | ```bash |
| | # 啟用詳細日誌 |
| | export LOG_LEVEL=DEBUG |
| | python app.py |
| | |
| | # 或使用 uvicorn |
| | uvicorn app:app --reload --log-level debug |
| | ``` |
| |
|
| | ## 監控與日誌 |
| |
|
| | ### 日誌配置 |
| |
|
| | 系統使用 Python 標準日誌庫: |
| |
|
| | ```bash |
| | LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR |
| | ``` |
| |
|
| | ### 健康檢查 |
| |
|
| | - `GET /health` - 服務健康狀態檢查 |
| | - 返回服務狀態、版本信息和資料庫連接狀態 |
| |
|
| | ## 安全考量 |
| |
|
| | - 知識庫搜索無需認證(匿名用戶也可使用) |
| | - 用戶健康檔案相關操作需要 JWT 認證 |
| | - 向量資料庫連接使用服務層級密鑰 |
| |
|
| | --- |
| |
|
| | **重要提醒**:本系統僅提供營養建議,無法替代專業醫療諮詢。如有健康問題,請諮詢專業醫師。 |
| |
|