記一次英語批改作業軟體的開發-除了老師和家長,它也可以批改作業

Al發表於2020-12-05

除了老師和家長,它也可以批改作業

最近一個家長退群的故事在某博上了熱搜。故事中老師和家長的矛盾由批改作業集中爆發,至於孰是孰非,還是交給吃瓜群眾去評價吧,作為一個技術工作者,我突發奇想,是否以後能讓機器來輔助老師批改作業呢?這彷彿是個維護世界和平的點子!

機智

經過一陣調(搜)研(索),在英語作文批改上,還真的有一些成熟的方案可以使用,而且學習成本相當之低,比如有道智雲的英語作文批改服務,只需閱讀文件按規則開發應用,即可得到詳盡的批改結果,作文可以是圖片,也可以是文字,等級可以從小學一直到雅思託福,覆蓋範圍極廣。

懷著激動的心情,我快速地開發了一個簡單的demo,下面分享一下開發過程。

呼叫API介面的準備工作

首先,是需要在有道智雲的個人頁面上建立例項、建立應用、繫結應用和例項,獲取到應用的id和金鑰。具體個人註冊的過程和應用建立過程詳見文章分享一次批量檔案翻譯的開發過程

這裡要特別說明一下,作文批改分為影像和文字兩種形式,分別呼叫了不同的api,因此需要建立兩個例項。

應用例項建立

開發過程詳細介紹

下面介紹具體的程式碼開發過程。

英語作文批改分為兩個API,分別對應影像識別文字輸入兩種形式的作文。呼叫方式大同小異,都是將待批改內容和時間戳等資訊生成的簽名post到API介面,而後介面返回批改結果。

影像識別API輸入所需引數如下表:

欄位名 型別 含義 必填 備註
q text 圖片的 base64。大小不超過 5MB True 圖片的 base64
langType text 語言,目前僅支援英文 False en
appKey text 應用標識(應用 ID) True 可在 應用管理 檢視
salt text 隨機字串 True hfa12lak56ja9gjl
sign text 簽名資訊:sha256(appKey+input+salt+金鑰) True xxxxx
grade text 作文等級。支援列表見下面 grade 支援列表,預設不管等級,只評價句子好壞 false default
title text 作文標題 false 0
modelContent text 使用者提供的範文內容 false 0
goodExpression text 使用者提供的好的表達 false 0
needTypo text 是否需要 typo,(true/false),預設為 true false true
signType text 簽名型別 true v3
limitedWords text 作文字數限制 false 1000

文字輸入API輸入引數如下表:

欄位名 型別 含義 必填 備註
q text 批改的文字。文字不超過 5000 字元 True text
langType text 語言,目前僅支援英文 False en
appKey text 應用標識(應用 ID) True 可在 應用管理 檢視
salt text 隨機字串 True hfa12lak56ja9gjl
sign text 簽名資訊:sha256(appKey+input+salt+金鑰) True xxxxx
grade text 作文等級。支援列表見下面 grade 支援列表,預設不管等級,只評價句子好壞 false default
title text 作文標題 false 0
modelContent text 使用者提供的範文內容 false 0
goodExpression text 使用者提供的好的表達 false 0
needTypo text 是否需要 糾錯,預設為 true(true/false) false true
signType text 簽名型別 true v3
limitedWords text 作文字數限制 false 1000
curtime text 當前UTC時間戳(秒) true 時間戳

最好傳輸 limitedWords,這樣評分更精確。
簽名生成演算法如下:

  • signType=v3,sha256(應用 ID+input+salt+curtime+金鑰),推薦使用

sha256 簽名計算方法為:sha256(應用 ID+input+salt+當前 UTC 時間戳+金鑰)。

其中,input 的計算方式為:input=多個q拼接後前10個字元 + 多個q拼接長度 + 多個q拼接後十個字元(當多個 q 拼接後長度大於 20)或 input=多個q拼接的字串(當多個 q 拼接後長度小於等於 20)。

在介面輸入引數中,grade為以下幾類:

級別 程式碼
不考慮級別,單純評價句子好壞 default
小學 elementary
初中 junior
高中 high
四級 cet4
六級 cet6
考研 graduate
託福 toefl
GRE gre
雅思 ielts

Demo開發:

這個demo使用python3開發,包括maindow.py,correctclass.py,HomeworkCorrect.py 三個檔案,分別為demo的介面、介面邏輯處理和英文作文批改介面呼叫方法的封裝。

  1. 介面部分:

    UI 部分較簡單,主要功能為選擇待批改作文檔案、選擇批改結果儲存路徑、選擇批改型別。其佈局程式碼如下:

    root=tk.Tk()
    root.title(" youdao correct writing test")
    frm = tk.Frame(root)
    frm.grid(padx='50', pady='50')
    
    # 文章選擇
    btn_get_file = tk.Button(frm, text='選擇待批改的作業(圖片或文字)', command=get_files)
    btn_get_file.grid(row=0, column=0, ipadx='3', ipady='3', padx='10', pady='20')
    text1 = tk.Text(frm, width='40', height='10')
    text1.grid(row=0, column=1)
    
    # 結果路徑選擇
    btn_get_result_path=tk.Button(frm,text='選擇批改結果存放路徑',command=set_result_path)
    btn_get_result_path.grid(row=1,column=0)
    text2=tk.Text(frm,width='40', height='2')
    text2.grid(row=1,column=1)
    
    # 級別選擇
    label=tk.Label(frm,text='選擇年級:')
    label.grid(row=3,column=0)
    combox=ttk.Combobox(frm,textvariable=tk.StringVar(),width=38)
    combox["value"]=select_type_dict
    combox.current(0)
    combox.bind("<<ComboboxSelected>>",get_grade_type)
    combox.grid(row=3,column=1)
    
    # 啟動批改
    btn_sure=tk.Button(frm,text="批改",command=correct_files)
    btn_sure.grid(row=4,column=1)
    
    root.mainloop()
    

    其中啟動按鈕btn_sure的繫結事件correct_files()來啟動批改,並在完成後開啟結果儲存路徑:

    def correct_files():
        correct.start_correct()
        os.system('start '+correct.result_path)
    
  2. correctclass.py

    這裡主要配合UI的邏輯,分析檔案型別,選取合適的介面來批改作文。

    首先定義一個類Correct:

    class Correct():
        def __init__(self,file_paths,grade,result_path):	
            self.file_paths=file_paths		# 待批改檔案路徑
            self.grade =grade				# 批改級別
            self.result_path=result_path	# 結果路徑
    

    get_correct_result()方法根據檔案型別判斷應呼叫的封裝方法,並處理返回值,將批改結果存入檔案系統。

        def get_correct_result(self,file_path):
            file_type=file_path.split(".")[1]
            if file_type=="txt":
                print(file_path)
                result=connect_context(file_path,self.grade)
                self.save_result(file_path,result)
            elif file_type=="png" or file_type=="jpg" or file_type=="jepg" :
                result=connect_pic(file_path,self.grade)
                self.save_result(file_path,result)
    

    save_result()方法實現了儲存結果的功能:

        def save_result(self,file_path,result):
            result_file_name=os.path.basename(file_path).split('.')[0]+'_result.txt'
            f=open(self.result_path+'/'+result_file_name,'w')
            f.write(str(result))
            f.close()
    
  3. HomeworkCorrect.py

    HomeworkCorrect.py 中封裝了請求兩種作業批改API的方法,兩個API主要區別在於URL和APP示例的不同。最核心的方法分別是connect_pic() 和 connect_context()

    connect_pic():

    def connect_pic(pic_path,grade):
        f = open(pic_path, 'rb')  # 二進位制方式開啟圖檔案
        q = base64.b64encode(f.read())  # 讀取檔案內容,轉換為base64編碼
        f.close()
        data = {}
    
        curtime = str(int(time.time()))
        data['curtime'] = curtime
        salt = str(uuid.uuid1())
        #print(q)
        signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET
        sign = encrypt(signStr)
        data['appKey'] = APP_KEY
        data['salt'] = salt
        data['q'] = q
        data['sign'] = sign
        data['grade'] = grade
        data['signType'] = 'v3'
    
        response = do_request(data,YOUDAO_URL_IMAGE)
        result=json.loads(str(response.content,'utf-8'))['Result']
        return result
    

    connect_context():

    def connect_context(file_path,grade):
        f=open(file_path,'rb')
        q=f.read()
        f.close()
        data = {}
        curtime = str(int(time.time()))
        data['curtime'] = curtime
        salt = str(uuid.uuid1())
        signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET
        sign = encrypt(signStr)
        data['appKey'] = APP_KEY
        data['q'] = q
        data['salt'] = salt
        data['sign'] = sign
        data['signType'] = "v3"
        data['grade'] = grade
        response = do_request(data,YOUDAO_URL_TEXT)
        print(response.content)
        result = json.loads(str(response.content, 'utf-8'))['Result']
        print(result)
        return result
    

效果展示

我分別選了一段英文的圖片和txt文件來進行測試:

效果
響應結果說明:

{
"errorCode": "錯誤碼",
"Result": {
    "uniqueKey": "每個請求獨一無二的字串標識",
    "essayLangName": "語言資訊",
    "rawEssay": "請求原文",
    "refEssay": "參考範文",
    "stLevel": "作文級別",
    "reqSource": "請求來源",
    "extraParams": "額外請求引數(擴充套件用引數)",
    "wordNum": "文章總詞數"
    "conjWordNum": "文章連線詞數",
    "AllFeatureAdvice": { # 作文各特徵的建議
        "WordNum": "詞數建議,如文章字數疑似超出該考試字數要求",
        "Spelling": "拼寫錯誤建議",
        "WordDiversity": "詞彙豐富度建議,如詞彙量積累非常少,只能給出一些零散的簡單詞彙,建議多積累詞彙",
        "Structure": "文章結構建議",
        "AdvanceVocab": [
            "xx", "xx", "xx" # 文中使用的高階詞彙
        ],
        "AdvanceVocabLeval": [0, 1, 2], # 對應高階詞彙的級別
        "lexicalSubs": [ # 詞彙替換(注意:candidates中詞彙可能為空,表示沒有推薦替換的近義詞,但word使用頻率超過3次)
            {"candidates": ["xx", "xx"], "count": 頻率, "word": xx 對應詞}, ...
        ]
        "Conjunction": [{
            "used": ["xx", "xx", "xx"] # 已使用連詞
            "advice": ["xx", "xx", "xx"] # 推薦詞
        }],
        "Topic": "主題相關性建議",
        "Grammar": "語法相關建議",
        "GoodExpression": ["xx", "xx", "xx"] # 好的表達
    },
    "AllFeatureScore": { # 對應上面AllFeatureAdvice各特徵得分,除NeuralScore是沒有Advice的,它代表神經網路作文打分結果,不是最終打分結果!
        "NeuralScore": 68.64,     # 範圍:[0,100]
        "WordNum": 10,  # 範圍:[0, 10] ---> 指的是詞數得分
        "Spelling": 10,   # 範圍:[0, 10]
        "WordDiversity": 0,  # 範圍:[0, 10]
        "Structure": 8,  # 範圍:[0, 10]
        "AdvanceVocab": 7.61,   # 範圍:[0, 10]
        "Conjunction": 6.94,   # 範圍:[0, 10]
        "Topic": 6.03,  # 範圍:[0, 10]
        "Grammar": 2.5  # 範圍:[0, 10]
        "SentComplex": 10 # 範圍: [0, 10]
    },
    "majorScore": { # 是AllFeatureScore中score整合結果
        "WordScore": 10, # 詞彙得分:包括詞數、豐富度、高階詞彙等得分
        "GrammarScore": 10, # 語法得分:包括拼寫、語法、句子複雜度得分等
        "StructureScore": 10, # 邏輯得分:包括段落和連線詞得分
        "topicScore": 10, # 內容(主題相關性)得分,如果沒有參考範文,該部分得分會從語法和複雜度上考慮
    },
    "essayFeedback":{
        "sentsFeedback": [
            {
                "sentId": "句子在全文的編號,從0開始",
                "paraId": "該句所在的段落號,從0開始",
                "rawSent": "原句",
                "segSent": "原句分詞後的結果",
                "correctedSent": "原句修正後的結果",
                "sentStartPos": "該句子在全文中相對於文章初始位置的偏移量",
                "errorPosInfos": [
                    {
                        "type": "錯誤型別(包括`grammar`,`typo`,`refactor`)",
                        "startPos": "錯誤起始位置相對rawSent起始位置的偏移量",
                        "endPos": "錯誤結束位置相對rawSent起始位置的偏移量",
                        "orgChunk": "錯誤塊的具體內容",
                        "correctChunk": "錯誤塊修正後的具體內容",
                        "error_type": "(棄用) 錯誤的具體類別(0表示拼寫錯誤,1表示冠詞錯誤,2表示動詞時態或者第三人稱單複數錯誤,3表示名詞單複數錯誤,4表示格錯誤,5表示介詞錯誤,6表示其他語法錯誤,7表示文字格式錯誤,8表示正確)",
                        "new_error_type": "錯誤類別(0表示完全正確,
                        1表示書寫格式不規範,2表示拼寫錯誤,
                        3表示標點錯誤,4表示冠詞錯誤,5表示動詞錯誤,
                        6表示名詞單複數錯誤,7表示代詞錯誤,8表示介詞錯誤,
                        9表示形容詞錯誤,10表示副詞錯誤,11表示連詞錯誤,
                        20表示其他錯誤,21表示代指所有語法錯誤(相容))"
                        "new_sub_error_type": "細分錯誤類別(0表示正確,1表示未知錯誤,2表示詞彙缺失,3表示詞彙冗餘,
                        4表示冠詞誤用,5表示介詞誤用,6表示動詞主謂一致錯誤,7表示動詞時態錯誤,8表示情態動詞後應接動詞原形錯誤,
                        9表示被動語態錯誤,10表示動詞不定式錯誤,11表示動詞錯誤,12表示形容詞比較級錯誤,
                        13表示形容詞最高階錯誤,14表示副詞比較級錯誤,15表示副詞最高階錯誤,16表示名詞單複數錯誤,
                        17表示名詞錯誤,18表示人稱代詞主賓格混淆,19表示人稱代詞和物主代詞混淆,20表示形容詞性和名詞性代詞混淆,
                        21表示人稱代詞和反身代詞混淆,22表示疑問/關係/連線代詞混淆,23表示指示代詞混淆,24表示不定代詞混淆,
                        25表示代詞錯誤,26表示標點符號誤用,27表示拼寫錯誤,28表示不規範錯誤)"
                            "舉例說明": 如果new_error_type=5, new_sub_error_type=2,說明是動詞缺失
                        "reason": "錯誤的具體原因",
                        "isValidLangChunk": "類似下面的isValidSent,判斷是否為合法片段(該片段如果語言檢測結果與期望不一致,則認為不合法)"
                        "analysis": "錯誤的原因的具體辨析(保留介面,暫時應該沒用)"
                    }, ..., {}
                ],
                "isValidLangSent": "是否為合法句子(合法與否取決於語言檢測對該句的語言資訊識別結果與期望結果是否一致)"
                "sentFeedback": "錯誤原因反饋,基於errorPosInfos中所有reason欄位拼接而成",
                "isContainTypoError": "返回是否含有typo錯誤",
                "isContainGrammarError": "返回是否含有語法錯誤",
                "sentScore": "句子得分(暫時沒有用,即將實現)"
            }
        ]
    }
    "totalScore": "文章最終得分"
    "fullScore": "對應級別滿分"
    "essayAdvice": "文章最終評價"
    "paraNum": "文章段落數"
    "sentNum": "文章句子數"
}
}

總結

有道智雲的英語作文批改API文件清晰,功能全面,可針對不同型別檔案、不同難度的作文進行多維度批改,評價指標明確,批改結果非常具有參考價值,贊!

相信在未來,會有更多型別的作業批改服務出現吧,到那時,老師和家長們就都能得到解放了...

專案地址:https://github.com/LemonQH/CorrectWriting

相關文章