Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
math_tutor_dev
public_math_tutor
Commits
d7152516
Commit
d7152516
authored
Feb 10, 2026
by
Kantz
Browse files
math_intent_LLM.py überarbeitet.
parent
39de7efd
Changes
3
Show whitespace changes
Inline
Side-by-side
math-tutor/README.md
View file @
d7152516
...
...
@@ -65,3 +65,4 @@ Retrieval settings:
## Testing
python -m test.hint_test ...
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?"
math-tutor/backend/app/LLM_services/math_intent_LLM.py
View file @
d7152516
import
json
import
sympy
as
sp
from
ollama
import
chat
from
app.deterministic_services
import
llm_client
def
sympy_solve
(
task
:
str
,
input
:
str
,
symbols
:
list
[
str
]
|
None
=
None
)
->
str
:
"""
task: simplify|solve|diff|integrate
input: SymPy-Expression als String, z.B. '((1/a)+a)/(a+1) - (a-1)/(a+1)'
symbols: z.B. ['a']
"""
symbols
=
symbols
or
[]
locals_map
=
{
name
:
sp
.
Symbol
(
name
)
for
name
in
symbols
}
expr
=
sp
.
sympify
(
input
,
locals
=
locals_map
)
MATH_INTENT_PROMPT
=
(
"Du bist ein Parser fuer Matheaufgaben. Entscheide, ob sympy_solve genutzt "
"werden soll. Wenn ja, gib ein JSON-Objekt mit {
\"
use_math
\"
: true, "
"
\"
task
\"
:
\"
solve|simplify|diff|integrate
\"
,
\"
input
\"
:
\"
...
\"
, "
"
\"
symbols
\"
: [
\"
x
\"
, ...]} zurueck. Wenn nein, gib "
"{
\"
use_math
\"
: false} zurueck. Antworte nur mit JSON."
)
if
task
==
"simplify"
:
return
str
(
sp
.
simplify
(
expr
))
if
task
==
"diff"
:
if
not
symbols
:
raise
ValueError
(
"diff requires symbols[0]"
)
return
str
(
sp
.
diff
(
expr
,
locals_map
[
symbols
[
0
]]))
if
task
==
"integrate"
:
if
not
symbols
:
raise
ValueError
(
"integrate requires symbols[0]"
)
return
str
(
sp
.
integrate
(
expr
,
locals_map
[
symbols
[
0
]]))
if
task
==
"solve"
:
if
not
symbols
:
raise
ValueError
(
"solve requires symbols[0]"
)
return
str
(
sp
.
solve
(
sp
.
Eq
(
expr
,
0
),
locals_map
[
symbols
[
0
]]))
raise
ValueError
(
f
"Unknown task:
{
task
}
"
)
def
extract_math_request
(
user_text
:
str
)
->
dict
|
None
:
result
=
llm_client
.
chat
(
messages
=
[
{
"role"
:
"system"
,
"content"
:
MATH_INTENT_PROMPT
},
SYSTEM
=
"""
Du bist ein Mathe-Assistent.
Wenn eine Rechnung nötig ist, nutze das Tool sympy_solve.
Wichtig: Übergib in input eine gültige SymPy-Expression (kein LaTeX).
Antworte final mit dem vereinfachten Ergebnis (kein Doppelbruch).
"""
def
solve_with_tools
(
user_text
:
str
,
model
:
str
=
"ministral-3"
)
->
str
:
messages
=
[
{
"role"
:
"system"
,
"content"
:
SYSTEM
},
{
"role"
:
"user"
,
"content"
:
user_text
},
],
use_ollama
=
True
,
)
content
=
llm_client
.
get_message_content
(
result
)
try
:
payload
=
json
.
loads
(
content
)
except
json
.
JSONDecodeError
:
return
None
if
not
payload
.
get
(
"use_math"
):
return
None
task
=
payload
.
get
(
"task"
)
input_text
=
payload
.
get
(
"input"
)
symbols
=
payload
.
get
(
"symbols"
)
or
[]
if
not
task
or
not
input_text
:
return
None
return
{
"task"
:
task
,
"input"
:
input_text
,
"symbols"
:
symbols
}
]
# 1) Modell darf Toolcalls erzeugen
resp
=
chat
(
model
=
model
,
messages
=
messages
,
tools
=
[
sympy_solve
],
stream
=
False
)
messages
.
append
(
resp
.
message
)
# 2) Toolcalls ausführen und Ergebnisse zurückgeben
if
resp
.
message
.
tool_calls
:
for
call
in
resp
.
message
.
tool_calls
:
if
call
.
function
.
name
==
"sympy_solve"
:
result
=
sympy_solve
(
**
call
.
function
.
arguments
)
else
:
result
=
f
"Unknown tool:
{
call
.
function
.
name
}
"
messages
.
append
({
"role"
:
"tool"
,
"tool_name"
:
call
.
function
.
name
,
"content"
:
str
(
result
),
})
# 3) Finalen Antwort-Call (Modell integriert Tool-Ergebnis)
final
=
chat
(
model
=
model
,
messages
=
messages
,
tools
=
[
sympy_solve
],
stream
=
False
)
return
final
.
message
.
content
# Falls kein Toolcall: direkt zurück
return
resp
.
message
.
content
math-tutor/backend/test/math_intent_test.py
0 → 100644
View file @
d7152516
import
argparse
import
json
from
typing
import
Iterable
from
app.LLM_services
import
math_intent_LLM
def
_iter_inputs
(
args
:
argparse
.
Namespace
)
->
Iterable
[
str
]:
if
args
.
input_file
:
with
open
(
args
.
input_file
,
"r"
,
encoding
=
"utf-8"
)
as
handle
:
for
line
in
handle
:
text
=
line
.
strip
()
if
text
:
yield
text
return
for
text
in
args
.
input
:
text
=
text
.
strip
()
if
text
:
yield
text
def
main
()
->
None
:
parser
=
argparse
.
ArgumentParser
(
description
=
"Isolierter Math-Intent Test."
)
parser
.
add_argument
(
"--input"
,
action
=
"append"
,
default
=
[],
help
=
"Eingabe fuer den Math-Intent (mehrfach angeben)."
,
)
parser
.
add_argument
(
"--input-file"
,
help
=
"Textdatei mit einer Eingabe pro Zeile."
,
)
args
=
parser
.
parse_args
()
if
not
args
.
input
and
not
args
.
input_file
:
raise
ValueError
(
"Bitte --input oder --input-file angeben."
)
for
text
in
_iter_inputs
(
args
):
result
=
math_intent_LLM
.
solve_with_tools
(
text
)
print
(
"INPUT:"
,
text
)
print
(
"OUTPUT:"
,
json
.
dumps
(
result
,
ensure_ascii
=
True
))
if
__name__
==
"__main__"
:
main
()
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment