lzl1005 commited on
Commit
38d98f8
·
verified ·
1 Parent(s): a8af684

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -85
app.py CHANGED
@@ -3,80 +3,75 @@ import time
3
  from typing import Dict, Any
4
  import gradio as gr
5
 
6
- # --- 模擬環境變數與 Firebase/API 設置 ---
7
- # Hugging Face 上部署時,我們使用模擬呼叫,避免 API Key 洩露和複雜的網路配置。
8
  LLM_MODEL = "gemini-2.5-flash-preview-09-2025"
9
- API_KEY = "MOCK_KEY"
10
- # API_URL_TEMPLATE = f"https://generativelanguage.googleapis.com/v1beta/models/{LLM_MODEL}:generateContent?key={API_KEY}" # 實際 API URL
11
-
12
- # --- 模擬 LLM API 呼叫 (保留原有的模擬邏輯) ---
13
- # 為了讓 Gradio 應用程式在沒有實際 API 密鑰和網路連線的情況下也能運行,
14
- # 這裡保留了對結構化 JSON 的模擬輸出。
15
 
16
  def simulate_gemini_api_call(payload: Dict[str, Any], fields: Dict[str, Any]) -> Dict[str, Any]:
17
  """
18
- 模擬對 Gemini API POST 呼叫,返回預設的結構化 JSON 輸出。
19
 
20
  Args:
21
- payload: 準備傳送給 API JSON 負載。
22
- fields: 用戶輸入的欄位,用於客製化模擬輸出。
23
 
24
  Returns:
25
- 一個模擬的 API 回應 JSON 結構。
26
  """
27
 
28
  user_query = payload['contents'][0]['parts'][0]['text']
29
- system_instruction = payload.get('systemInstruction', {}).get('parts', [{}])[0].get('text', '無系統指令')
30
 
31
- # 根據指令內容返回不同的模擬結果
32
  if "台灣中學學務處行政書記" in system_instruction:
33
- # 模擬 行政 Copilot (會議記錄) 的結構化輸出
34
  mock_text_result = json.dumps({
35
- "文件類型": "學務處會議記錄",
36
  "meeting_info": {
37
  "date": fields.get('date', '2025-01-10'),
38
- "location": fields.get('location', '學務處會議室'),
39
  "topic": fields.get('topic', '模擬會議主題')
40
  },
41
- "attendees": ["校長", "主任", "學務處全體同仁"],
42
  "key_points": [
43
- "期末獎懲核定程序已完成,共計 30 案。",
44
- "新生訓練籌備進度達 80%,需確認場地佈置事宜。"
45
  ],
46
  "resolutions": [
47
- {"item": "請人事室發佈獎懲公告。", "responsible": "人事組", "deadline": "2025-01-15"},
48
- {"item": "新生訓練場地桌椅於活動前一日完成配置。", "responsible": "總務處", "deadline": "2025-08-20"}
49
  ],
50
- "audit_note": "本文件依據校內「會議記錄書寫規範」生成,符合 MVP 規格 3.3 節規範。"
51
  }, ensure_ascii=False, indent=2)
52
 
53
  elif "台灣國高中資深教師與課程設計師" in system_instruction:
54
- # 模擬 教學設計器 (教案) 的結構化輸出
55
  mock_text_result = json.dumps({
56
- "文件類型": "單元教案與評量規準",
57
- "lesson_plan_title": f"【{fields.get('subject', 'N/A')}】探索 {fields.get('topic', 'N/A')} 的奧秘 ({fields.get('hours', 0)} )",
58
  "grade_level": fields.get('grade', 'N/A'),
59
- "curriculum_alignment": ["A2 邏輯運算與推理 (108 課綱素養)", "B3 獨立思考"],
60
- "learning_objectives": ["學生能解釋...。", "學生能應用...。"],
61
  "activities": [
62
- {"time_min": 15, "stage": "前導", "method": "提問法", "description": "利用日常生活案例引導學生思考主題的相關性。"},
63
- {"time_min": 30, "stage": "活動一", "method": "合作學習", "description": "分組討論並進行實作練習。"},
64
  ],
65
  "rubric": {
66
- "title": "單元評量規準 (Rubric) - 4 等第 x 4 指標",
67
  "criteria": [
68
- {"name": "概念理解", "A": "能清晰、準確地解釋所有核心概念。", "D": "僅能回答部分簡單問題。"},
69
- {"name": "團隊合作", "A": "積極參與並領導團隊完成任務。", "D": "不參與討論,需老師催促。"}
70
  ]
71
  },
72
- "differentiation_advice": "針對英語程度較弱學生,提供雙語關鍵字卡。"
73
  }, ensure_ascii=False, indent=2)
74
 
75
  else:
76
  mock_text_result = json.dumps({"error": "Unknown or missing task instruction."})
77
 
78
 
79
- # 模擬 API 回應的標準結構
80
  return {
81
  "candidates": [{
82
  "content": {
@@ -86,18 +81,11 @@ def simulate_gemini_api_call(payload: Dict[str, Any], fields: Dict[str, Any]) ->
86
  }]
87
  }
88
 
89
- # --- 模組 A:行政 Copilot 生成器 (Gradio Wrapper) ---
90
 
91
  def admin_copilot_generator(template_id: str, topic: str, date: str, location: str, key_input: str) -> str:
92
  """
93
- Gradio 呼叫的行政 Copilot 函式。
94
-
95
- Args:
96
- template_id: 模板 ID (MVP 固定為會議記錄)
97
- topic, date, location, key_input: 使用者輸入的欄位。
98
-
99
- Returns:
100
- 格式化後的 JSON 字串。
101
  """
102
  fields = {
103
  "topic": topic,
@@ -106,7 +94,7 @@ def admin_copilot_generator(template_id: str, topic: str, date: str, location: s
106
  "key_input": key_input
107
  }
108
 
109
- # 構造系統指令和結構
110
  system_prompt = (
111
  "角色:台灣中學學務處行政書記\n"
112
  "輸出:JSON(會議資訊、出席、重點、決議、待辦、負責人、期限)\n"
@@ -116,7 +104,7 @@ def admin_copilot_generator(template_id: str, topic: str, date: str, location: s
116
  response_schema = {
117
  "type": "OBJECT",
118
  "properties": {
119
- "文件類型": {"type": "STRING"},
120
  "meeting_info": {"type": "OBJECT", "properties": {"date": {"type": "STRING"}, "location": {"type": "STRING"}, "topic": {"type": "STRING"}}},
121
  "attendees": {"type": "ARRAY", "items": {"type": "STRING"}},
122
  "key_points": {"type": "ARRAY", "items": {"type": "STRING"}},
@@ -145,28 +133,19 @@ def admin_copilot_generator(template_id: str, topic: str, date: str, location: s
145
  }
146
  }
147
 
148
- # 呼叫模擬 API
149
  api_response = simulate_gemini_api_call(payload, fields)
150
 
151
- # 解析結果
152
  try:
153
  json_string = api_response['candidates'][0]['content']['parts'][0]['text']
154
- # 返回格式化的 JSON 字串供 Gradio 顯示
155
  return json_string
156
  except (KeyError, json.JSONDecodeError):
157
- return "錯誤:無法解析 LLM 結構化輸出。"
158
 
159
- # --- 模組 B:教師 AI 創新教學設計器 (Gradio Wrapper) ---
160
 
161
  def lesson_plan_designer(grade: str, subject: str, topic: str, hours: int, method: str, equipment: str, class_needs: str) -> str:
162
  """
163
- Gradio 呼叫的教學設計器函式。
164
-
165
- Args:
166
- grade, subject, topic, hours, method, equipment, class_needs: 使用者輸入的欄位。
167
-
168
- Returns:
169
- 格式化後的 JSON 字串。
170
  """
171
  fields = {
172
  "grade": grade,
@@ -178,7 +157,7 @@ def lesson_plan_designer(grade: str, subject: str, topic: str, hours: int, metho
178
  "class_needs": class_needs
179
  }
180
 
181
- # 規格 8.B 系統指令
182
  system_prompt = (
183
  "角色:台灣國高中資深教師與課程設計師\n"
184
  "輸出:JSON(教案標題、目標、課綱對齊、活動步驟、評量規準、差異化建議)\n"
@@ -186,22 +165,20 @@ def lesson_plan_designer(grade: str, subject: str, topic: str, hours: int, metho
186
  "對齊:請將輸出中的 'curriculum_alignment' 欄位,對齊台灣課綱的關鍵能力/素養。"
187
  )
188
 
189
- # 結構化輸出 Schema
190
  response_schema = {
191
  "type": "OBJECT",
192
  "properties": {
193
- "文件類型": {"type": "STRING"},
194
  "lesson_plan_title": {"type": "STRING"},
195
  "grade_level": {"type": "STRING"},
196
  "curriculum_alignment": {"type": "ARRAY", "items": {"type": "STRING"}},
197
  "learning_objectives": {"type": "ARRAY", "items": {"type": "STRING"}},
198
- "activities": {"type": "ARRAY", "items": {"type": "OBJECT"}}, # 簡化結構
199
- "rubric": {"type": "OBJECT"}, # 簡化結構
200
  "differentiation_advice": {"type": "STRING"}
201
  }
202
  }
203
 
204
- # 規格 4.1 使用者欄位組合 Query
205
  user_query = (
206
  f"請根據以下資訊設計一個單元教案、評量規準和差異化建議:\n"
207
  f"年級/學科/單元主題: {grade}/{subject}/{topic}\n"
@@ -211,7 +188,6 @@ def lesson_plan_designer(grade: str, subject: str, topic: str, hours: int, metho
211
  f"班級特性: {class_needs}"
212
  )
213
 
214
- # 構造 API 請求負載
215
  payload = {
216
  "contents": [{ "parts": [{ "text": user_query }] }],
217
  "systemInstruction": { "parts": [{ "text": system_prompt }] },
@@ -221,52 +197,49 @@ def lesson_plan_designer(grade: str, subject: str, topic: str, hours: int, metho
221
  }
222
  }
223
 
224
- # 呼叫模擬 API
225
  api_response = simulate_gemini_api_call(payload, fields)
226
 
227
- # 解析結果
228
  try:
229
  json_string = api_response['candidates'][0]['content']['parts'][0]['text']
230
- # 返回格式化的 JSON 字串供 Gradio 顯示
231
  return json_string
232
  except (KeyError, json.JSONDecodeError):
233
- return "錯誤:無法解析 LLM 結構化輸出。"
234
 
235
- # --- Gradio 介面定義 ---
236
 
237
- # 模組 A 介面 (行政 Copilot)
238
  admin_copilot_interface = gr.Interface(
239
  fn=admin_copilot_generator,
240
  inputs=[
241
- gr.Textbox(label="模板 ID (MVP 固定)", value="meeting_minutes_standard", interactive=False),
242
- gr.Textbox(label="會議主題 (Topic)", value="學務處期末獎懲與新生訓練籌備會議"),
243
  gr.Textbox(label="日期 (Date)", value="2025-01-10"),
244
  gr.Textbox(label="地點 (Location)", value="學務處會議室"),
245
- gr.Textbox(label="輸入重點/逐字稿 (Key Input)", value="討論期末獎懲核定程序。新生訓練場地佈置、人員編組確認。", lines=5),
246
  ],
247
- outputs=gr.JSON(label="AI 生成結構化 JSON 初稿 (將套入 DOCX 模板)"),
248
- title="行政 Copilot:會議記錄生成",
249
  description="🎯 模擬一鍵生成格式嚴謹的行政文件 JSON。實際系統將此 JSON 套入 DOCX 模板匯出。"
250
  )
251
 
252
- # 模組 B 介面 (教學設計器)
253
  lesson_plan_designer_interface = gr.Interface(
254
  fn=lesson_plan_designer,
255
  inputs=[
256
  gr.Dropdown(label="年級 (Grade)", choices=["國一", "高一", "小六"], value="高一"),
257
  gr.Textbox(label="學科 (Subject)", value="歷史"),
258
- gr.Textbox(label="單元主題 (Topic)", value="從茶葉看全球化:17-19世紀的貿易網絡"),
259
- gr.Slider(label="課時數 (Hours)", minimum=1, maximum=10, step=1, value=4),
260
- gr.Dropdown(label="教學法偏好 (Method)", choices=["探究式、PBL", "翻轉教學", "合作學習", "講述法"], value="探究式、PBL"),
261
- gr.Textbox(label="可用設備 (Equipment)", value="平板電腦、投影設備、網路"),
262
- gr.Textbox(label="班級���性 (Class Needs)", value="班級組成多元,需考慮多樣化的史料呈現方式。"),
263
  ],
264
- outputs=gr.JSON(label="AI 生成教案與評量規準 JSON"),
265
- title="教學 AI 設計器:教案與 Rubric 生成",
266
  description="📘 模擬生成符合課綱精神的單元教案結構和評量規準。實際系統將此 JSON 套入 DOCX 或 Google Slides 框架。"
267
  )
268
 
269
- # 整合兩個模組到一個 Tabbed Interface
270
  demo = gr.TabbedInterface([admin_copilot_interface, lesson_plan_designer_interface],
271
  ["模組 A: 行政 Copilot", "模組 B: 教學設計器"],
272
  title="CampusAI Suite (台灣校園 AI 文書/教學 MVP 演示)",
 
3
  from typing import Dict, Any
4
  import gradio as gr
5
 
6
+ # --- Simulation Setup for LLM API ---
7
+ # This section simulates the core AI generation logic without requiring a live API key.
8
  LLM_MODEL = "gemini-2.5-flash-preview-09-2025"
9
+ API_KEY = "MOCK_KEY" # Placeholder
 
 
 
 
 
10
 
11
  def simulate_gemini_api_call(payload: Dict[str, Any], fields: Dict[str, Any]) -> Dict[str, Any]:
12
  """
13
+ Simulates a structured response from the Gemini API based on the task type.
14
 
15
  Args:
16
+ payload: The constructed API payload (used for extracting system instructions).
17
+ fields: User input fields for customizing the mock output.
18
 
19
  Returns:
20
+ A dictionary simulating the API's JSON response structure.
21
  """
22
 
23
  user_query = payload['contents'][0]['parts'][0]['text']
24
+ system_instruction = payload.get('systemInstruction', {}).get('parts', [{}])[0].get('text', 'No system instruction')
25
 
26
+ # Check system instruction to determine the output type (Admin or Teaching)
27
  if "台灣中學學務處行政書記" in system_instruction:
28
+ # Simulate Admin Copilot (Meeting Minutes) output
29
  mock_text_result = json.dumps({
30
+ "文件類型 (Document Type)": "學務處會議記錄 (Academic Affairs Meeting Minutes)",
31
  "meeting_info": {
32
  "date": fields.get('date', '2025-01-10'),
33
+ "location": fields.get('location', '會議室'),
34
  "topic": fields.get('topic', '模擬會議主題')
35
  },
36
+ "attendees": ["Principal", "Director", "All Faculty"],
37
  "key_points": [
38
+ "End-of-term commendation process finalized, 30 cases approved.",
39
+ "New student orientation preparation is 80% complete; venue setup confirmed."
40
  ],
41
  "resolutions": [
42
+ {"item": "Issue official commendation announcement.", "responsible": "HR", "deadline": "2025-01-15"},
43
+ {"item": "Venue setup for orientation completed one day prior.", "responsible": "General Affairs", "deadline": "2025-08-20"}
44
  ],
45
+ "audit_note": "Document generated per internal school writing guidelines."
46
  }, ensure_ascii=False, indent=2)
47
 
48
  elif "台灣國高中資深教師與課程設計師" in system_instruction:
49
+ # Simulate Teaching Designer (Lesson Plan & Rubric) output
50
  mock_text_result = json.dumps({
51
+ "文件類型 (Document Type)": "單元教案與評量規準 (Lesson Plan & Rubric)",
52
+ "lesson_plan_title": f"【{fields.get('subject', 'N/A')}】Exploring {fields.get('topic', 'N/A')} ({fields.get('hours', 0)} sessions)",
53
  "grade_level": fields.get('grade', 'N/A'),
54
+ "curriculum_alignment": ["A2 Logical Reasoning (Curriculum Competency)", "B3 Independent Thinking"],
55
+ "learning_objectives": ["Students can explain X.", "Students can apply Y."],
56
  "activities": [
57
+ {"time_min": 15, "stage": "Introduction", "method": "Inquiry", "description": "Use real-life examples to introduce the core concept."},
58
+ {"time_min": 30, "stage": "Activity One", "method": "Collaborative Learning", "description": "Group project and practical exercise."},
59
  ],
60
  "rubric": {
61
+ "title": "Unit Assessment Rubric (4 Levels x 4 Indicators)",
62
  "criteria": [
63
+ {"name": "Conceptual Understanding", "A": "Clearly and accurately explains all core concepts.", "D": "Answers only simple questions."},
64
+ {"name": "Teamwork", "A": "Actively leads the team to complete the task.", "D": "Does not participate in discussion."}
65
  ]
66
  },
67
+ "differentiation_advice": "Provide bilingual vocabulary cards for students with lower English proficiency."
68
  }, ensure_ascii=False, indent=2)
69
 
70
  else:
71
  mock_text_result = json.dumps({"error": "Unknown or missing task instruction."})
72
 
73
 
74
+ # Return the simulated API response structure
75
  return {
76
  "candidates": [{
77
  "content": {
 
81
  }]
82
  }
83
 
84
+ # --- Module A: Admin Copilot Generator (Gradio Wrapper) ---
85
 
86
  def admin_copilot_generator(template_id: str, topic: str, date: str, location: str, key_input: str) -> str:
87
  """
88
+ Handles the Admin Copilot UI inputs and calls the simulation.
 
 
 
 
 
 
 
89
  """
90
  fields = {
91
  "topic": topic,
 
94
  "key_input": key_input
95
  }
96
 
97
+ # System Prompt and Schema defined as per spec 8.A
98
  system_prompt = (
99
  "角色:台灣中學學務處行政書記\n"
100
  "輸出:JSON(會議資訊、出席、重點、決議、待辦、負責人、期限)\n"
 
104
  response_schema = {
105
  "type": "OBJECT",
106
  "properties": {
107
+ "文件類型 (Document Type)": {"type": "STRING"},
108
  "meeting_info": {"type": "OBJECT", "properties": {"date": {"type": "STRING"}, "location": {"type": "STRING"}, "topic": {"type": "STRING"}}},
109
  "attendees": {"type": "ARRAY", "items": {"type": "STRING"}},
110
  "key_points": {"type": "ARRAY", "items": {"type": "STRING"}},
 
133
  }
134
  }
135
 
 
136
  api_response = simulate_gemini_api_call(payload, fields)
137
 
 
138
  try:
139
  json_string = api_response['candidates'][0]['content']['parts'][0]['text']
 
140
  return json_string
141
  except (KeyError, json.JSONDecodeError):
142
+ return "ERROR: Failed to parse LLM structured output."
143
 
144
+ # --- Module B: Teaching AI Designer (Gradio Wrapper) ---
145
 
146
  def lesson_plan_designer(grade: str, subject: str, topic: str, hours: int, method: str, equipment: str, class_needs: str) -> str:
147
  """
148
+ Handles the Teaching Designer UI inputs and calls the simulation.
 
 
 
 
 
 
149
  """
150
  fields = {
151
  "grade": grade,
 
157
  "class_needs": class_needs
158
  }
159
 
160
+ # System Prompt and Schema defined as per spec 8.B
161
  system_prompt = (
162
  "角色:台灣國高中資深教師與課程設計師\n"
163
  "輸出:JSON(教案標題、目標、課綱對齊、活動步驟、評量規準、差異化建議)\n"
 
165
  "對齊:請將輸出中的 'curriculum_alignment' 欄位,對齊台灣課綱的關鍵能力/素養。"
166
  )
167
 
 
168
  response_schema = {
169
  "type": "OBJECT",
170
  "properties": {
171
+ "文件類型 (Document Type)": {"type": "STRING"},
172
  "lesson_plan_title": {"type": "STRING"},
173
  "grade_level": {"type": "STRING"},
174
  "curriculum_alignment": {"type": "ARRAY", "items": {"type": "STRING"}},
175
  "learning_objectives": {"type": "ARRAY", "items": {"type": "STRING"}},
176
+ "activities": {"type": "ARRAY", "items": {"type": "OBJECT"}},
177
+ "rubric": {"type": "OBJECT"},
178
  "differentiation_advice": {"type": "STRING"}
179
  }
180
  }
181
 
 
182
  user_query = (
183
  f"請根據以下資訊設計一個單元教案、評量規準和差異化建議:\n"
184
  f"年級/學科/單元主題: {grade}/{subject}/{topic}\n"
 
188
  f"班級特性: {class_needs}"
189
  )
190
 
 
191
  payload = {
192
  "contents": [{ "parts": [{ "text": user_query }] }],
193
  "systemInstruction": { "parts": [{ "text": system_prompt }] },
 
197
  }
198
  }
199
 
 
200
  api_response = simulate_gemini_api_call(payload, fields)
201
 
 
202
  try:
203
  json_string = api_response['candidates'][0]['content']['parts'][0]['text']
 
204
  return json_string
205
  except (KeyError, json.JSONDecodeError):
206
+ return "ERROR: Failed to parse LLM structured output."
207
 
208
+ # --- Gradio Interface Definition ---
209
 
210
+ # Module A Interface (Admin Copilot)
211
  admin_copilot_interface = gr.Interface(
212
  fn=admin_copilot_generator,
213
  inputs=[
214
+ gr.Textbox(label="模板 ID (Template ID - Fixed for MVP)", value="meeting_minutes_standard", interactive=False),
215
+ gr.Textbox(label="會議主題 (Meeting Topic)", value="學務處期末獎懲與新生訓練籌備會議"),
216
  gr.Textbox(label="日期 (Date)", value="2025-01-10"),
217
  gr.Textbox(label="地點 (Location)", value="學務處會議室"),
218
+ gr.Textbox(label="輸入重點/逐字稿 (Key Input/Transcript)", value="討論期末獎懲核定程序。新生訓練場地佈置、人員編組確認。", lines=5),
219
  ],
220
+ outputs=gr.JSON(label="AI 生成結構化 JSON 初稿 (Structured JSON Draft for DOCX Templating)"),
221
+ title="行政 Copilot:會議記錄生成 (Admin Copilot: Meeting Minutes Generation)",
222
  description="🎯 模擬一鍵生成格式嚴謹的行政文件 JSON。實際系統將此 JSON 套入 DOCX 模板匯出。"
223
  )
224
 
225
+ # Module B Interface (Teaching Designer)
226
  lesson_plan_designer_interface = gr.Interface(
227
  fn=lesson_plan_designer,
228
  inputs=[
229
  gr.Dropdown(label="年級 (Grade)", choices=["國一", "高一", "小六"], value="高一"),
230
  gr.Textbox(label="學科 (Subject)", value="歷史"),
231
+ gr.Textbox(label="單元主題 (Unit Topic)", value="從茶葉看全球化:17-19世紀的貿易網絡"),
232
+ gr.Slider(label="課時數 (Number of Sessions)", minimum=1, maximum=10, step=1, value=4),
233
+ gr.Dropdown(label="教學法偏好 (Pedagogy Preference)", choices=["探究式、PBL", "翻轉教學", "合作學習", "講述法"], value="探究式、PBL"),
234
+ gr.Textbox(label="可用設備 (Available Equipment)", value="平板電腦、投影設備、網路"),
235
+ gr.Textbox(label="班級性 (Class Characteristics)", value="班級組成多元,需考慮多樣化的史料呈現方式。"),
236
  ],
237
+ outputs=gr.JSON(label="AI 生成教案與評量規準 JSON (Lesson Plan & Rubric JSON)"),
238
+ title="教學 AI 設計器:教案與 Rubric 生成 (Teaching AI Designer: Lesson Plan & Rubric)",
239
  description="📘 模擬生成符合課綱精神的單元教案結構和評量規準。實際系統將此 JSON 套入 DOCX 或 Google Slides 框架。"
240
  )
241
 
242
+ # Integrate the two modules into a Tabbed Interface
243
  demo = gr.TabbedInterface([admin_copilot_interface, lesson_plan_designer_interface],
244
  ["模組 A: 行政 Copilot", "模組 B: 教學設計器"],
245
  title="CampusAI Suite (台灣校園 AI 文書/教學 MVP 演示)",