Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import google.generativeai as genai | |
| import os | |
| import torch | |
| import sys | |
| import pysqlite3 | |
| from FlagEmbedding import FlagModel | |
| # 將 sqlite3 指向 pysqlite3 | |
| sys.modules['sqlite3'] = pysqlite3 | |
| import chromadb | |
| if hasattr(torch.classes, '__path__'): | |
| torch.classes.__path__ = [] | |
| ######################################## | |
| # 設定 Google Gemini API & 主要參數 | |
| ######################################## | |
| GEMINI_API_KEY = st.secrets["GEMINI_API_KEY"] | |
| genai.configure(api_key=GEMINI_API_KEY) | |
| ######################################## | |
| # 建立模型 (Gemini 2.0 Flash) | |
| ######################################## | |
| geminiModel = genai.GenerativeModel("gemini-2.0-flash") | |
| checkModel = genai.GenerativeModel("gemini-2.0-flash") | |
| ######################################## | |
| # 判斷是否需要 RAG 查詢 | |
| ######################################## | |
| def need_rag_check(model, user_input): | |
| prompt = f""" | |
| 你是一個智慧客服助理,專精於保險專業資訊。 | |
| 無論使用者是詢問保險方案推薦、投保流程或其他與保險相關的專業問題,只要提到保險相關資訊,就必須查詢保險資料庫來輔助回答。 | |
| 請根據下面的對話內容,僅輸出 "YES" ,表示需要查詢資料庫;否則輸出 "NO"。 | |
| 對話內容:{user_input} | |
| """ | |
| resp = model.generate_content(prompt) | |
| return ("YES" in resp.text.upper()) | |
| ######################################## | |
| # 載入 Chuxin Embedding 模型 (快取) | |
| ######################################## | |
| def load_embedding_model(): | |
| return FlagModel( | |
| 'chuxin-llm/Chuxin-Embedding', | |
| query_instruction_for_retrieval="为这个句子生成表示以用于检索相关文章:", | |
| use_fp16=True | |
| ) | |
| embedding_model = load_embedding_model() | |
| ######################################## | |
| # 取得向量 | |
| ######################################## | |
| def get_chuxin_embedding(text: str): | |
| return embedding_model.encode([text])[0].tolist() | |
| ######################################## | |
| # 初始化 ChromaDB | |
| ######################################## | |
| chroma_client = chromadb.PersistentClient(path="./chroma_db") | |
| collection = chroma_client.get_or_create_collection(name="insurance_database", embedding_function=None) | |
| ######################################## | |
| # Streamlit 介面配置 | |
| ######################################## | |
| st.title("💬 InsureGPT保險顧問") | |
| st.write("🔍 與 AI 互動,詢問保險相關問題。") | |
| # 自訂背景 | |
| st.markdown( | |
| """ | |
| <style> | |
| .stApp { | |
| background: linear-gradient( | |
| rgba(238, 100, 2, 0.5), | |
| rgba(238, 100, 2, 0.5) | |
| ), url("https://imgur.com/LvZQr6p.png") no-repeat center center fixed; | |
| background-size: 50%; | |
| background-color: #fdd692; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| z-index: -1; | |
| } | |
| </style> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| ######################################## | |
| # 建立 Chat Session 狀態 | |
| ######################################## | |
| if "chat" not in st.session_state: | |
| # 初始化對話 (System Prompt) | |
| st.session_state.chat = geminiModel.start_chat(history=[ | |
| { | |
| "role": "model", | |
| "parts": [( | |
| "背景設定: 你是一位精通台灣保險法規與保險商品的專業顧問,名字叫做InsureGPT。" | |
| "你可以使用知識庫中儲存的保險內容作為專業知識,回答使用者問題。" | |
| "也能根據使用者提供的個人資訊推薦客製化保險方案。" | |
| "若資訊不足或無法找到答案,請直接說明無法回答。" | |
| "請使用條列式或表格回答問題,以確保清楚易讀。" | |
| "請勿捏造資訊,如無法回答,請提供合理建議。" | |
| "在必要時,示範如何計算並提供適合的保險方案與試算金額。" | |
| )] | |
| } | |
| ]) | |
| ######################################## | |
| # 用戶輸入 UI | |
| ######################################## | |
| user_input = st.text_input("💡 你的問題(例如:什麼是強制險?)", key="user_input") | |
| ######################################## | |
| # 按鈕事件 | |
| ######################################## | |
| if st.button("送出"): | |
| if user_input.strip(): | |
| # 先判斷是否需要 RAG | |
| do_rag = need_rag_check(checkModel, user_input) | |
| if do_rag: | |
| # Embedding + Query | |
| emb = get_chuxin_embedding(user_input) | |
| results = collection.query(query_embeddings=[emb], n_results=10) | |
| if results["documents"]: | |
| retrieved_docs = "\n\n---\n\n".join(results["documents"][0]) | |
| else: | |
| retrieved_docs = "無匹配資料" | |
| else: | |
| retrieved_docs = "(模型判定此次提問無需檢索)" | |
| print(retrieved_docs) | |
| # 組合對話 | |
| user_message = ( | |
| f"你擁有以下與『{user_input}』相關的專業知識:\n\n" | |
| f"{retrieved_docs}\n\n" | |
| f"請回答下列問題:\n" | |
| f"{user_input}" | |
| ) | |
| # 發送給 Gemini Chat | |
| chat = st.session_state.chat | |
| response = chat.send_message(user_message) | |
| # 顯示回應 | |
| st.markdown(f"### 🤖 InsureGPT 回應:\n{response.text}") | |
| # 更新 chat 狀態 | |
| st.session_state.chat = chat | |
| st.session_state.input_value = "" | |
| else: | |
| st.warning("請輸入問題!") | |
| ######################################## | |
| # 顯示聊天紀錄 | |
| ######################################## | |
| st.subheader("💬 聊天記錄") | |
| for msg in st.session_state.chat.history: | |
| role = "user" if msg.role == "user" else "assistant" | |
| if role == "assistant" and "背景設定" in msg.parts[0].text: | |
| continue | |
| if role == "user": | |
| question_start = msg.parts[0].text.find("問題:\n") | |
| if question_start != -1: | |
| msg_text = msg.parts[0].text[question_start:] | |
| else: | |
| msg_text = msg.parts[0].text | |
| else: | |
| msg_text = msg.parts[0].text | |
| with st.chat_message(role): | |
| st.markdown(msg_text) | |