Локальное обезличивание документов перед отправкой в языковую модель — и обратная подстановка после ответа.
Документ никогда не покидает машину в исходном виде. Персональные данные
заменяются устойчивыми тегами (#PERSON_1#, #PHONE_2#, #ADDRESS_1#), в
модель уходит только текст с тегами, а полученный ответ восстанавливается
локально по сейфу соответствий.
документ ──▶ маска ──▶ контроль утечки ──▶ модель ──▶ обратная подстановка
локально локально сеть локально
Работает как скилл для Claude Code, как MCP-сервер для любого другого агента и как обычная утилита командной строки.
1. Маска. Документ разбирается на текстовые фрагменты — абзацы, ячейки,
узлы разметки. В каждом находятся персональные данные, каждое значение
получает устойчивый тег. Один и тот же человек получает один и тот же тег по
всему документу, включая падежные варианты и инициалы: «Иванов Иван Иванович»,
«Иванову» и «Иванов И.И.» — это один #PERSON_1#.
2. Контроль утечки. Замаскированный текст повторно прогоняется через все
детекторы плюс параноидальный проход: любой @, любая цепочка из семи и более
цифр, любой телефоноподобный набор. Если что-то осталось — отправка
блокируется исключением, а не предупреждением в логе.
3. Отправка. Наружу уходит только текст с тегами. Единственная точка
выхода в сеть — функция llm.send(), и она обязана вызвать проверку до
запроса. Каждая отправка пишется в журнал ~/.pii_shield/egress.jsonl: время,
провайдер, модель, размер, sha256, статус проверки. Содержимое не пишется.
4. Обратная подстановка. Ответ модели проходит через сейф: теги заменяются на оригиналы. Для ФИО подставляется восстановленный именительный падеж — если в документе человек упомянут только как «Кузнецову Ивану Петровичу», в ответе он станет «Кузнецов Иван Петрович».
git clone https://github.com/kpshinnik/docs_masked.git ~/.docs_masked/src
cd ~/.docs_masked/src && ./install.shСкрипт поставит зависимости, положит скилл в ~/.claude/skills/docs-masked и
напечатает готовый фрагмент конфигурации MCP. Подробности и варианты —
в docs/INSTALL.md.
| Способ | Кому | Как |
|---|---|---|
| Скилл | Claude Code, Claude.ai | ./install.sh либо /plugin marketplace add kpshinnik/docs_masked |
| MCP-сервер | Cursor, Windsurf, Codex CLI, Continue, Zed, Cline, Claude Desktop | python3 mcp_server.py как stdio-сервер |
| CLI и правило | всё остальное | команды в терминале плюс templates/AGENTS-rule.md в свой проект |
Пошагово по каждому харнесу — docs/HARNESSES.md.
MCP-сервер написан без зависимостей: нужен только python3. Он отдаёт шесть
инструментов — mask_text, unmask_text, verify_text, scan_document,
mask_document, unmask_document.
docs-masked scan договор.docx # что будет скрыто
docs-masked mask договор.docx # маска + сейф
docs-masked report договор.docx --open # посмотреть глазами
docs-masked ask договор.docx -p "Найди риски по срокам"
docs-masked unmask договор.masked.docx --vault договор.docx.vault.json| Команда | Что делает |
|---|---|
scan FILE |
Показывает, что будет замаскировано. Файл не меняется, сеть не трогается. |
mask FILE |
Обезличенная копия в том же формате плюс файл сейфа. |
unmask FILE --vault V |
Возвращает оригиналы. |
verify FILE |
Проверяет, что персональных данных не осталось. |
ask FILE -p "..." |
Полный круг: маска → проверка → модель → восстановленный ответ. |
report FILE |
HTML-страница ревью: каждая замена в контексте, значения закрашены. |
selftest |
Самопроверка круговорота. |
Полный список флагов — skills/docs-masked/references/cli.md.
ФИО в любом падеже (русские, латиница, транслит), организации, адреса, почта,
телефоны, паспорт и код подразделения, СНИЛС, ИНН, ОГРН, КПП, БИК, расчётные
счета, банковские карты, IBAN, полисы ОМС, водительские удостоверения,
автомобильные номера, IP-адреса, @никнеймы, даты рождения и выдачи
документов, коды реквизитов (ОКТМО, ОКПО, КБК), плюс ваши собственные строки.
Идентификаторы проверяются по-настоящему: контрольная сумма СНИЛС, контрольные разряды ИНН и ОГРН, алгоритм Луна для карт, mod-97 для IBAN. Полная таблица — references/coverage.md.
| Формат | Чтение | Запись на место |
|---|---|---|
.txt .md .rst .log .tex .yaml .ini |
да | да |
.docx |
да | да, с сохранением форматирования |
.xlsx .xlsm |
да | да |
.csv .tsv |
да | да |
.json |
да | да |
.html .htm |
да | да |
.pdf |
да | по флагу --pdf-redact, с физическим вымарыванием |
.rtf .doc .odt |
да | нет (только macOS, через textutil) |
DOCX обходится по XML, а не через document.paragraphs: иначе теряются
абзацы внутри полей контента и надписей — на настоящем договоре из-за этого
пропадала целая колонка блока реквизитов. В таблицах заголовок колонки
используется как контекст: ячейка 500100732259 сама по себе неотличима от
случайного числа, а в колонке «ИНН» распознаётся уверенно.
from pii_shield import ask_document
res = ask_document("договор.docx", "Составь резюме и найди риски",
provider="anthropic")
print(res.answer) # имена уже восстановленыРучной контроль каждого шага:
from pii_shield import mask_text, assert_clean, unmask_text
r = mask_text(raw) # r.text — с тегами, r.vault — сейф
assert_clean(r.text) # LeakGuardError, если что-то осталось
answer = call_model(r.text) # наружу уходит только маска
final, unknown = unmask_text(answer, r.vault, mode="canonical")Подробнее — references/api.md.
Сейф — единственное, что связывает теги с оригиналами. Без него обратная подстановка невозможна.
- Пишется рядом с документом как
<файл>.vault.json, права0600. - Шифруется по флагу
--pass-env(scrypt + Fernet). - Хранит каноничную форму, все встреченные варианты и журнал вхождений в порядке документа — благодаря журналу точное восстановление возвращает исходную словоформу, а не каноничную.
- Внесён в
.gitignore. Не коммитьте его.
Инструмент устроен так, чтобы ошибаться в безопасную сторону: лучше замаскировать лишнее, чем пропустить. Что стоит знать:
- Скан-PDF без текстового слоя не обрабатывается — нужен OCR.
- Однофамильцы без инициалов получают отдельные теги, а не сливаются в одного человека.
- Голое число без подсказок может быть не распознано как идентификатор — но параноидальный проход всё равно не выпустит такой текст наружу.
- Произвольные латинские имена без славянских окончаний и без обращения
(
Mr.,Dr.) не распознаются: ловить любую пару заглавных слов дало бы больше вреда, чем пользы.
На критичном документе стоит один раз посмотреть docs-masked report глазами.
python3 -m pytest tests/ -q # тесты
python3 -m pii_shield.cli selftest
python3 samples/make_samples.py # пересоздать тестовые документыИнварианты, которые нельзя ломать, перечислены в AGENTS.md.
Всё в samples/ синтетическое; каталог examples/ зарезервирован под ваши
локальные документы и в репозиторий не попадает.
MIT.