import json import re import requests from config import OLLAMA_URL, OLLAMA_MODEL from modules.database import get_all_rules_from_db def ask_ollama(prompt, system_prompt=None): """ Универсальная функция отправки запросов к локальной модели Ollama (Qwen 2.5). """ payload = { "model": OLLAMA_MODEL, "prompt": f"{system_prompt}\n\n{prompt}" if system_prompt else prompt, "stream": False } try: response = requests.post(OLLAMA_URL, json=payload, timeout=120) if response.status_code == 200: data = response.json() if "response" in data: return data["response"].strip() elif "message" in data and "content" in data["message"]: return data["message"]["content"].strip() else: return f"⚠️ Неизвестная структура ответа Ollama: {data}" else: return f"⚠️ Ошибка Ollama (Код {response.status_code}): {response.text}" except requests.exceptions.ConnectionError: return "⚠️ Не удалось подключиться к Ollama. Проверьте, запущен ли сервис (ollama run qwen2.5:14b)." except Exception as e: return f"⚠️ Ошибка при обращении к Ollama: {e}" def get_system_rules_context(): """ Загружает глобальные системные правила компании прямо из Базы Данных. """ rules = get_all_rules_from_db() if not rules: return "" rules_text = "\n".join([f"- {r}" for r in rules]) return f"\nОБЯЗАТЕЛЬНЫЕ ГЛОБАЛЬНЫЕ ПРАВИЛА И ПРИОРИТЕТЫ КОМПАНИИ:\n{rules_text}\n" def ai_verify_scud_against_staff(unrecognized_scud_fios, staff_fios): """ ИИ-сверка опечаток и несовпадений ФИО между СКУД и 1С:ЗУП. Записи из 1С считаются 100% эталоном. """ if not unrecognized_scud_fios or not staff_fios: return {} rules_context = get_system_rules_context() prompt = f""" Ты — кадровый аудитор безопасности СКУД. {rules_context} В СКУД записаны неопознанные ФИО: {json.dumps(unrecognized_scud_fios, ensure_ascii=False)} В официальном Штатном расписании 1С записаны ЭТАЛОНЫ: {json.dumps(staff_fios, ensure_ascii=False)} СТРОГИЕ ПРАВИЛА: 1. Запись из Штат 1С — это 100% ПРАВИЛЬНЫЙ эталон. 2. В СКУД допущена опечатка. 3. Любое присутствие сотрудника по СКУД при наличии в 1С документа отсутствия (кроме командировок) является гарантированной аномалией. 4. В поле "warning" опиши обнаруженную опечатку. 5. Запрещено выдумывать опечатки! ОТВЕЧАЙ ТОЛЬКО ИСКЛЮЧИТЕЛЬНО В ФОРМАТЕ JSON! Формат ответа: {{ "verified_matches": [ {{ "scud_fio": "ФИО из СКУД", "staff_fio": "эталон ФИО из Штат 1С", "warning": "Описание опечатки в СКУД или 'Точное совпадение (без опечаток)'" }} ] }} """ raw_response = ask_ollama( prompt, system_prompt="Ты — JSON API. Выдавай ТОЛЬКО валидный JSON без markdown-разметки." ) mapping = {} try: match = re.search(r'\{.*\}', raw_response, re.DOTALL) if match: json_str = match.group(0) json_str = re.sub(r'\}\s*[^}\]]*$', '}', json_str) data = json.loads(json_str) for item in data.get("verified_matches", []): scud_f = item.get("scud_fio") staff_f = item.get("staff_fio") warn = item.get("warning", "Точное совпадение (без опечаток)") if scud_f and staff_f: mapping[scud_f] = {"staff_fio": staff_f, "warning": warn} except Exception as e: print(f"[!] Ошибка разбора JSON от ИИ при сверке опечаток: {e}") return mapping