youngtsai commited on
Commit
d2468c1
·
1 Parent(s): 9d53202
Files changed (5) hide show
  1. .gitignore +43 -0
  2. .gradio/flagged/dataset1.csv +55 -0
  3. app.py +493 -0
  4. data/books.json +510 -0
  5. requirements.txt +1 -0
.gitignore ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # API Keys and Credentials
2
+ credential.json
3
+ *.key
4
+ *.pem
5
+ *.env
6
+
7
+ # Python
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+ *.so
12
+ .Python
13
+ env/
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+
30
+ # Virtual Environment
31
+ venv/
32
+ ENV/
33
+ env/
34
+
35
+ # IDE
36
+ .idea/
37
+ .vscode/
38
+ *.swp
39
+ *.swo
40
+
41
+ # OS
42
+ .DS_Store
43
+ Thumbs.db
.gradio/flagged/dataset1.csv ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 你現在最想達成什麼目標?,你平均多久能看完一本書?,你目前面臨什麼挑戰?,推薦結果,timestamp
2
+ 自我成長,3-7天,工作壓力大,"🎯 為你找到以下推薦書籍:
3
+
4
+ 📖 推薦書籍 1:《給逆境中的你》
5
+
6
+ 📚 這是一本心理勵志類型的書籍
7
+ 👉 特別適合想要自我成長的讀者
8
+ ⏰ 可以慢慢品味,每天讀一個章節
9
+ 💡 書中提供實用的壓力管理方法
10
+ -------------------
11
+
12
+ 📖 推薦書籍 2:《不孤單,一起走》
13
+
14
+ 📚 這是一本心理勵志類型的書籍
15
+ 👉 特別適合想要自我成長的讀者
16
+ ⏰ 可以慢慢品味,每天讀一個章節
17
+ 💡 書中提供實用的壓力管理方法
18
+ -------------------
19
+
20
+ 📖 推薦書籍 3:《發現我的天才》
21
+
22
+ 📚 這是一本心理勵志類型的書籍
23
+ 👉 特別適合想要自我成長的讀者
24
+ ⏰ 可以慢慢品味,每天讀一個章節
25
+ 💡 書中提供實用的壓力管理方法
26
+ -------------------
27
+
28
+ ",2025-04-03 18:31:34.559052
29
+ 自我成長,3-7天,工作壓力大,"🎯 為你找到以下推薦書籍:
30
+
31
+ 📖 推薦書籍 1:《給逆境中的你》
32
+
33
+ 📚 這是一本心理勵志類型的書籍
34
+ 👉 特別適合想要自我成長的讀者
35
+ ⏰ 可以慢慢品味,每天讀一個章節
36
+ 💡 書中提供實用的壓力管理方法
37
+ -------------------
38
+
39
+ 📖 推薦書籍 2:《不孤單,一起走》
40
+
41
+ 📚 這是一本心理勵志類型的書籍
42
+ 👉 特別適合想要自我成長的讀者
43
+ ⏰ 可以慢慢品味,每天讀一個章節
44
+ 💡 書中提供實用的壓力管理方法
45
+ -------------------
46
+
47
+ 📖 推薦書籍 3:《發現我的天才》
48
+
49
+ 📚 這是一本心理勵志類型的書籍
50
+ 👉 特別適合想要自我成長的讀者
51
+ ⏰ 可以慢慢品味,每天讀一個章節
52
+ 💡 書中提供實用的壓力管理方法
53
+ -------------------
54
+
55
+ ",2025-04-03 18:31:36.238013
app.py ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import random
4
+ import os
5
+ from typing import List, Dict
6
+ import openai
7
+
8
+ # 設定 OpenAI API 金鑰
9
+ def load_api_key():
10
+ # 嘗試從環境變數獲取 API 金鑰
11
+ api_key = os.environ.get("OPENAI_API_KEY")
12
+
13
+ # 如果環境變數中沒有 API 金鑰,嘗試從 credential.json 讀取
14
+ if not api_key:
15
+ try:
16
+ with open('credential.json', 'r', encoding='utf-8') as f:
17
+ credentials = json.load(f)
18
+ api_key = credentials.get("OPENAI_API_KEY")
19
+ except (FileNotFoundError, json.JSONDecodeError, KeyError) as e:
20
+ print(f"無法從 credential.json 讀取 API 金鑰: {e}")
21
+
22
+ return api_key
23
+
24
+ openai.api_key = load_api_key()
25
+ if not openai.api_key:
26
+ print("警告:未設定 OPENAI_API_KEY,請在環境變數或 credential.json 中設定")
27
+
28
+ # 載入書籍資料
29
+ def load_books() -> List[Dict]:
30
+ with open('data/books.json', 'r', encoding='utf-8') as f:
31
+ data = json.load(f)
32
+ return data['books']
33
+
34
+ books = load_books()
35
+
36
+ # 問題設定
37
+ QUESTIONS = {
38
+ "reading_goal": {
39
+ "question": "你現在最想達成什麼目標?",
40
+ "options": {
41
+ "提升工作效率": ["商業管理", "職涯發展"],
42
+ "自我成長": ["心理勵志", "溝通表達"],
43
+ "教育相關": ["教育"],
44
+ "技術創新": ["科技創新", "創新科技"],
45
+ "人文涵養": ["文學", "歷史", "歷史傳記", "哲學"]
46
+ }
47
+ },
48
+ "reading_time": {
49
+ "question": "你平均多久能看完一本書?",
50
+ "options": [
51
+ "1-2天",
52
+ "3-7天",
53
+ "2-3週",
54
+ "1個月以上"
55
+ ]
56
+ },
57
+ "current_challenge": {
58
+ "question": "你目前面臨什麼挑戰?",
59
+ "options": [
60
+ "工作壓力大",
61
+ "想轉換跑道",
62
+ "需要新技能",
63
+ "尋找人生方向",
64
+ "情緒管理"
65
+ ]
66
+ }
67
+ }
68
+
69
+ def get_book_recommendation(goal: str, reading_time: str, challenge: str, user_preferences=None) -> List[Dict]:
70
+ """根據使用者輸入和聊天歷史推薦書籍"""
71
+
72
+ # 根據目標選擇對應類別
73
+ target_categories = QUESTIONS["reading_goal"]["options"][goal]
74
+
75
+ # 篩選符合類別的書籍
76
+ potential_books = [
77
+ book for book in books
78
+ if book['category'] in target_categories
79
+ ]
80
+
81
+ # 如果找不到書,從所有書中選擇
82
+ if not potential_books:
83
+ potential_books = books
84
+
85
+ # 選擇3本書
86
+ if openai.api_key:
87
+ try:
88
+ # 使用 OpenAI API 來選擇最適合的書籍
89
+ book_info = []
90
+ for book in potential_books:
91
+ book_info.append(f"標題:{book['title']}, 作者:{book.get('author', '未知作者')}, 類別:{book['category']}, 簡介:{book.get('description', '無簡介')}")
92
+
93
+ # 如果有用戶偏好,將其納入 prompt 中
94
+ if user_preferences and openai.api_key:
95
+ # 修改 prompt,加入用戶偏好
96
+ prompt = f"""
97
+ 作為一個專業的選書顧問,請根據以下用戶資訊,從提供的書籍清單中選出最適合的3本書:
98
+
99
+ 用戶資訊:
100
+ - 閱讀目標:{goal}
101
+ - 閱讀時間:{reading_time}
102
+ - 目前面臨的挑戰:{challenge}
103
+
104
+ 用戶偏好:
105
+ - 喜愛的作者:{', '.join(user_preferences.get('authors', []))}
106
+ - 喜愛的類型:{', '.join(user_preferences.get('genres', []))}
107
+ - 感興趣的主題:{', '.join(user_preferences.get('topics', []))}
108
+ - 喜歡的書籍:{', '.join(user_preferences.get('liked_books', []))}
109
+ - 不喜歡的書籍:{', '.join(user_preferences.get('disliked_books', []))}
110
+
111
+ 可選書籍清單:
112
+ {book_info}
113
+
114
+ 請選出3本最適合的書籍,並以JSON格式返回,格式如下:
115
+ {{
116
+ "recommendations": [
117
+ {{"title": "書名1"}},
118
+ {{"title": "書名2"}},
119
+ {{"title": "書名3"}}
120
+ ]
121
+ }}
122
+ 只返回JSON,不要有其他文字。
123
+ """
124
+ else:
125
+ prompt = f"""
126
+ 作為一個專業的選書顧問,請根據以下用戶資訊,從提供的書籍清單中選出最適合的3本書:
127
+
128
+ 用戶資訊:
129
+ - 閱讀目標:{goal}
130
+ - 閱讀時間:{reading_time}
131
+ - 目前面臨的挑戰:{challenge}
132
+
133
+ 可選書籍清單:
134
+ {book_info}
135
+
136
+ 請選出3本最適合的書籍,並以JSON格式返回,格式如下:
137
+ {{
138
+ "recommendations": [
139
+ {{"title": "書名1"}},
140
+ {{"title": "書名2"}},
141
+ {{"title": "書名3"}}
142
+ ]
143
+ }}
144
+ 只返回JSON,不要有其他文字。
145
+ """
146
+
147
+ response = openai.chat.completions.create(
148
+ model="gpt-4o",
149
+ messages=[{"role": "system", "content": "你是一個專業的選書顧問,根據用戶需求推薦最適合的書籍。"},
150
+ {"role": "user", "content": prompt}],
151
+ temperature=0.7
152
+ )
153
+
154
+ # 解析 AI 回應
155
+ try:
156
+ ai_response = response.choices[0].message.content
157
+ # 提取 JSON 部分
158
+ import re
159
+ json_match = re.search(r'({.*})', ai_response, re.DOTALL)
160
+ if json_match:
161
+ ai_response = json_match.group(1)
162
+
163
+ recommendations_data = json.loads(ai_response)
164
+ recommended_titles = [rec["title"] for rec in recommendations_data["recommendations"]]
165
+
166
+ # 找出對應的書籍完整資料
167
+ recommended = []
168
+ for title in recommended_titles:
169
+ for book in potential_books:
170
+ if book['title'] == title:
171
+ recommended.append(book)
172
+ break
173
+
174
+ # 如果 AI 推薦的書籍不足3本,從 potential_books 中隨機補充
175
+ if len(recommended) < 3 and len(potential_books) >= 3:
176
+ remaining_books = [b for b in potential_books if b not in recommended]
177
+ additional = random.sample(remaining_books, min(3 - len(recommended), len(remaining_books)))
178
+ recommended.extend(additional)
179
+
180
+ except (json.JSONDecodeError, KeyError, IndexError) as e:
181
+ print(f"解析 AI 回應時出錯: {e}")
182
+ # 如果解析失敗,使用隨機選擇
183
+ recommended = random.sample(potential_books, min(3, len(potential_books)))
184
+
185
+ except Exception as e:
186
+ print(f"OpenAI API 錯誤: {e}")
187
+ # 如果 API 呼叫失敗,使用隨機選擇
188
+ recommended = random.sample(potential_books, min(3, len(potential_books)))
189
+ else:
190
+ # 如果沒有 API 金鑰,使用隨機選擇
191
+ recommended = random.sample(potential_books, min(3, len(potential_books)))
192
+
193
+ # 生成推薦理由
194
+ for book in recommended:
195
+ book['recommendation_reason'] = generate_recommendation_reason(
196
+ book, goal, reading_time, challenge
197
+ )
198
+
199
+ return recommended
200
+
201
+ def generate_recommendation_reason(book: Dict, goal: str, reading_time: str, challenge: str) -> str:
202
+ """生成客製化的推薦理由"""
203
+
204
+ if openai.api_key:
205
+ try:
206
+ prompt = f"""
207
+ 請為以下書籍生成一段個性化的推薦理由,考慮用戶的閱讀目標、閱讀時間和當前面臨的挑戰:
208
+
209
+ 書籍資訊:
210
+ - 標題:{book['title']}
211
+ - 作者:{book.get('author', '未知作者')}
212
+ - 類別:{book['category']}
213
+ - 簡介:{book.get('description', '無簡介')}
214
+
215
+ 用戶資訊:
216
+ - 閱讀目標:{goal}
217
+ - 閱讀時間:{reading_time}
218
+ - 目前面臨的挑戰:{challenge}
219
+
220
+ 請生成一段簡短但有說服力的推薦理由,包含以下幾點:
221
+ 1. 為什麼這本書適合用戶的閱讀目標
222
+ 2. 根據用戶的閱讀時間給出閱讀建議
223
+ 3. 這本書如何幫助用戶應對當前的挑戰
224
+ 4. 書中的一個關鍵洞見或亮點
225
+
226
+ 使用友善、專業的語氣,並加入適當的表情符號增加親和力。
227
+ """
228
+
229
+ response = openai.chat.completions.create(
230
+ model="gpt-4o",
231
+ messages=[{"role": "system", "content": "你是一個專業的書籍推薦專家,擅長為讀者找到最適合的書籍並提供個性化的推薦理由。"},
232
+ {"role": "user", "content": prompt}],
233
+ temperature=0.8,
234
+ max_tokens=300
235
+ )
236
+
237
+ return response.choices[0].message.content
238
+
239
+ except Exception as e:
240
+ print(f"生成推薦理由時出錯: {e}")
241
+ # 如果 API 呼叫失敗,使用基本推薦理由
242
+ return generate_basic_recommendation_reason(book, goal, reading_time, challenge)
243
+ else:
244
+ # 如果沒有 API 金鑰,使用基本推薦理由
245
+ return generate_basic_recommendation_reason(book, goal, reading_time, challenge)
246
+
247
+ def generate_basic_recommendation_reason(book: Dict, goal: str, reading_time: str, challenge: str) -> str:
248
+ """生成基本的推薦理由(當 API 不可用時)"""
249
+
250
+ reasons = [
251
+ f"📚 這是一本{book['category']}類型的書籍",
252
+ f"👉 特別適合想要{goal}的讀者",
253
+ ]
254
+
255
+ # 如果有作者資訊,加入作者
256
+ if 'author' in book:
257
+ reasons.insert(1, f"✍️ 作者:{book['author']}")
258
+
259
+ # 根據閱讀時間給建議
260
+ time_suggestions = {
261
+ "1-2天": "這本書結構清晰,適合快速閱讀",
262
+ "3-7天": "可以慢慢品味,每天讀一個章節",
263
+ "2-3週": "建議可以做筆記,深入思考",
264
+ "1個月以上": "這本書內容豐富,值得細細咀嚼"
265
+ }
266
+ reasons.append(f"⏰ {time_suggestions[reading_time]}")
267
+
268
+ # 根據當前挑戰提供建議
269
+ challenge_suggestions = {
270
+ "工作壓力大": "書中提供實用的壓力管理方法",
271
+ "想轉換跑道": "可以幫助你探索新的可能性",
272
+ "需要新技能": "提供紮實的知識基礎",
273
+ "尋找人生方向": "從中獲得人生啟發",
274
+ "情緒管理": "包含許多情緒調節的實用建議"
275
+ }
276
+ reasons.append(f"💡 {challenge_suggestions[challenge]}")
277
+
278
+ return "\n".join(reasons)
279
+
280
+ def recommend(goal: str, reading_time: str, challenge: str, chat_history=None) -> str:
281
+ """主要推薦函數"""
282
+
283
+ # 從聊天歷史中提取用戶偏好
284
+ user_preferences = extract_preferences_from_chat(chat_history) if chat_history else None
285
+
286
+ recommendations = get_book_recommendation(goal, reading_time, challenge, user_preferences)
287
+
288
+ output = "🎯 為你找到以下推薦書籍:\n\n"
289
+
290
+ # 如果有從對話中提取的偏好,先顯示這些洞察
291
+ if user_preferences:
292
+ output += "📝 根據我們的對話,我注意到:\n"
293
+ if user_preferences.get('authors') and len(user_preferences['authors']) > 0:
294
+ output += f"- 你喜歡的作者:{', '.join(user_preferences['authors'])}\n"
295
+ if user_preferences.get('genres') and len(user_preferences['genres']) > 0:
296
+ output += f"- 你偏好的類型:{', '.join(user_preferences['genres'])}\n"
297
+ if user_preferences.get('topics') and len(user_preferences['topics']) > 0:
298
+ output += f"- 你感興趣的主題:{', '.join(user_preferences['topics'])}\n"
299
+ if user_preferences.get('liked_books') and len(user_preferences['liked_books']) > 0:
300
+ output += f"- 你喜歡的書籍:{', '.join(user_preferences['liked_books'])}\n"
301
+ if user_preferences.get('disliked_books') and len(user_preferences['disliked_books']) > 0:
302
+ output += f"- 你不喜歡的書籍:{', '.join(user_preferences['disliked_books'])}\n"
303
+ output += "\n我根據這些偏好和你的回答為你推薦以下書籍:\n\n"
304
+
305
+ for i, book in enumerate(recommendations, 1):
306
+ output += f"📖 推薦書籍 {i}:《{book['title']}》\n\n"
307
+ output += f"{book['recommendation_reason']}\n"
308
+ output += "-------------------\n\n"
309
+
310
+ return output
311
+
312
+ def extract_preferences_from_chat(chat_history):
313
+ """從聊天歷史中提取用戶偏好"""
314
+ if not chat_history or not openai.api_key:
315
+ return None
316
+
317
+ try:
318
+ # 將聊天歷史轉換為文本
319
+ chat_text = ""
320
+ for exchange in chat_history:
321
+ if len(exchange) >= 2: # 確保有用戶訊息和回應
322
+ chat_text += f"用戶: {exchange[0]}\n助手: {exchange[1]}\n\n"
323
+
324
+ # 使用 OpenAI 分析聊天內容,提取用戶偏好
325
+ prompt = f"""
326
+ 請分析以下聊天記錄,提取用戶的閱讀偏好。關注以下幾點:
327
+ 1. 喜愛的作者
328
+ 2. 喜愛的書籍類型/類別
329
+ 3. 感興趣的主題
330
+ 4. 明確提到喜歡的書籍
331
+ 5. 明確提到不喜歡的書籍
332
+
333
+ 聊天記錄:
334
+ {chat_text}
335
+
336
+ 請以JSON格式返回結果,格式如下:
337
+ {{
338
+ "authors": ["作者1", "作者2"],
339
+ "genres": ["類型1", "類型2"],
340
+ "topics": ["主題1", "主題2"],
341
+ "liked_books": ["書名1", "書名2"],
342
+ "disliked_books": ["書名1", "書名2"]
343
+ }}
344
+ 如果某個類別沒有找到相關信息,請提供空列表。
345
+ 只返回JSON,不要有其他文字。
346
+ """
347
+
348
+ response = openai.chat.completions.create(
349
+ model="gpt-4o",
350
+ messages=[
351
+ {"role": "system", "content": "你是一個專業的數據分析師,專門從對話中提取用戶偏好。"},
352
+ {"role": "user", "content": prompt}
353
+ ],
354
+ temperature=0.3
355
+ )
356
+
357
+ # 解析 AI 回應
358
+ ai_response = response.choices[0].message.content
359
+ # 提取 JSON 部分
360
+ import re
361
+ json_match = re.search(r'({.*})', ai_response, re.DOTALL)
362
+ if json_match:
363
+ ai_response = json_match.group(1)
364
+
365
+ preferences = json.loads(ai_response)
366
+ return preferences
367
+
368
+ except Exception as e:
369
+ print(f"從聊天歷史提取偏好時出錯: {e}")
370
+ return None
371
+
372
+ # 聊天機器人相關函數
373
+ def chatbot_response(message, history, goal, reading_time, challenge):
374
+ """處理聊天機器人的回應"""
375
+ # 確保 history 是一個列表
376
+ if history is None:
377
+ history = []
378
+
379
+ # 初始化新的歷史記錄
380
+ new_history = history.copy()
381
+
382
+ # 如果有 OpenAI API 金鑰,使用 OpenAI 生成回應
383
+ if openai.api_key:
384
+ try:
385
+ # 構建對話歷史
386
+ conversation = []
387
+ for h in history:
388
+ conversation.append({"role": "user", "content": h[0]})
389
+ conversation.append({"role": "assistant", "content": h[1]})
390
+
391
+ # 添加當前用戶訊息
392
+ conversation.append({"role": "user", "content": message})
393
+
394
+ # 添加系統提示,讓 AI 更有個性和智能
395
+ system_prompt = f"""你是一個專業且充滿個性的選書助手,名叫「書蟲智慧」。
396
+
397
+ 關於用戶的資訊:
398
+ - 閱讀目標:{goal}
399
+ - 閱讀時間:{reading_time}
400
+ - 目前面臨的挑戰:{challenge}
401
+
402
+ 你的任務:
403
+ 1. 提供個性化的書籍推薦
404
+ 2. 分析用戶的閱讀偏好和習慣
405
+ 3. 主動提問,引導用戶發現新的閱讀興趣
406
+ 4. 分享閱讀技巧和書籍相關知識
407
+ 5. 使用生動活潑的語言,展現你的書蟲個性
408
+
409
+ 你可以:
410
+ - 詢問用戶喜歡的作者或書籍類型
411
+ - 分享書籍的有趣知識和背景
412
+ - 根據用戶的回答調整推薦
413
+ - 提供閱讀建議和技巧
414
+
415
+ 回應應該友善、專業、有趣,並使用繁體中文。如果是初次對話,請自我介紹並詢問用戶的閱讀偏好。"""
416
+
417
+ conversation.insert(0, {"role": "system", "content": system_prompt})
418
+
419
+ # 呼叫 OpenAI API
420
+ api_response = openai.chat.completions.create(
421
+ model="gpt-4o", # 使用更強大的模型
422
+ messages=conversation,
423
+ temperature=0.8, # 增加創意性
424
+ max_tokens=800 # 允許更長的回應
425
+ )
426
+
427
+ response = api_response.choices[0].message.content
428
+ new_history.append([message, response])
429
+
430
+ except Exception as e:
431
+ print(f"OpenAI API 錯誤:{e}")
432
+ # 如果 API 呼叫失敗,使用基本回應
433
+ new_history.append([message, "抱歉,我目前無法連接到我的知識庫。請稍後再試,或者告訴我你喜歡什麼類型的書籍,我會嘗試提供一些基本建議。"])
434
+ else:
435
+ # 如果沒有 API 金鑰,使用基本回應
436
+ new_history.append([message, "我需要連接到我的知識庫才能提供更智能的回應。請確保設置了正確的 API 金鑰。不過,你可以告訴我你喜歡什麼類型的書籍,我會嘗試提供一些基本建議。"])
437
+
438
+ return new_history
439
+
440
+ # 創建Gradio界面
441
+ with gr.Blocks(theme=gr.themes.Soft(), css="footer {display: none !important;}") as demo:
442
+ gr.Markdown("# 📚 選書引導師 BookMatch AI")
443
+ gr.Markdown("## 回答三個簡單問題,讓AI為您推薦最適合的書籍")
444
+
445
+ with gr.Row():
446
+ with gr.Column(scale=1):
447
+ goal = gr.Dropdown(
448
+ choices=list(QUESTIONS["reading_goal"]["options"].keys()),
449
+ label=QUESTIONS["reading_goal"]["question"],
450
+ info="選擇你的閱讀目標"
451
+ )
452
+ reading_time = gr.Radio(
453
+ choices=QUESTIONS["reading_time"]["options"],
454
+ label=QUESTIONS["reading_time"]["question"],
455
+ info="這能幫助我們推薦適合的閱讀材料"
456
+ )
457
+ challenge = gr.Radio(
458
+ choices=QUESTIONS["current_challenge"]["options"],
459
+ label=QUESTIONS["current_challenge"]["question"],
460
+ info="讓我們了解你的需求"
461
+ )
462
+
463
+ gr.Markdown("## 💬 與選書助手對話")
464
+ gr.Markdown("告訴我你讀過哪些書,喜歡什麼類型,我可以給你更個性化的推薦")
465
+
466
+ chatbot = gr.Chatbot(height=300)
467
+ msg = gr.Textbox(label="輸入訊息")
468
+
469
+ with gr.Column(scale=1):
470
+ submit_btn = gr.Button("獲取推薦", variant="primary")
471
+ recommendation_output = gr.Textbox(
472
+ label="推薦結果",
473
+ lines=20
474
+ )
475
+
476
+ submit_btn.click(
477
+ fn=recommend,
478
+ inputs=[goal, reading_time, challenge, chatbot],
479
+ outputs=recommendation_output
480
+ )
481
+
482
+ msg.submit(
483
+ fn=chatbot_response,
484
+ inputs=[msg, chatbot, goal, reading_time, challenge],
485
+ outputs=chatbot
486
+ ).then(
487
+ lambda x: "",
488
+ inputs=msg,
489
+ outputs=msg
490
+ )
491
+
492
+ if __name__ == "__main__":
493
+ demo.launch(share=True)
data/books.json ADDED
@@ -0,0 +1,510 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "books": [
3
+ {
4
+ "id": 1,
5
+ "title": "挪威的森林",
6
+ "author": "村上春樹",
7
+ "year": 1987,
8
+ "category": "小說"
9
+ },
10
+ {
11
+ "id": 2,
12
+ "title": "動物農莊",
13
+ "author": "喬治·歐威爾",
14
+ "year": 1945,
15
+ "category": "寓言小說"
16
+ },
17
+ {
18
+ "id": 3,
19
+ "title": "百年孤寂",
20
+ "author": "加西亞·馬奎斯",
21
+ "year": 1967,
22
+ "category": "魔幻寫實主義"
23
+ },
24
+ {
25
+ "id": 4,
26
+ "title": "流量新紅利時代:商業模式決定變現之道",
27
+ "category": "商業管理"
28
+ },
29
+ {
30
+ "id": 5,
31
+ "title": "50則非知不可的課程學概念",
32
+ "category": "教育"
33
+ },
34
+ {
35
+ "id": 6,
36
+ "title": "扳動轉轍器前的思考",
37
+ "category": "思考方法"
38
+ },
39
+ {
40
+ "id": 7,
41
+ "title": "寫作,是最好的自我投資",
42
+ "category": "寫作"
43
+ },
44
+ {
45
+ "id": 8,
46
+ "title": "創新者們_掀起數位革命的天才、怪傑和駭客",
47
+ "category": "創新科技"
48
+ },
49
+ {
50
+ "id": 9,
51
+ "title": "閱讀理解vol.20",
52
+ "category": "教育"
53
+ },
54
+ {
55
+ "id": 10,
56
+ "title": "閱讀理解vol.21",
57
+ "category": "教育"
58
+ },
59
+ {
60
+ "id": 11,
61
+ "title": "未來Family(44)",
62
+ "category": "教育"
63
+ },
64
+ {
65
+ "id": 12,
66
+ "title": "未來少年(99)",
67
+ "category": "教育"
68
+ },
69
+ {
70
+ "id": 13,
71
+ "title": "未來兒童(60)",
72
+ "category": "教育"
73
+ },
74
+ {
75
+ "id": 14,
76
+ "title": "為什麼Google、LinkedIn、波音、高通、迪士尼都找他合作? 募資提案教父1週談成6千萬的快‧精‧準攻心術",
77
+ "category": "商業管理"
78
+ },
79
+ {
80
+ "id": 15,
81
+ "title": "重新想像AI+HI智能革命下的商業與變革",
82
+ "category": "科技創新"
83
+ },
84
+ {
85
+ "id": 16,
86
+ "title": "恰如其分的自尊",
87
+ "category": "心理勵志"
88
+ },
89
+ {
90
+ "id": 17,
91
+ "title": "好好存在:ㄧ位心理學家的療癒書寫",
92
+ "category": "心理勵志"
93
+ },
94
+ {
95
+ "id": 18,
96
+ "title": "不懂程式也能學會的大數據分析術 - 使用 RapidMiner",
97
+ "category": "科技創新"
98
+ },
99
+ {
100
+ "id": 19,
101
+ "title": "厭世講堂:顛覆人生的十堂莊子課",
102
+ "category": "哲學"
103
+ },
104
+ {
105
+ "id": 20,
106
+ "title": "閱讀素養:黃國珍的閱讀理解課,從訊息到意義,帶你讀出深度思考力",
107
+ "category": "教育"
108
+ },
109
+ {
110
+ "id": 21,
111
+ "title": "AI人工智慧的現在.未來進行式",
112
+ "category": "科技創新"
113
+ },
114
+ {
115
+ "id": 22,
116
+ "title": "認知設計:提升學習體驗的藝術",
117
+ "category": "教育"
118
+ },
119
+ {
120
+ "id": 23,
121
+ "title": "動機革命:寫給不想為了錢工作的世代",
122
+ "category": "職涯發展"
123
+ },
124
+ {
125
+ "id": 24,
126
+ "title": "未來少年 第100期",
127
+ "category": "教育"
128
+ },
129
+ {
130
+ "id": 25,
131
+ "title": "未來兒童 第61期",
132
+ "category": "教育"
133
+ },
134
+ {
135
+ "id": 26,
136
+ "title": "國家為什麼會失敗:權力、富裕與貧困的根源",
137
+ "category": "社會科學"
138
+ },
139
+ {
140
+ "id": 27,
141
+ "title": "解決問題的商業框架圖鑑:七大類工作場景 ╳ 70款框架,改善企畫提案、執行力、組織管理效率,精準解決問題全圖解",
142
+ "category": "商業管理"
143
+ },
144
+ {
145
+ "id": 28,
146
+ "title": "人生勝利聖經:向100位世界強者學習健康、財富和人生智慧",
147
+ "category": "心理勵志"
148
+ },
149
+ {
150
+ "id": 29,
151
+ "title": "不對稱陷阱",
152
+ "category": "商業管理"
153
+ },
154
+ {
155
+ "id": 30,
156
+ "title": "鳳凰專案:看IT部門如何讓公司從谷底翻身的傳奇故事",
157
+ "category": "科技創新"
158
+ },
159
+ {
160
+ "id": 31,
161
+ "title": "巨人的工具:健康、財富與智慧自助寶典",
162
+ "category": "心理勵志"
163
+ },
164
+ {
165
+ "id": 32,
166
+ "title": "好人總是自以為是:政治與宗教如何將我們四分五裂",
167
+ "category": "社會科學"
168
+ },
169
+ {
170
+ "id": 33,
171
+ "title": "逆風台灣-民主開放崎嶇路我們一起走過",
172
+ "category": "社會科學"
173
+ },
174
+ {
175
+ "id": 34,
176
+ "title": "OKR:做最重要的事",
177
+ "category": "商業管理"
178
+ },
179
+ {
180
+ "id": 35,
181
+ "title": "學習的26種方法",
182
+ "category": "教育"
183
+ },
184
+ {
185
+ "id": 36,
186
+ "title": "BTS紅遍全球的商業內幕:穩抓1%的客群,符合消費者期待,在市場變熱時進場,把自己變成平台!",
187
+ "category": "商業管理"
188
+ },
189
+ {
190
+ "id": 37,
191
+ "title": "愛分享 vol.188",
192
+ "category": "生活"
193
+ },
194
+ {
195
+ "id": 38,
196
+ "title": "未來少年 第101期",
197
+ "category": "教育"
198
+ },
199
+ {
200
+ "id": 39,
201
+ "title": "未來兒童 第62期",
202
+ "category": "教育"
203
+ },
204
+ {
205
+ "id": 40,
206
+ "title": "未來family 第45期",
207
+ "category": "教育"
208
+ },
209
+ {
210
+ "id": 41,
211
+ "title": "其實,你正在做商業開發:5個解決商業挑戰的行動方案",
212
+ "category": "商業管理"
213
+ },
214
+ {
215
+ "id": 42,
216
+ "title": "共好與同贏: 哈佛快樂專家教你把個人潛力變成集體能力,擴散成功與快樂的傳染力",
217
+ "category": "心理勵志"
218
+ },
219
+ {
220
+ "id": 43,
221
+ "title": "矽谷最夯‧產品專案管理全書:專案管理大師教你用可實踐的流程打造人人都喜歡的產品",
222
+ "category": "商業管理"
223
+ },
224
+ {
225
+ "id": 44,
226
+ "title": "故事課1:3分鐘說18萬個故事,打造影響力",
227
+ "category": "溝通表達"
228
+ },
229
+ {
230
+ "id": 45,
231
+ "title": "大數據的關鍵思考",
232
+ "category": "科技創新"
233
+ },
234
+ {
235
+ "id": 46,
236
+ "title": "幫助每一個孩子成功",
237
+ "category": "教育"
238
+ },
239
+ {
240
+ "id": 47,
241
+ "title": "別再開會開到死",
242
+ "category": "商業管理"
243
+ },
244
+ {
245
+ "id": 48,
246
+ "title": "不必為悲傷感到抱歉",
247
+ "category": "心理勵志"
248
+ },
249
+ {
250
+ "id": 49,
251
+ "title": "不孤單,一起走",
252
+ "category": "心理勵志"
253
+ },
254
+ {
255
+ "id": 50,
256
+ "title": "餐桌上的家鄉",
257
+ "category": "生活"
258
+ },
259
+ {
260
+ "id": 51,
261
+ "title": "操作介面設計模式",
262
+ "category": "設計"
263
+ },
264
+ {
265
+ "id": 52,
266
+ "title": "曾國藩的正面與側面",
267
+ "category": "歷史傳記"
268
+ },
269
+ {
270
+ "id": 53,
271
+ "title": "茶水間的數學",
272
+ "category": "教育"
273
+ },
274
+ {
275
+ "id": 54,
276
+ "title": "產品路線圖",
277
+ "category": "商業管理"
278
+ },
279
+ {
280
+ "id": 55,
281
+ "title": "超級好! 用遊戲打倒生命裡的壞東西",
282
+ "category": "心理勵志"
283
+ },
284
+ {
285
+ "id": 56,
286
+ "title": "超級用戶時代",
287
+ "category": "商業管理"
288
+ },
289
+ {
290
+ "id": 57,
291
+ "title": "超越邏輯的情緒說服",
292
+ "category": "溝通表達"
293
+ },
294
+ {
295
+ "id": 58,
296
+ "title": "成大事者不糾結",
297
+ "category": "心理勵志"
298
+ },
299
+ {
300
+ "id": 59,
301
+ "title": "成為這樣的我:蜜雪兒·歐巴馬",
302
+ "category": "歷史傳記"
303
+ },
304
+ {
305
+ "id": 60,
306
+ "title": "成長型數學思維",
307
+ "category": "教育"
308
+ },
309
+ {
310
+ "id": 61,
311
+ "title": "誠品時光",
312
+ "category": "商業管理"
313
+ },
314
+ {
315
+ "id": 62,
316
+ "title": "創新的用途理論",
317
+ "category": "創新科技"
318
+ },
319
+ {
320
+ "id": 63,
321
+ "title": "創新人生",
322
+ "category": "創新科技"
323
+ },
324
+ {
325
+ "id": 64,
326
+ "title": "創意力",
327
+ "category": "創新科技"
328
+ },
329
+ {
330
+ "id": 65,
331
+ "title": "促進理解的認知學習",
332
+ "category": "教育"
333
+ },
334
+ {
335
+ "id": 66,
336
+ "title": "搭配詞的力量",
337
+ "category": "語言學習"
338
+ },
339
+ {
340
+ "id": 67,
341
+ "title": "大腦當家",
342
+ "category": "科學"
343
+ },
344
+ {
345
+ "id": 68,
346
+ "title": "大腦的秘密檔案",
347
+ "category": "科學"
348
+ },
349
+ {
350
+ "id": 69,
351
+ "title": "大腦喜歡這樣學",
352
+ "category": "教育"
353
+ },
354
+ {
355
+ "id": 70,
356
+ "title": "大自然的獵人",
357
+ "category": "自然科學"
358
+ },
359
+ {
360
+ "id": 71,
361
+ "title": "等候室",
362
+ "category": "文學"
363
+ },
364
+ {
365
+ "id": 72,
366
+ "title": "帝國的惆悵",
367
+ "category": "歷史"
368
+ },
369
+ {
370
+ "id": 73,
371
+ "title": "帝國的終結",
372
+ "category": "歷史"
373
+ },
374
+ {
375
+ "id": 74,
376
+ "title": "第8個習慣",
377
+ "category": "心理勵志"
378
+ },
379
+ {
380
+ "id": 75,
381
+ "title": "第二次機器時代",
382
+ "category": "科技創新"
383
+ },
384
+ {
385
+ "id": 76,
386
+ "title": "杜拉克談高效能的5個習慣",
387
+ "category": "商業管理"
388
+ },
389
+ {
390
+ "id": 77,
391
+ "title": "發現我的天才",
392
+ "category": "心理勵志"
393
+ },
394
+ {
395
+ "id": 78,
396
+ "title": "費城風雲",
397
+ "category": "歷史"
398
+ },
399
+ {
400
+ "id": 79,
401
+ "title": "改變從心",
402
+ "category": "心理勵志"
403
+ },
404
+ {
405
+ "id": 80,
406
+ "title": "感動,敢動【分組教學實戰手冊】",
407
+ "category": "教育"
408
+ },
409
+ {
410
+ "id": 81,
411
+ "title": "鋼鐵人馬斯克",
412
+ "category": "歷史傳記"
413
+ },
414
+ {
415
+ "id": 82,
416
+ "title": "高等會計學",
417
+ "category": "商業管理"
418
+ },
419
+ {
420
+ "id": 83,
421
+ "title": "高手思維",
422
+ "category": "心理勵志"
423
+ },
424
+ {
425
+ "id": 84,
426
+ "title": "格格不入的人生宣言",
427
+ "category": "心理勵志"
428
+ },
429
+ {
430
+ "id": 85,
431
+ "title": "給力",
432
+ "category": "心理勵志"
433
+ },
434
+ {
435
+ "id": 86,
436
+ "title": "給逆境中的你",
437
+ "category": "心理勵志"
438
+ },
439
+ {
440
+ "id": 87,
441
+ "title": "跟著哈佛修練職場好關係",
442
+ "category": "職涯發展"
443
+ },
444
+ {
445
+ "id": 88,
446
+ "title": "共好一備子【共備實務分享手冊】",
447
+ "category": "教育"
448
+ },
449
+ {
450
+ "id": 89,
451
+ "title": "穀倉效應",
452
+ "category": "社會科學"
453
+ },
454
+ {
455
+ "id": 90,
456
+ "title": "故事課1",
457
+ "category": "溝通表達"
458
+ },
459
+ {
460
+ "id": 91,
461
+ "title": "故事課2",
462
+ "category": "溝通表達"
463
+ },
464
+ {
465
+ "id": 92,
466
+ "title": "孩子的另一扇眼睛",
467
+ "category": "教育"
468
+ },
469
+ {
470
+ "id": 93,
471
+ "title": "孩子如何成功",
472
+ "category": "教育"
473
+ },
474
+ {
475
+ "id": 94,
476
+ "title": "好好說話",
477
+ "category": "溝通表達"
478
+ },
479
+ {
480
+ "id": 95,
481
+ "title": "核心問題-開啟學生理解之門",
482
+ "category": "教育"
483
+ },
484
+ {
485
+ "id": 96,
486
+ "title": "盒內思考--有效創新的簡單法則",
487
+ "category": "創新科技"
488
+ },
489
+ {
490
+ "id": 97,
491
+ "title": "懷中天使",
492
+ "category": "文學"
493
+ },
494
+ {
495
+ "id": 98,
496
+ "title": "會計學新論",
497
+ "category": "商業管理"
498
+ },
499
+ {
500
+ "id": 99,
501
+ "title": "賈伯斯傳",
502
+ "category": "歷史傳記"
503
+ },
504
+ {
505
+ "id": 100,
506
+ "title": "教的少學更多",
507
+ "category": "教育"
508
+ }
509
+ ]
510
+ }
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ gradio==5.23.3