import os import sys from dotenv import load_dotenv # Автоматическое определение корневой директории проекта BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Загружаем приватные переменные окружения из вашего .env load_dotenv(os.path.join(BASE_DIR, 'config', '.env')) # Карта соответствия: Переменная диспетчера -> Ключ из вашего .env # Объявлена в самом начале, чтобы скрипт знал имена карточек до обращения к API CREDENTIALS_MAP = { "FTP_PASS": os.getenv("PW_ID_FTP", "").strip(' "\'\r\n'), # Мягкий поиск по ключу "API_KEY": os.getenv("PW_ID_SD", "").strip(' "\'\r\n'), # Ищет карточку по значению из .env "AD_PASSWORD": os.getenv("PW_ID_AD", "").strip(' "\'\r\n'), # Ищет карточку по значению из .env } # Единственный токен, который может быть зашит в локальном .env, а не в Пассворке EMBEDDING_API_TOKEN = os.getenv("EMBEDDING_API_TOKEN", "").strip(' "\'\r\n') # ===================================================================== # ИНТЕГРАЦИЯ С PASSWORK (Динамическая загрузка паролей) # ===================================================================== PASSWORK_DIR = "/opt/passwork" if PASSWORK_DIR not in sys.path: sys.path.insert(0, PASSWORK_DIR) try: from passwork import get_passwork_secrets print("LOG: 🔐 Обращение к модулю Passwork и выгрузка сейфа для GigaAM...") # Формируем и пробрасываем список обязательных карточек для автозащиты от коллизий required_to_wait = [name for name in CREDENTIALS_MAP.values() if name] passwork_pool = get_passwork_secrets(required_cards=required_to_wait) except Exception as e: print(f"❌ КРИТИЧЕСКАЯ ОШИБКА: Не удалось подключить модуль Passwork: {e}") sys.exit(1) print("LOG: 🔐 Распределение секретов и логинов по модулям диспетчера...") for var_name, card_name in CREDENTIALS_MAP.items(): if not card_name: print(f"⚠️ Внимание: Название карточки для {var_name} не задано в .env. Используется локальное значение.") continue clean_card_name = card_name.strip(' "\'\r\n') card_data = passwork_pool.get(clean_card_name) if card_data is None: print(f"❌ КРИТИЧЕСКАЯ ОШИБКА: Карточка с именем '{clean_card_name}' не найдена в сейфе Passwork!") sys.exit(1) # Динамически заполняем пароль/токен в глобальную область видимости config.py globals()[var_name] = card_data["password"].strip(' "\'\r\n') # Динамически вытаскиваем сопутствующие логины из карточек if var_name == "FTP_PASS": globals()["FTP_USER"] = card_data.get("login", "").strip(' "\'\r\n') elif var_name == "AD_PASSWORD": globals()["AD_USER"] = card_data.get("login", "").strip(' "\'\r\n') print(f" ✓ {var_name} и логин успешно синхронизированы из карточки '{clean_card_name}'") # Интеграция с АТС (Выгрузка записей звонков по FTP) FTP_HOST = "10.0.0.7" FTP_DIR = "/zabbix" FTP_ARCHIVE = "/zabbix/archive" # ===================================================================== # 1. СИСАДМИНСКАЯ ИНФРАСТРУКТУРА, ФЛАСК И ЛОГИРОВАНИЕ # ===================================================================== # Порт, на котором Flask-вебхук слушает входящие JSON-пакеты от ManageEngine WEBHOOK_PORT = 5000 # Путь к файлу сквозного отладочного лога диспетчера WEBHOOK_LOG_FILE = os.path.join(BASE_DIR, 'dispatcher_log.txt') # Включение профилирования (замеры времени выполнения этапов обработки заявки) ENABLE_PROFILING = True # Ограничение длины текста исторических документов при выводе в файл лога LOG_EXAMPLE_MAX_CHARS = 350 # Ограничение длины строки текста заявки при выводе в интерактивную CLI-консоль CLI_LOG_MAX_CHARS = 80 # ===================================================================== # 2. КОНВЕЙЕР РАСПОЗНАВАНИЯ РЕЧИ И ПЕРВИЧНОЙ ОБРАБОТКИ (ASR PIPELINE) # ===================================================================== # Флаг включения суммаризации текста звонка (сейчас отключен для экономии CPU) ENABLE_TEXT_SUMMARIZATION = False # Рабочие директории для хранения входящих аудиозаписей и результатов транскрибации AUDIO_DIR = os.path.join(BASE_DIR, 'audio') JSON_DIR = os.path.join(BASE_DIR, 'json') # Длина одного чанка (куска аудио) при нарезке для модели распознавания (в секундах) CHUNK_SECONDS = 25 # Пути к словарям терминов и золотых ИТ-токенов для исправления ошибок транскрипции DICT_PATH = "/opt/dict/dict.json" GOLDEN_DICT_PATH = "/opt/dict/GOLDEN_TERMS.json" # Переключатели модулей постобработки и исправления текста после ASR ENABLE_REGEX_CORRECTION = True # Исправление по регулярным выражениям ENABLE_FUZZY_CORRECTION = True # Нечёткое исправление по Левенштейну/словарям # ===================================================================== # 3. ИНТЕГРАЦИЯ С MANAGEENGINE SERVICE DESK API # ===================================================================== # Корпоративный URL и API-эндпоинт для работы с заявками по протоколу v3 # Локальный Service Desk (для выгрузки данных) LOCAL_SD_URL = "https://192.168.1.237:8080" LOCAL_SD_API_KEY = "062CE056-C1F5-490C-A6DF-FD2C653A71DD" SERVER_URL = "https://servicedesk.sibcem.ru:8080" ENDPOINT = f"{SERVER_URL}/api/v3/requests" # Системный email-адрес учетной записи ИИ-сортировщика в Service Desk DEFAULT_REQUESTER = "ai@sibcem.ru" # ===================================================================== # 4. ИНТЕГРАЦИЯ С ACTIVE DIRECTORY (LDAP) # ===================================================================== # Контроллер домена и базовая точка поиска пользователей (Base DN) AD_SERVER = "ldap://172.16.20.20" AD_BASE = "OU=-Пользователи,DC=sibcem,DC=ru" # Сетевые тайм-ауты (в секундах) для защиты фоновых потоков от зависания на LDAP-запросах AD_CONNECT_TIMEOUT = 5 # Время на установку tcp-соединения AD_RECEIVE_TIMEOUT = 5 # Время ожидания ответа на поисковый запрос # ===================================================================== # 5. ПОЧТОВЫЕ УВЕДОМЛЕНИЯ И АЛЕРТЫ (SMTP) # ===================================================================== # Корпоративный почтовый релей и порт (используется стандартный 25 без SSL) SMTP_SERVER = "srvkem-mail.sibcem.ru" SMTP_PORT = 25 # Отправитель по умолчанию для ИИ-отчетов DEFAULT_EMAIL_FROM = "ai@sibcem.ru" # Списки рассылки и исключений для отправки логов принятия решений ALERTS_DISABLED_USERS = ["ds.krivochenko@sibcem.ru"] ALERTS_ALL_TICKETS = ["ds.krivochenko@sibcem.ru"] # ===================================================================== # 6. ВЕКТОРНЫЙ ПОИСК И ЭМБЕДДИНГИ (QDRANT API) # ===================================================================== # Адрес, порт и целевая коллекция локального сервера Qdrant QDRANT_HOST = "localhost" QDRANT_PORT = 6333 QDRANT_COLLECTION = "tickets" # Осредненный лимит выборки документов из Qdrant под дальнейший реранкинг (оптимально для CPU) QDRANT_SEARCH_LIMIT = 20 # Локальная служба генерации векторов (Embedding API) EMBEDDING_API_URL = "http://127.0.0.1:8010/embed" EMBEDDING_MODEL_NAME = "multilingual-e5-base" # ===================================================================== # 7. ПЕРЕРАНЖИРОВАНИЕ ПРЕЦЕДЕНТОВ (CROSS-ENCODER RERANKER) # ===================================================================== # Адрес выделенной службы реранкера (работает на порту 8081) RERANKER_URL = "http://127.0.0.1:8081/rerank" RERANKER_MODEL_NAME = "bge-reranker-v2-m3-Q8_0.gguf" # Предохранитель от OOM: максимальное количество символов исторического билета, скармливаемое реранкеру RERANKER_MAX_DOC_CHARS = 1200 # Жесткий тайм-аут сокета (в секундах) на ожидание пачки документов от реранкера RERANKER_TIMEOUT = 60 # Коэффициент смешивания скоров (Score Blending): # 1.0 — абсолютная власть реранкера, 0.0 — чистый Qdrant (база векторов) RERANKER_WEIGHT = 0.7 # ===================================================================== # 8. ЛОКАЛЬНЫЙ ИНТЕЛЛЕКТ И СЕРВЕРЫ LLM / STT # ===================================================================== # Переключатель вызова большой языковой модели ENABLE_OLLAMA = True # ГЛОБАЛЬНЫЙ ДИСПЕТЧЕР И АНАЛИЗАТОР: Классификация тикетов (Тяжелый квант Q8) OLLAMA_URL = "http://127.0.0.1:8080/v1/chat/completions" OLLAMA_MODEL = "gemma-4-12b-it-Q8_0" # РАСПОЗНАВАНИЕ ЗВОНКОВ: Локальный роутер моделей llama.service (Порт 8080) OLLAMA_SUBJECT_URL = "http://127.0.0.1:8080/v1/chat/completions" OLLAMA_SUBJECT_MODEL = "gemma-4-E4B-it-Q4_K_M" # МИКРОСЕРВИС ТРАНСКРИБАЦИИ: Точка подключения к демону GigaAM (Порт 5001) STT_SERVICE_URL = "http://127.0.0.1:5001/transcribe" # Размер контекстного окна для слота (динамическое выделение памяти из пула 16384) OLLAMA_NUM_CTX = 12288 # Параметры инференса текстовой модели OLLAMA_TEMPERATURE = 0.0 # Строгая детерминированность вывода OLLAMA_MAX_TOKENS = 65 # Ограничение длины ответа (модели нужно выдать только имя группы) OLLAMA_TIMEOUT = 1200 # Предельный тайм-аут генерации токенов (в секундах) под CPU-нагрузкой # ===================================================================== # 9. МАТЕМАТИЧЕСКИЕ ФИЛЬТРЫ, ТРИГГЕРЫ МУСОРА И ПОРОГОВЫЕ ОГРАНИЧЕНИЯ # ===================================================================== # Осредненный порог относительной уверенности Qdrant. Ниже него — принудительный вызов Qwen 2.5 CONFIDENCE_THRESHOLD = 0.85 # КРИТИЧЕСКИЙ ПРЕДОХРАНИТЕЛЬ: если абсолютный скор лучшего совпадения реранкера ниже этого веса, # сквозной байпас ИИ запрещается, и билет отправляется на перепроверку в Ollama FORCE_OLLAMA_SCORE_THRESHOLD = 0.75 # Сколько голосов (совпадений) максимум может забрать одна ИТ-группа в векторном пуле QDRANT_MAX_VOTES_PER_GROUP = 10 # Абсолютный скор, выше которого заявка считается стопроцентным историческим дублем EXACT_MATCH_SCORE_THRESHOLD = 0.9 # Фильтры детектирования аномального распределения (маркеры мусорных/непонятных заявок) GARBAGE_CONFIDENCE_THRESHOLD = 0.01 # Предельно низкая уверенность GARBAGE_UNIQUE_GROUPS_LIMIT = 4 # Слишком много разных групп в топ-выборке GARBAGE_VARIANCE_THRESHOLD = 0.02 # Слишком плотный шаг весов (нет явного лидера) GARBAGE_MIN_GROUPS_FOR_VARIANCE = 2 # Минимальное число конкурентов для оценки дисперсии GARBAGE_BYPASS_DOMINANCE = 0.90 # Порог доминации, снимающий метку мусора # Фильтры условий обхода вызова Ollama (Bypass) OLLAMA_BYPASS_DOMINANCE = 0.95 # Если одна группа заняла 80% топа выборки OLLAMA_BYPASS_MIN_CONFIDENCE = 0.10 # Минимальная базовая уверенность для байпаса OLLAMA_ACCEPTANCE_THRESHOLD = 0.65 # Минимальная уверенность логпробов Ollama для принятия ответа DOMINANCE_TOP_K = 10 # Глубина среза топ-результатов для расчёта коэффициента доминации LOG_TOP_GROUPS_LIMIT = 5 # Количество позиций истории, выводимых в логи структуры decision_log # ===================================================================== # 10. СПРАВОЧНИКИ МАРШРУТИЗАЦИИ, КАТЕГОРИИ И БИЗНЕС-ЛОГИКА АВТОМАТИЗАЦИИ # ===================================================================== # White-list валидных ИТ-групп в системе Service Desk Plus VALID_GROUPS = [ "CRM", "АХО", "Техподдержка", "1С", "Axapta (Microsoft Dynamics AX)", "АСУТП", "Босс-Кадровик", "ОПС, видеонаблюдение, СКУД, СМТ", "Программисты КТЦ", "Системное администрирование", "Телеком и сообщения", "Битрикс", "СЭД Directum", "Администрирование систем BI", "СБИС", "Служба информационной безопасности", "НСИ" ] # Группы, для которых открыто прямое автоматическое исполнение Put-запросов назначения через API ALLOWED_ASSIGNMENT_GROUPS = [ "Техподдержка КЦ", "Техподдержка ХК", "Техподдержка Тимлюй", "Техподдержка Ангарск", "Техподдержка Искитим", "Техподдержка Топки", "Техподдержка КТЦ", "Техподдержка Волна", "Техподдержка Москва","Телеком и сообщения" ] # Матрица ответственных менеджеров групп для отправки алертов о сбоях/назначениях билетов GROUP_MANAGERS = { "Техподдержка": ["e.voevodin@sibcem.ru"] } # Региональный справочник привязки городов к конкретным Кустовым Центрам (КЦ) и ХК CITY_SUPPORT = { "Ангарск": "Техподдержка Ангарск", "Слюдянка": "Техподдержка Ангарск", "Искитим": "Техподдержка Искитим", "Новосибирск": "Техподдержка КТЦ", "Топки": "Техподдержка Топки", "Кемерово": "Техподдержка ХК", "Москва": "Техподдержка Москва", "Тимлюй": "Техподдержка ХК", "Каменск": "Техподдержка ХК", "Улан-Удэ": "Техподдержка ХК", "Татарский Ключ": "Техподдержка ХК", "Спас-Клепики": "Техподдержка ХК", "Красноярск": "Техподдержка КЦ" } # Настройки матрицы автоматического создания уровней согласования руководителями ENABLE_AUTO_APPROVAL = True APPROVAL_CITIES = ["Новосибирск"] APPROVAL_GROUPS = ["1С", "1с", "Программисты КТЦ"] APPROVAL_CATEGORIES = { "ИТ": ["ПО КТЦ", "Проблемы с Договорной Работой"], "1С": ["*"], "1с": ["*"] } # План «Б» маршрутизации: жесткое определение локации по телефонному префиксу внутренней АТС PREFIX_ROUTING = { "700": {"city": "Красноярск", "company": "ООО Комбинат Волна"}, "702": {"city": "Кемерово"}, "703": {"city": "Новосибирск"}, "704": {"city": "Красноярск"}, "705": {"city": "Каменск"}, "701": {"city": "Ангарск"}, "706": {"city": "Ангарск"}, "707": {"city": "Ангарск"}, "709": {"city": "Москва"}, "724": {"city": "Искитим"}, "71": {"city": "Топки"} }