Рабочие агенты, но пока требуют улучшений
This commit is contained in:
113
agents.py
113
agents.py
@@ -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"}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
Твоя предыдущая попытка:
|
||||||
|
{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]}")
|
||||||
|
|
||||||
data = json.loads(content.strip())
|
|
||||||
return CodeSolution(
|
return CodeSolution(
|
||||||
files=data.get("files", {}),
|
files=parsed_data.get("files", {}),
|
||||||
description=data.get("description", ""),
|
description=parsed_data.get("description", ""),
|
||||||
tests=data.get("tests", {})
|
tests=parsed_data.get("tests", {})
|
||||||
)
|
)
|
||||||
except Exception as e:
|
|
||||||
raise ValueError(f"Failed to parse developer response: {e}\nResponse: {response.content}")
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
"""Строит граф процесса разработки"""
|
"""Строит граф процесса разработки"""
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user