# This is an app that uses the OpenAI API and the Chat GPT 4 model. # It generates Free Response Questions for students based on a Common Core Standard and Area of Interest # It takes as input: (1) The common core standard to use, (2) The area of interest import streamlit as st import json from langchain.chat_models import ChatOpenAI from langchain.schema import ( AIMessage, HumanMessage, SystemMessage ) #App UI starts here st.set_page_config(page_title="FRQ Generator", page_icon=":robot:") st.header("FRQs using GPT-4") st.subheader("IMPORTANT: Please wait 60-70 seconds between responses. Chat GPT takes time to think.") header_text=str(''' Paste a JSON file here, with the following information: 1. The name of the common core standard, 2. The student's area of interest. E.g. ``` { "input":"frq-generator", "entries":[ {"cc-standard": "CCSS.ELA-LITERACY.W.4.9", "area-of-interest": "Baseball" }, {"cc-standard": "CCSS.ELA-LITERACY.W.5.8", "area-of-interest": "Hiking" } ] } ``` ''') st.markdown(header_text) if "sessionMessages" not in st.session_state: st.session_state.sessionMessages = [ SystemMessage(content = "You are a bot that generates 'free response questions' for assesing school students based on the input of a Common Core Standard, and area of interest.") ] #Function to return the response def generateQuestion(question): questionPassageJSON="" ccStandard = question['cc-standard'] #st.session_state.sessionMessages.append(HumanMessage(content="Common Core standard is: {question[0]}, and the area of interest is: {question[1]}")) ## Ask the model to understand the common core standard specified st.session_state.sessionMessages.append(HumanMessage(content="Read the common core standard :"+ccStandard+" and summarize its key requirements for a student in 3 bullet points. what year grade is the above standard applicable to? Answer in simple one word. E.g. '4th grade',' or 'Kindergarten', or 'Nursery'.\ Also tell if the student needs to read a passage to complete an assessment related to this standard. \ Also tell if the student needs to independently research a topic to complete an assessment related to this standard. \ Also tell if the standard requires testing on a math problem. \ Return the result in JSON format, with keys including 'grade-level', 'standard-requirement', 'is-passage-required', 'is-research-required', 'is-math-problem'.")) assistant_answer = chat(st.session_state.sessionMessages) st.session_state.sessionMessages.append(AIMessage(content=assistant_answer.content)) passagePrompt="The area of interest to consider for the exercise is "+question['area-of-interest']+". " # Create a JSON Object out of the Chat response chatResponseDict = json.loads(assistant_answer.content) if (not chatResponseDict['is-math-problem']): allChecksPassed=False repCount =0; while not allChecksPassed and repCount<3: #If a passage is required, generate a 300 word passage, for the appropriate grade level. if (chatResponseDict['is-passage-required']): passagePrompt+="Generate a 300 word passage on the given area of interest. It should cover the key points being tested in the common core standard specified. It should draw on real life incidents. Make it about one particular incident that highlights various things about "+question['area-of-interest']+"." #If external research is required, inform the student what to do research on. if (chatResponseDict['is-research-required']): passagePrompt+="Also suggest what specific topic or idea the student can do external research on, to be able to answer questions related to the common core standard specified. Use language as if you are speaking to the student directly, in the formal tone of a teacher or examiner." passagePrompt+="Make sure any passage or topics generated are appropriate in terms of vocabulary and comprehensibility for grade "+chatResponseDict['grade-level'] passagePrompt+="Return the response in JSON format." # Ask the model st.session_state.sessionMessages.append(HumanMessage(content=passagePrompt)) passage_and_research = chat(st.session_state.sessionMessages) st.session_state.sessionMessages.append(AIMessage(content=passage_and_research.content)) # Based on the above generated passage and research topic, we will now generate 3 FRQs and their grading rubrics frqPrompt='Generate 3 questions based on the previously discussed common core standard, and the above generated passage. The answers should be available either in the above passage, or the student should be able to find them with easy web research. Good questions have: introduction, context, and open-ended question. Remember, the student is in grade: '+chatResponseDict['grade-level'] frqPrompt+='Generate a rubric for evaluating the student responses for the above questions. Return the result in JSON format. E.g. { "rubric":[{"question":"..text of the question", "rubric":".. text of the rubric"}] etc.' frqPrompt+='In the final response JSON, also incldue the passage and research JSONs created above.' # Ask the model st.session_state.sessionMessages.append(HumanMessage(content=frqPrompt)) question_and_rubric = chat(st.session_state.sessionMessages) st.session_state.sessionMessages.append(AIMessage(content=question_and_rubric.content)) #questionPassageJSON=json.loads(question_and_rubric.content) ## Do the QC Step qcPrompt='For the JSON generated in the previous step, please evaluate for each question and rubric:\ - Does the FRQ have an introduction, context, and an open-ended question\ - Does the Rubric prescribe checking for things that can be found in the passage, or suggested research areas.\ \ Pass and Fail answers only.\ Return the response in JSON Form. E.g.:\ {\ "type-of-response":"quality control",\ "qc-for-each-question":[\ {"FRQ":"Pass", "Rubric":"Fail"}\ ]\ }' # Ask the model st.session_state.sessionMessages.append(HumanMessage(content=qcPrompt)) qcResponseJson = json.loads(chat(st.session_state.sessionMessages).content) ## Write QC results st.write(qcResponseJson) ## Qc Response doesn't need to into the session state # Create a Array of qcResponses, and loop through them qcResponseArray = qcResponseJson['qc-for-each-question'] # If any of the QC checks have failed, we set allChecksPassed to False, and the while loop starts again. allChecksPassed=True for row in qcResponseArray: if (row["FRQ"]=="Fail" or row["Rubric"]=="Fail"): allChecksPassed=False ## Increase the repCount - we break the While loop after 3 reps, because using GPT4 is expensive, and we don't want it to go into a never ending loop repCount+=1 return question_and_rubric.content #Gets the user input def get_text(textAreaKey): input_text = st.text_area(label="Paste/Write here",key=textAreaKey) textAreaKey+=1 return input_text ## Temperature kept at 0.9, so that generated passages and questions are not too similar to each other. chat = ChatOpenAI(temperature=0.9, model_name="gpt-4") user_input = get_text(1) submit = st.button('Generate') #If generate button is clicked if submit: # Create a dict object from the JSON input inputDict = json.loads(user_input) questionArray=inputDict['entries'] responseArray=[] # iterate through the dict, and create a new dict with each response from the LLM responseDict=[] for row in questionArray: st.write(row) response = generateQuestion(row) responseArray.append(response) st.subheader("AI Generated Question & Rubric (Note - in a real world scenario, the rubric won't be shown to the student):") st.write(responseArray)