SarahXia0405 commited on
Commit
a5c6d3f
·
verified ·
1 Parent(s): 280edc8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +159 -44
app.py CHANGED
@@ -1,83 +1,161 @@
1
  import os
2
- from typing import List, Dict, Tuple
3
 
4
  import gradio as gr
5
  from openai import OpenAI
 
6
 
7
- # ---------- 读取 API Key ----------
8
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
9
  if not OPENAI_API_KEY:
10
  raise RuntimeError(
11
- "OPENAI_API_KEY is not set. Please go to Settings → Variables and secrets and add it."
12
  )
13
 
14
  client = OpenAI(api_key=OPENAI_API_KEY)
15
-
16
- # 你可以按需要换成 gpt-4.1, gpt-4o, gpt-4.1-nano 等
17
  DEFAULT_MODEL = "gpt-4.1-mini"
18
 
19
- # ---------- Clare 助教系统设定 ----------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  CLARE_SYSTEM_PROMPT = """
21
  You are Clare, an AI teaching assistant for Hanbridge University.
22
 
23
  Core identity:
24
  - You are patient, encouraging, and structured like a very good TA.
25
  - Your UI and responses should be in ENGLISH by default.
26
- - However, you can understand BOTH English and Chinese questions, and you may reply in Chinese if the user asks you to or is clearly more comfortable in Chinese.
 
27
 
28
- Your main jobs:
29
  1. Help students understand course concepts step by step.
30
  2. Ask short check-up questions to confirm understanding instead of giving huge long lectures.
31
- 3. When the student is confused, break content into smaller chunks and use simple language first.
32
  4. When the student is advanced, you can switch to more technical explanations.
33
 
34
- Teaching style:
35
- - Prefer short paragraphs and bullet points.
36
- - Use concrete examples and analogies.
37
- - Frequently summarize: “So far, we have …”
38
- - When appropriate, give simple practice questions or mini-exercises.
39
- - If the user asks something outside the course, you can still help, but be honest about uncertainty.
40
-
41
  Safety and honesty:
42
  - If you don’t know, say you are not sure and suggest how to verify.
43
  - Do not fabricate references, exam answers, or grades.
44
  """
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
 
 
47
  def build_messages(
48
  user_message: str,
49
  history: List[Tuple[str, str]],
50
  language_preference: str,
 
 
51
  ) -> List[Dict[str, str]]:
52
- """
53
- 把历史对话 + 当前问题 转成 OpenAI Chat API 需要的 messages 列表。
54
- history: List of (user, assistant)
55
- """
56
- messages: List[Dict[str, str]] = [{"role": "system", "content": CLARE_SYSTEM_PROMPT}]
57
 
58
- # 语言偏好控制(英文/中文/自动)
59
- if language_preference == "English":
 
60
  messages.append(
61
  {
62
  "role": "system",
63
- "content": "Please answer in English.",
64
  }
65
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  elif language_preference == "中文":
67
  messages.append(
68
- {
69
- "role": "system",
70
- "content": "请你用中文回答学生的问题。",
71
- }
72
  )
73
 
74
- # 历史对话加进去
75
  for user, assistant in history:
76
  messages.append({"role": "user", "content": user})
77
  if assistant is not None:
78
  messages.append({"role": "assistant", "content": assistant})
79
 
80
- # 当前这一轮的问题
81
  messages.append({"role": "user", "content": user_message})
82
  return messages
83
 
@@ -87,12 +165,17 @@ def chat_with_clare(
87
  history: List[Tuple[str, str]],
88
  model_name: str,
89
  language_preference: str,
 
 
90
  ):
91
- """
92
- Gradio 调用的主函数:输入当前 message + 历史 history,返回 Clare 的回答和新的 history。
93
- """
94
  try:
95
- messages = build_messages(message, history, language_preference)
 
 
 
 
 
 
96
  response = client.chat.completions.create(
97
  model=model_name or DEFAULT_MODEL,
98
  messages=messages,
@@ -102,7 +185,6 @@ def chat_with_clare(
102
  except Exception as e:
103
  answer = f"⚠️ Error talking to the model: {e}"
104
 
105
- # Gradio 需要返回 (回答, 更新后的 history)
106
  history = history + [(message, answer)]
107
  return answer, history
108
 
@@ -116,7 +198,8 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
116
 
117
  - Ask in English → Clare answers in English.
118
  - Ask in Chinese → Clare can answer in Chinese.
119
- - Use it as your study companion for courses, assignments, and exam review.
 
120
  """
121
  )
122
 
@@ -131,6 +214,30 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
131
  value="Auto",
132
  label="Preferred answer language",
133
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  chatbot = gr.Chatbot(
136
  label="Clare Chat",
@@ -142,19 +249,27 @@ with gr.Blocks(title="Clare – Hanbridge AI Teaching Assistant") as demo:
142
  )
143
  clear_btn = gr.Button("Reset conversation")
144
 
145
- # 使用 ChatInterface 的逻辑:手写一个简单的 handler
146
- def respond(message, chat_history):
147
  answer, new_history = chat_with_clare(
148
- message,
149
- chat_history,
150
  model_name=model_name.value,
151
  language_preference=language_preference.value,
 
 
152
  )
153
- return "", new_history # 文本框清空 + 更新 history
 
 
 
 
 
 
 
 
 
154
 
155
- user_input.submit(respond, [user_input, chatbot], [user_input, chatbot])
156
- clear_btn.click(lambda: None, None, chatbot, queue=False)
157
 
158
- # Gradio 启动
159
  if __name__ == "__main__":
160
  demo.launch()
 
1
  import os
2
+ from typing import List, Dict, Tuple, Optional
3
 
4
  import gradio as gr
5
  from openai import OpenAI
6
+ from docx import Document
7
 
8
+ # ---------- 环境变量 ----------
9
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
10
  if not OPENAI_API_KEY:
11
  raise RuntimeError(
12
+ "OPENAI_API_KEY is not set. Please go to Settings → Secrets and add it."
13
  )
14
 
15
  client = OpenAI(api_key=OPENAI_API_KEY)
 
 
16
  DEFAULT_MODEL = "gpt-4.1-mini"
17
 
18
+ # ---------- 默认 GenAI 课程大纲(来自你周表,稍作改写) ----------
19
+ DEFAULT_COURSE_TOPICS = [
20
+ "Week 0 – Welcome & What is Generative AI; course outcomes LO1–LO5.",
21
+ "Week 1 – Foundations of GenAI: LLMs, Transformer & self-attention, perplexity.",
22
+ "Week 2 – Foundation Models & multimodal models; data scale, bias & risks.",
23
+ "Week 3 – Choosing Pre-trained Models; open-source vs proprietary; cost vs quality.",
24
+ "Week 4 – Prompt Engineering: core principles; zero/few-shot; CoT; ReAct.",
25
+ "Week 5 – Building a Simple Chatbot; memory (short vs long term); LangChain & UI.",
26
+ "Week 6 – Review Week; cross-module consolidation & self-check prompts.",
27
+ "Week 7 – Retrieval-Augmented Generation (RAG); embeddings; hybrid retrieval.",
28
+ "Week 8 – Agents & Agentic RAG; planning, tools, knowledge augmentation.",
29
+ "Week 9 – Evaluating GenAI Apps; hallucination, bias/fairness, metrics.",
30
+ "Week 10 – Responsible AI; risks, governance, EU AI Act-style ideas.",
31
+ ]
32
+
33
+ # ---------- 学习模式列表 ----------
34
+ LEARNING_MODES = [
35
+ "Concept Explainer",
36
+ "Socratic Tutor",
37
+ "Exam Prep / Quiz",
38
+ "Assignment Helper",
39
+ "Quick Summary",
40
+ ]
41
+
42
+ LEARNING_MODE_INSTRUCTIONS = {
43
+ "Concept Explainer": (
44
+ "Explain concepts step by step. Use clear definitions, key formulas or structures, "
45
+ "and one or two simple examples. Focus on clarity over depth."
46
+ ),
47
+ "Socratic Tutor": (
48
+ "Use a Socratic style. Ask the student short questions first, guide them to reason "
49
+ "step by step, and only give full explanations after they try."
50
+ ),
51
+ "Exam Prep / Quiz": (
52
+ "Behave like an exam prep coach. Often propose short quiz-style questions "
53
+ "(multiple choice or short answer), then explain the solutions."
54
+ ),
55
+ "Assignment Helper": (
56
+ "Help with assignments without giving full final solutions. Clarify requirements, "
57
+ "break tasks into smaller steps, and provide hints or partial examples instead of "
58
+ "complete code or final answers."
59
+ ),
60
+ "Quick Summary": (
61
+ "Provide concise, bullet-point style summaries and cheat-sheet style notes. "
62
+ "Focus on key ideas and avoid long paragraphs."
63
+ ),
64
+ }
65
+
66
+ # ---------- Clare 的基础 System Prompt ----------
67
  CLARE_SYSTEM_PROMPT = """
68
  You are Clare, an AI teaching assistant for Hanbridge University.
69
 
70
  Core identity:
71
  - You are patient, encouraging, and structured like a very good TA.
72
  - Your UI and responses should be in ENGLISH by default.
73
+ - However, you can understand BOTH English and Chinese, and you may reply in Chinese
74
+ if the student clearly prefers Chinese or asks you to.
75
 
76
+ General responsibilities:
77
  1. Help students understand course concepts step by step.
78
  2. Ask short check-up questions to confirm understanding instead of giving huge long lectures.
79
+ 3. When the student seems confused, break content into smaller chunks and use simple language first.
80
  4. When the student is advanced, you can switch to more technical explanations.
81
 
 
 
 
 
 
 
 
82
  Safety and honesty:
83
  - If you don’t know, say you are not sure and suggest how to verify.
84
  - Do not fabricate references, exam answers, or grades.
85
  """
86
 
87
+ # ---------- syllabus 解析 ----------
88
+ def parse_syllabus_docx(file_path: str, max_lines: int = 15) -> List[str]:
89
+ """非常简单的 syllabus 解析:取前若干个非空段落当作主题行。"""
90
+ topics: List[str] = []
91
+ try:
92
+ doc = Document(file_path)
93
+ for para in doc.paragraphs:
94
+ text = para.text.strip()
95
+ if not text:
96
+ continue
97
+ topics.append(text)
98
+ if len(topics) >= max_lines:
99
+ break
100
+ except Exception as e:
101
+ topics = [f"[Error parsing syllabus: {e}]"]
102
+
103
+ return topics
104
 
105
+
106
+ # ---------- 构建 messages ----------
107
  def build_messages(
108
  user_message: str,
109
  history: List[Tuple[str, str]],
110
  language_preference: str,
111
+ learning_mode: str,
112
+ course_outline: Optional[List[str]],
113
  ) -> List[Dict[str, str]]:
114
+ messages: List[Dict[str, str]] = [
115
+ {"role": "system", "content": CLARE_SYSTEM_PROMPT}
116
+ ]
 
 
117
 
118
+ # 学习模式注入
119
+ if learning_mode in LEARNING_MODE_INSTRUCTIONS:
120
+ mode_instruction = LEARNING_MODE_INSTRUCTIONS[learning_mode]
121
  messages.append(
122
  {
123
  "role": "system",
124
+ "content": f"Current learning mode: {learning_mode}. {mode_instruction}",
125
  }
126
  )
127
+
128
+ # 课程大纲注入
129
+ topics = course_outline if course_outline else DEFAULT_COURSE_TOPICS
130
+ topics_text = " | ".join(topics)
131
+ messages.append(
132
+ {
133
+ "role": "system",
134
+ "content": (
135
+ "Here is the course syllabus context. Use this to stay aligned "
136
+ "with the course topics when answering: "
137
+ + topics_text
138
+ ),
139
+ }
140
+ )
141
+
142
+ # 语言偏好控制
143
+ if language_preference == "English":
144
+ messages.append(
145
+ {"role": "system", "content": "Please answer in English."}
146
+ )
147
  elif language_preference == "中文":
148
  messages.append(
149
+ {"role": "system", "content": "请用中文回答学生的问题。"}
 
 
 
150
  )
151
 
152
+ # 历史对话
153
  for user, assistant in history:
154
  messages.append({"role": "user", "content": user})
155
  if assistant is not None:
156
  messages.append({"role": "assistant", "content": assistant})
157
 
158
+ # 当前输入
159
  messages.append({"role": "user", "content": user_message})
160
  return messages
161
 
 
165
  history: List[Tuple[str, str]],
166
  model_name: str,
167
  language_preference: str,
168
+ learning_mode: str,
169
+ course_outline: Optional[List[str]],
170
  ):
 
 
 
171
  try:
172
+ messages = build_messages(
173
+ user_message=message,
174
+ history=history,
175
+ language_preference=language_preference,
176
+ learning_mode=learning_mode,
177
+ course_outline=course_outline,
178
+ )
179
  response = client.chat.completions.create(
180
  model=model_name or DEFAULT_MODEL,
181
  messages=messages,
 
185
  except Exception as e:
186
  answer = f"⚠️ Error talking to the model: {e}"
187
 
 
188
  history = history + [(message, answer)]
189
  return answer, history
190
 
 
198
 
199
  - Ask in English → Clare answers in English.
200
  - Ask in Chinese → Clare can answer in Chinese.
201
+ - Use different **learning modes** to change Clare's teaching style.
202
+ - Optionally upload your **course syllabus (.docx)** so Clare stays aligned with your course.
203
  """
204
  )
205
 
 
214
  value="Auto",
215
  label="Preferred answer language",
216
  )
217
+ learning_mode = gr.Dropdown(
218
+ choices=LEARNING_MODES,
219
+ value="Concept Explainer",
220
+ label="Learning mode",
221
+ )
222
+
223
+ with gr.Row():
224
+ syllabus_file = gr.File(
225
+ label="Upload course syllabus (.docx)",
226
+ file_types=[".docx"],
227
+ )
228
+ course_outline_state = gr.State(DEFAULT_COURSE_TOPICS)
229
+
230
+ def update_outline(file):
231
+ if file is None:
232
+ return DEFAULT_COURSE_TOPICS
233
+ topics = parse_syllabus_docx(file.name)
234
+ return topics
235
+
236
+ syllabus_file.change(
237
+ fn=update_outline,
238
+ inputs=[syllabus_file],
239
+ outputs=[course_outline_state],
240
+ )
241
 
242
  chatbot = gr.Chatbot(
243
  label="Clare Chat",
 
249
  )
250
  clear_btn = gr.Button("Reset conversation")
251
 
252
+ def respond(message, chat_history, course_outline):
 
253
  answer, new_history = chat_with_clare(
254
+ message=message,
255
+ history=chat_history,
256
  model_name=model_name.value,
257
  language_preference=language_preference.value,
258
+ learning_mode=learning_mode.value,
259
+ course_outline=course_outline,
260
  )
261
+ return "", new_history
262
+
263
+ user_input.submit(
264
+ respond,
265
+ [user_input, chatbot, course_outline_state],
266
+ [user_input, chatbot],
267
+ )
268
+
269
+ def clear_chat():
270
+ return []
271
 
272
+ clear_btn.click(clear_chat, None, chatbot, queue=False)
 
273
 
 
274
  if __name__ == "__main__":
275
  demo.launch()