Spaces:
Sleeping
Sleeping
update
Browse files- app.py +360 -134
- chatbot.py +1 -1
- educational_material.py +19 -17
app.py
CHANGED
@@ -367,7 +367,7 @@ def extract_youtube_id(url):
|
|
367 |
else:
|
368 |
return None
|
369 |
|
370 |
-
def
|
371 |
languages = ['zh-TW', 'zh-Hant', 'zh', 'en'] # 優先順序列表
|
372 |
for language in languages:
|
373 |
try:
|
@@ -380,7 +380,7 @@ def get_transcript(video_id):
|
|
380 |
continue # 當前語言的字幕沒有找到,繼續嘗試下一個語言
|
381 |
return None # 所有嘗試都失敗,返回None
|
382 |
|
383 |
-
def
|
384 |
youtube_url = f'https://www.youtube.com/watch?v={video_id}'
|
385 |
codec_name = "mp3"
|
386 |
outtmpl = f"{OUTPUT_PATH}/{video_id}.%(ext)s"
|
@@ -457,17 +457,17 @@ def process_transcript_and_screenshots_on_gcs(video_id):
|
|
457 |
print("逐字稿文件不存在于GCS中,重新建立")
|
458 |
# 从YouTube获取逐字稿并上传
|
459 |
try:
|
460 |
-
transcript =
|
461 |
except:
|
462 |
# call open ai whisper
|
463 |
print("===call open ai whisper===")
|
464 |
-
transcript =
|
465 |
|
466 |
if transcript:
|
467 |
print("成功獲取字幕")
|
468 |
else:
|
469 |
print("沒有找到字幕")
|
470 |
-
transcript =
|
471 |
|
472 |
transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
|
473 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, transcript_text)
|
@@ -572,6 +572,7 @@ def process_youtube_link(password, link):
|
|
572 |
questions = get_questions(video_id, formatted_simple_transcript, source)
|
573 |
questions_json = json.dumps(questions, ensure_ascii=False, indent=2)
|
574 |
summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
|
|
|
575 |
summary = summary_json["summary"]
|
576 |
key_moments_json = get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source)
|
577 |
key_moments = key_moments_json["key_moments"]
|
@@ -586,6 +587,7 @@ def process_youtube_link(password, link):
|
|
586 |
mind_map = mind_map_json["mind_map"]
|
587 |
mind_map_html = get_mind_map_html(mind_map)
|
588 |
reading_passage_json = get_reading_passage(video_id, formatted_simple_transcript, source)
|
|
|
589 |
reading_passage = reading_passage_json["reading_passage"]
|
590 |
meta_data = get_meta_data(video_id)
|
591 |
subject = meta_data["subject"]
|
@@ -598,6 +600,7 @@ def process_youtube_link(password, link):
|
|
598 |
questions[1] if len(questions) > 1 else "", \
|
599 |
questions[2] if len(questions) > 2 else "", \
|
600 |
original_transcript, \
|
|
|
601 |
summary, \
|
602 |
key_moments_text, \
|
603 |
key_moments_html, \
|
@@ -607,6 +610,7 @@ def process_youtube_link(password, link):
|
|
607 |
simple_html_content, \
|
608 |
first_image, \
|
609 |
first_text, \
|
|
|
610 |
reading_passage, \
|
611 |
subject, \
|
612 |
grade
|
@@ -750,6 +754,8 @@ def generate_reading_passage(df_string):
|
|
750 |
請一定要使用繁體中文 zh-TW,並用台灣人的口語
|
751 |
產生的結果不要前後文解釋,也不要敘述這篇文章怎麼產生的
|
752 |
只需要專注提供 Reading Passage,字數在 500 字以內
|
|
|
|
|
753 |
"""
|
754 |
messages = [
|
755 |
{"role": "system", "content": sys_content},
|
@@ -757,7 +763,7 @@ def generate_reading_passage(df_string):
|
|
757 |
]
|
758 |
|
759 |
request_payload = {
|
760 |
-
"model": "gpt-4-
|
761 |
"messages": messages,
|
762 |
"max_tokens": 4000,
|
763 |
}
|
@@ -834,7 +840,7 @@ def generate_mind_map(df_string):
|
|
834 |
]
|
835 |
|
836 |
request_payload = {
|
837 |
-
"model": "gpt-4-
|
838 |
"messages": messages,
|
839 |
"max_tokens": 4000,
|
840 |
}
|
@@ -916,22 +922,27 @@ def generate_summarise(df_string):
|
|
916 |
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
917 |
user_content = f"""
|
918 |
請根據 {df_string},判斷這份文本
|
919 |
-
|
920 |
-
|
921 |
-
如果是影片類型,請提估影片內容,告訴學生這部影片的意義,
|
922 |
整體摘要在一百字以內
|
923 |
-
|
924 |
-
注意不要遺漏任何一段時間軸的內容
|
925 |
-
格式為 【start - end】: 摘要
|
926 |
以及可能的結論與結尾延伸小問題提供學生作反思
|
|
|
|
|
927 |
|
928 |
整體格式為:
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
935 |
"""
|
936 |
|
937 |
# 🗂️ 1. 內容類型:?
|
@@ -947,7 +958,7 @@ def generate_summarise(df_string):
|
|
947 |
]
|
948 |
|
949 |
request_payload = {
|
950 |
-
"model": "gpt-4-turbo
|
951 |
"messages": messages,
|
952 |
"max_tokens": 4000,
|
953 |
}
|
@@ -1014,9 +1025,17 @@ def get_questions(video_id, df_string, source="gcs"):
|
|
1014 |
|
1015 |
def generate_questions(df_string):
|
1016 |
# 使用 OpenAI 生成基于上传数据的问题
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1017 |
|
1018 |
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
|
1019 |
-
user_content = f"請根據 {
|
1020 |
messages = [
|
1021 |
{"role": "system", "content": sys_content},
|
1022 |
{"role": "user", "content": user_content}
|
@@ -1029,7 +1048,7 @@ def generate_questions(df_string):
|
|
1029 |
|
1030 |
|
1031 |
request_payload = {
|
1032 |
-
"model": "gpt-4-
|
1033 |
"messages": messages,
|
1034 |
"max_tokens": 4000,
|
1035 |
"response_format": response_format
|
@@ -1147,7 +1166,7 @@ def generate_key_moments(formatted_simple_transcript, formatted_transcript):
|
|
1147 |
response_format = { "type": "json_object" }
|
1148 |
|
1149 |
request_payload = {
|
1150 |
-
"model": "gpt-4-
|
1151 |
"messages": messages,
|
1152 |
"max_tokens": 4096,
|
1153 |
"response_format": response_format
|
@@ -1189,7 +1208,7 @@ def generate_key_moments_keywords(transcript):
|
|
1189 |
{"role": "user", "content": user_content}
|
1190 |
]
|
1191 |
request_payload = {
|
1192 |
-
"model": "gpt-4-
|
1193 |
"messages": messages,
|
1194 |
"max_tokens": 100,
|
1195 |
}
|
@@ -1402,6 +1421,27 @@ def get_key_moments_html(key_moments):
|
|
1402 |
return key_moments_html
|
1403 |
|
1404 |
# ---- LLM CRUD ----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1405 |
def enable_edit_mode():
|
1406 |
return gr.update(interactive=True)
|
1407 |
|
@@ -1426,15 +1466,17 @@ def update_LLM_content(video_id, new_content, kind):
|
|
1426 |
blob_name = f"{video_id}/{file_name}"
|
1427 |
|
1428 |
if kind == "reading_passage":
|
|
|
|
|
1429 |
reading_passage_json = {"reading_passage": str(new_content)}
|
1430 |
reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
|
1431 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, reading_passage_text)
|
1432 |
-
updated_content =
|
1433 |
elif kind == "summary":
|
1434 |
summary_json = {"summary": str(new_content)}
|
1435 |
summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
|
1436 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, summary_text)
|
1437 |
-
updated_content =
|
1438 |
elif kind == "mind_map":
|
1439 |
mind_map_json = {"mind_map": str(new_content)}
|
1440 |
mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
|
@@ -1507,7 +1549,6 @@ def create_LLM_content(video_id, df_string, kind):
|
|
1507 |
|
1508 |
return gr.update(value=content, interactive=False)
|
1509 |
|
1510 |
-
|
1511 |
# AI 生成教學素材
|
1512 |
def get_meta_data(video_id, source="gcs"):
|
1513 |
if source == "gcs":
|
@@ -1611,7 +1652,7 @@ def generate_ai_content(password, df_string, topic, grade, level, specific_featu
|
|
1611 |
prompt = material.generate_content_prompt()
|
1612 |
user_content = material.build_user_content()
|
1613 |
messages = material.build_messages(user_content)
|
1614 |
-
ai_model_name = "gpt-4-
|
1615 |
request_payload = {
|
1616 |
"model": ai_model_name,
|
1617 |
"messages": messages,
|
@@ -1625,7 +1666,7 @@ def generate_exam_fine_tune_result(password, exam_result_prompt , df_string_outp
|
|
1625 |
material = EducationalMaterial(df_string_output, "", "", "", "", "")
|
1626 |
user_content = material.build_fine_tune_user_content(exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
|
1627 |
messages = material.build_messages(user_content)
|
1628 |
-
ai_model_name = "gpt-4-
|
1629 |
request_payload = {
|
1630 |
"model": ai_model_name,
|
1631 |
"messages": messages,
|
@@ -1661,7 +1702,7 @@ def get_instructions(content_subject, content_grade, key_moments):
|
|
1661 |
Language: Traditional Chinese ZH-TW (it's very important), suitable for {content_grade} th-grade level.
|
1662 |
Response:
|
1663 |
- Single question, under 100 characters
|
1664 |
-
- include math symbols (use LaTeX $ to cover before and after)
|
1665 |
- hint with video timestamp which format 【參考:00:00:00】.
|
1666 |
- Sometimes encourage user by Taiwanese style with relaxing atmosphere.
|
1667 |
- if user ask questions not include in context,
|
@@ -1670,19 +1711,28 @@ def get_instructions(content_subject, content_grade, key_moments):
|
|
1670 |
"""
|
1671 |
return instructions
|
1672 |
|
1673 |
-
def chat_with_ai(ai_name, password, video_id, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
|
1674 |
verify_password(password)
|
1675 |
|
1676 |
-
|
1677 |
-
|
|
|
|
|
|
|
1678 |
raise gr.Error(error_msg)
|
1679 |
|
|
|
|
|
|
|
1680 |
if ai_name == "jutor":
|
1681 |
ai_client = ""
|
1682 |
elif ai_name == "claude3":
|
1683 |
ai_client = BEDROCK_CLIENT
|
1684 |
elif ai_name == "groq":
|
1685 |
ai_client = GROQ_CLIENT
|
|
|
|
|
|
|
1686 |
if isinstance(trascript_state, str):
|
1687 |
simple_transcript = json.loads(trascript_state)
|
1688 |
else:
|
@@ -1696,7 +1746,7 @@ def chat_with_ai(ai_name, password, video_id, trascript_state, key_moments, user
|
|
1696 |
for moment in key_moments_json:
|
1697 |
moment.pop('images', None)
|
1698 |
moment.pop('end', None)
|
1699 |
-
moment.pop('
|
1700 |
key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
|
1701 |
|
1702 |
instructions = get_instructions(content_subject, content_grade, key_moments_text)
|
@@ -1735,9 +1785,12 @@ def chat_with_ai(ai_name, password, video_id, trascript_state, key_moments, user
|
|
1735 |
print(f"Error: {e}")
|
1736 |
return "请求失败,请稍后再试!", chat_history
|
1737 |
|
1738 |
-
def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
|
1739 |
verify_password(password)
|
1740 |
|
|
|
|
|
|
|
1741 |
# 先計算 user_message 是否超過 500 個字
|
1742 |
if len(user_message) > 1500:
|
1743 |
error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
|
@@ -1745,7 +1798,7 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
|
|
1745 |
|
1746 |
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
1747 |
if chat_history is not None and len(chat_history) > 10:
|
1748 |
-
error_msg = "
|
1749 |
raise gr.Error(error_msg)
|
1750 |
|
1751 |
try:
|
@@ -1754,14 +1807,14 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
|
|
1754 |
|
1755 |
client = OPEN_AI_CLIENT
|
1756 |
# 直接安排逐字稿資料 in instructions
|
1757 |
-
if isinstance(trascript_state, str):
|
1758 |
-
|
1759 |
-
else:
|
1760 |
-
|
1761 |
-
# 移除 embed_url, screenshot_path
|
1762 |
-
for entry in trascript_json:
|
1763 |
-
|
1764 |
-
trascript_text = json.dumps(trascript_json, ensure_ascii=False)
|
1765 |
|
1766 |
if isinstance(key_moments, str):
|
1767 |
key_moments_json = json.loads(key_moments)
|
@@ -1771,7 +1824,7 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
|
|
1771 |
for moment in key_moments_json:
|
1772 |
moment.pop('images', None)
|
1773 |
moment.pop('end', None)
|
1774 |
-
moment.pop('
|
1775 |
key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
|
1776 |
|
1777 |
instructions = get_instructions(content_subject, content_grade, key_moments_text)
|
@@ -1785,18 +1838,11 @@ def chat_with_opan_ai_assistant(password, youtube_id, thread_id, trascript_state
|
|
1785 |
else:
|
1786 |
thread = client.beta.threads.retrieve(thread_id)
|
1787 |
|
1788 |
-
user_msg_note = """\n (請一定要用繁體中文回答 zh-TW,
|
1789 |
-
請嚴格遵循instructions,擔任一位蘇格拉底家教,
|
1790 |
-
並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,
|
1791 |
-
不用提到「逐字稿」這個詞,用「內容」代替),
|
1792 |
-
回答時請用數學符號代替文字(Latex 用 $ 字號 render)
|
1793 |
-
"""
|
1794 |
-
user_msg_note = user_msg_note.replace(" ","").replace("\n","")
|
1795 |
# 向线程添加用户的消息
|
1796 |
client.beta.threads.messages.create(
|
1797 |
thread_id=thread.id,
|
1798 |
role="user",
|
1799 |
-
content=user_message +
|
1800 |
)
|
1801 |
|
1802 |
# 运行助手,生成响应
|
@@ -1900,9 +1946,12 @@ def poll_run_status(run_id, thread_id, timeout=600, poll_interval=5):
|
|
1900 |
|
1901 |
return run.status
|
1902 |
|
1903 |
-
def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id, trascript, key_moments, content_subject, content_grade):
|
1904 |
verify_password(password)
|
1905 |
|
|
|
|
|
|
|
1906 |
print("===streaming_chat_with_open_ai===")
|
1907 |
print(thread_id)
|
1908 |
|
@@ -1912,8 +1961,8 @@ def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id,
|
|
1912 |
raise gr.Error(error_msg)
|
1913 |
|
1914 |
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
1915 |
-
if chat_history is not None and len(chat_history) >
|
1916 |
-
error_msg = "
|
1917 |
raise gr.Error(error_msg)
|
1918 |
|
1919 |
try:
|
@@ -1921,13 +1970,13 @@ def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id,
|
|
1921 |
# assistant_id = "asst_5SaUElqvL3U0ybSi9PRM8x3P" #GPT 3.5 turbo
|
1922 |
client = OPEN_AI_CLIENT
|
1923 |
# 直接安排逐字稿資料 in instructions
|
1924 |
-
if isinstance(trascript, str):
|
1925 |
-
|
1926 |
-
else:
|
1927 |
-
|
1928 |
-
trascript_text = json.dumps(trascript_json, ensure_ascii=False)
|
1929 |
-
# trascript_text 移除 \n, 空白
|
1930 |
-
trascript_text = trascript_text.replace("\n", "").replace(" ", "")
|
1931 |
|
1932 |
if isinstance(key_moments, str):
|
1933 |
key_moments_json = json.loads(key_moments)
|
@@ -1952,7 +2001,7 @@ def streaming_chat_with_open_ai(user_message, chat_history, password, thread_id,
|
|
1952 |
client.beta.threads.messages.create(
|
1953 |
thread_id=thread.id,
|
1954 |
role="user",
|
1955 |
-
content=user_message + "/n
|
1956 |
)
|
1957 |
|
1958 |
with client.beta.threads.runs.stream(
|
@@ -1977,6 +2026,21 @@ def create_thread_id():
|
|
1977 |
print(f"create new thread_id: {thread_id}")
|
1978 |
return thread_id
|
1979 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1980 |
# --- Slide mode ---
|
1981 |
def update_slide(direction):
|
1982 |
global TRANSCRIPTS
|
@@ -2021,6 +2085,13 @@ def init_params(text, request: gr.Request):
|
|
2021 |
reading_passage_admin = gr.update(visible=True)
|
2022 |
summary_admin = gr.update(visible=True)
|
2023 |
see_detail = gr.update(visible=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024 |
|
2025 |
# if youtube_link in query_params
|
2026 |
if "youtube_id" in request.query_params:
|
@@ -2036,8 +2107,14 @@ def init_params(text, request: gr.Request):
|
|
2036 |
reading_passage_admin = gr.update(visible=False)
|
2037 |
summary_admin = gr.update(visible=False)
|
2038 |
see_detail = gr.update(visible=False)
|
|
|
|
|
|
|
2039 |
|
2040 |
-
return admin, reading_passage_admin, summary_admin, see_detail,
|
|
|
|
|
|
|
2041 |
|
2042 |
def update_state(content_subject, content_grade, trascript, key_moments, question_1, question_2, question_3):
|
2043 |
# inputs=[content_subject, content_grade, df_string_output],
|
@@ -2138,14 +2215,68 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2138 |
key_moments_state = gr.State() # 使用 gr.State 存储 key_moments
|
2139 |
streaming_chat_thread_id_state = gr.State() # 使用 gr.State 存储 streaming_chat_thread_id
|
2140 |
with gr.Tab("AI小精靈"):
|
2141 |
-
with gr.
|
2142 |
-
with gr.
|
2143 |
-
|
2144 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2145 |
latex_delimiters = [{"left": "$", "right": "$", "display": False}]
|
2146 |
-
|
2147 |
-
|
2148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2149 |
with gr.Row():
|
2150 |
with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion:
|
2151 |
btn_1 = gr.Button("問題一")
|
@@ -2153,12 +2284,19 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2153 |
btn_3 = gr.Button("問題一")
|
2154 |
gr.Markdown("### 重新生成問題")
|
2155 |
btn_create_question = gr.Button("生成其他問題", variant="primary")
|
2156 |
-
openai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60)
|
2157 |
with gr.Row():
|
2158 |
msg = gr.Textbox(label="訊息",scale=3)
|
2159 |
send_button = gr.Button("送出", variant="primary", scale=1)
|
2160 |
-
|
2161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2162 |
streaming_chat = gr.ChatInterface(
|
2163 |
fn=streaming_chat_with_open_ai,
|
2164 |
additional_inputs=additional_inputs,
|
@@ -2166,41 +2304,41 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2166 |
retry_btn=None,
|
2167 |
undo_btn="⏪ 上一步",
|
2168 |
clear_btn="🗑️ 清除全部",
|
2169 |
-
stop_btn=
|
|
|
2170 |
)
|
2171 |
-
|
2172 |
-
|
2173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2174 |
ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
|
2175 |
with gr.Row():
|
2176 |
with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion_2:
|
2177 |
ai_chatbot_question_1 = gr.Button("問題一")
|
2178 |
ai_chatbot_question_2 = gr.Button("問題一")
|
2179 |
ai_chatbot_question_3 = gr.Button("問題一")
|
2180 |
-
ai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60)
|
2181 |
with gr.Row():
|
2182 |
ai_msg = gr.Textbox(label="訊息輸入",scale=3)
|
2183 |
ai_send_button = gr.Button("送出", variant="primary",scale=1)
|
2184 |
with gr.Tab("文章模式"):
|
2185 |
-
with gr.Row() as reading_passage_admin:
|
2186 |
-
reading_passage_kind = gr.Textbox(value="reading_passage", show_label=False)
|
2187 |
-
reading_passage_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2188 |
-
reading_passage_update_button = gr.Button("更新", size="sm", variant="primary")
|
2189 |
-
reading_passage_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2190 |
-
reading_passage_create_button = gr.Button("建立", size="sm", variant="primary")
|
2191 |
with gr.Row():
|
2192 |
-
reading_passage = gr.
|
2193 |
reading_passage_speak_button = gr.Button("Speak", visible=False)
|
2194 |
reading_passage_audio_output = gr.Audio(label="Audio Output", visible=False)
|
2195 |
with gr.Tab("重點摘要"):
|
2196 |
-
with gr.Row() as summary_admmin:
|
2197 |
-
summary_kind = gr.Textbox(value="summary", show_label=False)
|
2198 |
-
summary_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2199 |
-
summary_update_button = gr.Button("更新", size="sm", variant="primary")
|
2200 |
-
summary_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2201 |
-
summary_create_button = gr.Button("建立", size="sm", variant="primary")
|
2202 |
with gr.Row():
|
2203 |
-
df_summarise = gr.
|
2204 |
with gr.Tab("關鍵時刻"):
|
2205 |
with gr.Row():
|
2206 |
key_moments_html = gr.HTML(value="")
|
@@ -2221,16 +2359,16 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2221 |
worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2222 |
worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
2223 |
worksheet_exam_result_retrun_original = gr.Button("返回原始結果")
|
2224 |
-
with gr.Accordion("prompt", open=False):
|
2225 |
worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
2226 |
with gr.Column(scale=2):
|
2227 |
# 生成對應不同模式的結果
|
2228 |
worksheet_exam_result_prompt = gr.Textbox(visible=False)
|
2229 |
worksheet_exam_result_original = gr.Textbox(visible=False)
|
2230 |
-
worksheet_exam_result = gr.Textbox(label="初次生成結果", show_copy_button=True, interactive=True, lines=40)
|
|
|
2231 |
worksheet_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2232 |
worksheet_exam_result_word_link = gr.File(label="Download Word")
|
2233 |
-
|
2234 |
with gr.Tab("課程計畫"):
|
2235 |
with gr.Row():
|
2236 |
with gr.Column(scale=1):
|
@@ -2242,17 +2380,16 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2242 |
lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2243 |
lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
2244 |
lesson_plan_exam_result_retrun_original = gr.Button("返回原始結果")
|
2245 |
-
with gr.Accordion("prompt", open=False):
|
2246 |
lesson_plan_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
2247 |
with gr.Column(scale=2):
|
2248 |
# 生成對應不同模式的結果
|
2249 |
lesson_plan_exam_result_prompt = gr.Textbox(visible=False)
|
2250 |
lesson_plan_exam_result_original = gr.Textbox(visible=False)
|
2251 |
-
lesson_plan_exam_result = gr.
|
2252 |
|
2253 |
lesson_plan_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2254 |
lesson_plan_exam_result_word_link = gr.File(label="Download Word")
|
2255 |
-
|
2256 |
with gr.Tab("出場券"):
|
2257 |
with gr.Row():
|
2258 |
with gr.Column(scale=1):
|
@@ -2264,13 +2401,13 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2264 |
exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2265 |
exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
2266 |
exit_ticket_exam_result_retrun_original = gr.Button("返回原始結果")
|
2267 |
-
with gr.Accordion("prompt", open=False):
|
2268 |
exit_ticket_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
2269 |
with gr.Column(scale=2):
|
2270 |
# 生成對應不同模式的結果
|
2271 |
exit_ticket_exam_result_prompt = gr.Textbox(visible=False)
|
2272 |
exit_ticket_exam_result_original = gr.Textbox(visible=False)
|
2273 |
-
exit_ticket_exam_result = gr.
|
2274 |
|
2275 |
exit_ticket_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2276 |
exit_ticket_exam_result_word_link = gr.File(label="Download Word")
|
@@ -2294,17 +2431,39 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2294 |
with gr.Tab("逐字稿本文"):
|
2295 |
with gr.Row() as transcript_admmin:
|
2296 |
transcript_kind = gr.Textbox(value="transcript", show_label=False)
|
|
|
2297 |
transcript_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2298 |
-
transcript_update_button = gr.Button("
|
2299 |
transcript_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2300 |
transcript_create_button = gr.Button("建立", size="sm", variant="primary")
|
2301 |
with gr.Row():
|
2302 |
df_string_output = gr.Textbox(lines=40, label="Data Text", interactive=False, show_copy_button=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2303 |
with gr.Tab("關鍵時刻本文"):
|
2304 |
with gr.Row() as key_moments_admin:
|
2305 |
key_moments_kind = gr.Textbox(value="key_moments", show_label=False)
|
|
|
2306 |
key_moments_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2307 |
-
key_moments_update_button = gr.Button("
|
2308 |
key_moments_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2309 |
key_moments_create_button = gr.Button("建立", size="sm", variant="primary")
|
2310 |
with gr.Row():
|
@@ -2312,8 +2471,9 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2312 |
with gr.Tab("問題本文"):
|
2313 |
with gr.Row() as question_list_admin:
|
2314 |
questions_kind = gr.Textbox(value="questions", show_label=False)
|
|
|
2315 |
questions_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2316 |
-
questions_update_button = gr.Button("
|
2317 |
questions_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2318 |
questions_create_button = gr.Button("建立", size="sm", variant="primary")
|
2319 |
with gr.Row():
|
@@ -2337,11 +2497,29 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2337 |
mind_map_html = gr.HTML()
|
2338 |
|
2339 |
# --- Event ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2340 |
# OPENAI ASSISTANT CHATBOT 模式
|
2341 |
send_button.click(
|
2342 |
chat_with_opan_ai_assistant,
|
2343 |
-
inputs=[password, video_id, thread_id, trascript_state, key_moments, msg, chatbot, content_subject, content_grade, socratic_mode_btn],
|
2344 |
-
outputs=[msg, chatbot, thread_id]
|
|
|
2345 |
)
|
2346 |
openai_chatbot_audio_input.change(
|
2347 |
process_open_ai_audio_to_chatbot,
|
@@ -2349,23 +2527,26 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2349 |
outputs=[msg]
|
2350 |
)
|
2351 |
# OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
|
2352 |
-
btn_1_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, trascript_state, key_moments, btn_1, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2353 |
-
btn_2_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, trascript_state, key_moments, btn_2, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2354 |
-
btn_3_chat_with_opan_ai_assistant_input =[password, video_id, thread_id, trascript_state, key_moments, btn_3, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2355 |
btn_1.click(
|
2356 |
chat_with_opan_ai_assistant,
|
2357 |
inputs=btn_1_chat_with_opan_ai_assistant_input,
|
2358 |
-
outputs=[msg, chatbot, thread_id]
|
|
|
2359 |
)
|
2360 |
btn_2.click(
|
2361 |
chat_with_opan_ai_assistant,
|
2362 |
inputs=btn_2_chat_with_opan_ai_assistant_input,
|
2363 |
-
outputs=[msg, chatbot, thread_id]
|
|
|
2364 |
)
|
2365 |
btn_3.click(
|
2366 |
chat_with_opan_ai_assistant,
|
2367 |
inputs=btn_3_chat_with_opan_ai_assistant_input,
|
2368 |
-
outputs=[msg, chatbot, thread_id]
|
|
|
2369 |
)
|
2370 |
btn_create_question.click(
|
2371 |
change_questions,
|
@@ -2373,30 +2554,34 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2373 |
outputs = [btn_1, btn_2, btn_3]
|
2374 |
)
|
2375 |
|
2376 |
-
# ai_chatbot 模式
|
2377 |
ai_send_button.click(
|
2378 |
chat_with_ai,
|
2379 |
-
inputs=[ai_name, password, video_id, trascript_state, key_moments, ai_msg, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn],
|
2380 |
-
outputs=[ai_msg, ai_chatbot]
|
|
|
2381 |
)
|
2382 |
-
# ai_chatbot 连接按钮点击事件
|
2383 |
-
ai_chatbot_question_1_chat_with_ai_input =[ai_name, password, video_id, trascript_state, key_moments, ai_chatbot_question_1, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2384 |
-
ai_chatbot_question_2_chat_with_ai_input =[ai_name, password, video_id, trascript_state, key_moments, ai_chatbot_question_2, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2385 |
-
ai_chatbot_question_3_chat_with_ai_input =[ai_name, password, video_id, trascript_state, key_moments, ai_chatbot_question_3, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2386 |
ai_chatbot_question_1.click(
|
2387 |
chat_with_ai,
|
2388 |
inputs=ai_chatbot_question_1_chat_with_ai_input,
|
2389 |
-
outputs=[ai_msg, ai_chatbot]
|
|
|
2390 |
)
|
2391 |
ai_chatbot_question_2.click(
|
2392 |
chat_with_ai,
|
2393 |
inputs=ai_chatbot_question_2_chat_with_ai_input,
|
2394 |
-
outputs=[ai_msg, ai_chatbot]
|
|
|
2395 |
)
|
2396 |
ai_chatbot_question_3.click(
|
2397 |
chat_with_ai,
|
2398 |
inputs=ai_chatbot_question_3_chat_with_ai_input,
|
2399 |
-
outputs=[ai_msg, ai_chatbot]
|
|
|
2400 |
)
|
2401 |
|
2402 |
# file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
|
@@ -2411,6 +2596,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2411 |
btn_2,
|
2412 |
btn_3,
|
2413 |
df_string_output,
|
|
|
2414 |
df_summarise,
|
2415 |
key_moments,
|
2416 |
key_moments_html,
|
@@ -2420,6 +2606,7 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2420 |
simple_html_content,
|
2421 |
slide_image,
|
2422 |
slide_text,
|
|
|
2423 |
reading_passage,
|
2424 |
content_subject,
|
2425 |
content_grade,
|
@@ -2469,50 +2656,65 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2469 |
# web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
2470 |
|
2471 |
# reading_passage event
|
|
|
|
|
|
|
|
|
|
|
2472 |
reading_passage_create_button.click(
|
2473 |
create_LLM_content,
|
2474 |
inputs=[video_id, df_string_output, reading_passage_kind],
|
2475 |
-
outputs=[
|
2476 |
)
|
2477 |
reading_passage_delete_button.click(
|
2478 |
delete_LLM_content,
|
2479 |
inputs=[video_id, reading_passage_kind],
|
2480 |
-
outputs=[
|
2481 |
)
|
2482 |
reading_passage_edit_button.click(
|
2483 |
enable_edit_mode,
|
2484 |
inputs=[],
|
2485 |
-
outputs=[
|
2486 |
)
|
2487 |
reading_passage_update_button.click(
|
2488 |
update_LLM_content,
|
2489 |
-
inputs=[video_id,
|
2490 |
-
outputs=[
|
2491 |
)
|
2492 |
|
2493 |
# summary event
|
|
|
|
|
|
|
|
|
|
|
2494 |
summary_create_button.click(
|
2495 |
create_LLM_content,
|
2496 |
inputs=[video_id, df_string_output, summary_kind],
|
2497 |
-
outputs=[
|
2498 |
)
|
2499 |
summary_delete_button.click(
|
2500 |
delete_LLM_content,
|
2501 |
inputs=[video_id, summary_kind],
|
2502 |
-
outputs=[
|
2503 |
)
|
2504 |
summary_edit_button.click(
|
2505 |
enable_edit_mode,
|
2506 |
inputs=[],
|
2507 |
-
outputs=[
|
2508 |
)
|
2509 |
summary_update_button.click(
|
2510 |
update_LLM_content,
|
2511 |
-
inputs=[video_id,
|
2512 |
-
outputs=[
|
2513 |
)
|
2514 |
|
2515 |
# transcript event
|
|
|
|
|
|
|
|
|
|
|
2516 |
transcript_create_button.click(
|
2517 |
create_LLM_content,
|
2518 |
inputs=[video_id, df_string_output, transcript_kind],
|
@@ -2535,6 +2737,11 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2535 |
)
|
2536 |
|
2537 |
# key_moments event
|
|
|
|
|
|
|
|
|
|
|
2538 |
key_moments_create_button.click(
|
2539 |
create_LLM_content,
|
2540 |
inputs=[video_id, df_string_output, key_moments_kind],
|
@@ -2557,6 +2764,11 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2557 |
)
|
2558 |
|
2559 |
# question_list event
|
|
|
|
|
|
|
|
|
|
|
2560 |
questions_create_button.click(
|
2561 |
create_LLM_content,
|
2562 |
inputs=[video_id, df_string_output, questions_kind],
|
@@ -2643,10 +2855,24 @@ with gr.Blocks(theme=gr.themes.Base(primary_hue=gr.themes.colors.orange, seconda
|
|
2643 |
)
|
2644 |
|
2645 |
# init_params
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2646 |
demo.load(
|
2647 |
init_params,
|
2648 |
inputs =[youtube_link],
|
2649 |
-
outputs =
|
2650 |
)
|
2651 |
|
2652 |
demo.launch(allowed_paths=["videos"])
|
|
|
367 |
else:
|
368 |
return None
|
369 |
|
370 |
+
def get_transcript_by_yt_api(video_id):
|
371 |
languages = ['zh-TW', 'zh-Hant', 'zh', 'en'] # 優先順序列表
|
372 |
for language in languages:
|
373 |
try:
|
|
|
380 |
continue # 當前語言的字幕沒有找到,繼續嘗試下一個語言
|
381 |
return None # 所有嘗試都失敗,返回None
|
382 |
|
383 |
+
def generate_transcription_by_whisper(video_id):
|
384 |
youtube_url = f'https://www.youtube.com/watch?v={video_id}'
|
385 |
codec_name = "mp3"
|
386 |
outtmpl = f"{OUTPUT_PATH}/{video_id}.%(ext)s"
|
|
|
457 |
print("逐字稿文件不存在于GCS中,重新建立")
|
458 |
# 从YouTube获取逐字稿并上传
|
459 |
try:
|
460 |
+
transcript = get_transcript_by_yt_api(video_id)
|
461 |
except:
|
462 |
# call open ai whisper
|
463 |
print("===call open ai whisper===")
|
464 |
+
transcript = generate_transcription_by_whisper(video_id)
|
465 |
|
466 |
if transcript:
|
467 |
print("成功獲取字幕")
|
468 |
else:
|
469 |
print("沒有找到字幕")
|
470 |
+
transcript = generate_transcription_by_whisper(video_id)
|
471 |
|
472 |
transcript_text = json.dumps(transcript, ensure_ascii=False, indent=2)
|
473 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, transcript_blob_name, transcript_text)
|
|
|
572 |
questions = get_questions(video_id, formatted_simple_transcript, source)
|
573 |
questions_json = json.dumps(questions, ensure_ascii=False, indent=2)
|
574 |
summary_json = get_video_id_summary(video_id, formatted_simple_transcript, source)
|
575 |
+
summary_text = summary_json["summary"]
|
576 |
summary = summary_json["summary"]
|
577 |
key_moments_json = get_key_moments(video_id, formatted_simple_transcript, formatted_transcript, source)
|
578 |
key_moments = key_moments_json["key_moments"]
|
|
|
587 |
mind_map = mind_map_json["mind_map"]
|
588 |
mind_map_html = get_mind_map_html(mind_map)
|
589 |
reading_passage_json = get_reading_passage(video_id, formatted_simple_transcript, source)
|
590 |
+
reading_passage_text = reading_passage_json["reading_passage"]
|
591 |
reading_passage = reading_passage_json["reading_passage"]
|
592 |
meta_data = get_meta_data(video_id)
|
593 |
subject = meta_data["subject"]
|
|
|
600 |
questions[1] if len(questions) > 1 else "", \
|
601 |
questions[2] if len(questions) > 2 else "", \
|
602 |
original_transcript, \
|
603 |
+
summary_text, \
|
604 |
summary, \
|
605 |
key_moments_text, \
|
606 |
key_moments_html, \
|
|
|
610 |
simple_html_content, \
|
611 |
first_image, \
|
612 |
first_text, \
|
613 |
+
reading_passage_text, \
|
614 |
reading_passage, \
|
615 |
subject, \
|
616 |
grade
|
|
|
754 |
請一定要使用繁體中文 zh-TW,並用台灣人的口語
|
755 |
產生的結果不要前後文解釋,也不要敘述這篇文章怎麼產生的
|
756 |
只需要專注提供 Reading Passage,字數在 500 字以內
|
757 |
+
敘述中,請把數學或是專業術語,用 Latex 包覆($...$),並且不要去改原本的文章
|
758 |
+
加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
|
759 |
"""
|
760 |
messages = [
|
761 |
{"role": "system", "content": sys_content},
|
|
|
763 |
]
|
764 |
|
765 |
request_payload = {
|
766 |
+
"model": "gpt-4-turbo",
|
767 |
"messages": messages,
|
768 |
"max_tokens": 4000,
|
769 |
}
|
|
|
840 |
]
|
841 |
|
842 |
request_payload = {
|
843 |
+
"model": "gpt-4-turbo",
|
844 |
"messages": messages,
|
845 |
"max_tokens": 4000,
|
846 |
}
|
|
|
922 |
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
923 |
user_content = f"""
|
924 |
請根據 {df_string},判斷這份文本
|
925 |
+
格式為 Markdown
|
|
|
|
|
926 |
整體摘要在一百字以內
|
927 |
+
重點概念列出 bullet points,至少三個,最多五個
|
|
|
|
|
928 |
以及可能的結論與結尾延伸小問題提供學生作反思
|
929 |
+
敘述中,請把數學或是專業術語,用 Latex 包覆($...$)
|
930 |
+
加減乘除、根號、次方等等的運算式口語也換成 LATEX 數學符號
|
931 |
|
932 |
整體格式為:
|
933 |
+
## 📚 整體摘要
|
934 |
+
- (一個 bullet point....)
|
935 |
+
|
936 |
+
## 🔖 重點概念
|
937 |
+
- xxx
|
938 |
+
- xxx
|
939 |
+
- xxx
|
940 |
+
|
941 |
+
## 💡 為什麼我們要學這個?
|
942 |
+
- (一個 bullet point....)
|
943 |
+
|
944 |
+
## ❓ 延伸小問題
|
945 |
+
- (一個 bullet point....)
|
946 |
"""
|
947 |
|
948 |
# 🗂️ 1. 內容類型:?
|
|
|
958 |
]
|
959 |
|
960 |
request_payload = {
|
961 |
+
"model": "gpt-4-turbo",
|
962 |
"messages": messages,
|
963 |
"max_tokens": 4000,
|
964 |
}
|
|
|
1025 |
|
1026 |
def generate_questions(df_string):
|
1027 |
# 使用 OpenAI 生成基于上传数据的问题
|
1028 |
+
if isinstance(df_string, str):
|
1029 |
+
df_string_json = json.loads(df_string)
|
1030 |
+
else:
|
1031 |
+
df_string_json = df_string
|
1032 |
+
content_text = ""
|
1033 |
+
|
1034 |
+
for entry in df_string_json:
|
1035 |
+
content_text += entry["text"] + ","
|
1036 |
|
1037 |
sys_content = "你是一個擅長資料分析跟影片教學的老師,user 為學生,請精讀資料文本,自行判斷資料的種類,並用既有資料為本質猜測用戶可能會問的問題,使用 zh-TW"
|
1038 |
+
user_content = f"請根據 {content_text} 生成三個問題,並用 JSON 格式返回 questions:[q1的敘述text, q2的敘述text, q3的敘述text]"
|
1039 |
messages = [
|
1040 |
{"role": "system", "content": sys_content},
|
1041 |
{"role": "user", "content": user_content}
|
|
|
1048 |
|
1049 |
|
1050 |
request_payload = {
|
1051 |
+
"model": "gpt-4-turbo",
|
1052 |
"messages": messages,
|
1053 |
"max_tokens": 4000,
|
1054 |
"response_format": response_format
|
|
|
1166 |
response_format = { "type": "json_object" }
|
1167 |
|
1168 |
request_payload = {
|
1169 |
+
"model": "gpt-4-turbo",
|
1170 |
"messages": messages,
|
1171 |
"max_tokens": 4096,
|
1172 |
"response_format": response_format
|
|
|
1208 |
{"role": "user", "content": user_content}
|
1209 |
]
|
1210 |
request_payload = {
|
1211 |
+
"model": "gpt-4-turbo",
|
1212 |
"messages": messages,
|
1213 |
"max_tokens": 100,
|
1214 |
}
|
|
|
1421 |
return key_moments_html
|
1422 |
|
1423 |
# ---- LLM CRUD ----
|
1424 |
+
def get_LLM_content(video_id, kind):
|
1425 |
+
print(f"===get_{kind}===")
|
1426 |
+
gcs_client = GCS_CLIENT
|
1427 |
+
bucket_name = 'video_ai_assistant'
|
1428 |
+
file_name = f'{video_id}_{kind}.json'
|
1429 |
+
blob_name = f"{video_id}/{file_name}"
|
1430 |
+
# 检查 file 是否存在
|
1431 |
+
is_file_exists = GCS_SERVICE.check_file_exists(bucket_name, blob_name)
|
1432 |
+
if is_file_exists:
|
1433 |
+
content = download_blob_to_string(gcs_client, bucket_name, blob_name)
|
1434 |
+
content_json = json.loads(content)
|
1435 |
+
if kind == "reading_passage":
|
1436 |
+
content_text = content_json["reading_passage"]
|
1437 |
+
elif kind == "summary":
|
1438 |
+
content_text = content_json["summary"]
|
1439 |
+
else:
|
1440 |
+
content_text = json.dumps(content_json, ensure_ascii=False, indent=2)
|
1441 |
+
else:
|
1442 |
+
content_text = ""
|
1443 |
+
return content_text
|
1444 |
+
|
1445 |
def enable_edit_mode():
|
1446 |
return gr.update(interactive=True)
|
1447 |
|
|
|
1466 |
blob_name = f"{video_id}/{file_name}"
|
1467 |
|
1468 |
if kind == "reading_passage":
|
1469 |
+
print("=========reading_passage=======")
|
1470 |
+
print(new_content)
|
1471 |
reading_passage_json = {"reading_passage": str(new_content)}
|
1472 |
reading_passage_text = json.dumps(reading_passage_json, ensure_ascii=False, indent=2)
|
1473 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, reading_passage_text)
|
1474 |
+
updated_content = new_content
|
1475 |
elif kind == "summary":
|
1476 |
summary_json = {"summary": str(new_content)}
|
1477 |
summary_text = json.dumps(summary_json, ensure_ascii=False, indent=2)
|
1478 |
upload_file_to_gcs_with_json_string(gcs_client, bucket_name, blob_name, summary_text)
|
1479 |
+
updated_content = new_content
|
1480 |
elif kind == "mind_map":
|
1481 |
mind_map_json = {"mind_map": str(new_content)}
|
1482 |
mind_map_text = json.dumps(mind_map_json, ensure_ascii=False, indent=2)
|
|
|
1549 |
|
1550 |
return gr.update(value=content, interactive=False)
|
1551 |
|
|
|
1552 |
# AI 生成教學素材
|
1553 |
def get_meta_data(video_id, source="gcs"):
|
1554 |
if source == "gcs":
|
|
|
1652 |
prompt = material.generate_content_prompt()
|
1653 |
user_content = material.build_user_content()
|
1654 |
messages = material.build_messages(user_content)
|
1655 |
+
ai_model_name = "gpt-4-turbo"
|
1656 |
request_payload = {
|
1657 |
"model": ai_model_name,
|
1658 |
"messages": messages,
|
|
|
1666 |
material = EducationalMaterial(df_string_output, "", "", "", "", "")
|
1667 |
user_content = material.build_fine_tune_user_content(exam_result_prompt, exam_result, exam_result_fine_tune_prompt)
|
1668 |
messages = material.build_messages(user_content)
|
1669 |
+
ai_model_name = "gpt-4-turbo"
|
1670 |
request_payload = {
|
1671 |
"model": ai_model_name,
|
1672 |
"messages": messages,
|
|
|
1702 |
Language: Traditional Chinese ZH-TW (it's very important), suitable for {content_grade} th-grade level.
|
1703 |
Response:
|
1704 |
- Single question, under 100 characters
|
1705 |
+
- include math symbols (use LaTeX $ to cover before and after, ex: $x^2$)
|
1706 |
- hint with video timestamp which format 【參考:00:00:00】.
|
1707 |
- Sometimes encourage user by Taiwanese style with relaxing atmosphere.
|
1708 |
- if user ask questions not include in context,
|
|
|
1711 |
"""
|
1712 |
return instructions
|
1713 |
|
1714 |
+
def chat_with_ai(ai_name, password, video_id, user_data, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
|
1715 |
verify_password(password)
|
1716 |
|
1717 |
+
print("=====user_data=====")
|
1718 |
+
print(f"user_data: {user_data}")
|
1719 |
+
|
1720 |
+
if chat_history is not None and len(chat_history) > 11:
|
1721 |
+
error_msg = "此次對話超過上限(對話一輪10次)"
|
1722 |
raise gr.Error(error_msg)
|
1723 |
|
1724 |
+
if not ai_name in ["jutor", "claude3", "groq"]:
|
1725 |
+
ai_name = "jutor"
|
1726 |
+
|
1727 |
if ai_name == "jutor":
|
1728 |
ai_client = ""
|
1729 |
elif ai_name == "claude3":
|
1730 |
ai_client = BEDROCK_CLIENT
|
1731 |
elif ai_name == "groq":
|
1732 |
ai_client = GROQ_CLIENT
|
1733 |
+
else:
|
1734 |
+
ai_client = ""
|
1735 |
+
|
1736 |
if isinstance(trascript_state, str):
|
1737 |
simple_transcript = json.loads(trascript_state)
|
1738 |
else:
|
|
|
1746 |
for moment in key_moments_json:
|
1747 |
moment.pop('images', None)
|
1748 |
moment.pop('end', None)
|
1749 |
+
moment.pop('transcript', None)
|
1750 |
key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
|
1751 |
|
1752 |
instructions = get_instructions(content_subject, content_grade, key_moments_text)
|
|
|
1785 |
print(f"Error: {e}")
|
1786 |
return "请求失败,请稍后再试!", chat_history
|
1787 |
|
1788 |
+
def chat_with_opan_ai_assistant(password, youtube_id, user_data, thread_id, trascript_state, key_moments, user_message, chat_history, content_subject, content_grade, socratic_mode=False):
|
1789 |
verify_password(password)
|
1790 |
|
1791 |
+
print("=====user_data=====")
|
1792 |
+
print(f"user_data: {user_data}")
|
1793 |
+
|
1794 |
# 先計算 user_message 是否超過 500 個字
|
1795 |
if len(user_message) > 1500:
|
1796 |
error_msg = "你的訊息太長了,請縮短訊息長度至五百字以內"
|
|
|
1798 |
|
1799 |
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
1800 |
if chat_history is not None and len(chat_history) > 10:
|
1801 |
+
error_msg = "此次對話超過上限(對話一輪10次)"
|
1802 |
raise gr.Error(error_msg)
|
1803 |
|
1804 |
try:
|
|
|
1807 |
|
1808 |
client = OPEN_AI_CLIENT
|
1809 |
# 直接安排逐字稿資料 in instructions
|
1810 |
+
# if isinstance(trascript_state, str):
|
1811 |
+
# trascript_json = json.loads(trascript_state)
|
1812 |
+
# else:
|
1813 |
+
# trascript_json = trascript_state
|
1814 |
+
# # 移除 embed_url, screenshot_path
|
1815 |
+
# for entry in trascript_json:
|
1816 |
+
# entry.pop('end_time', None)
|
1817 |
+
# trascript_text = json.dumps(trascript_json, ensure_ascii=False)
|
1818 |
|
1819 |
if isinstance(key_moments, str):
|
1820 |
key_moments_json = json.loads(key_moments)
|
|
|
1824 |
for moment in key_moments_json:
|
1825 |
moment.pop('images', None)
|
1826 |
moment.pop('end', None)
|
1827 |
+
moment.pop('transcript', None)
|
1828 |
key_moments_text = json.dumps(key_moments_json, ensure_ascii=False)
|
1829 |
|
1830 |
instructions = get_instructions(content_subject, content_grade, key_moments_text)
|
|
|
1838 |
else:
|
1839 |
thread = client.beta.threads.retrieve(thread_id)
|
1840 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1841 |
# 向线程添加用户的消息
|
1842 |
client.beta.threads.messages.create(
|
1843 |
thread_id=thread.id,
|
1844 |
role="user",
|
1845 |
+
content=user_message + "/n 請嚴格遵循instructions,擔任一位蘇格拉底家教,絕對不要重複 user 的問句,請用引導的方式指引方向,請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,請在回答的最後標註【參考:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內,回答時請用數學符號代替文字(Latex 用 $ 字號 render, ex: $x^2$)"
|
1846 |
)
|
1847 |
|
1848 |
# 运行助手,生成响应
|
|
|
1946 |
|
1947 |
return run.status
|
1948 |
|
1949 |
+
def streaming_chat_with_open_ai(user_message, chat_history, password, user_data, thread_id, trascript, key_moments, content_subject, content_grade):
|
1950 |
verify_password(password)
|
1951 |
|
1952 |
+
print("=====user_data=====")
|
1953 |
+
print(f"user_data: {user_data}")
|
1954 |
+
|
1955 |
print("===streaming_chat_with_open_ai===")
|
1956 |
print(thread_id)
|
1957 |
|
|
|
1961 |
raise gr.Error(error_msg)
|
1962 |
|
1963 |
# 如果 chat_history 超過 10 則訊息,直接 return "對話超過上限"
|
1964 |
+
if chat_history is not None and len(chat_history) > 11:
|
1965 |
+
error_msg = "此次對話超過上限(對話一輪10次)"
|
1966 |
raise gr.Error(error_msg)
|
1967 |
|
1968 |
try:
|
|
|
1970 |
# assistant_id = "asst_5SaUElqvL3U0ybSi9PRM8x3P" #GPT 3.5 turbo
|
1971 |
client = OPEN_AI_CLIENT
|
1972 |
# 直接安排逐字稿資料 in instructions
|
1973 |
+
# if isinstance(trascript, str):
|
1974 |
+
# trascript_json = json.loads(trascript)
|
1975 |
+
# else:
|
1976 |
+
# trascript_json = trascript
|
1977 |
+
# trascript_text = json.dumps(trascript_json, ensure_ascii=False)
|
1978 |
+
# # trascript_text 移除 \n, 空白
|
1979 |
+
# trascript_text = trascript_text.replace("\n", "").replace(" ", "")
|
1980 |
|
1981 |
if isinstance(key_moments, str):
|
1982 |
key_moments_json = json.loads(key_moments)
|
|
|
2001 |
client.beta.threads.messages.create(
|
2002 |
thread_id=thread.id,
|
2003 |
role="user",
|
2004 |
+
content=user_message + "/n 請嚴格遵循instructions,擔任一位蘇格拉底家教,請一定要用繁體中文回答 zh-TW,並用台灣人的禮貌口語表達,回答時不要特別說明這是台灣人的語氣,不用提到「逐字稿」這個詞,用「內容」代替)),請在回答的最後標註【參考資料:(時):(分):(秒)】,(如果是反問學生,就只問一個問題,請幫助學生更好的理解資料,字數在100字以內)"
|
2005 |
)
|
2006 |
|
2007 |
with client.beta.threads.runs.stream(
|
|
|
2026 |
print(f"create new thread_id: {thread_id}")
|
2027 |
return thread_id
|
2028 |
|
2029 |
+
def chatbot_select(chatbot_name):
|
2030 |
+
chatbot_select_accordion_visible = gr.update(open=False)
|
2031 |
+
chatbot_open_ai_visible = gr.update(visible=False)
|
2032 |
+
chatbot_open_ai_streaming_visible = gr.update(visible=False)
|
2033 |
+
chatbot_jutor_visible = gr.update(visible=False)
|
2034 |
+
|
2035 |
+
if chatbot_name == "chatbot_open_ai":
|
2036 |
+
chatbot_open_ai_visible = gr.update(visible=True)
|
2037 |
+
elif chatbot_name == "chatbot_open_ai_streaming":
|
2038 |
+
chatbot_open_ai_streaming_visible = gr.update(visible=True)
|
2039 |
+
elif chatbot_name == "chatbot_jutor":
|
2040 |
+
chatbot_jutor_visible = gr.update(visible=True)
|
2041 |
+
|
2042 |
+
return chatbot_select_accordion_visible, chatbot_open_ai_visible, chatbot_open_ai_streaming_visible, chatbot_jutor_visible
|
2043 |
+
|
2044 |
# --- Slide mode ---
|
2045 |
def update_slide(direction):
|
2046 |
global TRANSCRIPTS
|
|
|
2085 |
reading_passage_admin = gr.update(visible=True)
|
2086 |
summary_admin = gr.update(visible=True)
|
2087 |
see_detail = gr.update(visible=True)
|
2088 |
+
worksheet_accordion = gr.update(visible=True)
|
2089 |
+
lesson_plan_accordion = gr.update(visible=True)
|
2090 |
+
exit_ticket_accordion = gr.update(visible=True)
|
2091 |
+
|
2092 |
+
chatbot_open_ai = gr.update(visible=False)
|
2093 |
+
chatbot_open_ai_streaming = gr.update(visible=False)
|
2094 |
+
chatbot_jutor = gr.update(visible=False)
|
2095 |
|
2096 |
# if youtube_link in query_params
|
2097 |
if "youtube_id" in request.query_params:
|
|
|
2107 |
reading_passage_admin = gr.update(visible=False)
|
2108 |
summary_admin = gr.update(visible=False)
|
2109 |
see_detail = gr.update(visible=False)
|
2110 |
+
worksheet_accordion = gr.update(visible=False)
|
2111 |
+
lesson_plan_accordion = gr.update(visible=False)
|
2112 |
+
exit_ticket_accordion = gr.update(visible=False)
|
2113 |
|
2114 |
+
return admin, reading_passage_admin, summary_admin, see_detail, \
|
2115 |
+
worksheet_accordion, lesson_plan_accordion, exit_ticket_accordion, \
|
2116 |
+
password_text, youtube_link, \
|
2117 |
+
chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor
|
2118 |
|
2119 |
def update_state(content_subject, content_grade, trascript, key_moments, question_1, question_2, question_3):
|
2120 |
# inputs=[content_subject, content_grade, df_string_output],
|
|
|
2215 |
key_moments_state = gr.State() # 使用 gr.State 存储 key_moments
|
2216 |
streaming_chat_thread_id_state = gr.State() # 使用 gr.State 存储 streaming_chat_thread_id
|
2217 |
with gr.Tab("AI小精靈"):
|
2218 |
+
with gr.Accordion("選擇 AI 小精靈", open=True) as chatbot_select_accordion:
|
2219 |
+
with gr.Row():
|
2220 |
+
with gr.Column(scale=1, variant="panel"):
|
2221 |
+
chatbot_avatar_url = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
2222 |
+
chatbot_description = """Hi,我是你的AI學伴【飛特精靈】,\n
|
2223 |
+
我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
|
2224 |
+
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!\n
|
2225 |
+
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!\n
|
2226 |
+
🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
|
2227 |
+
💤 但我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!\n
|
2228 |
+
🦄 如果達到上限,或是遇到精靈很累,請問問其他朋友,像是飛特音速說話的速度比較快,你是否跟得上呢?你也可以和其他精靈互動看看喔!\n
|
2229 |
+
"""
|
2230 |
+
chatbot_open_ai_name = gr.State("chatbot_open_ai")
|
2231 |
+
gr.Image(value=chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
|
2232 |
+
chatbot_open_ai_select_btn = gr.Button("👆選擇【飛特精靈】", elem_id="chatbot_btn", visible=True, variant="primary")
|
2233 |
+
gr.Markdown(value=chatbot_description, visible=True)
|
2234 |
+
with gr.Column(scale=1, variant="panel"):
|
2235 |
+
streaming_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2020/11/1-%E6%98%9F%E7%A9%BA%E9%A0%AD%E8%B2%BC-%E5%A4%AA%E7%A9%BA%E7%8B%90%E7%8B%B8%E8%B2%93-150x150.png"
|
2236 |
+
streaming_chatbot_description = """Hi,我是【飛特音速】, \n
|
2237 |
+
說話比較快,但有什麼問題都可以問我喔! \n
|
2238 |
+
🚀 我沒有預設問題、也沒有語音輸入,適合快問快答,一起練習問出好問題吧 \n
|
2239 |
+
🔠 擅長用文字表達的你,可以用鍵盤輸入你的問題,我會盡力回答你的問題喔\n
|
2240 |
+
💤 我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔~
|
2241 |
+
"""
|
2242 |
+
chatbot_open_ai_streaming_name = gr.State("chatbot_open_ai_streaming")
|
2243 |
+
gr.Image(value=streaming_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
|
2244 |
+
chatbot_open_ai_streaming_select_btn = gr.Button("👆選擇【飛特音速】", elem_id="streaming_chatbot_btn", visible=True, variant="primary")
|
2245 |
+
gr.Markdown(value=streaming_chatbot_description, visible=True)
|
2246 |
+
with gr.Column(scale=1, variant="panel"):
|
2247 |
+
jutor_chatbot_avatar_url = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2019/11/%E5%9B%9B%E6%A0%BC%E6%95%85%E4%BA%8B-04.jpg"
|
2248 |
+
jutor_chatbot_description = """Hi,我們是【梨梨、麥麥、狐狸貓】,\n
|
2249 |
+
也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!\n
|
2250 |
+
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!\n
|
2251 |
+
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!\n
|
2252 |
+
🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
|
2253 |
+
💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!\n
|
2254 |
+
"""
|
2255 |
+
chatbot_jutor_name = gr.State("chatbot_jutor")
|
2256 |
+
gr.Image(value=jutor_chatbot_avatar_url, height=100, width=100, show_label=False, show_download_button=False)
|
2257 |
+
chatbot_jutor_select_btn = gr.Button("👆選擇【梨梨、麥麥、狐狸貓】", elem_id="jutor_chatbot_btn", visible=True, variant="primary")
|
2258 |
+
gr.Markdown(value=jutor_chatbot_description, visible=True)
|
2259 |
+
|
2260 |
+
with gr.Row("飛特精靈") as chatbot_open_ai:
|
2261 |
+
with gr.Column():
|
2262 |
+
user_avatar = "https://em-content.zobj.net/source/google/263/flushed-face_1f633.png"
|
2263 |
+
bot_avatar = "https://junyitopicimg.s3.amazonaws.com/s4byy--icon.jpe?v=20200513013523726"
|
2264 |
latex_delimiters = [{"left": "$", "right": "$", "display": False}]
|
2265 |
+
chatbot_greeting = [[
|
2266 |
+
None,
|
2267 |
+
"""Hi,我是你的AI學伴【飛特精靈】,我可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
|
2268 |
+
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
|
2269 |
+
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
|
2270 |
+
🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!
|
2271 |
+
💤 但我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
|
2272 |
+
🦄 如果達到上限,或是��到精靈很累,請問問其他朋友,像是飛特音速說話的速度比較快,你是否跟得上呢?你也可以和其他精靈互動看看喔!
|
2273 |
+
""",
|
2274 |
+
]]
|
2275 |
+
with gr.Row():
|
2276 |
+
chatbot = gr.Chatbot(avatar_images=[user_avatar, bot_avatar], label="OPEN AI", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters,value=chatbot_greeting)
|
2277 |
+
with gr.Row():
|
2278 |
+
thread_id = gr.Textbox(label="thread_id", visible=False)
|
2279 |
+
socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
|
2280 |
with gr.Row():
|
2281 |
with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion:
|
2282 |
btn_1 = gr.Button("問題一")
|
|
|
2284 |
btn_3 = gr.Button("問題一")
|
2285 |
gr.Markdown("### 重新生成問題")
|
2286 |
btn_create_question = gr.Button("生成其他問題", variant="primary")
|
2287 |
+
openai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60, label="語音輸入")
|
2288 |
with gr.Row():
|
2289 |
msg = gr.Textbox(label="訊息",scale=3)
|
2290 |
send_button = gr.Button("送出", variant="primary", scale=1)
|
2291 |
+
with gr.Row("飛特音速") as chatbot_open_ai_streaming:
|
2292 |
+
with gr.Column():
|
2293 |
+
streaming_chat_greeting = """
|
2294 |
+
Hi,我是【飛特音速】,說話比較快,但有什麼問題都可以問我喔! \n
|
2295 |
+
🚀 我沒有預設問題、也沒有語音輸入,適合快問快答的你 \n
|
2296 |
+
🔠 鍵盤輸入你的問題,我會盡力回答你的問題喔!\n
|
2297 |
+
💤 我還在成長,體力有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
|
2298 |
+
"""
|
2299 |
+
additional_inputs = [password, user_data, streaming_chat_thread_id_state, trascript_state, key_moments_state, content_subject_state, content_grade_state]
|
2300 |
streaming_chat = gr.ChatInterface(
|
2301 |
fn=streaming_chat_with_open_ai,
|
2302 |
additional_inputs=additional_inputs,
|
|
|
2304 |
retry_btn=None,
|
2305 |
undo_btn="⏪ 上一步",
|
2306 |
clear_btn="🗑️ 清除全部",
|
2307 |
+
stop_btn=None,
|
2308 |
+
description=streaming_chat_greeting
|
2309 |
)
|
2310 |
+
with gr.Row("其他精靈") as chatbot_jutor:
|
2311 |
+
with gr.Column():
|
2312 |
+
ai_chatbot_greeting = [[
|
2313 |
+
None,
|
2314 |
+
"""Hi,我是飛特精靈的朋友們【梨梨、麥麥、狐狸貓】,也可以陪你一起學習本次的內容,有什麼問題都可以問我喔!
|
2315 |
+
🤔 如果你不知道怎麼發問,可以點擊左下方的問題一、問題二、問題三,我會幫你生成問題!
|
2316 |
+
🗣️ 也可以點擊右下方用語音輸入,我會幫你轉換成文字,厲害吧!
|
2317 |
+
🔠 或是直接鍵盤輸入你的問題,我會盡力回答你的問題喔!
|
2318 |
+
💤 精靈們體力都有限,每一次學習只能回答十個問題,請讓我休息一下再問問題喔!
|
2319 |
+
""",
|
2320 |
+
]]
|
2321 |
+
ai_chatbot_bot_avatar = "https://storage.googleapis.com/wpassets.junyiacademy.org/1/2019/11/%E5%9B%9B%E6%A0%BC%E6%95%85%E4%BA%8B-04.jpg"
|
2322 |
+
ai_name = gr.Dropdown(label="選擇 AI 助理", choices=[("梨梨","jutor"), ("麥麥","claude3"), ("狐狸貓","groq")], value="jutor")
|
2323 |
+
ai_chatbot = gr.Chatbot(avatar_images=[user_avatar, ai_chatbot_bot_avatar], label="ai_chatbot", show_share_button=False, likeable=True, show_label=False, latex_delimiters=latex_delimiters, value=ai_chatbot_greeting)
|
2324 |
ai_chatbot_socratic_mode_btn = gr.Checkbox(label="蘇格拉底家教助理模式", value=True, visible=False)
|
2325 |
with gr.Row():
|
2326 |
with gr.Accordion("你也有類似的問題想問嗎?", open=False) as ask_questions_accordion_2:
|
2327 |
ai_chatbot_question_1 = gr.Button("問題一")
|
2328 |
ai_chatbot_question_2 = gr.Button("問題一")
|
2329 |
ai_chatbot_question_3 = gr.Button("問題一")
|
2330 |
+
ai_chatbot_audio_input = gr.Audio(sources=["microphone"], type="filepath", max_length=60, label="語音輸入")
|
2331 |
with gr.Row():
|
2332 |
ai_msg = gr.Textbox(label="訊息輸入",scale=3)
|
2333 |
ai_send_button = gr.Button("送出", variant="primary",scale=1)
|
2334 |
with gr.Tab("文章模式"):
|
|
|
|
|
|
|
|
|
|
|
|
|
2335 |
with gr.Row():
|
2336 |
+
reading_passage = gr.Markdown(show_label=False, latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
2337 |
reading_passage_speak_button = gr.Button("Speak", visible=False)
|
2338 |
reading_passage_audio_output = gr.Audio(label="Audio Output", visible=False)
|
2339 |
with gr.Tab("重點摘要"):
|
|
|
|
|
|
|
|
|
|
|
|
|
2340 |
with gr.Row():
|
2341 |
+
df_summarise = gr.Markdown(show_label=False, latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
2342 |
with gr.Tab("關鍵時刻"):
|
2343 |
with gr.Row():
|
2344 |
key_moments_html = gr.HTML(value="")
|
|
|
2359 |
worksheet_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2360 |
worksheet_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
2361 |
worksheet_exam_result_retrun_original = gr.Button("返回原始結果")
|
2362 |
+
with gr.Accordion("prompt", open=False) as worksheet_accordion:
|
2363 |
worksheet_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
2364 |
with gr.Column(scale=2):
|
2365 |
# 生成對應不同模式的結果
|
2366 |
worksheet_exam_result_prompt = gr.Textbox(visible=False)
|
2367 |
worksheet_exam_result_original = gr.Textbox(visible=False)
|
2368 |
+
# worksheet_exam_result = gr.Textbox(label="初次生成結果", show_copy_button=True, interactive=True, lines=40)
|
2369 |
+
worksheet_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
2370 |
worksheet_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2371 |
worksheet_exam_result_word_link = gr.File(label="Download Word")
|
|
|
2372 |
with gr.Tab("課程計畫"):
|
2373 |
with gr.Row():
|
2374 |
with gr.Column(scale=1):
|
|
|
2380 |
lesson_plan_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2381 |
lesson_plan_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
2382 |
lesson_plan_exam_result_retrun_original = gr.Button("返回原始結果")
|
2383 |
+
with gr.Accordion("prompt", open=False) as lesson_plan_accordion:
|
2384 |
lesson_plan_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
2385 |
with gr.Column(scale=2):
|
2386 |
# 生成對應不同模式的結果
|
2387 |
lesson_plan_exam_result_prompt = gr.Textbox(visible=False)
|
2388 |
lesson_plan_exam_result_original = gr.Textbox(visible=False)
|
2389 |
+
lesson_plan_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
2390 |
|
2391 |
lesson_plan_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2392 |
lesson_plan_exam_result_word_link = gr.File(label="Download Word")
|
|
|
2393 |
with gr.Tab("出場券"):
|
2394 |
with gr.Row():
|
2395 |
with gr.Column(scale=1):
|
|
|
2401 |
exit_ticket_exam_result_fine_tune_prompt = gr.Textbox(label="根據結果,輸入你想更改的想法")
|
2402 |
exit_ticket_exam_result_fine_tune_btn = gr.Button("微調結果", variant="primary")
|
2403 |
exit_ticket_exam_result_retrun_original = gr.Button("返回原始結果")
|
2404 |
+
with gr.Accordion("prompt", open=False) as exit_ticket_accordion:
|
2405 |
exit_ticket_prompt = gr.Textbox(label="worksheet_prompt", show_copy_button=True, lines=40)
|
2406 |
with gr.Column(scale=2):
|
2407 |
# 生成對應不同模式的結果
|
2408 |
exit_ticket_exam_result_prompt = gr.Textbox(visible=False)
|
2409 |
exit_ticket_exam_result_original = gr.Textbox(visible=False)
|
2410 |
+
exit_ticket_exam_result = gr.Markdown(label="初次生成結果", latex_delimiters = [{"left": "$", "right": "$", "display": False}])
|
2411 |
|
2412 |
exit_ticket_download_exam_result_button = gr.Button("轉成 word,完成後請點擊右下角 download 按鈕", variant="primary")
|
2413 |
exit_ticket_exam_result_word_link = gr.File(label="Download Word")
|
|
|
2431 |
with gr.Tab("逐字稿本文"):
|
2432 |
with gr.Row() as transcript_admmin:
|
2433 |
transcript_kind = gr.Textbox(value="transcript", show_label=False)
|
2434 |
+
transcript_get_button = gr.Button("取得", size="sm", variant="primary")
|
2435 |
transcript_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2436 |
+
transcript_update_button = gr.Button("儲存", size="sm", variant="primary")
|
2437 |
transcript_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2438 |
transcript_create_button = gr.Button("建立", size="sm", variant="primary")
|
2439 |
with gr.Row():
|
2440 |
df_string_output = gr.Textbox(lines=40, label="Data Text", interactive=False, show_copy_button=True)
|
2441 |
+
with gr.Tab("文章本文"):
|
2442 |
+
with gr.Row() as reading_passage_admin:
|
2443 |
+
reading_passage_kind = gr.Textbox(value="reading_passage", show_label=False)
|
2444 |
+
reading_passage_get_button = gr.Button("取得", size="sm", variant="primary")
|
2445 |
+
reading_passage_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2446 |
+
reading_passage_update_button = gr.Button("更新", size="sm", variant="primary")
|
2447 |
+
reading_passage_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2448 |
+
reading_passage_create_button = gr.Button("建立", size="sm", variant="primary")
|
2449 |
+
with gr.Row():
|
2450 |
+
reading_passage_text = gr.Textbox(label="reading_passage", lines=40, interactive=False, show_copy_button=True)
|
2451 |
+
with gr.Tab("重點摘要本文"):
|
2452 |
+
with gr.Row() as summary_admmin:
|
2453 |
+
summary_kind = gr.Textbox(value="summary", show_label=False)
|
2454 |
+
summary_get_button = gr.Button("取得", size="sm", variant="primary")
|
2455 |
+
summary_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2456 |
+
summary_update_button = gr.Button("更新", size="sm", variant="primary")
|
2457 |
+
summary_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2458 |
+
summary_create_button = gr.Button("建立", size="sm", variant="primary")
|
2459 |
+
with gr.Row():
|
2460 |
+
summary_text = gr.Textbox(label="Summary", lines=40, interactive=False, show_copy_button=True)
|
2461 |
with gr.Tab("關鍵時刻本文"):
|
2462 |
with gr.Row() as key_moments_admin:
|
2463 |
key_moments_kind = gr.Textbox(value="key_moments", show_label=False)
|
2464 |
+
key_moments_get_button = gr.Button("取得", size="sm", variant="primary")
|
2465 |
key_moments_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2466 |
+
key_moments_update_button = gr.Button("儲存", size="sm", variant="primary")
|
2467 |
key_moments_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2468 |
key_moments_create_button = gr.Button("建立", size="sm", variant="primary")
|
2469 |
with gr.Row():
|
|
|
2471 |
with gr.Tab("問題本文"):
|
2472 |
with gr.Row() as question_list_admin:
|
2473 |
questions_kind = gr.Textbox(value="questions", show_label=False)
|
2474 |
+
questions_get_button = gr.Button("取得", size="sm", variant="primary")
|
2475 |
questions_edit_button = gr.Button("編輯", size="sm", variant="primary")
|
2476 |
+
questions_update_button = gr.Button("儲存", size="sm", variant="primary")
|
2477 |
questions_delete_button = gr.Button("刪除", size="sm", variant="primary")
|
2478 |
questions_create_button = gr.Button("建立", size="sm", variant="primary")
|
2479 |
with gr.Row():
|
|
|
2497 |
mind_map_html = gr.HTML()
|
2498 |
|
2499 |
# --- Event ---
|
2500 |
+
# CHATBOT SELECT
|
2501 |
+
chatbot_open_ai_select_btn.click(
|
2502 |
+
chatbot_select,
|
2503 |
+
inputs=[chatbot_open_ai_name],
|
2504 |
+
outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
|
2505 |
+
)
|
2506 |
+
chatbot_open_ai_streaming_select_btn.click(
|
2507 |
+
chatbot_select,
|
2508 |
+
inputs=[chatbot_open_ai_streaming_name],
|
2509 |
+
outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
|
2510 |
+
)
|
2511 |
+
chatbot_jutor_select_btn.click(
|
2512 |
+
chatbot_select,
|
2513 |
+
inputs=[chatbot_jutor_name],
|
2514 |
+
outputs=[chatbot_select_accordion, chatbot_open_ai, chatbot_open_ai_streaming, chatbot_jutor]
|
2515 |
+
)
|
2516 |
+
|
2517 |
# OPENAI ASSISTANT CHATBOT 模式
|
2518 |
send_button.click(
|
2519 |
chat_with_opan_ai_assistant,
|
2520 |
+
inputs=[password, video_id, user_data, thread_id, trascript_state, key_moments, msg, chatbot, content_subject, content_grade, socratic_mode_btn],
|
2521 |
+
outputs=[msg, chatbot, thread_id],
|
2522 |
+
scroll_to_output=True
|
2523 |
)
|
2524 |
openai_chatbot_audio_input.change(
|
2525 |
process_open_ai_audio_to_chatbot,
|
|
|
2527 |
outputs=[msg]
|
2528 |
)
|
2529 |
# OPENAI ASSISTANT CHATBOT 連接按鈕點擊事件
|
2530 |
+
btn_1_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_1, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2531 |
+
btn_2_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_2, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2532 |
+
btn_3_chat_with_opan_ai_assistant_input =[password, video_id, user_data, thread_id, trascript_state, key_moments, btn_3, chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2533 |
btn_1.click(
|
2534 |
chat_with_opan_ai_assistant,
|
2535 |
inputs=btn_1_chat_with_opan_ai_assistant_input,
|
2536 |
+
outputs=[msg, chatbot, thread_id],
|
2537 |
+
scroll_to_output=True
|
2538 |
)
|
2539 |
btn_2.click(
|
2540 |
chat_with_opan_ai_assistant,
|
2541 |
inputs=btn_2_chat_with_opan_ai_assistant_input,
|
2542 |
+
outputs=[msg, chatbot, thread_id],
|
2543 |
+
scroll_to_output=True
|
2544 |
)
|
2545 |
btn_3.click(
|
2546 |
chat_with_opan_ai_assistant,
|
2547 |
inputs=btn_3_chat_with_opan_ai_assistant_input,
|
2548 |
+
outputs=[msg, chatbot, thread_id],
|
2549 |
+
scroll_to_output=True
|
2550 |
)
|
2551 |
btn_create_question.click(
|
2552 |
change_questions,
|
|
|
2554 |
outputs = [btn_1, btn_2, btn_3]
|
2555 |
)
|
2556 |
|
2557 |
+
# 其他精靈 ai_chatbot 模式
|
2558 |
ai_send_button.click(
|
2559 |
chat_with_ai,
|
2560 |
+
inputs=[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_msg, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn],
|
2561 |
+
outputs=[ai_msg, ai_chatbot],
|
2562 |
+
scroll_to_output=True
|
2563 |
)
|
2564 |
+
# 其他精靈 ai_chatbot 连接按钮点击事件
|
2565 |
+
ai_chatbot_question_1_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_1, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2566 |
+
ai_chatbot_question_2_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_2, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2567 |
+
ai_chatbot_question_3_chat_with_ai_input =[ai_name, password, video_id, user_data, trascript_state, key_moments, ai_chatbot_question_3, ai_chatbot, content_subject, content_grade, ai_chatbot_socratic_mode_btn]
|
2568 |
ai_chatbot_question_1.click(
|
2569 |
chat_with_ai,
|
2570 |
inputs=ai_chatbot_question_1_chat_with_ai_input,
|
2571 |
+
outputs=[ai_msg, ai_chatbot],
|
2572 |
+
scroll_to_output=True
|
2573 |
)
|
2574 |
ai_chatbot_question_2.click(
|
2575 |
chat_with_ai,
|
2576 |
inputs=ai_chatbot_question_2_chat_with_ai_input,
|
2577 |
+
outputs=[ai_msg, ai_chatbot],
|
2578 |
+
scroll_to_output=True
|
2579 |
)
|
2580 |
ai_chatbot_question_3.click(
|
2581 |
chat_with_ai,
|
2582 |
inputs=ai_chatbot_question_3_chat_with_ai_input,
|
2583 |
+
outputs=[ai_msg, ai_chatbot],
|
2584 |
+
scroll_to_output=True
|
2585 |
)
|
2586 |
|
2587 |
# file_upload.change(process_file, inputs=file_upload, outputs=df_string_output)
|
|
|
2596 |
btn_2,
|
2597 |
btn_3,
|
2598 |
df_string_output,
|
2599 |
+
summary_text,
|
2600 |
df_summarise,
|
2601 |
key_moments,
|
2602 |
key_moments_html,
|
|
|
2606 |
simple_html_content,
|
2607 |
slide_image,
|
2608 |
slide_text,
|
2609 |
+
reading_passage_text,
|
2610 |
reading_passage,
|
2611 |
content_subject,
|
2612 |
content_grade,
|
|
|
2656 |
# web_link.change(process_web_link, inputs=web_link, outputs=[btn_1, btn_2, btn_3, df_summarise, df_string_output])
|
2657 |
|
2658 |
# reading_passage event
|
2659 |
+
reading_passage_get_button.click(
|
2660 |
+
get_LLM_content,
|
2661 |
+
inputs=[video_id, reading_passage_kind],
|
2662 |
+
outputs=[reading_passage_text]
|
2663 |
+
)
|
2664 |
reading_passage_create_button.click(
|
2665 |
create_LLM_content,
|
2666 |
inputs=[video_id, df_string_output, reading_passage_kind],
|
2667 |
+
outputs=[reading_passage_text]
|
2668 |
)
|
2669 |
reading_passage_delete_button.click(
|
2670 |
delete_LLM_content,
|
2671 |
inputs=[video_id, reading_passage_kind],
|
2672 |
+
outputs=[reading_passage_text]
|
2673 |
)
|
2674 |
reading_passage_edit_button.click(
|
2675 |
enable_edit_mode,
|
2676 |
inputs=[],
|
2677 |
+
outputs=[reading_passage_text]
|
2678 |
)
|
2679 |
reading_passage_update_button.click(
|
2680 |
update_LLM_content,
|
2681 |
+
inputs=[video_id, reading_passage_text, reading_passage_kind],
|
2682 |
+
outputs=[reading_passage_text]
|
2683 |
)
|
2684 |
|
2685 |
# summary event
|
2686 |
+
summary_get_button.click(
|
2687 |
+
get_LLM_content,
|
2688 |
+
inputs=[video_id, summary_kind],
|
2689 |
+
outputs=[summary_text]
|
2690 |
+
)
|
2691 |
summary_create_button.click(
|
2692 |
create_LLM_content,
|
2693 |
inputs=[video_id, df_string_output, summary_kind],
|
2694 |
+
outputs=[summary_text]
|
2695 |
)
|
2696 |
summary_delete_button.click(
|
2697 |
delete_LLM_content,
|
2698 |
inputs=[video_id, summary_kind],
|
2699 |
+
outputs=[summary_text]
|
2700 |
)
|
2701 |
summary_edit_button.click(
|
2702 |
enable_edit_mode,
|
2703 |
inputs=[],
|
2704 |
+
outputs=[summary_text]
|
2705 |
)
|
2706 |
summary_update_button.click(
|
2707 |
update_LLM_content,
|
2708 |
+
inputs=[video_id, summary_text, summary_kind],
|
2709 |
+
outputs=[summary_text]
|
2710 |
)
|
2711 |
|
2712 |
# transcript event
|
2713 |
+
transcript_get_button.click(
|
2714 |
+
get_LLM_content,
|
2715 |
+
inputs=[video_id, transcript_kind],
|
2716 |
+
outputs=[df_string_output]
|
2717 |
+
)
|
2718 |
transcript_create_button.click(
|
2719 |
create_LLM_content,
|
2720 |
inputs=[video_id, df_string_output, transcript_kind],
|
|
|
2737 |
)
|
2738 |
|
2739 |
# key_moments event
|
2740 |
+
key_moments_get_button.click(
|
2741 |
+
get_LLM_content,
|
2742 |
+
inputs=[video_id, key_moments_kind],
|
2743 |
+
outputs=[key_moments]
|
2744 |
+
)
|
2745 |
key_moments_create_button.click(
|
2746 |
create_LLM_content,
|
2747 |
inputs=[video_id, df_string_output, key_moments_kind],
|
|
|
2764 |
)
|
2765 |
|
2766 |
# question_list event
|
2767 |
+
questions_get_button.click(
|
2768 |
+
get_LLM_content,
|
2769 |
+
inputs=[video_id, questions_kind],
|
2770 |
+
outputs=[questions_json]
|
2771 |
+
)
|
2772 |
questions_create_button.click(
|
2773 |
create_LLM_content,
|
2774 |
inputs=[video_id, df_string_output, questions_kind],
|
|
|
2855 |
)
|
2856 |
|
2857 |
# init_params
|
2858 |
+
init_outputs = [
|
2859 |
+
admin,
|
2860 |
+
reading_passage_admin,
|
2861 |
+
summary_admmin,
|
2862 |
+
see_details,
|
2863 |
+
worksheet_accordion,
|
2864 |
+
lesson_plan_accordion,
|
2865 |
+
exit_ticket_accordion,
|
2866 |
+
password,
|
2867 |
+
youtube_link,
|
2868 |
+
chatbot_open_ai,
|
2869 |
+
chatbot_open_ai_streaming,
|
2870 |
+
chatbot_jutor
|
2871 |
+
]
|
2872 |
demo.load(
|
2873 |
init_params,
|
2874 |
inputs =[youtube_link],
|
2875 |
+
outputs = init_outputs
|
2876 |
)
|
2877 |
|
2878 |
demo.launch(allowed_paths=["videos"])
|
chatbot.py
CHANGED
@@ -82,7 +82,7 @@ class Chatbot:
|
|
82 |
"Content-Type": "application/json",
|
83 |
"x-api-key": self.jutor_chat_key,
|
84 |
}
|
85 |
-
model = "gpt-4-
|
86 |
# model = "gpt-3.5-turbo-0125"
|
87 |
data = {
|
88 |
"data": {
|
|
|
82 |
"Content-Type": "application/json",
|
83 |
"x-api-key": self.jutor_chat_key,
|
84 |
}
|
85 |
+
model = "gpt-4-turbo"
|
86 |
# model = "gpt-3.5-turbo-0125"
|
87 |
data = {
|
88 |
"data": {
|
educational_material.py
CHANGED
@@ -30,14 +30,13 @@ class EducationalMaterial:
|
|
30 |
self.level = level
|
31 |
self.specific_feature = specific_feature
|
32 |
self.content_type = content_type # 'worksheet' or 'lesson_plan'
|
33 |
-
self.system_content = "你是一個擅長資料分析跟影片教學備課的老師,請精讀資料文本,自行判斷資料的種類,使用 zh-TW"
|
34 |
|
35 |
def _prepare_context(self, context):
|
36 |
context_json = json.loads(context)
|
|
|
37 |
for entry in context_json:
|
38 |
-
entry.
|
39 |
-
entry.pop('screenshot_path', None)
|
40 |
-
processed_context = json.dumps(context_json, ensure_ascii=False, indent=2)
|
41 |
return processed_context
|
42 |
|
43 |
def generate_content_prompt(self):
|
@@ -50,7 +49,8 @@ class EducationalMaterial:
|
|
50 |
|
51 |
def _generate_worksheet_prompt(self):
|
52 |
bloom_worksheet_prompt = """
|
53 |
-
|
|
|
54 |
|
55 |
【認知領域】涉及知能及其運作,著重心智、學習以及問題解決的工作。認知目標從簡單的認識或記憶能力到複雜的評鑑能力。大部分的教育目標都屬於這個領域。認知領域的目標分為六個層次,每個層次代表不同的心智功能。
|
56 |
- 📖 知識:在認知目標中知識是最低層次的能力,包括名詞、事實、規則和原理原則等的認識和記憶。用來表示此種能力的行為動詞有:指出、寫出、界定、說明、舉例、命名、認明等。例:能在地圖上指出長江流經的省分。
|
@@ -74,12 +74,12 @@ class EducationalMaterial:
|
|
74 |
🌐【主題】:認識公里
|
75 |
|
76 |
🎓【對象】
|
77 |
-
科目: 數學
|
78 |
-
年級: 三年級
|
79 |
-
難度: 基礎
|
80 |
|
81 |
🏞️【情境描述】
|
82 |
-
狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
|
83 |
|
84 |
🔑【影片重點】
|
85 |
- 公里是用來測量長距離的單位,通常用於測量很遠的距離。
|
@@ -101,7 +101,8 @@ class EducationalMaterial:
|
|
101 |
"""
|
102 |
|
103 |
Polya_worksheet_prompt = """
|
104 |
-
|
|
|
105 |
Polya提出了一個四步驟的數學問題解決策略,在他影響深遠的經典著作 How to solve it《如何解題》中指出解難過程可分為四個階段:
|
106 |
(1) 理解問題 (understanding the problem)
|
107 |
(2) 設計解題策略 (devising a plan)
|
@@ -115,9 +116,9 @@ class EducationalMaterial:
|
|
115 |
🌐 主題:【概念】認識公里
|
116 |
|
117 |
🎓【對象】
|
118 |
-
科目: 數學
|
119 |
-
年級: 三年級
|
120 |
-
難度: 基礎
|
121 |
|
122 |
🏞️【情境描述】
|
123 |
狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
|
@@ -148,7 +149,8 @@ class EducationalMaterial:
|
|
148 |
|
149 |
# CRA教學法
|
150 |
cra_worksheet_prompt = """
|
151 |
-
|
|
|
152 |
CRA正是一種用來促進學生學習和記憶數學的三步教學法,它闡明瞭用這種方式進行教學的具體步驟。
|
153 |
CRA的三個步驟互相依賴,運用CRA能建立起一種概念結構,從而形成知識的意義關聯。
|
154 |
CRA策略的第一個階段, 即實例(C)階段,是一個“做”的階段。在這一階段,教師用加工材料建模,這些材料包括彩色圓片、立方體、十進位積木、六形六色積木,以及分數積木,等等。在使用這些材料時,必須考���到兒童的視覺、觸覺及動感經驗。
|
@@ -160,9 +162,9 @@ class EducationalMaterial:
|
|
160 |
🌐 主題:【概念】認識公里
|
161 |
|
162 |
🎓【對象】
|
163 |
-
科目: 數學
|
164 |
-
年級: 三年級
|
165 |
-
難度: 基礎
|
166 |
|
167 |
【實例(C)階段】
|
168 |
1. 用彩色圓片來解釋什麼是分數?
|
|
|
30 |
self.level = level
|
31 |
self.specific_feature = specific_feature
|
32 |
self.content_type = content_type # 'worksheet' or 'lesson_plan'
|
33 |
+
self.system_content = "你是一個擅長資料分析跟影片教學備課的老師,請精讀資料文本,自行判斷資料的種類,使用 zh-TW,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。"
|
34 |
|
35 |
def _prepare_context(self, context):
|
36 |
context_json = json.loads(context)
|
37 |
+
processed_context = ""
|
38 |
for entry in context_json:
|
39 |
+
processed_context += entry.get('text', '') + "\n"
|
|
|
|
|
40 |
return processed_context
|
41 |
|
42 |
def generate_content_prompt(self):
|
|
|
49 |
|
50 |
def _generate_worksheet_prompt(self):
|
51 |
bloom_worksheet_prompt = """
|
52 |
+
你是個專業的教師,,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
|
53 |
+
熟悉布魯姆(Benjamin Bloom, 1964) 的認知理論。布魯姆認為人類的能力,大致可分為三個領域(domains),即認知領域(cognitive domain)、情意領域(affective domain)、技能領域 (psychomotor domain)。
|
54 |
|
55 |
【認知領域】涉及知能及其運作,著重心智、學習以及問題解決的工作。認知目標從簡單的認識或記憶能力到複雜的評鑑能力。大部分的教育目標都屬於這個領域。認知領域的目標分為六個層次,每個層次代表不同的心智功能。
|
56 |
- 📖 知識:在認知目標中知識是最低層次的能力,包括名詞、事實、規則和原理原則等的認識和記憶。用來表示此種能力的行為動詞有:指出、寫出、界定、說明、舉例、命名、認明等。例:能在地圖上指出長江流經的省分。
|
|
|
74 |
🌐【主題】:認識公里
|
75 |
|
76 |
🎓【對象】
|
77 |
+
- 科目: 數學
|
78 |
+
- 年級: 三年級
|
79 |
+
- 難度: 基礎
|
80 |
|
81 |
🏞️【情境描述】
|
82 |
+
- 狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
|
83 |
|
84 |
🔑【影片重點】
|
85 |
- 公里是用來測量長距離的單位,通常用於測量很遠的距離。
|
|
|
101 |
"""
|
102 |
|
103 |
Polya_worksheet_prompt = """
|
104 |
+
你是個專業的教師,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
|
105 |
+
熟悉 George Polya(1945) 的數學問題解決策略。
|
106 |
Polya提出了一個四步驟的數學問題解決策略,在他影響深遠的經典著作 How to solve it《如何解題》中指出解難過程可分為四個階段:
|
107 |
(1) 理解問題 (understanding the problem)
|
108 |
(2) 設計解題策略 (devising a plan)
|
|
|
116 |
🌐 主題:【概念】認識公里
|
117 |
|
118 |
🎓【對象】
|
119 |
+
- 科目: 數學
|
120 |
+
- 年級: 三年級
|
121 |
+
- 難度: 基礎
|
122 |
|
123 |
🏞️【情境描述】
|
124 |
狐狸貓和家人出遊,過程中認識測量較長距離的單位「公里」。
|
|
|
149 |
|
150 |
# CRA教學法
|
151 |
cra_worksheet_prompt = """
|
152 |
+
你是個專業的教師,,遇到數學符號或是敘述請用 Latex 語法($...$),例如:$x^2$。
|
153 |
+
熟悉CRA教學法,CRA教學法是一種教學策略,
|
154 |
CRA正是一種用來促進學生學習和記憶數學的三步教學法,它闡明瞭用這種方式進行教學的具體步驟。
|
155 |
CRA的三個步驟互相依賴,運用CRA能建立起一種概念結構,從而形成知識的意義關聯。
|
156 |
CRA策略的第一個階段, 即實例(C)階段,是一個“做”的階段。在這一階段,教師用加工材料建模,這些材料包括彩色圓片、立方體、十進位積木、六形六色積木,以及分數積木,等等。在使用這些材料時,必須考���到兒童的視覺、觸覺及動感經驗。
|
|
|
162 |
🌐 主題:【概念】認識公里
|
163 |
|
164 |
🎓【對象】
|
165 |
+
- 科目: 數學
|
166 |
+
- 年級: 三年級
|
167 |
+
- 難度: 基礎
|
168 |
|
169 |
【實例(C)階段】
|
170 |
1. 用彩色圓片來解釋什麼是分數?
|