SarahXia0405 commited on
Commit
aeec0a9
·
verified ·
1 Parent(s): 10d0ace

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +245 -17
app.py CHANGED
@@ -63,6 +63,14 @@ LEARNING_MODE_INSTRUCTIONS = {
63
  ),
64
  }
65
 
 
 
 
 
 
 
 
 
66
  # ---------- Clare 的基础 System Prompt ----------
67
  CLARE_SYSTEM_PROMPT = """
68
  You are Clare, an AI teaching assistant for Hanbridge University.
@@ -106,13 +114,40 @@ def parse_syllabus_docx(file_path: str, max_lines: int = 15) -> List[str]:
106
  return topics
107
 
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  # ---------- 构建 messages ----------
110
  def build_messages(
111
  user_message: str,
112
  history: List[Tuple[str, str]],
113
  language_preference: str,
114
  learning_mode: str,
 
115
  course_outline: Optional[List[str]],
 
116
  ) -> List[Dict[str, str]]:
117
  messages: List[Dict[str, str]] = [
118
  {"role": "system", "content": CLARE_SYSTEM_PROMPT}
@@ -142,6 +177,32 @@ def build_messages(
142
  }
143
  )
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  # 语言偏好控制
146
  if language_preference == "English":
147
  messages.append(
@@ -169,7 +230,9 @@ def chat_with_clare(
169
  model_name: str,
170
  language_preference: str,
171
  learning_mode: str,
 
172
  course_outline: Optional[List[str]],
 
173
  ):
174
  try:
175
  messages = build_messages(
@@ -177,7 +240,9 @@ def chat_with_clare(
177
  history=history,
178
  language_preference=language_preference,
179
  learning_mode=learning_mode,
 
180
  course_outline=course_outline,
 
181
  )
182
  response = client.chat.completions.create(
183
  model=model_name or DEFAULT_MODEL,
@@ -192,6 +257,98 @@ def chat_with_clare(
192
  return answer, history
193
 
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  # ---------- Gradio UI ----------
196
  with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
197
  gr.Markdown(
@@ -202,7 +359,7 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
202
  - Ask in English → Clare answers in English.
203
  - Ask in Chinese → Clare can answer in Chinese.
204
  - Use different **learning modes** to change Clare's teaching style.
205
- - Optionally upload your **course syllabus (.docx)** so Clare stays aligned with your course.
206
  """
207
  )
208
 
@@ -225,21 +382,32 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
225
 
226
  with gr.Row():
227
  syllabus_file = gr.File(
228
- label="Upload course syllabus (.docx)",
229
  file_types=[".docx"],
230
  )
231
- course_outline_state = gr.State(DEFAULT_COURSE_TOPICS)
 
 
 
 
 
 
 
 
232
 
233
- # syllabus 上传后更新课程大纲
234
- def update_outline(file):
235
  if file is None:
236
  return DEFAULT_COURSE_TOPICS
237
- topics = parse_syllabus_docx(file.name)
238
- return topics
 
 
 
239
 
240
  syllabus_file.change(
241
  fn=update_outline,
242
- inputs=[syllabus_file],
243
  outputs=[course_outline_state],
244
  )
245
 
@@ -251,37 +419,97 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
251
  label="Your question",
252
  placeholder="Ask Clare anything about your course, assignment, or study plan...",
253
  )
254
- clear_btn = gr.Button("Reset conversation")
255
 
256
- # 注意这里显式把组件的当前值传进来,而不是 .value
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  def respond(
258
  message,
259
  chat_history,
260
  course_outline,
 
261
  model_name_val,
262
  language_pref_val,
263
  learning_mode_val,
 
264
  ):
 
 
265
  answer, new_history = chat_with_clare(
266
  message=message,
267
  history=chat_history,
268
  model_name=model_name_val,
269
  language_preference=language_pref_val,
270
  learning_mode=learning_mode_val,
 
271
  course_outline=course_outline,
 
272
  )
273
- return "", new_history
274
 
275
  user_input.submit(
276
  respond,
277
- [user_input, chatbot, course_outline_state, model_name, language_preference, learning_mode],
278
- [user_input, chatbot],
 
 
 
 
 
 
 
 
 
279
  )
280
 
281
- def clear_chat():
282
- return []
 
 
 
 
 
 
 
 
283
 
284
- clear_btn.click(clear_chat, None, chatbot, queue=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
  if __name__ == "__main__":
287
- demo.launch()
 
63
  ),
64
  }
65
 
66
+ # ---------- 上传文件类型 ----------
67
+ DOC_TYPES = [
68
+ "Syllabus",
69
+ "Lecture Slides / PPT",
70
+ "Literature Review / Paper",
71
+ "Other Course Document",
72
+ ]
73
+
74
  # ---------- Clare 的基础 System Prompt ----------
75
  CLARE_SYSTEM_PROMPT = """
76
  You are Clare, an AI teaching assistant for Hanbridge University.
 
114
  return topics
115
 
116
 
117
+ # ---------- 简单“弱项”检测 ----------
118
+ WEAKNESS_KEYWORDS = [
119
+ "don't understand",
120
+ "do not understand",
121
+ "not understand",
122
+ "not sure",
123
+ "confused",
124
+ "hard to",
125
+ "difficult",
126
+ "struggle",
127
+ "不会",
128
+ "不懂",
129
+ "看不懂",
130
+ "搞不清",
131
+ "很难",
132
+ ]
133
+
134
+ def update_weaknesses_from_message(message: str, weaknesses: List[str]) -> List[str]:
135
+ lower_msg = message.lower()
136
+ if any(k in lower_msg for k in WEAKNESS_KEYWORDS):
137
+ weaknesses = weaknesses or []
138
+ weaknesses.append(message)
139
+ return weaknesses
140
+
141
+
142
  # ---------- 构建 messages ----------
143
  def build_messages(
144
  user_message: str,
145
  history: List[Tuple[str, str]],
146
  language_preference: str,
147
  learning_mode: str,
148
+ doc_type: str,
149
  course_outline: Optional[List[str]],
150
+ weaknesses: Optional[List[str]],
151
  ) -> List[Dict[str, str]]:
152
  messages: List[Dict[str, str]] = [
153
  {"role": "system", "content": CLARE_SYSTEM_PROMPT}
 
177
  }
178
  )
179
 
180
+ # 上传文件类型提示(仅作语境)
181
+ if doc_type and doc_type != "Syllabus":
182
+ messages.append(
183
+ {
184
+ "role": "system",
185
+ "content": (
186
+ f"The student also uploaded a {doc_type} document as supporting material. "
187
+ "You do not see the full content directly, but you may assume it is relevant "
188
+ "to the same course and topics."
189
+ ),
190
+ }
191
+ )
192
+
193
+ # 学生弱项提示(会话内记忆)
194
+ if weaknesses:
195
+ weak_text = " | ".join(weaknesses[-5:]) # 最近几条即可
196
+ messages.append(
197
+ {
198
+ "role": "system",
199
+ "content": (
200
+ "The student seems to struggle with the following questions or topics. "
201
+ "Be extra gentle and clear when these appear: " + weak_text
202
+ ),
203
+ }
204
+ )
205
+
206
  # 语言偏好控制
207
  if language_preference == "English":
208
  messages.append(
 
230
  model_name: str,
231
  language_preference: str,
232
  learning_mode: str,
233
+ doc_type: str,
234
  course_outline: Optional[List[str]],
235
+ weaknesses: Optional[List[str]],
236
  ):
237
  try:
238
  messages = build_messages(
 
240
  history=history,
241
  language_preference=language_preference,
242
  learning_mode=learning_mode,
243
+ doc_type=doc_type,
244
  course_outline=course_outline,
245
+ weaknesses=weaknesses,
246
  )
247
  response = client.chat.completions.create(
248
  model=model_name or DEFAULT_MODEL,
 
257
  return answer, history
258
 
259
 
260
+ # ---------- 导出对话为 Markdown ----------
261
+ def export_conversation(
262
+ history: List[Tuple[str, str]],
263
+ course_outline: List[str],
264
+ learning_mode_val: str,
265
+ weaknesses: List[str],
266
+ ) -> str:
267
+ lines: List[str] = []
268
+ lines.append("# Clare – Conversation Export\n")
269
+ lines.append(f"- Learning mode: **{learning_mode_val}**\n")
270
+ lines.append("- Course topics (short): " + "; ".join(course_outline[:5]) + "\n")
271
+
272
+ if weaknesses:
273
+ lines.append("- Observed student difficulties:\n")
274
+ for w in weaknesses[-5:]:
275
+ lines.append(f" - {w}\n")
276
+ lines.append("\n---\n\n")
277
+
278
+ for user, assistant in history:
279
+ lines.append(f"**Student:** {user}\n\n")
280
+ lines.append(f"**Clare:** {assistant}\n\n")
281
+ lines.append("---\n\n")
282
+
283
+ return "".join(lines)
284
+
285
+
286
+ # ---------- 生成 3 个 quiz 题目 ----------
287
+ def generate_quiz_from_history(
288
+ history: List[Tuple[str, str]],
289
+ course_outline: List[str],
290
+ weaknesses: List[str],
291
+ model_name: str,
292
+ language_preference: str,
293
+ ) -> str:
294
+ conversation_text = ""
295
+ for user, assistant in history[-8:]: # 用最近几轮
296
+ conversation_text += f"Student: {user}\nClare: {assistant}\n"
297
+
298
+ topics_text = "; ".join(course_outline[:8])
299
+ weakness_text = "; ".join(weaknesses[-5:]) if weaknesses else "N/A"
300
+
301
+ messages = [
302
+ {"role": "system", "content": CLARE_SYSTEM_PROMPT},
303
+ {
304
+ "role": "system",
305
+ "content": (
306
+ "Now your task is to create a **short concept quiz** for the student. "
307
+ "Based on the conversation and course topics, generate **3 questions** "
308
+ "(a mix of multiple-choice and short-answer is fine). After listing the "
309
+ "questions, provide an answer key at the end under a heading 'Answer Key'. "
310
+ "Number the questions Q1, Q2, Q3."
311
+ ),
312
+ },
313
+ {
314
+ "role": "system",
315
+ "content": f"Course topics: {topics_text}",
316
+ },
317
+ {
318
+ "role": "system",
319
+ "content": f"Student known difficulties: {weakness_text}",
320
+ },
321
+ {
322
+ "role": "user",
323
+ "content": (
324
+ "Here is the recent conversation between you and the student:\n\n"
325
+ + conversation_text
326
+ + "\n\nPlease create the quiz now."
327
+ ),
328
+ },
329
+ ]
330
+
331
+ if language_preference == "中文":
332
+ messages.append(
333
+ {
334
+ "role": "system",
335
+ "content": "请用中文给出问题和答案。",
336
+ }
337
+ )
338
+
339
+ try:
340
+ response = client.chat.completions.create(
341
+ model=model_name or DEFAULT_MODEL,
342
+ messages=messages,
343
+ temperature=0.5,
344
+ )
345
+ quiz_text = response.choices[0].message.content
346
+ except Exception as e:
347
+ quiz_text = f"⚠️ Error generating quiz: {e}"
348
+
349
+ return quiz_text
350
+
351
+
352
  # ---------- Gradio UI ----------
353
  with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
354
  gr.Markdown(
 
359
  - Ask in English → Clare answers in English.
360
  - Ask in Chinese → Clare can answer in Chinese.
361
  - Use different **learning modes** to change Clare's teaching style.
362
+ - Optionally upload your **course syllabus / slides / literature (.docx)** so Clare stays aligned with your course.
363
  """
364
  )
365
 
 
382
 
383
  with gr.Row():
384
  syllabus_file = gr.File(
385
+ label="Upload course file (.docx)",
386
  file_types=[".docx"],
387
  )
388
+ doc_type = gr.Dropdown(
389
+ choices=DOC_TYPES,
390
+ value="Syllabus",
391
+ label="File type",
392
+ )
393
+
394
+ # 状态:课程大纲 + 学生弱项
395
+ course_outline_state = gr.State(DEFAULT_COURSE_TOPICS)
396
+ weakness_state = gr.State([])
397
 
398
+ # syllabus 上传后更新课程大纲(仅当类型是 Syllabus 时解析)
399
+ def update_outline(file, doc_type_val):
400
  if file is None:
401
  return DEFAULT_COURSE_TOPICS
402
+ if doc_type_val == "Syllabus" and file.name.lower().endswith(".docx"):
403
+ topics = parse_syllabus_docx(file.name)
404
+ return topics
405
+ # 其他类型文件目前不解析,只保留默认大纲
406
+ return DEFAULT_COURSE_TOPICS
407
 
408
  syllabus_file.change(
409
  fn=update_outline,
410
+ inputs=[syllabus_file, doc_type],
411
  outputs=[course_outline_state],
412
  )
413
 
 
419
  label="Your question",
420
  placeholder="Ask Clare anything about your course, assignment, or study plan...",
421
  )
 
422
 
423
+ with gr.Row():
424
+ clear_btn = gr.Button("Reset conversation")
425
+ export_btn = gr.Button("Export conversation")
426
+ quiz_btn = gr.Button("Generate 3 quiz questions")
427
+
428
+ export_box = gr.Textbox(
429
+ label="Conversation export (Markdown)",
430
+ lines=10,
431
+ )
432
+ quiz_box = gr.Textbox(
433
+ label="Generated quiz (with answer key)",
434
+ lines=10,
435
+ )
436
+
437
+ # 主对话逻辑:更新弱项 + 调用 Clare
438
  def respond(
439
  message,
440
  chat_history,
441
  course_outline,
442
+ weaknesses,
443
  model_name_val,
444
  language_pref_val,
445
  learning_mode_val,
446
+ doc_type_val,
447
  ):
448
+ # 更新弱项记忆
449
+ weaknesses = update_weaknesses_from_message(message, weaknesses or [])
450
  answer, new_history = chat_with_clare(
451
  message=message,
452
  history=chat_history,
453
  model_name=model_name_val,
454
  language_preference=language_pref_val,
455
  learning_mode=learning_mode_val,
456
+ doc_type=doc_type_val,
457
  course_outline=course_outline,
458
+ weaknesses=weaknesses,
459
  )
460
+ return "", new_history, weaknesses
461
 
462
  user_input.submit(
463
  respond,
464
+ [
465
+ user_input,
466
+ chatbot,
467
+ course_outline_state,
468
+ weakness_state,
469
+ model_name,
470
+ language_preference,
471
+ learning_mode,
472
+ doc_type,
473
+ ],
474
+ [user_input, chatbot, weakness_state],
475
  )
476
 
477
+ # 清空对话 & 弱项 & 导出/quiz
478
+ def clear_all():
479
+ return [], [], "", ""
480
+
481
+ clear_btn.click(
482
+ clear_all,
483
+ None,
484
+ [chatbot, weakness_state, export_box, quiz_box],
485
+ queue=False,
486
+ )
487
 
488
+ # 导出对话按钮
489
+ def on_export(chat_history, course_outline, learning_mode_val, weaknesses):
490
+ return export_conversation(chat_history, course_outline, learning_mode_val, weaknesses or [])
491
+
492
+ export_btn.click(
493
+ on_export,
494
+ [chatbot, course_outline_state, learning_mode, weakness_state],
495
+ [export_box],
496
+ )
497
+
498
+ # 生成 quiz 按钮
499
+ def on_quiz(chat_history, course_outline, weaknesses, model_name_val, language_pref_val):
500
+ return generate_quiz_from_history(
501
+ chat_history,
502
+ course_outline,
503
+ weaknesses or [],
504
+ model_name_val,
505
+ language_pref_val,
506
+ )
507
+
508
+ quiz_btn.click(
509
+ on_quiz,
510
+ [chatbot, course_outline_state, weakness_state, model_name, language_preference],
511
+ [quiz_box],
512
+ )
513
 
514
  if __name__ == "__main__":
515
+ demo.launch()