Files
MultiAgent/agent/developer.py
2026-04-12 09:03:37 +05:00

138 lines
5.7 KiB
Python
Raw Permalink 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.
from typing import Optional, Dict, Any
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_ollama import ChatOllama
import json
from config import MODEL_NAME, TEMPERATURE, LLM_HOST
from models import CodeSolution, ReviewResult, ReviewComment, Task
import re
class DeveloperAgent:
"""Агент-разработчик: генерирует код по требованию"""
def __init__(self):
self.llm = ChatOllama(
model=MODEL_NAME,
temperature=TEMPERATURE,
base_url=LLM_HOST,
num_predict=8192
)
def develop(self, task: Task, previous_comments: list = None) -> CodeSolution:
"""Генерирует код с улучшенным парсингом JSON"""
system_prompt = """Ты - опытный разработчик. Твоя задача - написать качественный код.
Ты должен:
1. Писать чистый, читаемый код с комментариями
2. Добавлять обработку ошибок
3. Писать юнит-тесты (pytest/unittest)
4. Следовать PEP8 (для Python) или стандартам языка
ВАЖНО: Возвращай ТОЛЬКО валидный JSON без лишних символов.
ЗАПРЕЩЕНЫ trailing commas (запятые перед закрывающей скобкой).
Формат ответа (пример):
{
"files": {
"main.py": "print('hello')"
},
"description": "Краткое описание",
"tests": {
"test_main.py": "def test_hello(): assert True"
}
}
Не добавляй пояснений вне JSON.
"""
user_prompt = f"""
Требование: {task.requirement}
Текущая итерация: {task.iteration}
"""
if previous_comments:
user_prompt += f"""
Замечания с предыдущего ревью:
{json.dumps(previous_comments, indent=2, ensure_ascii=False)}
Пожалуйста, исправь код согласно замечаниям.
"""
messages = [
SystemMessage(content=system_prompt),
HumanMessage(content=user_prompt)
]
response = self.llm.invoke(messages)
raw_content = response.content
# Пытаемся извлечь JSON из разных форматов
parsed_data = self._extract_json(raw_content)
if parsed_data is None:
# Если не удалось, пробуем повторно с explicit инструкцией
print("⚠️ Не удалось распарсить JSON, повторный запрос с исправлением...")
correction_prompt = f"""
Предыдущий ответ содержал невалидный JSON (возможно, trailing commas).
Пожалуйста, исправь и верни ТОЛЬКО валидный JSON в формате:
{{
"files": {{"filename": "content"}},
"description": "text",
"tests": {{"testfile": "test_content"}}
}}
Твоя предыдущая попытка:
{raw_content[:1000]}
Требование: {task.requirement}
"""
messages2 = [
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