Spaces:
Sleeping
Sleeping
File size: 7,662 Bytes
0f261a1 97407fe 2d8971f 52a8efb 97407fe dd9371d 97407fe c556336 0f261a1 2d8971f 97407fe 2d8971f 97407fe dd9371d 52a8efb 2d8971f 52a8efb 97407fe 2d8971f 97407fe 2d8971f 97407fe 2d8971f 97407fe 2d8971f c556336 2d8971f 97407fe 2d8971f 97407fe 0f261a1 2d8971f 97407fe 2d8971f 52a8efb 2d8971f 52a8efb 2d8971f 52a8efb 2d8971f 52a8efb 2d8971f 52a8efb 0f261a1 2d8971f c556336 2d8971f 52a8efb c556336 2d8971f 97407fe 2d8971f 52a8efb c556336 97407fe 2d8971f 97407fe 2d8971f 97407fe 2d8971f c556336 2d8971f c556336 2d8971f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | 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 "⚠️ Отказ перезагрузки: Файл поврежден или пуст. Ядро продолжает работу на старой конфигурации из кэша."
|