Spaces:
Sleeping
Sleeping
| from .base_agent import BaseAgent | |
| import os | |
| import openai | |
| from dotenv import load_dotenv | |
| import json | |
| class LevelAssessmentAgent(BaseAgent): | |
| def __init__(self): | |
| super().__init__("LevelAssessmentAgent") | |
| load_dotenv() | |
| api_key = os.getenv("OPENAI_API_KEY") | |
| self.client = openai.OpenAI(api_key=api_key) | |
| def generate_questions(self, topic, level): | |
| system_prompt = ( | |
| "You are a Master Educator and Curriculum Designer with deep expertise in pedagogical theory and assessment creation. " | |
| "Your primary goal is to design a short, insightful assessment to precisely gauge a student's mastery of a topic at a specific proficiency level.\n\n" | |
| "**Your Guiding Principles:**\n\n" | |
| "1. **Cognitive Depth:** The questions must go beyond rote memorization. They should probe the student's reasoning, problem-solving abilities, and their ability to connect concepts.\n" | |
| "2. **Level-Specific Targeting:** You must strictly adhere to the specified 'level':\n" | |
| " - **Beginner:** Focus on core concepts, definitions, and simple \"how\" or \"why\" explanations. (Testing Comprehension & Application)\n" | |
| " - **Intermediate:** Focus on comparing/contrasting concepts, applying knowledge to simple scenarios, and analyzing processes. (Testing Application & Analysis)\n" | |
| " - **Advanced:** Demand synthesis of multiple concepts, evaluation of complex scenarios, and creation of novel solutions or arguments. (Testing Synthesis & Evaluation)\n" | |
| "3. **Clarity and Precision:** Each question must be unambiguous, concise, and clearly worded.\n\n" | |
| "**Output Format:**\n" | |
| "You must generate exactly 5 to 6 questions. Your response should contain ONLY the numbered list of questions. Do not include a title, introduction, conclusion, or any other text." | |
| ) | |
| user_prompt = ( | |
| f"Generate a assessment for 5 questions to test if a student is truly at the '{level}' level in the topic '{topic}'. " | |
| "The question should be open-ended and require a thoughtful answer." | |
| ) | |
| response = self.client.chat.completions.create(model = "gpt-4o-mini", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt} | |
| ] | |
| ) | |
| return response.choices[0].message.content.strip() | |
| def evaluate_answer(self, topic, level, question, user_answer): | |
| system_prompt = ( | |
| "You are a meticulous and impartial Grader AI. Your task is to evaluate a student's answer based on a provided question and the student's claimed proficiency level. You must adhere to the following STRICT rubric:\n\n" | |
| "1. **Precision Requirement:** The answer must be specific, detailed, and demonstrate clear understanding. Vague, generic, or one-word answers are automatically INCORRECT.\n" | |
| "2. **Correctness:** Is the answer factually accurate and relevant to the question?\n" | |
| "3. **Depth:** Does the answer's depth match the expected level?\n" | |
| " - **Novice/Beginner:** The answer should demonstrate basic comprehension and recall of key concepts with specific examples.\n" | |
| " - **Intermediate:** The answer should show an ability to apply concepts, compare, and analyze with clear reasoning.\n" | |
| " - **Advanced:** The answer must demonstrate synthesis, evaluation, and nuanced understanding with sophisticated insights.\n" | |
| "4. **Completeness:** The answer must address all parts of the question comprehensively.\n\n" | |
| "**AUTOMATIC FAILURES:**\n" | |
| "- Answers under 20 words are automatically incorrect\n" | |
| "- Vague responses like 'yes', 'no', 'it depends', 'I think so', 'maybe', 'probably'\n" | |
| "- Responses that don't directly address the specific question asked\n" | |
| "- Copy-paste definitions without personal understanding or application\n\n" | |
| "You must respond ONLY with a JSON object. Do not include any other text or markdown formatting. The JSON object must have three keys:\n" | |
| "1. `\"evaluation\"`: a string with a value of either `\"correct\"` or `\"incorrect\"`.\n" | |
| "2. `\"reasoning\"`: a detailed explanation for your decision, including what was missing or incorrect.\n" | |
| "3. `\"hint\"`: a subtle hint to guide the student toward the correct answer (only if incorrect)." | |
| ) | |
| user_prompt = ( | |
| f"Evaluate the following data based on the rubric. Provide your response in the required JSON format.\n\n" | |
| f"**Topic:** \"{topic}\"\n" | |
| f"**Proficiency Level to Evaluate:** \"{level}\"\n" | |
| f"**Question:** \"{question}\"\n" | |
| f"**Student's Answer:** \"{user_answer}\"" | |
| ) | |
| response = self.client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt} | |
| ] | |
| ) | |
| return response.choices[0].message.content.strip() | |
| def challenge_feedback(self, topic, level, question, user_answer, original_evaluation, original_reasoning): | |
| """ | |
| Allows a student to challenge the feedback given. Re-evaluates the answer | |
| with a focus on being fair and reconsidering the evaluation. | |
| Returns a JSON string with the challenge result. | |
| """ | |
| system_prompt = ( | |
| "You are a fair and reconsidering Grader AI. A student has challenged your previous evaluation. " | |
| "Your task is to carefully re-examine the student's answer with fresh eyes, considering that " | |
| "your initial evaluation may have been too strict or may have missed valid points.\n\n" | |
| "**Re-evaluation Guidelines:**\n" | |
| "1. Be open to reconsidering your initial assessment\n" | |
| "2. Look for valid points in the answer that may have been overlooked\n" | |
| "3. Consider alternative interpretations of the question\n" | |
| "4. If the answer demonstrates understanding (even if not perfect), acknowledge it\n" | |
| "5. Be fair and balanced in your re-evaluation\n\n" | |
| "You must respond ONLY with a JSON object containing:\n" | |
| "1. `\"evaluation\"`: either `\"correct\"` or `\"incorrect\"`\n" | |
| "2. `\"reasoning\"`: detailed explanation of your re-evaluation, including whether you changed your mind and why\n" | |
| "3. `\"original_was_fair\"`: true/false indicating if the original evaluation was fair\n" | |
| "4. `\"hint\"`: a hint if still incorrect" | |
| ) | |
| user_prompt = ( | |
| f"**Topic:** {topic}\n" | |
| f"**Proficiency Level:** {level}\n" | |
| f"**Question:** {question}\n" | |
| f"**Student's Answer:** {user_answer}\n\n" | |
| f"**Original Evaluation:** {original_evaluation}\n" | |
| f"**Original Reasoning:** {original_reasoning}\n\n" | |
| "Please re-evaluate this answer fairly. The student believes the original evaluation may have been incorrect." | |
| ) | |
| response = self.client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=[ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt} | |
| ] | |
| ) | |
| return response.choices[0].message.content.strip() | |
| def challenge_discussion(self, topic, level, question, user_answer, original_evaluation, original_reasoning, conversation_history, student_message): | |
| """ | |
| Handles a conversational challenge discussion between student and grader. | |
| conversation_history: list of [user_message, assistant_message] pairs | |
| student_message: the current student's argument/question | |
| Returns: assistant response and final evaluation (if discussion is ending) | |
| """ | |
| system_prompt = ( | |
| "You are a fair and patient Grader AI engaged in a discussion with a student who is challenging your evaluation. " | |
| "You should be open to reconsidering your assessment, but also maintain academic standards.\n\n" | |
| "**Your Role:**\n" | |
| "1. Listen carefully to the student's arguments\n" | |
| "2. Be willing to reconsider if the student makes valid points\n" | |
| "3. Explain your reasoning clearly and respectfully\n" | |
| "4. If the student is right, acknowledge it and update your evaluation\n" | |
| "5. If the student's argument doesn't change your assessment, explain why clearly\n\n" | |
| "**Context:**\n" | |
| f"- Topic: {topic}\n" | |
| f"- Level: {level}\n" | |
| f"- Question: {question}\n" | |
| f"- Student's Original Answer: {user_answer}\n" | |
| f"- Original Evaluation: {original_evaluation}\n" | |
| f"- Original Reasoning: {original_reasoning}\n\n" | |
| "Respond naturally in a conversational manner. Be helpful and educational, not defensive." | |
| ) | |
| messages = [{"role": "system", "content": system_prompt}] | |
| # Add conversation history | |
| for user_msg, assistant_msg in conversation_history: | |
| messages.append({"role": "user", "content": user_msg}) | |
| messages.append({"role": "assistant", "content": assistant_msg}) | |
| # Add current student message | |
| messages.append({"role": "user", "content": student_message}) | |
| response = self.client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=messages | |
| ) | |
| return response.choices[0].message.content.strip() | |
| def process(self, topic, level): | |
| print(f"\n[Assessment] Let's test your knowledge for the '{level}' level in '{topic}'.") | |
| print("I will ask you 5 questions.") | |
| NUM_QUESTIONS = 5 | |
| # New scoring thresholds based on your requirements | |
| ADVANCED_PASS = 0.70 # 70% for advanced level | |
| INTERMEDIATE_PASS = 0.65 # 65% for intermediate level | |
| MINIMUM_THRESHOLD = 0.30 # 30% minimum to avoid novice | |
| all_questions_str = self.generate_questions(topic, level) | |
| questions = [q.strip() for q in all_questions_str.split('\n') if q.strip()] | |
| questions = questions[:NUM_QUESTIONS] | |
| correct_answers = 0 | |
| per_question_feedback = [] # collect feedback for summary | |
| for i, question_text in enumerate(questions): | |
| current_question = ". ".join(question_text.split('. ')[1:]) | |
| print(f"\n----------\n[Question {i+1}/{len(questions)}] {current_question}") | |
| user_answer = input("Your answer: ") | |
| try: | |
| evaluation_json_str = self.evaluate_answer(topic, level, current_question, user_answer) | |
| evaluation_data = json.loads(evaluation_json_str) | |
| evaluation = str(evaluation_data.get("evaluation", "incorrect")).lower() | |
| reasoning = evaluation_data.get("reasoning", "No explanation provided.") | |
| hint = evaluation_data.get("hint", "") | |
| is_correct = evaluation == "correct" | |
| if is_correct: | |
| correct_answers += 1 | |
| print(f"[Feedback] {reasoning}") | |
| print("β Correct!") | |
| else: | |
| print(f"[Feedback] {reasoning}") | |
| print("β Incorrect.") | |
| if hint: | |
| print(f"π‘ Hint: {hint}") | |
| per_question_feedback.append({ | |
| "question": current_question, | |
| "correct": is_correct, | |
| "reason": reasoning, | |
| "hint": hint, | |
| }) | |
| except (json.JSONDecodeError, AttributeError) as e: | |
| print(f"[System Error] Could not parse the evaluation. Let's skip this one. Error: {e}") | |
| continue | |
| score_percentage = (correct_answers / len(questions)) * 100 | |
| # Print detailed feedback summary | |
| print("\n----------") | |
| print("ASSESSMENT FEEDBACK SUMMARY") | |
| for i, fb in enumerate(per_question_feedback, 1): | |
| tag = "β " if fb["correct"] else "β" | |
| print(f"{tag} Q{i}: {fb['question']}") | |
| if fb.get("reason"): | |
| print(f" Reason: {fb['reason']}") | |
| if (not fb["correct"]) and fb.get("hint"): | |
| print(f" Hint: {fb['hint']}") | |
| print(f"\n[Assessment Complete] You scored {score_percentage:.1f}% ({correct_answers}/{len(questions)} questions correct).") | |
| # Level determination based on claimed level thresholds | |
| if level == "advanced": | |
| if score_percentage >= 70: | |
| print("π― Assigned Level: advanced") | |
| return "advanced" | |
| elif score_percentage >= 65: | |
| print("π― Assigned Level: intermediate") | |
| return "intermediate" | |
| else: | |
| print("π― Assigned Level: novice") | |
| return "novice" | |
| elif level == "intermediate": | |
| if score_percentage >= 65: | |
| print("π― Assigned Level: intermediate") | |
| return "intermediate" | |
| else: | |
| print("π― Assigned Level: novice") | |
| return "novice" | |
| else: | |
| print("π― Assigned Level: novice") | |
| return "novice" |