Web3CyberAgent / api /assistant_factory.py
web3cyberservices
Update assistant_factory.py
2d8971f unverified
Raw
History Blame Contribute Delete
7.66 kB
import json
import logging
import asyncio
from pathlib import Path
from typing import Dict, Any, Optional
# Настройка локального логгера для сервера
logger = logging.getLogger("CyberAgentFactory")
class AssistantFactory:
def __init__(self, logger_agent=None, rules_path: str = 'api/coding_rules.json'):
"""
Фабрика для сборки контекста и системных инструкций ИИ-агента.
Реализована поддержка неблокирующего чтения конфигураций.
"""
self.logger_agent = logger_agent
self.rules_path = Path(rules_path)
# Кэш в оперативной памяти для исключения избыточного дискового I/O
self._cached_rules: Optional[Dict[str, Any]] = None
# Блокировка для предотвращения Race Condition при горячей перезагрузке кэша
self._lock = asyncio.Lock()
def _read_file_sync(self) -> Dict[str, Any]:
"""Синхронный низкоуровневый запуск чтения (для выполнения в отдельном потоке)"""
with open(self.rules_path, 'r', encoding='utf-8') as file:
return json.load(file)
async def _load_rules(self) -> Dict[str, Any]:
"""
Безопасный асинхронный метод загрузки и кэширования правил.
Выносит дисковое чтение из основного event loop приложения FastAPI.
"""
if self._cached_rules is not None:
return self._cached_rules
async with self._lock:
# Двойная проверка (Double-checked locking паттерн)
if self._cached_rules is not None:
return self._cached_rules
if not self.rules_path.exists():
warn_msg = f"Файл гайдлайнов не найден по пути: {self.rules_path}. Используется дефолтный промпт."
logger.warning(warn_msg)
if self.logger_agent and hasattr(self.logger_agent, 'log'):
self.logger_agent.log('factory_warning', warn_msg)
self._cached_rules = {}
return self._cached_rules
try:
# Делегируем блокирующий ввод-вывод в пул потоков операционной системы
self._cached_rules = await asyncio.to_thread(self._read_file_sync)
log_msg = f"Конфигурация правил успешно инициализирована из {self.rules_path} и кэширована."
logger.info(log_msg)
if self.logger_agent and hasattr(self.logger_agent, 'log'):
self.logger_agent.log('factory_init', log_msg)
return self._cached_rules
except json.JSONDecodeError as e:
err_msg = f"Критическая ошибка синтаксиса JSON в {self.rules_path}: {str(e)}. Активирован защитный режим."
logger.error(err_msg)
if self.logger_agent and hasattr(self.logger_agent, 'log'):
self.logger_agent.log('factory_json_error', err_msg)
self._cached_rules = {}
return self._cached_rules
except Exception as e:
err_msg = f"Непредвиденный сбой ввода-вывода при чтении {self.rules_path}: {str(e)}"
logger.error(err_msg)
if self.logger_agent and hasattr(self.logger_agent, 'log'):
self.logger_agent.log('factory_io_error', err_msg)
self._cached_rules = {}
return self._cached_rules
def _format_rules_to_markdown(self, rules: Dict[str, Any], depth: int = 2) -> str:
"""
Рекурсивно преобразует JSON-словарь любой вложенности в валидный Markdown список.
Максимизирует показатель Prompt Adherence для LLM-моделей.
"""
md_lines = []
prefix = "#" * min(depth, 6)
if not isinstance(rules, dict):
return str(rules)
for key, val in rules.items():
display_key = key.replace("_", " ").capitalize()
if isinstance(val, dict):
md_lines.append(f"\n{prefix} {display_key}")
md_lines.append(self._format_rules_to_markdown(val, depth + 1))
elif isinstance(val, list):
md_lines.append(f"\n{prefix} {display_key}:")
for item in val:
md_lines.append(f" - {item}")
else:
# Базовый тип данных (строка, число, булево)
if depth == 2:
md_lines.append(f"**{display_key}**: {val}")
else:
md_lines.append(f" * **{key}**: {val}")
return "\n".join(md_lines)
async def create_assistant_prompt(self, user_prompt: str) -> str:
"""
Формирует структурированный системный промпт, изолируя правила безопасности
в оптимизированные под LLM-инференс Markdown теги.
"""
rules = await self._load_rules()
if not rules:
return user_prompt
# Трансляция древовидной JSON-структуры в читаемый ИИ-моделями Markdown-контекст
formatted_rules = self._format_rules_to_markdown(rules)
system_prompt = (
"### SYSTEM SECURITY GUARDRAILS AND OPERATIONAL RULES ###\n"
f"{formatted_rules}\n"
"### END OF GUARDRAILS ###\n\n"
"Текущая операционная задача и контекст окружения:\n"
f"{user_prompt}"
)
return system_prompt
async def hot_reload_rules(self) -> str:
"""
Метод 'горячей перезагрузки'. Атомарно обновляет правила в памяти
на лету без блокирования асинхронного цикла обработки входящих вебхуков.
"""
async with self._lock:
old_cache = self._cached_rules
self._cached_rules = None
# Принудительный вызов загрузки через пул потоков
new_rules = await self._load_rules()
if new_rules and len(new_rules) > 0:
return "🔄 Контур управления: Кэш сброшен. Новые правила успешно развернуты в оперативной памяти!"
# В случае повреждения структуры файла возвращаем стабильный старый кэш
self._cached_rules = old_cache
return "⚠️ Отказ перезагрузки: Файл поврежден или пуст. Ядро продолжает работу на старой конфигурации из кэша."