Spaces:
Runtime error
Runtime error
Commit ·
a4137be
1
Parent(s): a9e6507
feat : upgrade embedding
Browse files- .env.example +6 -1
- app.py +1 -11
- db/chroma_init.py +0 -35
- db/postgres_init.py +57 -0
- rag.py +5 -2
- requirements.txt +2 -2
.env.example
CHANGED
|
@@ -1 +1,6 @@
|
|
| 1 |
-
GOOGLE_API_KEY=your_google_ai_api_key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
GOOGLE_API_KEY=your_google_ai_api_key
|
| 2 |
+
DB_HOST=your_db_host
|
| 3 |
+
DB_PORT=your_db_port
|
| 4 |
+
DB_DATABASE=your_db_database
|
| 5 |
+
DB_USER=your_db_user
|
| 6 |
+
DB_PASSWORD=your_db_password
|
app.py
CHANGED
|
@@ -5,24 +5,14 @@ from fastapi import FastAPI, HTTPException
|
|
| 5 |
from pydantic import BaseModel
|
| 6 |
from dotenv import load_dotenv
|
| 7 |
from rag import build_rag_chain
|
| 8 |
-
from db.chroma_init import load_recipes_to_chroma
|
| 9 |
|
| 10 |
load_dotenv()
|
| 11 |
app = FastAPI(
|
| 12 |
title="Recipe RAG API (Gemini 2.0)",
|
| 13 |
-
description="使用 FastAPI + LangChain +
|
| 14 |
version="1.0.0"
|
| 15 |
)
|
| 16 |
|
| 17 |
-
# 啟動時若 Chroma 尚未建立,嘗試載入 sample data(可按需註解)
|
| 18 |
-
CHROMA_DIR = "./chroma_db"
|
| 19 |
-
if not os.path.exists(CHROMA_DIR) or not os.listdir(CHROMA_DIR):
|
| 20 |
-
try:
|
| 21 |
-
print("load : chroma init")
|
| 22 |
-
load_recipes_to_chroma()
|
| 23 |
-
except Exception as e:
|
| 24 |
-
print("Warning: chroma init failed:", e)
|
| 25 |
-
|
| 26 |
# 建立 RAG chain(可共用)
|
| 27 |
RAG_CHAIN = build_rag_chain(k=4)
|
| 28 |
|
|
|
|
| 5 |
from pydantic import BaseModel
|
| 6 |
from dotenv import load_dotenv
|
| 7 |
from rag import build_rag_chain
|
|
|
|
| 8 |
|
| 9 |
load_dotenv()
|
| 10 |
app = FastAPI(
|
| 11 |
title="Recipe RAG API (Gemini 2.0)",
|
| 12 |
+
description="使用 FastAPI + LangChain + postgres + PGVector + HuggingFace Embeddings 的服務",
|
| 13 |
version="1.0.0"
|
| 14 |
)
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
# 建立 RAG chain(可共用)
|
| 17 |
RAG_CHAIN = build_rag_chain(k=4)
|
| 18 |
|
db/chroma_init.py
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
# db/chroma_init.py
|
| 2 |
-
from langchain_chroma import Chroma
|
| 3 |
-
from langchain_huggingface import HuggingFaceEmbeddings
|
| 4 |
-
|
| 5 |
-
CHROMA_DIR = "./chroma_db"
|
| 6 |
-
MODEL_NAME="sentence-transformers/all-MiniLM-L6-v2"
|
| 7 |
-
|
| 8 |
-
def load_recipes_to_chroma(texts):
|
| 9 |
-
|
| 10 |
-
# 使用 HuggingFace Embeddings(推薦)
|
| 11 |
-
embeddings = HuggingFaceEmbeddings(
|
| 12 |
-
model_name=MODEL_NAME
|
| 13 |
-
)
|
| 14 |
-
|
| 15 |
-
# 建立 Chroma DB
|
| 16 |
-
db = Chroma.from_texts(
|
| 17 |
-
texts=texts,
|
| 18 |
-
embedding=embeddings,
|
| 19 |
-
persist_directory=CHROMA_DIR
|
| 20 |
-
)
|
| 21 |
-
|
| 22 |
-
db.persist()
|
| 23 |
-
return db
|
| 24 |
-
|
| 25 |
-
# --- 建立 Chroma DB ---
|
| 26 |
-
def get_vectordb():
|
| 27 |
-
embeddings = HuggingFaceEmbeddings(
|
| 28 |
-
model_name=MODEL_NAME
|
| 29 |
-
)
|
| 30 |
-
|
| 31 |
-
db = Chroma(
|
| 32 |
-
persist_directory=CHROMA_DIR,
|
| 33 |
-
embedding_function=embeddings
|
| 34 |
-
)
|
| 35 |
-
return db
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
db/postgres_init.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import psycopg2
|
| 2 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 3 |
+
# 💡 將 Chroma 替換為 PostgreSQL 的向量儲存類
|
| 4 |
+
# 這裡我們使用 LangChain 提供的 PostgresVectorStore
|
| 5 |
+
from langchain_community.vectorstores.pgvector import PGVector
|
| 6 |
+
import os
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
|
| 9 |
+
load_dotenv()
|
| 10 |
+
|
| 11 |
+
# ⚠️ 請替換為您的實際配置
|
| 12 |
+
# 假設您的資料庫中已經創建了 'recipes_vectors' 表格
|
| 13 |
+
# --- 1. 資料庫連線設定 ---
|
| 14 |
+
# --- 資料庫連線設定 ---
|
| 15 |
+
DB_HOST=os.getenv("DB_HOST", "localhost")
|
| 16 |
+
DB_PORT=os.getenv("DB_PORT")
|
| 17 |
+
DB_DATABASE=os.getenv("DB_DATABASE")
|
| 18 |
+
DB_USER=os.getenv("DB_USER")
|
| 19 |
+
DB_PASSWORD=os.getenv("DB_PASSWORD")
|
| 20 |
+
|
| 21 |
+
DB_CONFIG = {
|
| 22 |
+
"host": DB_HOST, # 如果找不到,提供預設值
|
| 23 |
+
"database": DB_DATABASE,
|
| 24 |
+
"user": DB_USER,
|
| 25 |
+
"password": DB_PASSWORD,
|
| 26 |
+
"port": DB_PORT
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
TABLE_NAME = "recipes"
|
| 30 |
+
VECTOR_DIM = 384 # 確保這個維度和您的 'embedding' 欄位定義一致
|
| 31 |
+
# 🚨 選擇一個多語言模型。例如:BGE-M3 (維度 1024) 或 paraphrase-multilingual-mpnet-base-v2 (維度 768)
|
| 32 |
+
MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
|
| 33 |
+
|
| 34 |
+
# 這是 PGVector 實例使用的連接字串格式
|
| 35 |
+
CONNECTION_STRING = PGVector.connection_string_from_db_params(
|
| 36 |
+
driver="psycopg2",
|
| 37 |
+
**DB_CONFIG
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
# --- 將此函數定義為 get_vectordb ---
|
| 41 |
+
def get_vectordb():
|
| 42 |
+
"""
|
| 43 |
+
獲取已存在的 PostgreSQL 向量儲存實例 (PGVector)。
|
| 44 |
+
此函數取代了原先獲取 Chroma 實例的功能。
|
| 45 |
+
"""
|
| 46 |
+
embeddings = HuggingFaceEmbeddings(
|
| 47 |
+
model_name=MODEL_NAME
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
# 使用您的 PostgreSQL 連接字串和集合名稱
|
| 51 |
+
db = PGVector(
|
| 52 |
+
connection_string=CONNECTION_STRING,
|
| 53 |
+
embedding_function=embeddings,
|
| 54 |
+
collection_name=TABLE_NAME, # 您的 recipe table 名稱
|
| 55 |
+
use_jsonb=True
|
| 56 |
+
)
|
| 57 |
+
return db
|
rag.py
CHANGED
|
@@ -10,7 +10,7 @@ from langchain_core.prompts import SystemMessagePromptTemplate, HumanMessageProm
|
|
| 10 |
from langchain_core.prompts import PromptTemplate # 確保導入這個,用於 HumanMessage 的子模板
|
| 11 |
from langchain_core.prompts import ChatPromptTemplate
|
| 12 |
from models.model_wrapper import get_llm
|
| 13 |
-
from db.
|
| 14 |
from langchain_core.exceptions import OutputParserException
|
| 15 |
|
| 16 |
# --- 🎯 食譜 Pydantic 結構定義 ---
|
|
@@ -87,12 +87,15 @@ def build_rag_chain(k=4):
|
|
| 87 |
structured_llm = llm.with_structured_output(RecipeList)
|
| 88 |
|
| 89 |
def get_context_and_query(query: str):
|
| 90 |
-
|
|
|
|
| 91 |
context = "\n".join([d.page_content for d in docs])
|
| 92 |
return {"context": context, "query": query, "docs": docs}
|
| 93 |
|
| 94 |
# 調整 rag 函式以返回更清晰的結果
|
| 95 |
def rag(query: str):
|
|
|
|
|
|
|
| 96 |
|
| 97 |
# ----------------------------------------------------
|
| 98 |
# 1. 執行檢索 (RunnableLambda 讓我們在 LCEL 外執行並拿到中間結果)
|
|
|
|
| 10 |
from langchain_core.prompts import PromptTemplate # 確保導入這個,用於 HumanMessage 的子模板
|
| 11 |
from langchain_core.prompts import ChatPromptTemplate
|
| 12 |
from models.model_wrapper import get_llm
|
| 13 |
+
from db.postgres_init import get_vectordb
|
| 14 |
from langchain_core.exceptions import OutputParserException
|
| 15 |
|
| 16 |
# --- 🎯 食譜 Pydantic 結構定義 ---
|
|
|
|
| 87 |
structured_llm = llm.with_structured_output(RecipeList)
|
| 88 |
|
| 89 |
def get_context_and_query(query: str):
|
| 90 |
+
# 這裡的 retriever.invoke() 現在會對 PostgreSQL 執行向量相似性搜索
|
| 91 |
+
docs = retriever.invoke(query)
|
| 92 |
context = "\n".join([d.page_content for d in docs])
|
| 93 |
return {"context": context, "query": query, "docs": docs}
|
| 94 |
|
| 95 |
# 調整 rag 函式以返回更清晰的結果
|
| 96 |
def rag(query: str):
|
| 97 |
+
|
| 98 |
+
docs = []
|
| 99 |
|
| 100 |
# ----------------------------------------------------
|
| 101 |
# 1. 執行檢索 (RunnableLambda 讓我們在 LCEL 外執行並拿到中間結果)
|
requirements.txt
CHANGED
|
@@ -5,9 +5,9 @@ langchain-core
|
|
| 5 |
langchain-community
|
| 6 |
langchain-google-genai
|
| 7 |
langchain-huggingface
|
| 8 |
-
langchain-chroma
|
| 9 |
-
chromadb
|
| 10 |
sentence-transformers
|
| 11 |
huggingface-hub
|
| 12 |
python-dotenv
|
| 13 |
pydantic
|
|
|
|
|
|
|
|
|
| 5 |
langchain-community
|
| 6 |
langchain-google-genai
|
| 7 |
langchain-huggingface
|
|
|
|
|
|
|
| 8 |
sentence-transformers
|
| 9 |
huggingface-hub
|
| 10 |
python-dotenv
|
| 11 |
pydantic
|
| 12 |
+
psycopg2-binary
|
| 13 |
+
pgvector
|