feat(etl): stable pipeline, exception registry in SQLite, multi-pass aggregation and db_cli
This commit is contained in:
+30
-11
@@ -151,7 +151,7 @@ def ai_verify_scud_against_staff(unrecognized_scud_fios, staff_fios):
|
||||
if not unrecognized_scud_fios or not staff_fios:
|
||||
return {}
|
||||
|
||||
# 🛡 Исключаем точные совпадения (чтобы ИИ не совершал ложных замен)
|
||||
# Исключаем точные совпадения
|
||||
staff_fios_set = set(staff_fios)
|
||||
real_unrecognized = [f for f in unrecognized_scud_fios if f not in staff_fios_set]
|
||||
|
||||
@@ -163,7 +163,7 @@ def ai_verify_scud_against_staff(unrecognized_scud_fios, staff_fios):
|
||||
prompt = f"""
|
||||
Ты — кадровый аудитор безопасности СКУД.
|
||||
{rules_context}
|
||||
В СКУД записаны неопознанные ФИО: {json.dumps(unrecognized_scud_fios, ensure_ascii=False)}
|
||||
В СКУД записаны неопознанные ФИО: {json.dumps(real_unrecognized, ensure_ascii=False)}
|
||||
В официальном Штатном расписании 1С записаны ЭТАЛОНЫ: {json.dumps(staff_fios, ensure_ascii=False)}
|
||||
|
||||
СТРОГИЕ ПРАВИЛА:
|
||||
@@ -173,40 +173,59 @@ def ai_verify_scud_against_staff(unrecognized_scud_fios, staff_fios):
|
||||
4. В поле "warning" опиши обнаруженную опечатку.
|
||||
5. Запрещено выдумывать опечатки и объединять разных людей/однофамильцев!
|
||||
|
||||
ОТВЕЧАЙ ТОЛЬКО ИСКЛЮЧИТЕЛЬНО В ФОРМАТЕ JSON!
|
||||
|
||||
Формат ответа:
|
||||
ОТВЕЧАЙ ТОЛЬКО ИСКЛЮЧИТЕЛЬНО В ФОРМАТЕ ВАЛИДНОГО JSON:
|
||||
{{
|
||||
"verified_matches": [
|
||||
{{
|
||||
"scud_fio": "ФИО из СКУД",
|
||||
"staff_fio": "эталон ФИО из Штат 1С",
|
||||
"warning": "Описание опечатки в СКУД или 'Точное совпадение (без опечаток)'"
|
||||
"warning": "Описание опечатки в СКУД"
|
||||
}}
|
||||
]
|
||||
}}
|
||||
"""
|
||||
raw_response = ask_ollama(
|
||||
prompt,
|
||||
system_prompt="Ты — JSON API. Выдавай ТОЛЬКО валидный JSON без markdown-разметки."
|
||||
system_prompt="Ты — строгий JSON API генератор. Отвечай только валидным JSON объектом без пояснительного текста."
|
||||
)
|
||||
|
||||
mapping = {}
|
||||
if not raw_response:
|
||||
return mapping
|
||||
|
||||
# Фаза 1: Попытка прямого разбора с санитарной очисткой
|
||||
try:
|
||||
match = re.search(r'\{.*\}', raw_response, re.DOTALL)
|
||||
if match:
|
||||
json_str = match.group(0)
|
||||
json_str = re.sub(r'\}\s*[^}\]]*$', '}', json_str)
|
||||
# Убираем висячие запятые: {"a": 1,} -> {"a": 1}
|
||||
json_str = re.sub(r',\s*([\}\]])', r'\1', json_str)
|
||||
# Заменяем одинарные кавычки в ключах/значениях на двойные при необходимости
|
||||
json_str = re.sub(r"(?<=\{|\,)\s*'([^']+)'\s*:", r'"\1":', 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:
|
||||
if scud_f and staff_f and staff_f in staff_fios_set:
|
||||
mapping[scud_f] = {"staff_fio": staff_f, "warning": warn}
|
||||
return mapping
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Фаза 2: Резервный Regex-парсер (если JSON синтаксически сломан, но пары ключ-значение есть)
|
||||
try:
|
||||
pattern = r'["\']scud_fio["\']\s*:\s*["\']([^"\']+)["\'].*?["\']staff_fio["\']\s*:\s*["\']([^"\']+)["\']'
|
||||
matches = re.findall(pattern, raw_response, re.DOTALL)
|
||||
for scud_f, staff_f in matches:
|
||||
scud_clean = scud_f.strip()
|
||||
staff_clean = staff_f.strip()
|
||||
if staff_clean in staff_fios_set:
|
||||
mapping[scud_clean] = {"staff_fio": staff_clean, "warning": "Восстановлено парсером опечаток"}
|
||||
except Exception as e:
|
||||
print(f"[!] Ошибка разбора JSON от ИИ при сверке опечаток: {e}")
|
||||
|
||||
print(f"[!] Ошибка резервного парсинга опечаток: {e}")
|
||||
|
||||
return mapping
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user