# Запрос к Sonnet: анализ обфускации

**Дата:** 2026-07-13

---

**Задача:** Найти причину расхождения между ожидаемыми кодами замен и реальными заменами.

## Ожидаемое поведение

`_next_token()` в `drhider/scanner.py` генерирует коды замен формата:
- `Технология_0001`, `Прогресс_0002` (для компаний)
- `+7_000_000_0001` (для телефонов)
- `email_0001` (для email)
- `ИНН_0001` (для ИНН)
- `Иванов_0001` (для ФИО)

## Реальный результат — `TMP/mapping.csv`

```
тип_данных,оригинал,замена
phone,+7 (495) 789-41-35,+7 (495) 906-87-80        ← НЕ код замены, реальный номер
email,info@nubes.ru,ypiorej@company.local           ← НЕ код замены, реальный email
company,ЗАО ****XXX****001****,АО «Прогресс»        ← НЕ код замены, реальное название
company,ЗАО XXX001,ООО «Альянс»                     ← НЕ код замены, реальное название
inn_ul,ИНН 9706005293,ИНН 1846691249                ← НЕ код замены, реальный ИНН
kpp,КПП 772401001,КПП 350443068                     ← НЕ код замены, реальный КПП
ks,Кор/сч 30101810400000000225,Кор/сч 30101201490272090211  ← НЕ код замены
ogrn,ОГРН 1207700098759,ОГРН 1048672318430          ← НЕ код замены
company,ООО "НУБЕС",ООО «Спектр»                    ← НЕ код замены
text,Исполнителем Заказчику Сторонами,Кузнецов О.В.
text,"Обязанности Заказчика\nСвоевременно",Степанов А.В.
text,"Обязанности Исполнителя\nОказывать",Петров П.Е.
text,"Ответственность Заказчика\nЗаказчик",Новиков А.М.
text,"Ответственность Исполнителя\nИсполнитель",Попов О.М.
company,ПАО Сбербанк,АО «Формат»
```

**Ни одного кода замены формата `XXX_0000` в колонке «замена».**

## Реальный результат — `TMP/договор-XXX001-03700_obfuscated.md` (первые строки)

```markdown
**АО «Прогресс»** ... и **ООО **"**НУБЕС**" ... Новиков И.В. ...
```

- `Новиков И.В.` — НЕ заменён (должен быть `Иванов_0001`)
- `ООО "НУБЕС"` — НЕ заменён в тексте (есть в mapping но замена не применена?)
- `г. Москва, 1-я Стекольная 7с5` — адрес не заменён

## Файлы для анализа

**Обязательно прочитать:**
1. `drhider/scanner.py` — `_next_token()`, `scan_regex()`, `scan_llm_ner()`, TYPE_POOLS, промпт LLM (строки 109-151)
2. `drhider/obfuscator.py` — порядок вызова regex → LLM, как объединяется mapping (строки 63-95)
3. `drhider/config.py` — ENTITY_PATTERNS, COMPANY_PATTERN, PERSON_PATTERN
4. `drhider/llm_client.py` — `LLMClient.complete()`

**Прочитать для контекста:**
5. `TMP/mapping.csv` — полный результат
6. `TMP/договор-XXX001-03700_obfuscated.md` — обфусцированный файл
7. `TMP/примеры_договоров_для_ИИ/договор-XXX001-03700.docx` — оригинал (для сверки)

**Не лезть:** `builder.py`, `extractor.py`, `replacer.py`, `session.py`, `templates/`, docs/, History/.

## Ключевые точки для проверки

### 1. `scanner.py` — `_next_token()` (стр. 63-82)

```python
def _next_token(entity_type, counters):
    pool, prefix_key = TYPE_POOLS.get(entity_type, _FALLBACK_POOL)
    template = random.choice(pool)
    key = f"{prefix_key}_{template}"
    counters[key] = counters.get(key, 0) + 1
    return f"{template}_{counters[key]:04d}"
```

Возвращает `Технология_0001`, `+7_000_000_0001`. **Никак не может вернуть** `АО «Прогресс»` или `+7 (495) 906-87-80`.

### 2. `scanner.py` — `scan_regex()` (стр. 89-108)

```python
mapping[original] = _next_token(entity_type, counters)
```

Всегда вызывает `_next_token`. Результат должен быть кодом замены.

### 3. `scanner.py` — `scan_llm_ner()` (стр. 109-179)

```python
mapping[val] = _next_token(ent_type, counters)
```

Тоже вызывает `_next_token`. Результат должен быть кодом замены.

### 4. `obfuscator.py` — порядок вызова (стр. 86-92)

```python
scanner.scan_regex(text, self._mapping, self._counters)  # сначала
scanner.scan_llm_ner(all_texts, self._mapping, ...)        # потом
```

### 5. `config.py` — regex-паттерны:

- `phone`: `(?:\+7|8)[\s\-]?\(?\d{3}\)?...` — должно матчить `+7 (495) 789-41-35`
- `email`: `[a-zA-Z0-9._%+-]+@...` — должно матчить `info@nubes.ru`
- `inn_ul`: `ИНН\s*\d{10}` — должно матчить `ИНН 9706005293`
- `COMPANY_PATTERN`: `(?:ООО|ЗАО|...)\s+(?:«[^»]+»|"[^"]+"|...)` — должно матчить `ООО "НУБЕС"` и `ЗАО "XXX001"`
- `PERSON_PATTERN`: `[А-Я][а-я]+\s+[А-Я]\.[А-Я]\.` — должно матчить `Новиков И.В.`

## Главный вопрос

Каждый путь (regex и LLM) вызывает `_next_token()`. `_next_token()` физически не может выдать `АО «Прогресс»` или `+7 (495) 906-87-80`. Откуда в `mapping.csv` берутся реалистичные замены? Кто и где их генерирует В ОБХОД `_next_token()`?
