Files
ekzamen-samokorrektiruyusch…/src/index.py
T

197 lines
5.8 KiB
Python
Raw 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.
#!/usr/bin/env python3
"""
Self-Correcting Agent
This module implements a simple selfcorrecting agent that can solve
arithmetic expressions and learn from user feedback. The agent keeps a
knowledge base of previously solved problems and their correct answers.
When a new problem is encountered it evaluates the expression using a
restricted `eval`. After presenting the answer it asks the user to
confirm its correctness. If the user indicates that the answer is
incorrect, the agent records the userprovided correct answer and
updates its knowledge base. Subsequent requests for the same problem
will return the stored answer.
Author: Artur Kuzakhmetov
License: MIT
"""
from __future__ import annotations
import ast
import operator
import sys
from pathlib import Path
from typing import Dict, Tuple
# Allowed operators for safe evaluation
_ALLOWED_OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
def _safe_eval(expr: str) -> float:
"""
Safely evaluate a simple arithmetic expression.
Parameters
----------
expr : str
The arithmetic expression to evaluate.
Returns
-------
float
The numerical result of the expression.
Raises
------
ValueError
If the expression contains unsupported syntax or operators.
"""
try:
node = ast.parse(expr, mode="eval")
except SyntaxError as exc:
raise ValueError(f"Invalid expression: {expr}") from exc
def _eval(node: ast.AST) -> float:
if isinstance(node, ast.Expression):
return _eval(node.body)
if isinstance(node, ast.Num): # Python <3.8
return node.n
if isinstance(node, ast.Constant): # Python 3.8+
if isinstance(node.value, (int, float)):
return node.value
raise ValueError(f"Unsupported constant type: {type(node.value)}")
if isinstance(node, ast.BinOp):
left = _eval(node.left)
right = _eval(node.right)
op_type = type(node.op)
if op_type in _ALLOWED_OPERATORS:
return _ALLOWED_OPERATORS[op_type](left, right)
raise ValueError(f"Unsupported operator: {op_type}")
if isinstance(node, ast.UnaryOp):
operand = _eval(node.operand)
op_type = type(node.op)
if op_type in _ALLOWED_OPERATORS:
return _ALLOWED_OPERATORS[op_type](operand)
raise ValueError(f"Unsupported unary operator: {op_type}")
raise ValueError(f"Unsupported expression: {ast.dump(node)}")
return _eval(node)
class SelfCorrectingAgent:
"""
A simple selfcorrecting agent that learns from user feedback.
Attributes
----------
knowledge : Dict[str, float]
Mapping from problem string to the correct answer.
"""
def __init__(self, knowledge_file: Path | None = None) -> None:
self.knowledge: Dict[str, float] = {}
self.knowledge_file = knowledge_file
if knowledge_file and knowledge_file.exists():
self._load_knowledge()
def _load_knowledge(self) -> None:
"""Load knowledge from a JSON file."""
import json
with self.knowledge_file.open("r", encoding="utf-8") as f:
data = json.load(f)
self.knowledge = {k: float(v) for k, v in data.items()}
def _save_knowledge(self) -> None:
"""Persist knowledge to a JSON file."""
if not self.knowledge_file:
return
import json
with self.knowledge_file.open("w", encoding="utf-8") as f:
json.dump(self.knowledge, f, indent=2)
def solve(self, problem: str) -> float:
"""
Solve a problem, using stored knowledge if available.
Parameters
----------
problem : str
The arithmetic expression to solve.
Returns
-------
float
The computed answer.
"""
if problem in self.knowledge:
return self.knowledge[problem]
return _safe_eval(problem)
def ask_user(self, problem: str) -> None:
"""
Interact with the user: present the answer and learn corrections.
Parameters
----------
problem : str
The arithmetic expression to solve.
"""
try:
answer = self.solve(problem)
except ValueError as exc:
print(f"Error: {exc}")
return
print(f"Answer: {answer}")
while True:
resp = input("Is this correct? (y/n): ").strip().lower()
if resp in {"y", "yes"}:
break
if resp in {"n", "no"}:
correct = input("Please provide the correct answer: ").strip()
try:
correct_val = float(correct)
except ValueError:
print("Invalid number. Try again.")
continue
self.knowledge[problem] = correct_val
print("Knowledge updated.")
break
print("Please answer 'y' or 'n'.")
def run(self) -> None:
"""
Run an interactive loop until the user exits.
"""
print("SelfCorrecting Agent")
print("Type 'exit' to quit.")
while True:
problem = input("Enter problem: ").strip()
if problem.lower() in {"exit", "quit"}:
print("Goodbye!")
self._save_knowledge()
break
if not problem:
continue
self.ask_user(problem)
def main() -> None:
"""Entry point for the commandline interface."""
agent = SelfCorrectingAgent(knowledge_file=Path("knowledge.json"))
agent.run()
if __name__ == "__main__":
main()