adamtobegreat commited on
Commit
69ee495
·
verified ·
1 Parent(s): bc2dd3b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +10 -140
app.py CHANGED
@@ -1,109 +1,7 @@
1
  import os, re, base64
2
  from langchain_core.documents import Document
3
  from langchain_chroma import Chroma
4
- from openai import OpenAI
5
- from langchain.embeddings.base import Embeddings
6
- from langchain_google_genai import ChatGoogleGenerativeAI
7
- from langchain_community.vectorstores import FAISS
8
- import gradio as gr
9
- from langchain.memory import ConversationBufferMemory
10
-
11
- # =============================================
12
- # 1️⃣ 內建 Embedding:使用 Gemini embedding API
13
- # =============================================
14
- from langchain_community.embeddings import HuggingFaceEmbeddings
15
-
16
- embedding = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
17
-
18
- # =============================================
19
- # 2️⃣ 載入 QA 檔案並分類
20
- # =============================================
21
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
22
- path = os.path.join(BASE_DIR, "QA_v2.txt")
23
-
24
- if not os.path.exists(path):
25
- raise FileNotFoundError(f"❌ 找不到 QA 檔案:{path}")
26
-
27
- with open(path, "r", encoding="utf-8") as f:
28
- text = f.read()
29
-
30
- pattern = r"(Q[::].*?)(?=Q[::]|$)"
31
- qas = re.findall(pattern, text, flags=re.S)
32
-
33
- qa_docs = {"證券": [], "期貨": [], "複委託": []}
34
- for qa in qas:
35
- if "證券" in qa:
36
- qa_docs["證券"].append(Document(page_content=qa.strip(), metadata={"source": path}))
37
- elif "期貨" in qa:
38
- qa_docs["期貨"].append(Document(page_content=qa.strip(), metadata={"source": path}))
39
- elif "複委託" in qa:
40
- qa_docs["複委託"].append(Document(page_content=qa.strip(), metadata={"source": path}))
41
-
42
- print("✅ 已成功讀取 QA 並完成分類:", {k: len(v) for k, v in qa_docs.items()})
43
-
44
- # =============================================
45
- # 3️⃣ 建立向量資料庫(使用 FAISS,記憶體型)
46
- # =============================================
47
- vectordbs = {}
48
- for k, docs in qa_docs.items():
49
- vectordbs[k] = FAISS.from_documents(docs, embedding)
50
-
51
- # =============================================
52
- # 4️⃣ 初始化 Gemini LLM
53
- # =============================================
54
- API_KEY = os.getenv("GOOGLE_API_KEY")
55
- if not API_KEY:
56
- raise ValueError("⚠️ 未設定 GOOGLE_API_KEY,請在 Hugging Face Secrets 中新增。")
57
-
58
- llm = ChatGoogleGenerativeAI(model='gemini-2.5-flash', google_api_key=API_KEY)
59
- memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
60
-
61
- # =============================================
62
- # 5️⃣ 對話邏輯
63
- # =============================================
64
- def auto_detect_category(text):
65
- if any(k in text for k in ["股票", "證券", "開戶", "下單", "交割", "現股"]):
66
- return "證券"
67
- elif any(k in text for k in ["期貨", "選擇權", "結算", "保證金", "契約"]):
68
- return "期貨"
69
- elif any(k in text for k in ["複委託", "海外", "美股", "港股", "國外"]):
70
- return "複委託"
71
- else:
72
- return "證券"
73
-
74
- def chat_fn(message, history):
75
- category = auto_detect_category(message)
76
- vectordb = vectordbs.get(category)
77
- if not vectordb:
78
- return "目前尚無此類別的知識庫。"
79
-
80
- docs = vectordb.similarity_search(message, k=2)
81
- context = "\n\n".join([d.page_content for d in docs]) if docs else "查無相關內容。"
82
-
83
- prompt = f"""
84
- 我是一位金融客服人員。根據以下公司規章內容回答使用者問題:
85
- ---
86
- {context}
87
- ---
88
- 使用者問題:{message}
89
- """
90
-
91
- try:
92
- response = llm.invoke(prompt)
93
- reply = response.content.strip()
94
- except Exception as e:
95
- reply = f"⚠️ 生成錯誤:{e}"
96
-
97
- return reply or "請洽營業員"
98
-
99
- # =============================================
100
- # 6️⃣ Gradio 介面
101
- # =============================================
102
- import os, re, base64
103
- from langchain_core.documents import Document
104
- from langchain_chroma import Chroma
105
- from openai import OpenAI
106
- from langchain.embeddings.base import Embeddings
107
  from langchain_google_genai import ChatGoogleGenerativeAI
108
  import chromadb
109
  import gradio as gr
@@ -119,21 +17,9 @@ except ImportError:
119
 
120
 
121
  # =============================================
122
- # 1️⃣ 自訂 LM Studio Embedding 類別
123
  # =============================================
124
- class LmStudioEmbeddings(Embeddings):
125
- def __init__(self, model_name, url):
126
- self.model_name = model_name
127
- self.client = OpenAI(base_url=url, api_key="lm-studio")
128
-
129
- def embed_query(self, text: str):
130
- res = self.client.embeddings.create(input=text, model=self.model_name)
131
- return res.data[0].embedding
132
-
133
- def embed_documents(self, texts: list[str]):
134
- res = self.client.embeddings.create(input=texts, model=self.model_name)
135
- return [x.embedding for x in res.data]
136
-
137
 
138
  # =============================================
139
  # 2️⃣ 載入 QA 檔案並分類
@@ -166,13 +52,9 @@ for k, v in qa_docs.items():
166
  # =============================================
167
  # 3️⃣ 建立向量資料庫
168
  # =============================================
169
- embedding = LmStudioEmbeddings(
170
- model_name="text-embedding-bge-large-zh-v1.5",
171
- url="http://127.0.0.1:1234/v1"
172
- )
173
  client = chromadb.PersistentClient(path="./chroma_db")
174
-
175
  collection_names = {"證券": "stocks", "期貨": "futures", "複委託": "overseas"}
 
176
  vectordbs = {}
177
  for cat, docs in qa_docs.items():
178
  vectordbs[cat] = Chroma(
@@ -186,7 +68,7 @@ print("✅ 各類別向量資料庫建立完成")
186
 
187
 
188
  # =============================================
189
- # 4️⃣ 初始化 Gemini LLM
190
  # =============================================
191
  API_KEY = os.getenv("GOOGLE_API_KEY")
192
  if not API_KEY:
@@ -233,7 +115,7 @@ def chat_fn(message, history):
233
 
234
 
235
  # =============================================
236
- # 6️⃣ 介面(LINE風格 + 純白footer + 小輸入按鈕)
237
  # =============================================
238
  logo_path = os.path.join(BASE_DIR, "mega.png")
239
  logo_base64 = ""
@@ -268,16 +150,10 @@ with gr.Blocks(
268
 
269
  /* ====== footer(純白背景) ====== */
270
  #footer {
271
- position: fixed;
272
- bottom: 40px;
273
- left: 0;
274
- width: 100%;
275
- text-align: center;
276
- font-size: 13px;
277
- color: #aaa; /* ✅ 更淡的灰色 */
278
- border-top: 1px solid #ddd; /* ✅ 細灰分隔線 */
279
- padding-top: 8px;
280
- background-color: transparent; /* ✅ 移除反白 */
281
  }
282
  @media (max-width: 768px) {
283
  #footer { position: relative; margin-top: 40px; }
@@ -309,13 +185,11 @@ with gr.Blocks(
309
  #send-btn:active { transform: scale(0.95); }
310
  """
311
  ) as demo:
312
- # 左上角 logo
313
  if logo_base64:
314
  gr.HTML(f"""
315
  <div id="logo-top"><img src="data:image/png;base64,{logo_base64}" alt="logo"></div>
316
  """)
317
 
318
- # 標題區
319
  gr.HTML("""
320
  <div id="main-title-wrapper" style="text-align:center; margin-top:20px;">
321
  <h1 id='main-title'><span>👨‍💼 我是小智&nbsp;&nbsp;您的金融好幫手 🫰</span></h1>
@@ -323,11 +197,9 @@ with gr.Blocks(
323
  </div>
324
  """)
325
 
326
- # 聊天介面
327
  with gr.Row():
328
  with gr.Column(scale=4):
329
  chatbox = gr.Chatbot(label="💬 對話紀錄", type="messages")
330
-
331
  with gr.Row(elem_id="input-row"):
332
  user_input = gr.Textbox(elem_id="user-input", show_label=False, placeholder="輸入訊息...", scale=8)
333
  send_btn = gr.Button("輸入", elem_id="send-btn", scale=1)
@@ -363,10 +235,8 @@ with gr.Blocks(
363
  return [], gr.update(value="", placeholder="輸入訊息...")
364
  gr.Button("🧹 整理畫面").click(clear_memory, outputs=[chatbox, user_input])
365
 
366
- # 底部 footer(純白)
367
  gr.HTML("<div id='footer'>© Fintech Assistant — 僅業務使用,非官方授權</div>")
368
 
369
- # 手機鍵盤彈出自動捲動
370
  demo.load(None, None, None, js="""
371
  window.addEventListener('focusin', () => {
372
  document.querySelector('textarea')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
 
1
  import os, re, base64
2
  from langchain_core.documents import Document
3
  from langchain_chroma import Chroma
4
+ from langchain_huggingface import HuggingFaceEmbeddings # ✅ 雲端可直接用
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  from langchain_google_genai import ChatGoogleGenerativeAI
6
  import chromadb
7
  import gradio as gr
 
17
 
18
 
19
  # =============================================
20
+ # 1️⃣ 使用 Hugging Face 雲端 embedding 模型
21
  # =============================================
22
+ embedding = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  # =============================================
25
  # 2️⃣ 載入 QA 檔案並分類
 
52
  # =============================================
53
  # 3️⃣ 建立向量資料庫
54
  # =============================================
 
 
 
 
55
  client = chromadb.PersistentClient(path="./chroma_db")
 
56
  collection_names = {"證券": "stocks", "期貨": "futures", "複委託": "overseas"}
57
+
58
  vectordbs = {}
59
  for cat, docs in qa_docs.items():
60
  vectordbs[cat] = Chroma(
 
68
 
69
 
70
  # =============================================
71
+ # 4️⃣ 初始化 Gemini LLM(雲端)
72
  # =============================================
73
  API_KEY = os.getenv("GOOGLE_API_KEY")
74
  if not API_KEY:
 
115
 
116
 
117
  # =============================================
118
+ # 6️⃣ Gradio 介面(純雲端版)
119
  # =============================================
120
  logo_path = os.path.join(BASE_DIR, "mega.png")
121
  logo_base64 = ""
 
150
 
151
  /* ====== footer(純白背景) ====== */
152
  #footer {
153
+ position: fixed; bottom: 40px; left: 0; width: 100%;
154
+ text-align: center; font-size: 13px; color: #aaa;
155
+ border-top: 1px solid #ddd; padding-top: 8px;
156
+ background-color: transparent;
 
 
 
 
 
 
157
  }
158
  @media (max-width: 768px) {
159
  #footer { position: relative; margin-top: 40px; }
 
185
  #send-btn:active { transform: scale(0.95); }
186
  """
187
  ) as demo:
 
188
  if logo_base64:
189
  gr.HTML(f"""
190
  <div id="logo-top"><img src="data:image/png;base64,{logo_base64}" alt="logo"></div>
191
  """)
192
 
 
193
  gr.HTML("""
194
  <div id="main-title-wrapper" style="text-align:center; margin-top:20px;">
195
  <h1 id='main-title'><span>👨‍💼 我是小智&nbsp;&nbsp;您的金融好幫手 🫰</span></h1>
 
197
  </div>
198
  """)
199
 
 
200
  with gr.Row():
201
  with gr.Column(scale=4):
202
  chatbox = gr.Chatbot(label="💬 對話紀錄", type="messages")
 
203
  with gr.Row(elem_id="input-row"):
204
  user_input = gr.Textbox(elem_id="user-input", show_label=False, placeholder="輸入訊息...", scale=8)
205
  send_btn = gr.Button("輸入", elem_id="send-btn", scale=1)
 
235
  return [], gr.update(value="", placeholder="輸入訊息...")
236
  gr.Button("🧹 整理畫面").click(clear_memory, outputs=[chatbox, user_input])
237
 
 
238
  gr.HTML("<div id='footer'>© Fintech Assistant — 僅業務使用,非官方授權</div>")
239
 
 
240
  demo.load(None, None, None, js="""
241
  window.addEventListener('focusin', () => {
242
  document.querySelector('textarea')?.scrollIntoView({ behavior: 'smooth', block: 'center' });