Корпоративная архитектура RAG: База знаний с учетом прав доступа на PostgreSQL и pgvector
Мы подробно описываем этапы разбиения на чанки, векторизации, поиска с фильтрацией по правам доступа и указания источников для безопасного подключения корпоративных документов к локальной языковой модели с примерами SQL.
RAG (Retrieval-Augmented Generation) — это метод, при котором языковая модель при генерации ответов находит соответствующие фрагменты из внутренней документации организации и использует их в качестве контекста. Это позволяет предоставлять актуальную и специфичную для компании информацию без необходимости дообучения модели. Однако главной сложностью в корпоративной среде является не столько «найти нужный фрагмент», сколько «найти только тот фрагмент, на просмотр которого у пользователя есть права». В этой статье мы обрисовываем основные контуры инфраструктуры RAG с учетом прав доступа с использованием PostgreSQL и расширения pgvector.
Почему именно PostgreSQL?
Вместо развертывания отдельной векторной базы данных расширение pgvector добавляет векторный поиск в уже используемую вами PostgreSQL. С корпоративной точки зрения это дает два больших преимущества: права на документы и векторы сосуществуют в рамках одной транзакции и одной модели прав (Row Level Security); кроме того, не требуется изучать новую систему для резервного копирования и мониторинга.
Шаг 1: Разбиение документов на чанки
Документы (договоры, регламенты, техническая документация) разбиваются на фрагменты, которые помещаются в контекст модели. Стратегия разбиения напрямую влияет на качество результатов: подход, учитывающий границы заголовков и оставляющий небольшое перекрытие с соседними фрагментами, обычно работает лучше, чем деление по фиксированному количеству символов. Каждый фрагмент должен быть связан с исходным документом и областью доступа документа (отдел, компания, класс конфиденциальности).
create extension if not exists vector;
create table doc_chunks (
id bigserial primary key,
document_id uuid not null references documents (id) on delete cascade,
company_id uuid not null,
scope text not null, -- например, 'hr', 'finance', 'public'
content text not null,
embedding vector(768) not null, -- размерность зависит от используемой модели эмбеддингов
created_at timestamptz not null default now()
);
create index on doc_chunks using hnsw (embedding vector_cosine_ops);
Шаг 2: Генерация эмбеддингов
Модель эмбеддингов создает числовой вектор для каждого фрагмента. При локальном развертывании можно использовать модели с открытыми весами; выбор конкретной модели зависит от языковой поддержки (качество важно для текстов на турецком языке), размерности векторов и аппаратного обеспечения. Важное правило: модель эмбеддингов, используемая во время запроса, должна совпадать с моделью, использовавшейся при индексации фрагментов; при изменении модели все фрагменты подлежат переиндексации.
Шаг 3: Поиск с фильтрацией по правам доступа
Это самый критически важный шаг. Запрос векторного сходства выполняется вместе с областью доступа пользователя. В таблице с включенным RLS условие политики автоматически добавляется к запросу; таким образом, даже если код приложения забудет применить фильтр, база данных не вернет неавторизованный фрагмент.
alter table doc_chunks enable row level security;
create policy chunks_read on doc_chunks for select using (
company_id = current_company_id() -- компания в сеансе
and scope = any (current_user_scopes()) -- области доступа пользователя
);
-- Запрос (политика применяется автоматически)
select id, document_id, content
from doc_chunks
order by embedding <=> $1 -- $1: вектор эмбеддинга вопроса
limit 8;
Самая опасная ошибка в системах RAG — когда ассистент предоставляет пользователю документ, который тот не должен открывать, путем его «суммирования». Фильтр должен находиться внутри данных, а не перед моделью.
Шаг 4: Формирование контекста и цитирование источников
Найденные фрагменты передаются модели вместе с вопросом. При генерации ответа пользователю в качестве источников показываются использованные фрагменты: название документа, раздел и ссылка. Цитирование источников выполняет две функции: пользователь может проверить ответ, а также становится проще заметить, когда модель «выдумывает» информацию.
Шаг 5: Журнал аудита
Для каждого запроса в таблицу аудита записываются пользователь, время, вопрос, идентификаторы возвращенных фрагментов и ответ модели. Эти записи используются как для проверок безопасности, так и для повышения качества. Поскольку сами записи могут содержать персональные данные, необходимо определить срок их хранения и права доступа к ним.
Качество: улучшение невозможно без тестового набора
Перед запуском в продакшн подготовьте небольшой тестовый набор, состоящий из реальных вопросов пользователей: вопрос, ожидаемый исходный документ и приемлемый ответ. Этот набор запускается заново при изменении стратегии разбиения, количества фрагментов или модели. Мы не даем числовых целевых показателей; документы и ожидания у каждой организации разные. Главное — чтобы изменения вносились на основе измерений.
Примечания по локальному запуску
- Модель и служба эмбеддингов должны работать в отдельных контейнерах и быть доступными только из внутренней сети.
- При индексации документов контент должен маркироваться в соответствии с классом конфиденциальности; вариант «общедоступно» не должен быть по умолчанию.
- Выходные данные модели не должны напрямую инициировать транзакцию (например, удаление записи); между ними должно быть подтверждение человека.
Гибридный поиск: одного вектора недостаточно
Векторное сходство улавливает семантическую близость, но может оказаться слабым в запросах, требующих точного совпадения (номер договора, код продукта, имя человека). Объединение векторного поиска с функцией полнотекстового поиска PostgreSQL (tsvector) и сопоставление результатов обоих методов в большинстве корпоративных сценариев дает более точные результаты. Подходящая конфигурация словаря (стемминг и стоп-слова) определяет качество полнотекстовой части для турецкого языка.
-- Пример: гибрид полнотекстового и векторного поиска (концептуально)
with lexical as (
select id, ts_rank(search_vector, plainto_tsquery('turkish', $q)) as score
from doc_chunks where search_vector @@ plainto_tsquery('turkish', $q)
order by score desc limit 20
), semantic as (
select id, 1 - (embedding <=> $qvec) as score
from doc_chunks order by embedding <=> $qvec limit 20
)
select coalesce(l.id, s.id) as id,
coalesce(l.score, 0) * 0.4 + coalesce(s.score, 0) * 0.6 as combined
from lexical l full outer join semantic s on s.id = l.id
order by combined desc limit 8;
Веса (0.4 / 0.6) приведены в качестве примера; их следует откалибровать с помощью тестового набора.
Что происходит при обновлении документа?
Корпоративные документы меняются: регламент обновляется, договор продлевается. Индекс должен отслеживать эти изменения; в противном случае ассистент будет выдавать старую версию как «актуальную». Практичный метод: для каждой версии документа хранится хэш-значение, фрагменты измененного документа удаляются и пересоздаются, старая версия остается в архиве, но не попадает в поиск. Фрагменты удаленного документа также должны быть немедленно удалены из индекса; «документ, к которому больше нет доступа» и «документ, которого больше не существует» обрабатываются одинаково.
Выбор модели и аппаратное обеспечение
При локальном развертывании размер модели представляет собой баланс между качеством ответа и стоимостью оборудования. Маленькие и средние модели с открытыми весами могут удовлетворить большинство корпоративных потребностей в задачах суммирования с указанием источников и вопросов-ответов; сценарии, требующие сложных многошаговых рассуждений, предполагают использование более крупной модели. Решение принимается на основе реальных вопросов из тестового набора, а не общих сравнительных таблиц. На стороне оборудования решающее значение имеет память графического процессора, а по мере роста числа одновременных пользователей требуется управление очередями.
Корпоративные правила при проектировании промптов
- Модель должна отвечать только на основе предоставленного контекста; если информации в контексте нет, она должна сказать: «У меня нет этой информации».
- Каждый ответ должен возвращать идентификаторы использованных фрагментов; цитирование источников генерируется на стороне приложения из этих идентификаторов.
- Контекст, содержащий персональные данные, должен предоставляться только авторизованному пользователю и только в той степени, в какой это необходимо для вопроса.
- Ввод пользователя должен добавляться в контекст как «данные», а не как «инструкция»; тексты внутри документов также не должны считаться инструкциями (инъекция промпта).
Пользовательский опыт: доверие строится в интерфейсе
Ссылки на источники должны быть кликабельными и открывать соответствующий раздел документа; должно быть наглядно видно, что ответ является «интерпретацией модели»; пользователь должен иметь возможность оценивать ответ, и этот отзыв должен попадать в тестовый набор. Системы RAG — это не те системы, которые настраиваются один раз и оставляются без внимания, а системы, постоянно корректируемые на основе обратной связи.
В услугах искусственного интеллекта и автоматизации от Cy Logic установка RAG выполняется по следующим принципам: права в данных, источники в ответе, аудит в журналах.