| |
| |
|
|
| """ |
| Problem Understanding Module |
| Ensures we understand WHAT is being asked before attempting to solve. |
| """ |
|
|
| import json |
| import re |
| from utils.safe_json import safe_extract_json |
|
|
|
|
| def get_problem_understanding_prompt(ocr_text: str, data_anchor: dict) -> str: |
| """ |
| Prompt LLM to analyze problem structure. |
| |
| Returns JSON with: |
| - problem_type |
| - sub_questions (all parts א, ב, ג, etc.) |
| - solving_order |
| - dependencies |
| """ |
| return f""" |
| ANALYZE this math problem structure. DO NOT SOLVE - only understand what is being asked. |
| |
| Problem Text: |
| {ocr_text} |
| |
| Extracted Data: |
| {json.dumps(data_anchor, ensure_ascii=False, indent=2)} |
| |
| Your task: Identify ALL parts of this problem and create a solving plan. |
| |
| Return JSON: |
| {{ |
| "problem_type": "CIRCLE_EQUATION | LINE_EQUATION | GEOMETRIC_LOCUS | DERIVATIVE_QUOTIENT | etc.", |
| "main_question": "Brief description of main question", |
| "sub_questions": [ |
| {{ |
| "id": "א", |
| "question": "Full text of sub-question א", |
| "requires": ["center", "radius"], |
| "specific_values": ["m=2"], |
| "expected_output": "equation | number | point | etc.", |
| "topic": "CIRCLE_EQUATION" |
| }}, |
| {{ |
| "id": "ב", |
| "question": "Full text of sub-question ב", |
| "requires": ["equation_from_א", "point"], |
| "specific_values": ["a=1"], |
| "expected_output": "line_equation", |
| "topic": "LINE_TANGENT" |
| }} |
| ], |
| "solving_order": ["א", "ב", "ג"], |
| "dependencies": {{ |
| "ב": ["א"], |
| "ג": ["א"] |
| }} |
| }} |
| |
| CRITICAL RULES: |
| 1. Include ALL sub-questions (א, ב, ג, ד, etc.) |
| 2. **CHRONOLOGICAL DATA ISOLATION (V310.0 - CRITICAL):** If a specific value (e.g., a=1, m=2) is mentioned ONLY in a specific sub-question, you MUST include it in the `specific_values` array for THAT sub-question only. NEVER put it in the top-level anchor if it's not global. This prevents data leakage (e.g., using a=1 from section ב' to solve section א'). |
| 3. **EXCEPTION:** If the problem asks for a **Geometric Locus (מקום גיאומטרי)**: |
| - This is a SINGLE QUESTION (even if it looks long). |
| - Set `problem_type` = "GEOMETRIC_LOCUS". |
| - Create ONLY ONE sub-question (id="א") containing the entire text. |
| 4. Identify dependencies (ב needs א's result) |
| 5. Determine topic for EACH sub-question |
| 6. DO NOT solve - only analyze structure |
| |
| Return ONLY valid JSON. |
| """ |
|
|
|
|
| def parse_understanding(response_text: str) -> dict: |
| """Parse LLM understanding response using canonical safe_extract_json.""" |
| result = safe_extract_json(response_text, caller="PROBLEM_UNDERSTANDING") |
| |
| if isinstance(result, dict) and result.get("logic_error"): |
| return { |
| "problem_type": "UNKNOWN", |
| "sub_questions": [], |
| "solving_order": [], |
| "dependencies": {} |
| } |
| return result |
|
|
|
|
|
|
| def validate_understanding(understanding: dict) -> bool: |
| """Validate understanding structure.""" |
| required_keys = ["problem_type", "sub_questions", "solving_order"] |
| |
| if not all(key in understanding for key in required_keys): |
| return False |
| |
| if not isinstance(understanding["sub_questions"], list): |
| return False |
| |
| if len(understanding["sub_questions"]) == 0: |
| return False |
| |
| |
| for sq in understanding["sub_questions"]: |
| if not all(key in sq for key in ["id", "question", "topic"]): |
| return False |
| |
| return True |
|
|
| def enforce_locus_rule(understanding: dict, ocr_text: str) -> dict: |
| """ |
| V260.2: Hard Rule - If 'מקום גיאומטרי' exists, FORCE Locus type. |
| """ |
| if any(k in ocr_text for k in ["מקום גיאומטרי", "Locus", "מצא את המקום", "המקום הגיאומטרי"]): |
| print("🛡️ [BIT-LOG] Hard Logic: Detected 'Geometric Locus' - Forcing Single Question structure.") |
| return { |
| "problem_type": "GEOMETRIC_LOCUS", |
| "main_question": understanding.get("main_question", "Find the Locus"), |
| "sub_questions": [{ |
| "id": "א", |
| "question": ocr_text, |
| "requires": [], |
| "expected_output": "equation", |
| "topic": "GEOMETRIC_LOCUS" |
| }], |
| "solving_order": ["א"], |
| "dependencies": {} |
| } |
| return understanding |
|
|
|
|
| |
|
|
| if __name__ == "__main__": |
| |
| ocr = """ |
| מעגל עם משוואה x² + y² = 12 |
| א. מצא את משוואת המעגל |
| ב. מצא משיק למעגל בנקודה A |
| ג. מצא את שטח המעגל |
| """ |
| |
| data = { |
| "function_equations": ["x^2 + y^2 = 12"], |
| "points": ["A"], |
| "specific_values": [], |
| "constraints": [] |
| } |
| |
| prompt = get_problem_understanding_prompt(ocr, data) |
| print(prompt) |
|
|