Commit 43e305bb authored by Kantz's avatar Kantz
Browse files

überarbeitung des decision_LLM und integration der neuen Varianten

parent d7152516
...@@ -66,3 +66,4 @@ Retrieval settings: ...@@ -66,3 +66,4 @@ Retrieval settings:
python -m test.hint_test ... python -m test.hint_test ...
python -m test.vector_store_test --query "Was ist eine Teilmenge?" --k 8 --expand python -m test.vector_store_test --query "Was ist eine Teilmenge?" --k 8 --expand
python -m test.math_intent_test --input "Integrate x^2" --input "Was ist 2+2?" python -m test.math_intent_test --input "Integrate x^2" --input "Was ist 2+2?"
python -m test.decision_test --chat-id draft_session_mlgeyn5z_unpxyh
\ No newline at end of file
import json from ollama import chat
from app.deterministic_services import llm_client def context_decision(needs_more_context: bool, reason: str) -> dict:
# Kannst auch einfach nur return {"needs_more_context": needs_more_context, "reason": reason}
return {"needs_more_context": bool(needs_more_context), "reason": str(reason)}
CLASSIFIER_PROMPT = (
"Du bist ein Klassifikator. Entscheide, ob die vorhandenen Informationen "
"aus Historie und Kontextblatt ausreichen, um einen naechsten didaktisch "
"wertvollen Hinweis zu geben. Antworte ausschliesslich mit gueltigem JSON "
"im Format {\"needs_more_context\": true/false, \"reason\": \"...\"}."
)
CLASSIFIER_SYSTEM = """
Du bist ein Klassifikator für didaktische Tutoring-Hinweise.
def needs_more_context(history: str, context_sheet: str) -> dict: Aufgabe:
prompt = ( Entscheide, ob Historie + Kontextblatt ausreichen, um den NÄCHSTEN didaktisch wertvollen Hinweis zu geben.
Wenn irgendetwas Wesentliches fehlt, ist needs_more_context=true.
Der Kontext ist ausreichend wenn:
1. Die aktuelle Problemstellung und Lösungsidee klar ist.
2. aktueller Lösungsstand klar ist? (was wurde schon probiert)
3. der typischer nächster Schritt ableitbar ist.
4. es keine fehlen Variablen/Definitionen/Teilaufgabe gibt.
WICHTIG:
- Antworte NICHT mit freiem Text.
- Rufe IMMER das Tool context_decision auf.
- needs_more_context ist boolean.
- reason ist eine kurze Begründung (1–3 Sätze), konkret was fehlt oder warum es reicht.
- Löse nicht die Aufgabe, sondern bewerte nur die Situation bzgl. der nächsten didaktischen Schritte.
"""
def needs_more_context(history: str, context_sheet: str, model: str = "ministral-3") -> dict:
user_prompt = (
"Kontextblatt:\n" "Kontextblatt:\n"
+ context_sheet f"{context_sheet}\n\n"
+ "\n\nHistorie:\n" "Historie:\n"
+ history f"{history}\n\n"
+ "\n\nAntwortformat: JSON." "Treffe eine Entscheidung."
) )
result = llm_client.chat(
messages=[ messages = [
{"role": "user", "content": prompt}, {"role": "system", "content": CLASSIFIER_SYSTEM},
{"role": "system", "content": CLASSIFIER_PROMPT}, {"role": "user", "content": user_prompt},
], ]
use_ollama=True,
resp = chat(
model=model,
messages=messages,
tools=[context_decision],
stream=False,
options={"temperature": 0},
) )
content = llm_client.get_message_content(result)
try: # 1) Ideal: strukturiertes Toolcall-Objekt
payload = json.loads(content) if resp.message.tool_calls:
return { call = resp.message.tool_calls[0]
"needs_more_context": bool(payload.get("needs_more_context")), if call.function.name != "context_decision":
"reason": content, return {"needs_more_context": True, "reason": f"Unexpected tool: {call.function.name}"}
}
except json.JSONDecodeError: args = call.function.arguments
return {"needs_more_context": True, "reason": content} # Tool ausführen (oder alternativ direkt args zurückgeben)
return context_decision(**args)
# 2) Fallback: falls Modell doch keinen Toolcall gemacht hat
return {"needs_more_context": True, "reason": resp.message.content or "No tool_call returned"}
...@@ -11,7 +11,7 @@ def generate_hint( ...@@ -11,7 +11,7 @@ def generate_hint(
) -> str: ) -> str:
system_prompt = ( system_prompt = (
"Du bist ein didaktischer Tutor. " "Du bist ein didaktischer Tutor. "
"Gebe dem Nutzer hilfreiche Tipps um mathematische Aufgaben zu lösen." "Gebe dem Nutzer einen hilfreichen Tipp um mathematische Aufgaben zu lösen."
"Gebe keine Beispiele oder Erklärungen, sondern nur den Tipp." "Gebe keine Beispiele oder Erklärungen, sondern nur den Tipp."
"Gebe keine komplette Lösung der Aufgabe." "Gebe keine komplette Lösung der Aufgabe."
"Die Mathematische Lösung hat immer Vorrang vor der LLM Lösung." "Die Mathematische Lösung hat immer Vorrang vor der LLM Lösung."
...@@ -33,7 +33,7 @@ def generate_hint( ...@@ -33,7 +33,7 @@ def generate_hint(
prompt = "\nKontext:\n" + sources + "\n" + prompt prompt = "\nKontext:\n" + sources + "\n" + prompt
if query: if query:
prompt += "\n Aktuelle Frage:" + query prompt += "\n Aktuelle Frage:" + query
prompt += "\nGebe einen hilfreichen Tipp zur Lösung der Aufgabe der mir bei meiner aktuellen Frage hilft. Halte dich kurz und prägnant." prompt += "\nGebe einen hilfreichen Tipp zum nächsten Schritt der Aufgabe der mir bei meiner aktuellen Frage hilft. Halte dich kurz und prägnant."
result = llm_client.chat( result = llm_client.chat(
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}], messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}],
) )
......
...@@ -98,16 +98,10 @@ def update_retrieval_context( ...@@ -98,16 +98,10 @@ def update_retrieval_context(
def add_math_solution( def add_math_solution(
sheet: dict[str, Any], sheet: dict[str, Any],
task: str,
input_text: str,
symbols: list[str] | None,
solution: str, solution: str,
) -> None: ) -> None:
sheet["math_solutions"].append( sheet["math_solutions"].append(
{ {
"task": task,
"input": input_text,
"symbols": symbols or [],
"solution": solution, "solution": solution,
} }
) )
......
...@@ -24,17 +24,10 @@ def _bootstrap_context(sheet: dict, query_text: str, tool_log: list[dict]) -> No ...@@ -24,17 +24,10 @@ def _bootstrap_context(sheet: dict, query_text: str, tool_log: list[dict]) -> No
context_store.add_retrieval_context(sheet, query_text, sources) context_store.add_retrieval_context(sheet, query_text, sources)
_append_tool_log(tool_log, "retrieve_context", {"query": query_text}, {"sources": [source.to_string() for source in sources]}) _append_tool_log(tool_log, "retrieve_context", {"query": query_text}, {"sources": [source.to_string() for source in sources]})
math_request = math_intent_LLM.extract_math_request(query_text) math_solution = math_intent_LLM.solve_with_tools(query_text)
if math_request: if math_solution:
solution = math_tool.sympy_solve(**math_request) context_store.add_math_solution(sheet, math_solution)
context_store.add_math_solution( _append_tool_log(tool_log, "math_intent_LLM", {"query": query_text}, math_solution)
sheet,
math_request["task"],
math_request["input"],
math_request.get("symbols"),
solution,
)
_append_tool_log(tool_log, "sympy_solve", math_request, solution)
def _extract_user_messages(messages: list[dict]) -> list[str]: def _extract_user_messages(messages: list[dict]) -> list[str]:
""" """
......
import argparse
import json
from typing import Any, Iterable
from app.LLM_services import decision_LLM
from app.deterministic_services import context_store
def _load_sheet_from_path(path: str) -> dict[str, Any]:
with open(path, "r", encoding="utf-8") as handle:
return json.load(handle)
def _resolve_sheet(args: argparse.Namespace) -> dict[str, Any]:
if args.sheet:
return _load_sheet_from_path(args.sheet)
if args.chat_id:
sheet = context_store.load_sheet(args.chat_id)
if sheet is None:
raise FileNotFoundError(f"Kein Context-Sheet gefunden fuer chat_id={args.chat_id}")
return sheet
raise ValueError("Bitte --sheet oder --chat-id angeben.")
def _last_user_inputs(history: list[dict[str, Any]], count: int) -> list[str]:
if count <= 0:
return []
user_msgs = [msg.get("content", "") for msg in history if msg.get("role") == "user"]
return [text for text in user_msgs[-count:] if text]
def main() -> None:
parser = argparse.ArgumentParser(description="Isolierter Decision-LLM Test mit Context Sheet.")
parser.add_argument("--sheet", help="Pfad zu einem Context Sheet JSON.")
parser.add_argument("--chat-id", help="Chat-ID zum Laden aus logs/context_sheets.")
parser.add_argument(
"--last-user-count",
type=int,
default=1,
help="Wie viele der letzten Nutzer-Eingaben ausgegeben werden sollen.",
)
args = parser.parse_args()
sheet = _resolve_sheet(args)
history_text = context_store.get_history(sheet)
context_text = context_store.format_sheet(sheet)
decision = decision_LLM.needs_more_context(history_text, context_text)
last_inputs = _last_user_inputs(sheet.get("history", []), args.last_user_count)
print("LAST_USER_INPUTS:", json.dumps(last_inputs, ensure_ascii=True))
print("OUTPUT:", json.dumps(decision, ensure_ascii=True))
if __name__ == "__main__":
main()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment