text_to_solver_program/understanding/understand.py
dschlueter b07dd16143 feat: Aufgaben mit Abbildungen (docling-Markdown), mit Gegenprobe beim Ablesen
Eingebettete Base64-Bilder und Bilddateien werden aus dem Aufgabentext gelöst und nur
der Verstehensstufe als Bild übergeben; sie überträgt jede Abbildung in Text. Eine
zweite, unabhängige Lesung wird Zahl für Zahl verglichen (bei Abweichung entscheidet
eine dritte, sonst reliable=False). Alle weiteren Stufen rechnen mit der Übertragung,
der Report zeigt Original und Übertragung nebeneinander. LLM_MODEL_UNDERSTANDING
wählt das Bildmodell.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 17:52:52 +02:00

54 lines
2.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Stufe 1 der Pipeline: erfassen, was im Aufgabentext steht – Teilfragen und Zahlen, noch kein Modell."""
from pathlib import Path
from typing import List
from core.llm import LLMSession
from core.model_schema import CheckIssue, FigureInput, FigureReading, TaskUnderstanding
from understanding.figures import image_parts
UNDERSTAND_PROMPT_FILE = Path(__file__).parent / "Verstehen-Prompt.txt"
FIGURE_PROMPT_FILE = Path(__file__).parent / "Abbildungs-Prompt.txt"
def _content(text: str, figures: List[FigureInput]):
"""Reiner Text – oder, mit Abbildungen, eine Liste aus Text und Bildern (OpenAI-Format)."""
if not figures:
return text
hinweis = (f"\n\nDER AUFGABE LIEGEN {len(figures)} ABBILDUNG(EN) BEI – sie folgen als Bilder; "
"übertrage jede vollständig in figures.")
return [{"type": "text", "text": text + hinweis}, *image_parts(figures)]
def _create_with_images(session: LLMSession, response_model, messages, step: str, figures):
"""Wie session.create – mit verständlicher Meldung, falls das Modell keine Bilder annimmt."""
try:
return session.create(response_model, messages, step=step)
except Exception as exc:
if figures and any(w in str(exc).lower() for w in ("image", "vision", "modalit", "multimodal")):
raise RuntimeError(f"Das Modell dieser Stufe nimmt offenbar keine Bilder an ({str(exc)[:160]}). "
"Ein Bildmodell über LLM_MODEL_UNDERSTANDING wählen.") from exc
raise
def understand_task(session: LLMSession, task: str, issues: List[CheckIssue] = (),
data_prompt: str = "", figures: List[FigureInput] = ()) -> TaskUnderstanding:
"""Stufe 1: erfassen, was im Text steht – Teilfragen, Zahlen, Machbarkeit, Abbildungen. Noch kein Modell."""
nachtrag = ""
if issues:
nachtrag = ("\n\nDEIN VORIGER VERSUCH WAR UNVOLLSTÄNDIG:\n"
+ "\n".join(f"- [{i.code}] {i.message}" for i in issues)
+ "\nErfasse die fehlenden Punkte, behalte den Rest bei.")
text = f"AUFGABENTEXT:\n{task}\n\n{data_prompt}{nachtrag}".strip()
return _create_with_images(session, TaskUnderstanding, [
{"role": "system", "content": UNDERSTAND_PROMPT_FILE.read_text(encoding="utf-8")},
{"role": "user", "content": _content(text, list(figures))},
], "Verstehen", figures)
def read_figures(session: LLMSession, task: str, figures: List[FigureInput]) -> FigureReading:
"""Unabhängige Übertragung nur der Abbildungen – die Gegenprobe zur Verstehensstufe."""
text = f"AUFGABENTEXT (nur zur Einordnung der Abbildungen):\n{task}"
return _create_with_images(session, FigureReading, [
{"role": "system", "content": FIGURE_PROMPT_FILE.read_text(encoding="utf-8")},
{"role": "user", "content": _content(text, figures)},
], "Abbildungen", figures)