Spaces:
Sleeping
Sleeping
| import subprocess | |
| import sys | |
| import os | |
| import re | |
| import pandas as pd | |
| from pydub import AudioSegment | |
| try: | |
| import openai | |
| except ImportError: | |
| subprocess.check_call([sys.executable, "-m", "pip", "install", "openai"]) | |
| import openai # Import the library after installing it | |
| from datetime import datetime | |
| import pytz | |
| taipei_tz = pytz.timezone('Asia/Taipei') | |
| current_time_taipei = datetime.now(taipei_tz) | |
| def openai_api(prompt, key): | |
| openai.api_key = key | |
| completion = openai.chat.completions.create( | |
| model="o1-preview", | |
| messages=[{"role": "user", "content": prompt}] | |
| ) | |
| return completion.choices[0].message.content | |
| def extract_option(text): | |
| # 使用正規表達式來尋找模式 "|sel-D:| 選項D" | |
| match = re.search(r'\|sel-D:\|\s*(.*?)\s*(?:\||\.$|$)', text) | |
| if match: | |
| return match.group(1) # 返回匹配的部分 | |
| else: | |
| return "No match found" | |
| def extract_letter(s): | |
| # 使用正則表達式匹配單獨的A、B、C或D,或括號中的A、B、C或D | |
| match = re.search(r'\b([A-D])\b|\(([A-D])\)', s) | |
| # 如果匹配成功,返回匹配的字符 | |
| if match: | |
| return match.group(1) if match.group(1) else match.group(2) | |
| return None | |
| def generation(course_name, keywords, num, filename, key): | |
| q_num = int(num) | |
| # CSV標題列 題型* 題幹* 難易度* 答案* 正確答案解釋 選項-A 選項-B 選項-C 選項-D | |
| columns = ['題型*', '題幹*', '難易度*', '答案*', '正確答案解釋', '選項-A', '選項-B', '選項-C', '選項-D', '考生一作答', '考生二作答', '考生三作答', '驗證通過'] | |
| data = {} | |
| df = pd.DataFrame() | |
| for num in range(1, q_num+1) : | |
| # 第一階段:「生考題」提問 | |
| prompt1 = "請扮演命題專家,並注意下列原則與命題關鍵字,請挑選適合關鍵字以出題一題,\ | |
| 原則1:單選題四個選項包含:A、B、C、D。難易度為難、中、易。\ | |
| 原則2:考試目標屬於初階認證考試,通過考試者,代表理解初步概念,同時要有專業程度。\ | |
| 原則3:禁止命題否定問句,禁止選項出現「以上皆是」或「以上皆非」。\ | |
| 原則4:輸出格式(但不用出現本句):|type:| 題型 |quiz:| 題幹 |level:| 難易度 |answer:| 答案 |solution:| 正確答案解釋 |sel-A:| 選項A |sel-B:| 選項B |sel-C:| 選項C |sel-D:| 選項D \ | |
| 原則5:以提升題目難度方式,採多層推理、情境題、結合多個概念、精確理解的干擾選項設計。\ | |
| 原則6:以繁體中文來命題。\ | |
| 原則7:課程名稱:" + course_name + "。命題關鍵字: " + keywords; | |
| response = openai_api(prompt1, key); | |
| #出題 | |
| quiz = response.split("|quiz:|")[1].split("|level:|")[0].strip() + " (A) " + response.split("|sel-A:|")[1].split("|sel-B:|")[0].strip() +" (B) " + response.split("|sel-B:|")[1].split("|sel-C:|")[0].strip() +" (C) " + response.split("|sel-C:|")[1].split("|sel-D:|")[0].strip() +" (D) " + extract_option(response) | |
| #正確答案 | |
| qanswer = extract_letter(response.split("|answer:|")[1].split("|solution:|")[0].strip()) | |
| qtype = response.split("|type:|")[1].split("|quiz:|")[0].strip() | |
| qlevel = response.split("|level:|")[1].split("|answer:|")[0].strip() | |
| qsolution = response.split("|solution:|")[1].split("|sel-A:|")[0].strip() | |
| # 第二階段:「考題審查」提問 | |
| #prompt2="依下列原則,請扮演審題專家,檢查考題內容及選項並符合格式輸出。\ | |
| #原則1:盡量少用「否定問句」或「否定敘述選項」。\ | |
| #原則2:不要使用「以上皆是」和「以上皆非」的答案。\ | |
| #原則3:請避免爭議題,混淆、主觀、誤導、描述不清、定義不明、多個選項皆可算分。\ | |
| #原則4:請避免送分題,考同樣的知識點,只是換句話說的題目。\ | |
| #考題內容:"+quiz+"。\ | |
| #輸出格式:若「違背」出題原則,請提供「專家建議」,若「未違背」可以直接回覆「No」不用多以解釋"; | |
| #quiz_suggest = openai_api(prompt2, key); | |
| # 第三階段:「依專家建議調整」提問 | |
| #prompt3="請扮演出題專家,依據下列「專家意見」更新「考題內容」並依格式輸出。\ | |
| #原則1:題型出單選題,即以「單選題」回覆。\ | |
| #原則2:答案以「A、B、C、D」依題幹作回覆。難易度以「難、中、易」擇一回覆。\ | |
| #原則3:輸出格式(但不用出現本句):|type:| 題型 |quiz:| 題幹 |level:| 難易度 |answer:| 答案 |solution:| 正確答案解釋 |sel-A:| 選項A |sel-B:| 選項B |sel-C:| 選項C |sel-D:| 選項D \ | |
| #原則4:以繁體中文來命題。考題內容:"+ quiz +"。\ | |
| #原則5;專家意見:"+ quiz_suggest; | |
| #if quiz_suggest != "No" : | |
| # response2 = openai_api(prompt3, key); | |
| #else: | |
| # response2 = response | |
| #quiz = response2.split("|quiz:|")[1].split("|level:|")[0].strip() + " (A) " + response2.split("|sel-A:|")[1].split("|sel-B:|")[0].strip() +" (B) " + response2.split("|sel-B:|")[1].split("|sel-C:|")[0].strip() +" (C) " + response2.split("|sel-C:|")[1].split("|sel-D:|")[0].strip() +" (D) " + extract_option(response2) | |
| #qanswer = extract_letter(response2.split("|answer:|")[1].split("|solution:|")[0].strip()) | |
| #qtype = response2.split("|type:|")[1].split("|quiz:|")[0].strip() | |
| #qlevel = response2.split("|level:|")[1].split("|answer:|")[0].strip() | |
| #qsolution = response2.split("|solution:|")[1].split("|sel-A:|")[0].strip() | |
| #if qanswer == None : qanswer = "E" | |
| print("第"+str(num)+"題:") | |
| print(quiz) | |
| print("答案:"+qanswer) | |
| # 第四階段:「考生一號試考」提問 | |
| prompt4="請回覆答案(A、B、C、D)即可不用解釋,請扮演「機器學習」考題專家並注意下列原則。\ | |
| 原則1:考題專家具了解演算法、數據前處理、模型訓練、評估方法以及機器學習的最新進展。\ | |
| 原則2:教育背景具統計分析、人工智慧、數據分析,可持續發展或相關領域的高等教育背景。\ | |
| 原則3:考試的常勝軍,每次都可以考第一名,都可以考滿分。\ | |
| 原則4:考題內容:"+quiz; | |
| ans_tester1= openai_api(prompt4, key); | |
| # 第五階段:「考生二號試考」提問 | |
| prompt5="請回覆答案(A、B、C、D)即可不用解釋,請扮演「人工智慧」考題專家並注意下列原則。\ | |
| 原則1:考題專家具人工智慧的專業知識(包括機器學習、自然語言處理、計算機視覺等),包括對基本理論、算法、實踐應用和行業趨勢的全面掌握。\ | |
| 原則2:教育背景具備人工智慧知識,該領域為一個快速發展的領域,專家需要不斷學習和更新知識,以确保考题内容的时效性和前瞻性。\ | |
| 原則3:考試的常勝軍,每次都可以考第一名,都可以考滿分。\ | |
| 原則4:考題內容:"+ quiz; | |
| ans_tester2= openai_api(prompt5, key); | |
| # 第六階段:「考生三號試考」提問 | |
| prompt6="請回覆答案(A、B、C、D)即可不用解釋,請扮演「數據分析」考題專家並注意下列原則。\ | |
| 原則1:考題專家具擁有數據分析深厚知識,包括數據處理、統計分析、機器學習、數據視覺化等。\ | |
| 原則2:教育背景具備數據分析領域的最新發展和趨勢,並保證考題的邏輯性和合理性。\ | |
| 原則3:考試的常勝軍,每次都可以考第一名,都可以考滿分。\ | |
| 原則4:考題內容:"+ quiz; | |
| ans_tester3= openai_api(prompt6, key); | |
| print("考生一作答:"+ans_tester1+"\n"+"考生二作答:"+ans_tester2+"\n"+"考生三作答:"+ans_tester3); | |
| data[num] = {'題型*': qtype,'題幹*': response.split("|quiz:|")[1].split("|level:|")[0].strip(),'選項-A': response.split("|sel-A:|")[1].split("|sel-B:|")[0].strip(), | |
| '選項-B': response.split("|sel-B:|")[1].split("|sel-C:|")[0].strip(),'選項-C': response.split("|sel-C:|")[1].split("|sel-D:|")[0].strip(), | |
| '選項-D': extract_option(response),'答案*': qanswer,'正確答案解釋': qsolution,'難易度*': qlevel, | |
| '驗證通過': extract_letter(ans_tester1) == extract_letter(ans_tester2) == extract_letter(ans_tester3) == extract_letter(qanswer),'考生一作答': ans_tester1,'考生二作答': ans_tester2,'考生三作答': ans_tester3} | |
| print(extract_letter(ans_tester1)) | |
| print(extract_letter(ans_tester2)) | |
| print(extract_letter(ans_tester3)) | |
| data_list = [value for key, value in data.items()] | |
| df = pd.DataFrame(data_list, columns=columns) | |
| df = df.applymap(lambda x: x.replace('|', '') if isinstance(x, str) else x) | |
| filename = current_time_taipei.strftime("%Y%m%d%H%M%S")+"_"+filename + ".csv" | |
| df.to_csv(filename, index=False, encoding='utf-8-sig') | |
| return filename | |
| def setup_gradio_interface(): | |
| with gr.Blocks() as demo: | |
| with gr.Row(): | |
| api_key_input = gr.Textbox(label="Enter OpenAI API Key", placeholder="OpenAI API 金鑰") | |
| filename_input = gr.Textbox(label="考題檔案名稱", placeholder="檔案名稱") | |
| course_input = gr.Textbox(label="課程名稱", placeholder="課程名稱") | |
| keywords_input = gr.Textbox(label="生題關鍵字", placeholder="關鍵字") | |
| q_num_input = gr.Textbox(label="生成題數", placeholder="題數") | |
| submit_button = gr.Button("生成考題") | |
| file_output = gr.File(label="Download CSV") | |
| submit_button.click( | |
| generation, | |
| inputs=[course_input, keywords_input, q_num_input, filename_input, api_key_input], | |
| outputs=[file_output] | |
| ) | |
| return demo | |
| try: | |
| import gradio as gr | |
| except ImportError: | |
| import sys | |
| import gradio as gr | |
| # Run the interface | |
| if __name__ == "__main__": | |
| demo = setup_gradio_interface() | |
| demo.launch(share=True) |