feat: папка dictionaries/prompts — словари промтов для каждой ИИ-кнопки

- prompts/suggest_object_params.json: жёсткие правила подбора характеристик —
  выбирать ТОЛЬКО из предоставленных списков (сферы/типы/архитектуры/
  типовые объекты), ничего не выдумывать; назначение строго по форме
  «Объект предназначен для … с целью …»; типовой объект — дословно из 360-р
- prompts/suggest_threats.json, check_security.json, check_company.json,
  declension.json — промты для остальных ИИ-кнопок
- verify.py: _load_prompt() + подключение промтов в suggest_object_params_ai,
  suggest_threats_ai, check_security_ai; ИИ получает полные списки
  (spheres/object_types/architectures) и выбирает из них
- ui.py: окно рекомендаций ИИ — галочки у каждого поля, применяются
  только отмеченные (ИИ может ошибаться в архитектуре — оператор решает)
This commit is contained in:
2026-08-04 23:32:58 +04:00
parent acb7aeaa64
commit e4e7bb444a
7 changed files with 149 additions and 57 deletions
+75 -41
View File
@@ -14,6 +14,18 @@
"""
import json
def _load_prompt(name):
"""Загружает промт из dictionaries/prompts/<name>.json (system/rules/response_format)."""
import os as _os
p = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)),
'dictionaries', 'prompts', name + '.json')
try:
with open(p, encoding='utf-8') as f:
return json.load(f)
except Exception:
return {}
import re
from datetime import datetime, timedelta
import urllib.parse
@@ -526,20 +538,24 @@ def suggest_threats_ai(security_data, api_key, model='deepseek-chat', base_url=N
else:
threat_list_text = "(перечень не предоставлен — используй знания об актуальном перечне БДУ ФСТЭК)"
system = (
pr = _load_prompt('suggest_threats')
system = pr.get('system', (
"Ты — эксперт ФСТЭК по безопасности критической информационной инфраструктуры "
"(187-ФЗ, приказ ФСТЭК № 236). На основе характеристик объекта КИИ выбери из "
"предоставленного перечня АКТУАЛЬНЫЕ угрозы безопасности информации и верни "
"ТОЛЬКО JSON: "
'{"threats": ["УБИ.xxx Название угрозы", ...]} — от 10 до 40 угроз, '
"без пояснений и комментариев. Не выдумывай угрозы, которых нет в перечне; "
"используй полные строки из перечня (код + название) как они есть."
)
"предоставленного перечня АКТУАЛЬНЫЕ угрозы безопасности информации."
))
rules = pr.get('rules', [])
resp = pr.get('response_format',
'Верни ТОЛЬКО JSON: {"threats": ["УБИ.xxx Название угрозы", ...]} '
'— от 10 до 40 угроз, без пояснений.')
user = (
"Характеристики объекта КИИ:\n"
f"{json.dumps(security_data, ensure_ascii=False)}\n\n"
"Актуальный перечень угроз БДУ ФСТЭК:\n"
f"{threat_list_text}"
"Актуальный перечень угроз БДУ ФСТЭК (выбирай ТОЛЬКО из него, "
"используй полные строки как они есть):\n"
f"{threat_list_text}\n\n"
+ ("Правила:\n" + "\n".join(f"{i+1}. {r}" for i, r in enumerate(rules)) + "\n\n" if rules else "")
+ resp
)
js, err = _ai_json(api_key, model, base_url, system, user)
if err:
@@ -552,48 +568,66 @@ def suggest_threats_ai(security_data, api_key, model='deepseek-chat', base_url=N
def suggest_object_params_ai(object_data, api_key, model='deepseek-chat', base_url=None,
typical_objects=None):
typical_objects=None, spheres=None, object_types=None,
architectures=None):
"""ИИ предлагает характеристики объекта КИИ: назначение, сферу, тип, архитектуру,
типовой отраслевой объект — как рекомендацию оператору.
object_data — dict: name, description, purpose (что известно об объекте).
typical_objects — перечень типовых отраслевых объектов (из 360-р) для выбора.
typical_objects — перечень типовых отраслевых объектов (из 360-р) для выбора;
spheres / object_types / architectures — полные списки допустимых значений.
Промт берётся из dictionaries/prompts/suggest_object_params.json.
Возвращает (dict, ошибка): dict с полями
purpose / sphere / object_type / architecture / typical_object / обоснование.
назначение / сфера / тип / архитектура / типовой_объект / обоснование.
"""
if not api_key:
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
# Загрузка промта из папки dictionaries/prompts
import os as _os
prompt_path = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)),
'dictionaries', 'prompts', 'suggest_object_params.json')
try:
with open(prompt_path, encoding='utf-8') as _f:
prompt = json.load(_f)
except Exception:
prompt = {}
system = prompt.get('system', 'Ты — эксперт ФСТЭК по категорированию объектов КИИ.')
rules = prompt.get('rules', [])
response_format = prompt.get('response_format',
'Верни ТОЛЬКО JSON: {"назначение": "...", "сфера": "...", '
'"тип": "...", "архитектура": "...", "типовой_объект": "...", '
'"обоснование": "..."}')
# Полные списки допустимых значений
def _fmt_list(name, items):
if not items:
return f"{name}: (список не предоставлен)"
return f"{name}:\n" + "\n".join(f"- {i}" for i in items)
lists_text = "\n\n".join([
_fmt_list("Сферы деятельности (выбирай ТОЛЬКО из них)", spheres),
_fmt_list("Типы объектов (выбирай ТОЛЬКО из них)", object_types),
_fmt_list("Архитектуры (выбирай ТОЛЬКО из них)", architectures),
])
if typical_objects:
to_text = "\n".join(f"{i+1}. {t}" for i, t in enumerate(typical_objects[:400]))
else:
to_text = "(перечень не предоставлен)"
system = (
"Ты — эксперт ФСТЭК по категорированию объектов критической информационной инфраструктуры "
"(187-ФЗ, ПП РФ № 127, Распоряжение ПРФ № 360-р, приказ ФСТЭК № 236). "
"По описанию объекта КИИ предложи его характеристики. Верни ТОЛЬКО JSON: "
'{"назначение": "короткое назначение объекта", '
'"сфера": "одна из сфер: Здравоохранение, Наука, Транспорт, Связь, Энергетика, '
'Государственная регистрация прав, Банковская сфера и финансовый рынок, '
'Топливно-энергетический комплекс, Атомная энергетика, Оборонная промышленность, '
'Ракетно-космическая промышленность, Горнодобывающая промышленность, '
'Металлургическая промышленность, Химическая промышленность, Иная сфера деятельности", '
'"тип": "Информационная система | Автоматизированные системы управления | '
'Информационно-телекоммуникационная сеть", '
'"архитектура": "одно из: клиент-серверная система, автономная (standalone), '
'распределенная система, иное", '
'"типовой_объект": "полная строка из предоставленного перечня, наиболее подходящая, '
'или пустая строка", '
'"обоснование": "почему выбраны такие характеристики (1-2 предложения)"} '
"Не выдумывай типовой объект — бери только из перечня."
)
rules_text = "\n".join(f"{i+1}. {r}" for i, r in enumerate(rules))
user = (
"Информация об объекте КИИ:\n"
f"{json.dumps(object_data, ensure_ascii=False)}\n\n"
"Перечень типовых отраслевых объектов КИИ (Распоряжение ПРФ № 360-р):\n"
f"{to_text}"
f"{lists_text}\n\n"
"Перечень типовых отраслевых объектов КИИ (Распоряжение ПРФ № 360-р, "
"выбирай ТОЛЬКО из него, копируй строку дословно):\n"
f"{to_text}\n\n"
"Правила:\n"
f"{rules_text}\n\n"
f"{response_format}"
)
js, err = _ai_json(api_key, model, base_url, system, user)
if err:
@@ -615,18 +649,18 @@ def check_security_ai(security_data, api_key, model='deepseek-chat', base_url=No
"""
if not api_key:
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
system = (
pr = _load_prompt('check_security')
system = pr.get('system', (
"Ты — эксперт по безопасности критической информационной инфраструктуры (187-ФЗ, "
"приказ ФСТЭК России от 25.12.2017 № 239 «Об утверждении Требований по обеспечению "
"безопасности значимых объектов КИИ» и приказ № 236). Проверь заполненные сведения "
"об объекте КИИ и дай рекомендации. Верни ТОЛЬКО JSON: "
'{"оценка": "ok|warning|error", '
'"замечания": ["..."], '
"приказ ФСТЭК России от 25.12.2017 № 239 и приказ № 236). Проверь заполненные "
"сведения об объекте КИИ и дай рекомендации."
))
system = system + " " + pr.get('response_format',
'Верни ТОЛЬКО JSON: {"оценка": "ok|warning|error", "замечания": ["..."], '
'"предложения": {"attacker_category": "...", "threats": "...", "incidents": "...", '
'"criteria_values": "...", "criteria_justification": "...", '
'"org_measures": "...", "tech_measures": "..."}} '
"В «предложениях» укажи, что добавить/исправить по каждому разделу, если нужно, иначе пустые строки."
)
'В «предложениях» укажи, что добавить/исправить по каждому разделу, иначе пустые строки.')
user = json.dumps(security_data, ensure_ascii=False)
js, err = _ai_json(api_key, model, base_url, system, user)
if err: