File size: 6,410 Bytes
69f3993
 
 
 
 
 
 
f9c1b83
69f3993
f9c1b83
69f3993
 
 
 
f9c1b83
 
 
5355578
69f3993
 
f9c1b83
 
 
 
 
 
 
 
 
 
 
0ea06d4
 
 
f9c1b83
 
 
 
 
0ea06d4
f9c1b83
 
 
69f3993
 
f9c1b83
69f3993
 
 
 
 
 
 
f9c1b83
 
 
 
 
 
 
 
 
69f3993
 
 
f9c1b83
 
 
4d16bf0
cb20315
 
f9c1b83
cb20315
 
 
557979f
 
f9c1b83
557979f
 
f9c1b83
4b4084a
cb20315
 
 
 
 
f9c1b83
cb20315
 
 
 
 
69f3993
f9c1b83
 
 
69f3993
f9c1b83
 
69f3993
 
 
4d16bf0
f9c1b83
 
 
 
 
 
69f3993
 
 
 
f9c1b83
 
 
cb20315
 
f9c1b83
 
 
69f3993
f9c1b83
 
 
 
 
 
 
 
 
 
 
 
 
 
3573828
f9c1b83
69f3993
f9c1b83
69f3993
f9c1b83
 
69f3993
cb20315
f9c1b83
69f3993
 
 
 
4d16bf0
69f3993
f9c1b83
69f3993
6f25afa
69f3993
cb20315
69f3993
f9c1b83
 
 
69f3993
 
 
 
 
 
f9c1b83
69f3993
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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 模型 (快取)
########################################
@st.cache_resource
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)