Рабочие агенты, но пока требуют улучшений

This commit is contained in:
ki.sagidullin
2026-04-11 10:20:47 +05:00
parent 58497365d9
commit f110f52e91
4 changed files with 95 additions and 32 deletions

119
agents.py
View File

@@ -1,11 +1,12 @@
# agents.py # agents.py
from typing import Dict, Any from typing import Optional, Dict, Any
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage from langchain_core.messages import HumanMessage, SystemMessage
from langchain_community.chat_models import ChatOllama from langchain_community.chat_models import ChatOllama
import json import json
from config import MODEL_NAME, TEMPERATURE from config import MODEL_NAME, TEMPERATURE, LLM_HOST
from models import CodeSolution, ReviewResult, ReviewComment, Task from models import CodeSolution, ReviewResult, ReviewComment, Task
import re
class DeveloperAgent: class DeveloperAgent:
"""Агент-разработчик: генерирует код по требованию""" """Агент-разработчик: генерирует код по требованию"""
@@ -14,11 +15,11 @@ class DeveloperAgent:
self.llm = ChatOllama( self.llm = ChatOllama(
model=MODEL_NAME, model=MODEL_NAME,
temperature=TEMPERATURE, temperature=TEMPERATURE,
base_url="http://localhost:11434" base_url=LLM_HOST
) )
def develop(self, task: Task, previous_comments: list = None) -> CodeSolution: def develop(self, task: Task, previous_comments: list = None) -> CodeSolution:
"""Генерирует код на основе требования и замечаний""" """Генерирует код с улучшенным парсингом JSON"""
system_prompt = """Ты - опытный разработчик. Твоя задача - написать качественный код. system_prompt = """Ты - опытный разработчик. Твоя задача - написать качественный код.
Ты должен: Ты должен:
@@ -26,17 +27,26 @@ class DeveloperAgent:
2. Добавлять обработку ошибок 2. Добавлять обработку ошибок
3. Писать юнит-тесты (pytest/unittest) 3. Писать юнит-тесты (pytest/unittest)
4. Следовать PEP8 (для Python) или стандартам языка 4. Следовать PEP8 (для Python) или стандартам языка
5. Возвращать решение в строго определённом JSON формате
В ответе должен быть JSON с полями: ВАЖНО: Возвращай ТОЛЬКО валидный JSON без лишних символов.
- files: dict (имя_файла -> содержимое) ЗАПРЕЩЕНЫ trailing commas (запятые перед закрывающей скобкой).
- description: str (описание решения)
- tests: dict (имя_теста -> содержимое теста) Формат ответа (пример):
{
"files": {
"main.py": "print('hello')"
},
"description": "Краткое описание",
"tests": {
"test_main.py": "def test_hello(): assert True"
}
}
Не добавляй пояснений вне JSON.
""" """
user_prompt = f""" user_prompt = f"""
Требование: {task.requirement} Требование: {task.requirement}
Текущая итерация: {task.iteration} Текущая итерация: {task.iteration}
""" """
@@ -55,26 +65,77 @@ class DeveloperAgent:
] ]
response = self.llm.invoke(messages) response = self.llm.invoke(messages)
raw_content = response.content
# Парсим JSON ответ # Пытаемся извлечь JSON из разных форматов
try: parsed_data = self._extract_json(raw_content)
# Извлекаем JSON из ответа
content = response.content if parsed_data is None:
# Находим JSON в тексте (между ```json и ``` или просто сам JSON) # Если не удалось, пробуем повторно с explicit инструкцией
if "```json" in content: print("⚠️ Не удалось распарсить JSON, повторный запрос с исправлением...")
content = content.split("```json")[1].split("```")[0] correction_prompt = f"""
elif "```" in content: Предыдущий ответ содержал невалидный JSON (возможно, trailing commas).
content = content.split("```")[1].split("```")[0] Пожалуйста, исправь и верни ТОЛЬКО валидный JSON в формате:
{{
"files": {{"filename": "content"}},
"description": "text",
"tests": {{"testfile": "test_content"}}
}}
data = json.loads(content.strip()) Твоя предыдущая попытка:
return CodeSolution( {raw_content[:1000]}
files=data.get("files", {}),
description=data.get("description", ""), Требование: {task.requirement}
tests=data.get("tests", {}) """
)
except Exception as e: messages2 = [
raise ValueError(f"Failed to parse developer response: {e}\nResponse: {response.content}") SystemMessage(content="Ты - разработчик. Верни только валидный JSON без лишних символов."),
HumanMessage(content=correction_prompt)
]
response2 = self.llm.invoke(messages2)
parsed_data = self._extract_json(response2.content)
if parsed_data is None:
raise ValueError(f"Failed to parse JSON after retry. Raw: {response2.content[:500]}")
return CodeSolution(
files=parsed_data.get("files", {}),
description=parsed_data.get("description", ""),
tests=parsed_data.get("tests", {})
)
def _extract_json(self, text: str) -> Optional[dict]:
"""Извлекает и очищает JSON из текста ответа LLM"""
# 1. Извлечь блок между ```json и ``` или просто ```
if "```json" in text:
text = text.split("```json")[1].split("```")[0]
elif "```" in text:
text = text.split("```")[1].split("```")[0]
text = text.strip()
# 2. Удалить trailing commas в объектах и массивах
# Удаляем запятую перед } или ]
text = re.sub(r',\s*}', '}', text)
text = re.sub(r',\s*]', ']', text)
# 3. Удалить комментарии (// ...) если есть
text = re.sub(r'//.*?(\n|$)', '', text)
# 4. Попробовать распарсить
try:
return json.loads(text)
except json.JSONDecodeError as e:
print(f"JSON decode error: {e}")
# 5. Еще одна попытка: найти любой JSON объект в строке
match = re.search(r'\{.*\}|\[.*\]', text, re.DOTALL)
if match:
try:
return json.loads(match.group())
except:
pass
return None
class ReviewerAgent: class ReviewerAgent:
"""Агент-ревьювер: проверяет качество кода""" """Агент-ревьювер: проверяет качество кода"""
@@ -82,7 +143,7 @@ class ReviewerAgent:
self.llm = ChatOllama( self.llm = ChatOllama(
model=MODEL_NAME, model=MODEL_NAME,
temperature=0.1, temperature=0.1,
base_url="http://localhost:11434" base_url=LLM_HOST
) )
def review(self, task: Task, solution: CodeSolution) -> ReviewResult: def review(self, task: Task, solution: CodeSolution) -> ReviewResult:

View File

@@ -6,7 +6,8 @@ load_dotenv()
# Конфигурация модели # Конфигурация модели
# OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
MODEL_NAME = "qwen3.5:latest" # или "gpt-3.5-turbo" для тестов LLM_HOST = "http://192.168.212.100:11434"
MODEL_NAME = "qwen3.5-16k:latest" # или "gpt-3.5-turbo" для тестов
TEMPERATURE = 0.3 TEMPERATURE = 0.3
MAX_ITERATIONS = 3 MAX_ITERATIONS = 3

View File

@@ -18,7 +18,8 @@ class Orchestrator:
# Создаем граф состояний # Создаем граф состояний
self.workflow = self._build_workflow() self.workflow = self._build_workflow()
self.app = self.workflow.compile(checkpointer=MemorySaver()) self.checkpointer = MemorySaver()
self.app = self.workflow.compile()
def _build_workflow(self) -> StateGraph: def _build_workflow(self) -> StateGraph:
"""Строит граф процесса разработки""" """Строит граф процесса разработки"""

View File

@@ -1,6 +1,6 @@
# requirements.txt # requirements.txt
langgraph>=0.2.0 langgraph>=0.3.0
langchain>=0.2.0 langchain>=0.2.0
langchain-openai>=0.1.0 langchain-openai>=0.1.0
langchain-community>=0.0.16 langchain-community>=0.0.16