Перейти к содержимому
arckep.ru — все нейросети в одном месте без VPN Перейти
>AISTUDY_
Поддержать
AUTHORСвежий выпуск №024 → Куда внедрять агентов: фронт или тыл
Авторская колонка · комплаенс на сайте: от цели до внедренияСЕРИЯ 013
Авторская колонка · выпуск №013

Комплаенс
на сайте

Полный процесс работы с юридическими документами через агентов — от задачи привести продукт в соответствие закону до фиксации согласий в базе, которую можно предъявить на проверке.

В одном из моих проектов накопился новый функционал, и под него надо было привести в порядок правовую часть: убедиться, что всё соответствует законодательству, обновить документы и разбить единственную страницу «политика конфиденциальности» на нормальный пакет — соглашение, политику, отдельные согласия на обработку данных, условия для партнёров.

Для правовой части важны три факта об этом сервисе. У пользователей есть личные кабинеты. Кроме них на площадке работают партнёры-организации — по закону каждая самостоятельный оператор персональных данных. И онлайн-оплаты нет: расчёты идут мимо площадки.

Делал я это с ИИ, и работа естественно разбилась на две части. Первая — собрать и проверить саму базу документов: тексты, нормы, непротиворечивость. Вторая, как логичное продолжение, — внедрить её в продукт, потому что готовый и выверенный документ без записи о согласии остаётся просто бумагой.

Схема 1Маршрут: от цели до внедрения
Как читать этот выпускПервая часть — как собрать базу документов агентами и чем её проверять, чтобы не подписаться под выдумкой модели. Вторая, как продолжение, — код: журнал согласий, две обязательные галочки, запись, которая не валит регистрацию, и бэкфилл старым пользователям. В конце первой части — готовый промпт для агента.
Часть 1Собрать и проверить документытексты · нормы · непротиворечивость
Раздел 01

Разведка раньше текстов

Не бросаться писать с первого сообщения. Сначала функциональный бриф по живому коду: кто участники и какой у каждого статус, что с деньгами, какие данные собираются и куда уходят, что из согласий уже реализовано. У меня собственная разработка под мои проекты — модуль строит графы всех зависимостей проекта и его функций, даже там, где абсолютно разный язык кода. Перед стартом агенты с помощью графа за пару минут собрали полный функционал сайта, ориентированный под задачу.

У площадки, например, нет платёжного шлюза — один этот факт снимает целый пласт требований, который ИИ навесил бы на «сервис с оплатой» по умолчанию. Бриф стал фундаментом: документы строились от единой картины бизнеса, а не от семи независимых догадок о том, что это за сервис.

Раздел 02

Параллельная сборка и сведение

Бриф ушел в Cowork внутри приложения Claude на компьютере, там заведен специально проект для решения юридических вопросов с чёткими инструкциями. Документы я передал основному агенту, который дальше раскидал их по субагентам: семь на черновики, пять на правки. Быстро, но с проблемами — агенты писали независимо, не видя соседние тексты, и статус площадки, термины, роль партнёров начали расходиться от документа к документу: где-то «сервис», где-то «платформа», одна роль описана по-разному. По отдельности каждый текст складный, вместе пакет противоречит сам себе.

Поэтому после параллельной сборки обязателен сводный проход на непротиворечивость — это часть работы, а не опция.

Раздел 03

Проверка: норма только из первоисточника

Ни одной правовой нормы из головы. Каждую статью — через поиск по первоисточнику, в текст идёт ссылка. База знаний модели обрывается во времени, а тон у неё одинаковый и когда она права, и когда цитирует прошлогоднюю редакцию. Одна из юридических баз отдала нужную статью в битой кодировке — чистый текст не вытащил, разбирал структуру и добивал формулировки из других источников.

Сам пакет я проверял программно — грепом по маркерам (статус оператора, старые формулировки, незакрытые заглушки), а не вычитывая тысячу семьсот строк подряд. То есть такой объём я не даю читать агенту для валидации: он начнёт плыть и путаться даже с самыми точными инструкциями, поэтому сначала мы с ним собираем маркеры проверки, и он делает это программно. Это честная граница: греп находит только то, что догадался искать, и не заметит юридически тонкую неточность. Поэтому финальная человеческая вычитка не убирается — ИИ готовит и проверяет, но подпись ставит человек.

Раздел 04

Чему верить из выданного

Отдельный шаг «что принять, а что перепроверить» нужен потому, что на доклады модели о себе нельзя опираться без сверки.

Первое: она не отделяет «я проверила» от «мне дали на вход». В бриф попало утверждение, требовавшее подтверждения, — модель вписала его в документ как факт, «подтвердить» сама не пометила. Лечится только тем, что заранее просишь помечать допущения отдельным списком.

Второе: она переоценивает сделанное. На переводе пакета агенты отрапортовали готовый результат, который не совпал с файлами — число разделов в отчёте расходилось с реальностью. Сверять пришлось по самим файлам, а не по их «готово».

Отсюда разделение ролей: позицию (что публикуем, объём, спорные решения) держит человек, сбор и проверку по источникам берёт модель, а её результат сверяется по факту, не по отчёту.

Что отдать своему агенту

Правила, которые снимают часть ошибок на этом этапе. Дайте агенту в начале:

Работаем над юридическими документами. Правила:
1. Ни одной нормы по памяти. Каждую статью проверяй по первоисточнику и давай
   ссылку. Не уверен в редакции, пиши "нужно подтвердить", не выдавай за факт.
2. Непроверенное из входных данных помечай как допущение, отдельным списком:
   "это принято на веру, подтвердить".
3. Отчитываясь о сделанном, не пиши "готово" по памяти. Приводи проверяемый
   результат (что и где сделано), чтобы можно было сверить по факту.
4. Если документов несколько, держи единые термины, статус и роли во всех.
   В конце сведи их между собой на непротиворечивость.

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

Часть 2Внедрениекод, который превращает текст в доказательство

Документы собраны и выверены — но это пока тексты. Когда я сверял их с продуктом, выяснилось главное, и оно не про формулировки: согласия пользователей нигде не фиксировались. Человек ставил галочку при регистрации, но записи не оставалось — ни какой текст принял, ни когда, ни с какого устройства. Доказать при проверке, что согласие было, нечем. Вот здесь документ и превращается из бумаги в рабочий: за текстами должна стоять запись.

Повторная сверка — уже внутри проекта

Эту сверку я вёл не на глаз. После сбора документов в Cowork весь пакет ушёл на повторную валидацию — уже к Claude Code, который работает прямо внутри проекта. Бриф с собранным функционалом, тот же, что уходил в Cowork, у него уже был на руках. Задача — сверить документы и прописанные в них юридические нюансы с фактическим функционалом.

Дальше он исследовал готовность кодовой базы и объём необходимых правок, составил план, согласовал его со мной и учёл новые вводные. И только после этого началась собственно фиксация — журнал согласий.

Раздел 05

Журнал согласий

Одна таблица, куда пишется неизменяемая запись на каждое согласие:

CREATE TABLE user_consents (
  id            UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
  account_id    BIGINT      NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
  consent_type  TEXT        NOT NULL CHECK (consent_type IN
                            ('offer_policy','personal_data','marketing')),
  text_version  TEXT        NOT NULL,
  accepted_at   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  accepted_ip   TEXT,
  accepted_ua   TEXT,
  revoked_at    TIMESTAMPTZ,
  created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Схема 2Строка журнала согласий

Главный инвариант — revoked_at. Отзыв согласия не удаляет строку, а проставляет дату отзыва; исходная запись остаётся навсегда. Нужно уметь показать не только что согласие есть сейчас, но и что оно было в прошлом и когда отозвано. Удалить строку — потерять доказательство.

text_version — вторая опора. В неё пишется версия принятого текста, привязанная к дате редакции документов. Поправил формулировку оферты — версия сменилась, и по каждой записи видно, какую именно редакцию человек принял. Без этого «он соглашался» повисает: соглашался с чем?

Миграцию накатываешь под пишущей ролью приложения, не под суперпользователем:

SET ROLE app_rw;   -- пишущая роль приложения
CREATE TABLE ... ;
RESET ROLE;
Один пропущенный SET ROLE роняет деплойНакатишь миграцию от суперпользователя — владельцем таблицы станет он, и приложение под своей ролью упрётся в отказ доступа уже на проде. Это не косметика, а причина падения, которую на дев-базе не всегда видно.
Раздел 06

Две галочки, которые нельзя слить

На форме регистрации — две обязательные галочки, раздельно:

offerAccepted: z.literal(true, 'Необходимо принять оферту и политику'),
consentGiven:  z.literal(true, 'Необходимо согласие на обработку ПД'),
marketingConsent: z.boolean().optional(),   // по умолчанию выкл

z.literal(true) — не «галочка по желанию», а жёсткое условие: без неё форма не проходит валидацию вообще. Принятие оферты и согласие на обработку персональных данных — разные галочки специально: закон требует, чтобы согласие на обработку данных было самостоятельным актом, его нельзя зашить в «я принимаю условия» (статья 9 152-ФЗ). Маркетинг — третья, опциональная, по умолчанию снятая. Заодно закрыли прореху: партнёры-организации раньше регистрировались вообще без согласия на обработку данных.

Раздел 07

Запись, которая не валит регистрацию

Факт согласия пишется после создания аккаунта. Тонкость: сбой записи в журнал не должен ронять саму регистрацию — но и потеряться молча не должен.

const results = await Promise.allSettled(
  types.map((t) => recordConsent({ accountId, consentType: t, textVersion, ip, ua }))
);
results.forEach((r, i) => {
  if (r.status === 'rejected') {
    console.error(`consent ${types[i]} failed for ${accountId}`, r.reason);
    captureException(r.reason, { tags: { area: 'consent-audit' } });  // в Sentry
  }
});

allSettled, а не Promise.all: упавшая запись одного согласия не отменяет остальные и не бросает наверх. Человек регистрируется в любом случае, но каждая несостоявшаяся запись летит в лог и в Sentry — потому что пропажа доказательной записи это то, о чём надо узнать сразу, а не на проверке. Сам recordConsent всегда делает INSERT новой строки, не обновляет старую: повторный accept или новая версия текста — новая запись, история накапливается.

Схема 3Поток: галочка → запись → проверка
Раздел 08

Бэкфилл старым пользователям

У всех, кто зарегистрировался до раздела /legal, доказательной записи нет вообще, а это самая массовая часть базы. Их закрывает миграция, которая реконструирует запись из факта регистрации:

INSERT INTO user_consents (account_id, consent_type, text_version, accepted_at, accepted_ip, accepted_ua)
SELECT a.id, ct.consent_type, 'legacy-backfill', a.created_at, NULL, NULL
FROM accounts a
CROSS JOIN (VALUES ('offer_policy'), ('personal_data')) AS ct(consent_type)
WHERE a.deleted_at IS NULL
  AND <у аккаунта есть реальный профиль>
  AND NOT EXISTS (
    SELECT 1 FROM user_consents uc
    WHERE uc.account_id = a.id AND uc.consent_type = ct.consent_type
  );

Несколько решений в этих строках:

  • accepted_at = a.created_at — дата согласия равна дате регистрации, когда оно фактически давалось. Не «сегодня», а честная историческая дата.
  • accepted_ip / accepted_ua = NULL — их в старом флоу не сохраняли, подставлять выдуманные нельзя.
  • text_version = 'legacy-backfill' — маркер: запись реконструирована, а не получена свежим явным кликом. Видно, где живое согласие, а где восстановленное.
  • CROSS JOIN (VALUES ...) — на каждый аккаунт сразу две строки (оферта и ПД) без дублирования запроса.
  • NOT EXISTS — идемпотентность: миграцию можно гонять повторно, второй раз ничего не задвоит.
  • deleted_at IS NULL и фильтр на реальный профиль — удалённые и пустые аккаунты пропускаются. Маркетинг бэкфиллится отдельно и только тем, у кого стоял флаг согласия на рассылку.

Текст — из кода наружу

Сами формулировки документов вынесены из кода в отдельные файлы и рендерятся через markdown с санитайзером. Меняется формулировка — правится один файл, версия согласия сдвигается, формы регистрации не трогаются. Юрист правит текст, разработчик не лезет в разметку.

Итог

Что в итоге

Первая часть дала выверенную базу документов: тексты собраны и сведены, нормы проверены по источникам, спорные решения остались за мной. Вторая превратила их из бумаги в работающие: согласие фиксируется неизменяемой записью с версией и временем, отзыв сохраняет историю, старые пользователи закрыты бэкфиллом. Документ теперь подкреплён записью, которую можно предъявить.

ИИ собрал тексты почти сам. Но проверка по источникам, сверка его докладов по факту и инженерия фиксации — работа в паре с человеком, который держит позицию и ставит подпись.

Серия 013 · выпуск опубликован · 2026-06-17
Авторская колонка · выпуск №013 · комплаенс на сайте: от цели до внедрения

// Обсуждение

Можно писать анонимно. Укажите email, чтобы получать уведомления об ответах.