Перейти к содержанию
Публикация AiManual

Как построить мультиарендный агентный чат по документам на Amazon Bedrock Managed Knowledge Base

Практическая архитектура мультиарендного чата по документам на Amazon Bedrock Managed Knowledge Base: как связать файлы с пользователями, применить server-side

Коротко

Что будет в материале

  1. 01

    Как работает мультиарендный чат по документам на Amazon Bedrock

  2. 02

    Архитектура RAG на Amazon Bedrock: компоненты и границы доверия

  3. 03

    Как организовать загрузку документов от разных пользователей

  4. 04

    Асинхронное индексирование: когда документ становится searchable

Мультиарендный чат по документам на Amazon Bedrock Managed Knowledge Base строится вокруг двух потоков: загрузки файлов и retrieval по ним. Managed Knowledge Base сокращает объем самостоятельно управляемой retrieval-инфраструктуры, однако backend, аутентификация, авторизация, метаданные, статусы обработки и пользовательский интерфейс остаются ответственностью приложения.

Граница безопасности проходит по серверному контексту пользователя. Backend получает доверенный user_id из проверенной сессии или токена, связывает его с метаданными документов и добавляет фильтр при каждом retrieval-запросе. Значение user_id из тела запроса, имени файла или состояния интерфейса нельзя использовать как основание для доступа.

После загрузки файл сначала попадает в объектное хранилище и прикладную модель документов. Затем запускается асинхронная обработка источника и поискового представления. Поэтому успешный прием файла и состояние searchable разделяют несколько этапов: документ можно считать готовым для вопросов после подтверждения завершения соответствующей операции, а не сразу после ответа upload-endpoint.

Как работает мультиарендный чат по документам на Amazon Bedrock

Главная идея: один чат, изолированные наборы документов

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

Представим двух пользователей: user-101 загрузил договоры, а user-202 загрузил внутренние инструкции. Оба задают вопрос «Какие сроки указаны в документах?». Поисковый запрос должен использовать один и тот же текст, но разные серверные фильтры. Для первого пользователя допустимы фрагменты только с метаданными user_id = user-101, для второго, только с user_id = user-202.

Инвариант системы выглядит так: результат retrieval всегда подчинен доверенному контексту пользователя. LLM получает уже ограниченный набор фрагментов и не должна самостоятельно решать, кому принадлежат документы.

  • Аутентификация устанавливает, кто отправил запрос.
  • Backend сопоставляет пользователя с внутренним user_id или tenant_id.
  • Документ связывается с этим идентификатором до индексирования.
  • Retrieval применяет metadata filter для каждого вопроса и follow-up.
  • Агент формирует ответ только по разрешенному контексту.

Что Managed Knowledge Base берет на себя, а что не берет

Managed Knowledge Base закрывает управляемую часть работы с источниками и retrieval-представлением документов. Сервис помогает убрать из прикладной системы часть задач, связанных с подготовкой базы знаний и поиском по обработанному содержимому. Точная схема источников, индексации, фильтров и поддерживаемых режимов зависит от выбранной конфигурации и актуальной документации AWS.

Приложение продолжает отвечать за полный жизненный цикл пользовательского действия:

Зона ответственностиЧто контролирует приложение
ПользовательАутентификация, сессия, права доступа и связь с внутренней учетной записью
ФайлРазмер, тип, имя, целостность, безопасное хранение и повторная отправка
ПринадлежностьСвязь document_id с user_id или tenant_id
IngestionЗапуск операции, отслеживание состояния, таймауты и ограниченные повторы
RetrievalПостроение серверного фильтра, передача запроса и обработка пустого результата
UXОтображение принятия, обработки, готовности и ошибки
АудитФиксация загрузок, вопросов, изменений прав и операций удаления

Общая настройка Managed Knowledge Base и варианты агентного поиска разобраны в практическом руководстве по Amazon Bedrock Managed Knowledge Base. Для мультиарендного чата к этой схеме добавляется собственный слой контроля доступа.

Архитектура RAG на Amazon Bedrock: компоненты и границы доверия

Целевую систему удобно разделить на семь логических компонентов: клиентское приложение, слой аутентификации, backend/API, объектное хранилище, прикладную базу документов, Amazon Bedrock Managed Knowledge Base и агентный или генеративный слой.

КомпонентЗадачаДоверенный контекст
КлиентВыбор файла, отправка вопроса, отображение статуса и ответаНедоверенный
АутентификацияПроверка токена или сессииИсточник identity
BackendАвторизация, фильтрация, управление операциями и аудитГлавная прикладная граница
Object storageХранение исходного файлаДоступ через серверные политики
База документовСвязи документа, владельца, статуса и операцииИсточник прикладной правды
Managed Knowledge BaseПодготовка поискового представления и retrievalПолучает ограниченный запрос
Агент или LLMПланирование взаимодействия и формирование ответаРаботает с разрешенным контекстом

Клиент может передать имя файла, выбранный режим чата или идентификатор для отображения в интерфейсе. Такие поля помогают UI, но не определяют право доступа. Решение принимает backend после проверки identity.

Поток загрузки: от файла до источника для retrieval

Загрузка проходит последовательно:

  1. Клиент отправляет файл и прикладные параметры.
  2. Backend проверяет сессию или токен.
  3. Сервер получает канонический user_id из проверенного контекста.
  4. Приложение проверяет тип, размер, имя и состояние файла.
  5. Объект сохраняется в хранилище, а запись документа получает владельца и идентификатор операции.
  6. Приложение запускает обработку источника или ставит задачу в очередь.
  7. Система отслеживает состояние до момента, когда документ можно использовать в retrieval.

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

Поток retrieval: от вопроса до ответа агента

Запрос пользователя проходит другой путь:

  1. Клиент передает текст вопроса и идентификатор диалога.
  2. Backend проверяет identity и получает серверный user_id.
  3. Сервер выбирает область документов, доступную этому пользователю или его организации.
  4. Retrieval выполняется с metadata filter, построенным backend.
  5. Система получает релевантные фрагменты и проверяет, что они относятся к разрешенной области.
  6. Агент или генеративный слой использует эти фрагменты для ответа.
  7. В журнал попадают идентификатор пользователя, операции и технический результат без раскрытия лишних данных клиенту.

Для сложных вопросов, которые требуют нескольких поисковых шагов, полезно отделять выбор retrieval-режима от контроля доступа. Агентный поиск может планировать подзадачи, но каждый подзапрос должен сохранять тот же серверный фильтр. Особенности многошагового поиска разобраны в статье про AgenticRetrieveStream и сложные запросы к неструктурированным данным.

Почему агент не заменяет авторизацию

Системная инструкция не превращает LLM в механизм контроля доступа. Модель может ошибиться, неверно интерпретировать вопрос или попытаться вызвать инструмент с неожиданными параметрами. Prompt не блокирует прямой вызов retrieval endpoint и не исправляет ошибку в метаданных.

Правильное разделение выглядит так:

  • backend определяет разрешения;
  • retrieval получает ограниченный запрос;
  • агент выбирает способ работы с уже разрешенным контекстом;
  • генеративный слой не получает чужие фрагменты для последующей фильтрации.

Архитектура самописного агентного слоя, включая оркестрацию, инструменты и обработку ошибок, разобрана в материале о создании AI-агента с нуля. Для document chat к агенту добавляется обязательное ограничение области поиска.

Как организовать загрузку документов от разных пользователей

Сначала аутентификация, потом прием файла

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

После этого сервер сам получает user_id. Источником может служить идентификатор subject в проверенном токене, внутренняя таблица пользователей или результат серверного сопоставления внешней учетной записи с локальной моделью.

Минимальный порядок действий можно выразить псевдокодом:

def upload_document(request):
    principal = authenticate(request)
    if principal is None:
        return error('unauthorized')

    user_id = resolve_internal_user_id(principal)
    validate_file(request.file)
    document_id = create_document(user_id=user_id, status='accepted')
    store_object(document_id, request.file)
    enqueue_ingestion(document_id)
    return document_status(document_id)

Поле request.user_id намеренно отсутствует. Клиент не должен выбирать владельца документа. Если пользовательская модель содержит организации, сервер отдельно проверяет membership и право загрузки в конкретный tenant_id.

Как связать объект, метаданные и владельца

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

ПолеНазначение
document_idУникальная ссылка на документ внутри приложения
user_idВладелец или субъект, для которого разрешен поиск
tenant_idОрганизация или рабочее пространство, если доступ групповой
object_keyПуть объекта в хранилище
original_nameИмя файла для UI и аудита
content_typeТип содержимого после серверной проверки
ingestion_statusТекущее состояние обработки
operation_idСвязь с конкретной операцией загрузки или обновления
created_atВремя создания записи

Поле, используемое для фильтра retrieval, должно иметь одинаковое имя, тип и смысл во всех документах. Если часть объектов получает строковый user_id, а часть, числовой идентификатор, фильтр может давать пустые или непредсказуемые результаты.

При корпоративной модели прав одного user_id может быть мало. Например, документ доступен группе из пяти сотрудников, но запрещен остальным пользователям организации. В такой схеме потребуется определить, фильтруется ли документ по tenant_id, списку групп, отдельным ACL-метаданным или комбинации условий. Этот выбор нужно зафиксировать до наполнения базы, потому что исправление метаданных после массовой загрузки потребует повторной обработки.

Что возвращать клиенту после загрузки

Upload-endpoint должен возвращать состояние операции, а не создавать впечатление мгновенной готовности. Внутри приложения удобно использовать логические этапы:

СостояниеСмыслМожно задавать вопросы
acceptedФайл принят и получил document_idНет
processingИсточник передан на обработкуОбычно нет
readyСервис подтвердил завершение нужной операцииДа
errorОбработка завершилась ошибкой или исчерпала повторыНет

Это внутренние состояния приложения. Точные статусы и переходы нужно сопоставить с API и конфигурацией AWS, которые используются в конкретном проекте. Клиенту достаточно понятного сообщения: «Файл принят», «Документ обрабатывается», «Документ готов к вопросам» или «Не удалось обработать файл».

Асинхронное индексирование: когда документ становится searchable

Почему успешная загрузка еще не означает доступность в чате

Между сохранением файла и появлением фрагментов в retrieval находятся несколько операций: чтение объекта, извлечение текста, подготовка содержимого, построение поискового представления и обновление доступного состояния. Эти операции могут выполняться отдельно от HTTP-запроса пользователя.

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

Корректная формула проще: файл принят, когда сохранение подтверждено; документ searchable, когда завершение обработки подтверждено выбранным поисковым контуром.

Как построить ожидание готовности

Есть два распространенных подхода:

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

В обоих вариантах нужны одинаковые элементы надежности:

  1. Отдельная запись операции с привязкой к document_id.
  2. Ограниченный интервал повторной проверки.
  3. Таймаут, после которого операция получает понятное состояние ошибки.
  4. Разделение временного сбоя и окончательного отказа.
  5. Идемпотентный повтор без создания второго документа.
  6. Ручной запуск повторной обработки из интерфейса или административного API.

Не считайте документ готовым по факту принятия запроса на ingestion. Нужен сигнал завершения, который приложение связывает с конкретным документом и конкретной версией файла.

Гонки между загрузкой и первым вопросом

Пользователь может открыть чат и задать вопрос сразу после выбора файла. Система должна заранее определить поведение в этот момент.

  • Заблокировать вопросы по документу до состояния ready.
  • Разрешить вопрос, но вернуть явный ответ о продолжающейся обработке.
  • Показать только документы, которые уже доступны для retrieval.
  • Не подменять отсутствие фрагментов общим ответом без предупреждения.

Бесконечное ожидание ухудшает UX и усложняет диагностику. Лучше показать промежуточное состояние, идентификатор документа и возможность повторить запрос после завершения обработки.

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

Retrieval с server-side фильтрацией по user_id

Откуда сервер берет user_id

Доверенным источником служит проверенная сессия, JWT или другой механизм аутентификации, который backend умеет валидировать. После проверки сервер сопоставляет внешний identity с внутренним пользователем и его правами.

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

def resolve_access_context(request):
    principal = authenticate(request)
    if principal is None:
        raise UnauthorizedError()

    user = find_user_by_identity(principal.identity)
    return {
        'user_id': user.id,
        'tenant_id': user.tenant_id,
        'groups': user.groups,
    }

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

Фильтр должен применяться к каждому запросу

Фильтрация нужна для нового вопроса, follow-up, повторного запроса, регенерации ответа и любого агентного действия, которое инициирует retrieval. Нельзя рассчитывать на то, что предыдущий запрос уже отфильтровал контекст или что интерфейс скрыл чужие документы.

Серверный путь должен выглядеть единообразно:

  1. Проверить identity.
  2. Получить актуальный контекст пользователя.
  3. Построить metadata filter из серверных данных.
  4. Проверить, что выбранный режим retrieval принимает этот фильтр.
  5. Передать вопрос и фильтр в поисковый слой.
  6. Передать агенту только результат из разрешенной области.

Псевдокод запроса:

def answer_question(request):
    access = resolve_access_context(request)
    query = validate_query(request.query)
    metadata_filter = build_filter(
        user_id=access['user_id'],
        tenant_id=access['tenant_id']
    )
    chunks = retrieve(query=query, filter=metadata_filter)
    return generate_answer(query=query, context=chunks)

Названия параметров здесь условные. Формат фильтра нужно сверить с используемым API и проверить на реальных метаданных. Смысл остается постоянным: сервер формирует ограничение до получения контекста.

Защита от подмены и обхода фильтра

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

Атака или ошибкаЧто может произойтиКонтроль
Подмена user_idВопрос выполняется по чужой областиИгнорировать значение клиента и брать identity из проверенной сессии
Прямой вызов retrieval endpointПользователь обходит UI и отправляет собственные параметрыЗакрыть прямой доступ, проверять аутентификацию и фильтр на сервере
Запрос по чужому document_idРаскрываются имя, статус или содержимое документаПроверять принадлежность документа до любой операции
Пропущенные метаданныеДокумент выпадает из фильтра или становится трудно проверяемымОтклонять объект без обязательных метаданных и вести аудит
Общий кэш ответовОдинаковый вопрос возвращает чужой контекстВключать tenant context в ключ и проверять права до чтения кэша
Групповой доступ без проверки membershipПользователь получает документы организации без нужной ролиПроверять группу, роль и состояние членства

Проверка должна включать смену параметров, повторное использование старого токена, удаление прав между двумя запросами и запрос к документу, который принадлежит другому пользователю. UI-тестов недостаточно: запросы нужно отправлять напрямую к backend.

Какие части системы остаются на стороне приложения

Backend как точка контроля доступа

Backend связывает аутентифицированного пользователя с документами, операциями и retrieval-фильтрами. Через него проходят загрузка, список файлов, вопросы, удаление, замена и повторная обработка.

Клиенту не следует выдавать полномочия на самостоятельный выбор tenant_id, списка документов или готового фильтра. Даже если UI передает эти параметры, сервер обязан пересобрать их из проверенного контекста.

В прикладной базе полезно хранить аудит минимум для следующих событий:

  • создание документа;
  • изменение или замена файла;
  • запуск ingestion;
  • смена статуса операции;
  • retrieval-запрос и его область доступа;
  • удаление документа;
  • изменение группового доступа.

Состояния, ошибки и повторные попытки

Одна строка status = error редко помогает разобраться в проблеме. Разделяйте состояние документа, состояние операции и техническую причину сбоя. Например, документ может оставаться доступным в старой версии, пока новая версия ожидает повторной обработки.

СостояниеРешение приложения
Временная ошибкаПоставить ограниченный повтор с задержкой и сохранить причину
Ошибка валидацииПопросить пользователя заменить файл или исправить параметры
Истек таймаутОстановить автоматические повторы и создать задачу для диагностики
Дубликат операцииВернуть существующий результат по ключу идемпотентности
Успешная обработкаПеревести документ в состояние, доступное для retrieval

Ключ идемпотентности можно связать с пользователем, логическим документом и хэшем файла. При повторной отправке сеть может доставить тот же запрос дважды, поэтому backend должен уметь вернуть уже созданную операцию вместо создания второй записи.

Удаление, замена и аудит документов

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

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

После отзыва доступа backend должен запретить новый retrieval сразу на прикладном уровне. Удаление или обновление поискового представления может завершиться позже, поэтому серверный фильтр и проверка записи документа должны учитывать актуальное состояние прав.

Масштабирование и ограничения multi-tenant document chat

Массовые загрузки и ограничение конкурентности

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

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

  • Ограничивайте число одновременных операций.
  • Разделяйте одиночные загрузки и пакетный импорт.
  • Показывайте состояние каждого документа отдельно.
  • Храните причину последней ошибки.
  • Отделяйте очередь повторов от очереди новых файлов.
  • Проверяйте фактические квоты и ограничения AWS перед расчетом производительности.

Неравномерная нагрузка между арендаторами

Средняя latency по всей системе может скрывать проблему одного крупного арендатора. Метрики нужно группировать по tenant_id или другому разрешенному техническому идентификатору.

МетрикаЗачем нужна
Количество документовПоказывает рост объема по каждому арендатору
Размер очередиПомогает увидеть накопление операций
Длительность ingestionПоказывает задержки обработки файлов
Доля ошибокОтделяет единичный сбой от системной проблемы
Частота retrievalПомогает оценить нагрузку на поисковый и генеративный слой
Количество повторовВыявляет проблемы с идемпотентностью или временными сбоями

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

Кэширование и повторные запросы без утечки данных

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

Пример логического ключа:

tenant_id:user_id:filter_version:document_scope:normalized_query

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

Нельзя кэшировать retrieval-результат только по тексту вопроса. Такая схема смешивает контексты пользователей и создает риск раскрытия содержимого даже при корректной авторизации основного запроса.

Когда управляемого retrieval уже недостаточно

Managed Knowledge Base подходит для сценария, где приложение передает документы в управляемый контур и получает поиск по ним с контролируемыми метаданными. Дополнительное проектирование или другой поисковый слой может потребоваться при следующих условиях:

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

Выбор зависит от модели прав, объема данных, стоимости ошибок и требований к latency. Управляемый сервис уменьшает инфраструктурную нагрузку, но не отменяет проверку архитектурных ограничений на реальном наборе документов.

Практический чек-лист перед запуском

Минимальный набор проверок безопасности

  • Identity: user_id берется из проверенной сессии или токена.
  • Подмена: изменение user_id в теле запроса не меняет область поиска.
  • Прямой retrieval: вызов endpoint без UI не позволяет убрать или изменить фильтр.
  • Чужой документ: запрос по чужому document_id возвращает отказ или пустой результат.
  • Метаданные: документ без обязательного владельца не становится доступным для поиска.
  • Группы: пользователь видит только документы разрешенной организации или группы.
  • Кэш: ключ учитывает пользователя, арендатора и версию области доступа.
  • Токен: просроченная или повторно использованная сессия обрабатывается по правилам системы.

Минимальный набор проверок жизненного цикла

  1. Новый документ не появляется в ответах до подтверждения готовности.
  2. После успешной обработки retrieval находит ожидаемый фрагмент.
  3. Временная ошибка запускает ограниченный повтор.
  4. Повторная отправка того же файла не создает дубликат.
  5. Ошибка обработки видна пользователю и записывается в журнал.
  6. Удаленный документ перестает участвовать в новых ответах.
  7. Замена файла не смешивает версии без явного правила.
  8. Массовая загрузка сохраняет отдельный статус по каждому объекту.
  9. Отзыв прав блокирует доступ на backend, даже если поисковое представление еще обновляется.

Итоговая модель ответственности

Amazon Bedrock Managed Knowledge Base сокращает объем кода вокруг управляемого retrieval. Безопасный мультиарендный чат строится приложением вокруг трех дисциплин: доверенной аутентификации, server-side фильтрации по user_id и явного управления асинхронным жизненным циклом индексирования.

Перед запуском проверьте четыре связи: пользователь связан с разрешенной областью, документ связан с владельцем, ingestion связан с конкретной версией файла, retrieval связан с серверным фильтром. После этого добавьте контроль очереди, идемпотентные повторы, аудит, изолированный кэш и негативные тесты. Точные квоты, статусы и поддерживаемые режимы AWS подтвердите по документации выбранной конфигурации.

Подписаться на канал