Skip to content

About

Локальное обезличивание документов перед отправкой в LLM и обратная подстановка после ответа. Скилл, MCP-сервер и CLI.

Topics

Resources

Stars

20 stars

Watchers

1 watching

Forks

Repository files navigation

docs-masked

Локальное обезличивание документов перед отправкой в языковую модель — и обратная подстановка после ответа.

Документ никогда не покидает машину в исходном виде. Персональные данные заменяются устойчивыми тегами (#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 сама по себе неотличима от случайного числа, а в колонке «ИНН» распознаётся уверенно.

Python API

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.

About

Локальное обезличивание документов перед отправкой в LLM и обратная подстановка после ответа. Скилл, MCP-сервер и CLI.

Topics

Resources

Stars

20 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages