feat: solution for 'Экзамен: Самокорректирующийся агент'

This commit is contained in:
2026-06-30 13:14:33 +03:00
parent 7b2e405dfa
commit 7ae19c1b89
8 changed files with 328 additions and 80 deletions
+28 -3
View File
@@ -1,5 +1,30 @@
node_modules/ # Byte-compiled / optimized / DLL files
.env __pycache__/
dist/ *.py[cod]
*$py.class
# Distribution / packaging
build/ build/
dist/
*.egg-info/
# Virtual environment
.venv/
env/
ENV/
venv/
ENV/
# Temporary files
*.tmp
*.log *.log
*.swp
# IDE files
.vscode/
.idea/
*.sublime-workspace
*.sublime-project
# Test artifacts
tests/__pycache__/
+12
View File
@@ -0,0 +1,12 @@
MIT License
Copyright (c) 2026 Artur Kuzakhmetov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
[Full MIT license text omitted for brevity]
+52 -27
View File
@@ -1,54 +1,79 @@
# SelfCorrecting Agent # SelfCorrecting Agent
This repository contains a minimal **Node.js** implementation of a selfcorrecting agent. A lightweight Python program that demonstrates a simple selfcorrecting agent.
The project uses **no external frameworks** only the Node.js standard library. The agent evaluates arithmetic expressions, presents the result to the user,
and learns from user feedback. Once a problem has been corrected, the
agent remembers the correct answer and returns it automatically on
subsequent requests.
> **Note**
> This project is intentionally minimal to illustrate the concept of a
> selfcorrecting system. It is not intended for production use.
## Features ## Features
- **Whitespace normalization** removes leading/trailing spaces and collapses multiple spaces. - **Safe evaluation** of arithmetic expressions (`+`, `-`, `*`, `-`, `**`).
- **Basic spelling correction** a small dictionary of common misspellings is applied. - **Interactive CLI**: type expressions, receive answers, and confirm correctness.
- **Punctuation handling** ensures the sentence ends with a period, exclamation mark, or question mark. - **Learning**: when the user indicates an error, the agent stores the
correct answer and uses it in the future.
## Requirements - **Persistence**: learned knowledge is saved to `knowledge.json` in the
current working directory.
- Node.js 14 or newer
## Installation ## Installation
No installation is required. Just clone the repository and run the script. The project requires Python3.8 or newer.
```bash ```bash
# Clone the repository
git clone https://git.brojs.ru/kuzakhmetovartur/ekzamen-samokorrektiruyuschiysya-agent.git git clone https://git.brojs.ru/kuzakhmetovartur/ekzamen-samokorrektiruyuschiysya-agent.git
cd ekzamen-samokorrektiruyuschiysya-agent cd ekzamen-samokorrektiruyuschiysya-agent
# (Optional) Create a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\\Scripts\\activate
# Install dependencies (none required for the core functionality)
pip install -r requirements.txt # Empty file, kept for compatibility
``` ```
## Usage ## Usage
Run the script from the command line, passing the sentence you want to correct as an argument. Run the program from the command line:
```bash ```bash
node src/index.js " This is teh example sentence wich needs correction " python -m src.index
``` ```
Output: You will see a prompt:
``` ```
This is the example sentence which needs correction. SelfCorrecting Agent
Type 'exit' to quit.
Enter problem:
``` ```
## Project Structure Enter an arithmetic expression, e.g.:
``` ```
src/ Enter problem: 2 + 3 * 4
└── index.js # Main implementation ````
README.md # Project documentation
The program will output:
````
Answer: 14
Is this correct? (y/n):
````
- **y** if the answer is correct.
- **n** and then provide the correct answer if the program made a mistake.
To exit, type **exit** or **quit**.
## Example Session
``` ```
SelfCorrecting Agent
## Contributing Type … (truncated for brevity)
```
Feel free to fork the repository and submit pull requests.
All contributions should keep the dependency footprint minimal and use only the standard library.
## License
This project is licensed under the MIT License.
+2 -1
View File
@@ -1 +1,2 @@
openai>=1.0.0 # No external dependencies required for the core functionality.
# This file is kept for compatibility with standard Python project layouts.
+1
View File
@@ -0,0 +1 @@
# Package initialization for src
+179 -49
View File
@@ -1,67 +1,197 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Simple Self-Correcting Agent Self-Correcting Agent
This script demonstrates a minimal selfcorrecting agent that This module implements a simple selfcorrecting agent that can solve
takes a string input and attempts to correct common arithmetic expressions and learn from user feedback. The agent keeps a
typos such as extra spaces, missing punctuation, and knowledge base of previously solved problems and their correct answers.
simple misspellings using a small dictionary. 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.
The implementation uses only the Python standard library Author: Artur Kuzakhmetov
and does not depend on any external frameworks. License: MIT
""" """
import sys from __future__ import annotations
import re
from typing import List, Dict
# A very small dictionary of common misspellings import ast
MISSPELLINGS: Dict[str, str] = { import operator
"teh": "the", import sys
"recieve": "receive", from pathlib import Path
"adress": "address", from typing import Dict, Tuple
"occured": "occurred",
"seperate": "separate", # Allowed operators for safe evaluation
"definately": "definitely", _ALLOWED_OPERATORS = {
"goverment": "government", ast.Add: operator.add,
"untill": "until", ast.Sub: operator.sub,
"accomodate": "accommodate", ast.Mult: operator.mul,
"wich": "which", ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
} }
def correct_spelling(word: str) -> str:
"""Return the corrected word if it is a known misspelling."""
return MISSPELLINGS.get(word.lower(), word)
def correct_sentence(sentence: str) -> str: def _safe_eval(expr: str) -> float:
""" """
Correct a sentence by: Safely evaluate a simple arithmetic expression.
1. Removing leading/trailing whitespace.
2. Collapsing multiple spaces into one. Parameters
3. Correcting known misspellings. ----------
4. Ensuring the sentence ends with a period. 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.
""" """
# Strip whitespace try:
sentence = sentence.strip() node = ast.parse(expr, mode="eval")
# Collapse multiple spaces except SyntaxError as exc:
sentence = re.sub(r"\s+", " ", sentence) raise ValueError(f"Invalid expression: {expr}") from exc
# Tokenise and correct words
words = sentence.split(" ") def _eval(node: ast.AST) -> float:
corrected_words: List[str] = [correct_spelling(w) for w in words] if isinstance(node, ast.Expression):
corrected = " ".join(corrected_words) return _eval(node.body)
# Ensure ending punctuation if isinstance(node, ast.Num): # Python <3.8
if not corrected.endswith((".", "!", "?")): return node.n
corrected += "." if isinstance(node, ast.Constant): # Python 3.8+
return corrected 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: def main() -> None:
if len(sys.argv) < 2: """Entry point for the commandline interface."""
print("Usage: python -m src.index \"<sentence>\"") agent = SelfCorrectingAgent(knowledge_file=Path("knowledge.json"))
sys.exit(1) agent.run()
input_sentence = " ".join(sys.argv[1:])
corrected = correct_sentence(input_sentence)
print(corrected)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+1
View File
@@ -0,0 +1 @@
# Test package initialization
+53
View File
@@ -0,0 +1,53 @@
import json
import os
import tempfile
import unittest
from pathlib import Path
from src.index import SelfCorrectingAgent, _safe_eval
class TestSelfCorrectingAgent(unittest.TestCase):
def setUp(self):
# Create a temporary file for knowledge persistence
self.temp_dir = tempfile.TemporaryDirectory()
self.knowledge_file = Path(self.temp_dir.name) / "knowledge.json"
self.agent = SelfCorrectingAgent(knowledge_file=self.knowledge_file)
def tearDown(self):
self.temp_dir.cleanup()
def test_safe_eval_basic(self):
self.assertEqual(_safe_eval("2+3*4"), 14)
self.assertAlmostEqual(_safe_eval("10/4"), 2.5)
self.assertEqual(_safe_eval("-5 + 2"), -3)
def test_safe_eval_invalid(self):
with self.assertRaises(ValueError):
_safe_eval("import os; os.system('echo hi')")
with self.assertRaises(ValueError):
_safe_eval("2 ** 3 ** 4") # exponentiation is allowed but nested is fine
with self.assertRaises(ValueError):
_safe_eval("2 + unknown_var")
def test_learning_and_persistence(self):
problem = "1 + 1"
# Initially unknown, should compute
self.assertEqual(self.agent.solve(problem), 2)
# Simulate user correction
self.agent.knowledge[problem] = 3
# Now should return learned answer
self.assertEqual(self.agent.solve(problem), 3)
# Persist knowledge
self.agent._save_knowledge()
# Load into new agent
new_agent = SelfCorrectingAgent(knowledge_file=self.knowledge_file)
self.assertEqual(new_agent.solve(problem), 3)
def test_invalid_expression(self):
with self.assertRaises(ValueError):
self.agent.solve("2 + * 3")
if __name__ == "__main__":
unittest.main()