Назад в блог
RAG Systems

Метаданные и фильтры в RAG: как сузить поиск до правильного контекста

Similarity находит candidates; deterministic scope решает eligibility

Tenant, audience, language, lifecycle и product filters до того, как модель увидит context

Авторский META-7 retrieval contract с failure modes и production acceptance gate
метаданные RAG, фильтры RAG, vector search filters, архитектура RAG, AI база знаний и META-7
ТемаRetrieval scope boundary
ФокусMETA-7
СтатусPUBLISHED / 2026-08-24
Поисковый запрос проходит через метаданные и фильтры tenant, role, language, lifecycle и document type до разрешённых документов
Поисковый запрос проходит через метаданные и фильтры tenant, role, language, lifecycle и document type до разрешённых документов
TERMINAL_PREVIEW.LOG
$ scope rag --contract META-7
> identify: source / owner / locator / version
> classify: tenant / audience / language / type
> filter: permissions / lifecycle / product / context
> verify: evidence / citation / no-answer
> route: answer / review / deny
Разбор

Retrieval может вернуть passage, который лингвистически близок к вопросу, но не подходит конкретному пользователю. Он может относиться к другому tenant, старой версии policy, другому продукту, internal audience или другому языку. Метаданные и filters — deterministic boundary, который не позволяет vector search считать каждый близкий document допустимым context.

Здесь разобрана именно эта граница: какие metadata нужны RAG-системе, где должны работать filters, как выглядят failure modes и как проверить дизайн. Материал дополняет широкий гайд по RAG-системам, но не заменяет полный архитектурный или коммерческий review.

1. Проблема: semantic similarity не доказывает применимость

Vector retrieval отвечает на ограниченный вопрос: какие сохранённые passages embedding model считает связанными с query? Он не устанавливает, кому можно показать passage, current ли он, относится ли к нужной country или plan и является ли authoritative version.

Представим internal assistant для product support. Вопрос об отмене подписки находит понятную cancellation procedure. Без metadata это может оказаться инструкция для enterprise contract, retired product или internal escalation team. Текст может быть релевантным, но использование будет неверным или небезопасным. Проблему не исправят больший topK, другая модель или более строгий prompt.

Практическое правило: similarity даёт candidates; filters определяют eligibility. В answer layer должны попасть только candidates, которые разрешены, актуальны и относятся к scope текущего запроса.

2. META-7: компактный retrieval contract

META-7 — это checklist, который превращает documents в governed retrieval evidence:

  1. IDENTIFY — задать source и owner через стабильные document и passage ID.
  2. CLASSIFY — указать tenant, audience или role, language, document type и product/business scope.
  3. VERSION — сохранить effective date, lifecycle, source revision и проверяемый locator.
  4. AUTHORIZE — установить права caller до выбора context для модели.
  5. FILTER — применить deterministic conditions до ranking или сразу после retrieval engine, если так устроена его access model.
  6. VERIFY — проверить, что найденного evidence достаточно, а не только то, что оно допустимо.
  7. ROUTE — выдать ответ с citation, отправить на review или честно вернуть no-answer.

Названия полей могут отличаться, но их смысл должен пережить ingestion, indexing, query construction, ranking, answer generation и logging. Поле language, исчезающее на этапе chunking, или tenantId, который retrieval adapter не передаёт в query, не является контролем.

ts
type RetrievalMetadata = {
  tenantId: string;
  audience: "public" | "customer" | "employee" | "admin";
  language: "en" | "ru" | "hy";
  documentType: "policy" | "manual" | "release-note";
  productId?: string;
  lifecycle: "current" | "superseded" | "quarantined";
  effectiveFrom?: string;
  sourceVersion: string;
  locator: string;
};

Metadata — не набор необязательных labels. У каждого поля должны быть owner, допустимые значения, событие обновления и цель в query-time decision. Если никакое решение поле не использует, его лучше удалить. Если решение зависит от факта, которому не соответствует поле, добавьте поле до того, как доверять retrieval.

3. Архитектура: policy работает до того, как модель увидела context

Полезны три этапа. На ingestion валидируйте source, создавайте passage-level metadata и отклоняйте records без обязательного owner, version или access classification. На retrieval собирайте filters из authenticated caller и request context; эти значения должны прийти из trusted systems, а не из free-text user prompt. На answer stage проверяйте, что selected passages действительно поддерживают ответ, показывайте citations и сохраняйте decision trail.

В multi-tenant application tenantId — не preference релевантности, а isolation boundary. Retrieval call должен быть невозможен без него. Для role-sensitive material используйте allow-list или policy decision из identity claims, а не просьбу модели решить, что caller может читать.

Того же требуют language и lifecycle. Система для Armenian, Russian и English может предпочесть язык caller, но не должна молча превращать fallback в утверждение, что source говорит то же самое. Superseded document обычно нужно исключить до ranking, а не оставить десятым в выдаче и надеяться, что prompt его проигнорирует.

ts
const scope = policyScope(authenticatedCaller, request);

const candidates = vectorIndex.search(queryVector, {
  topK: 20,
  filter: {
    tenantId: scope.tenantId,
    audience: { $in: scope.allowedAudiences },
    language: { $in: scope.languages },
    lifecycle: "current",
    productId: scope.productId,
  },
});

const evidence = candidates.filter((item) => item.metadata.sourceVersion);

Пример намеренно оставляет authorization вне модели. Модель может помочь разобрать вопрос, но не должна создавать permissions или ослаблять deterministic scope.

4. Типовые failure modes

Metadata chunk не совпадает с metadata document. У document есть access label, но chunks индексируются без него. Тогда retrieval не может применить document policy. Копируйте inherited fields в каждую indexed unit и проверяйте denied query.

Filters добавляются после сборки context. Если unfiltered passages уже оказались в prompt, а следующий компонент решает, что цитировать, disclosure уже произошёл. Фильтруйте до context creation.

Lifecycle только описательный. Старые documents часто сохраняют для audit, но забывают исключить из обычного search. Используйте явное условие current и отдельный audited path для historical questions.

Отсутствующее поле ведёт себя как wildcard. Отсутствие tenant, audience, language или lifecycle должно отправлять record в quarantine, а не разрешать широкий поиск. Permissive default превращает defect ingestion в риск раскрытия.

Business labels берутся из query. Пользователь, написавший «я administrator», не должен управлять role filter. Scope нужно получать из authenticated identity и facts system of record.

Filters подменяют evaluation. Eligible evidence всё ещё может быть неполным, противоречивым или не по теме. Фильтрация сокращает candidates, но не доказывает ответ. Groundedness, точность citations и no-answer behavior тестируйте отдельно.

5. Практический test set до production

Соберите небольшой, но representative set. Начните с обычных questions, которые должны находить current evidence. Затем добавьте cross-tenant question, denied-role question, retired-version question, mixed-language query, product mismatch, вопрос без supporting source и source с неполными metadata. Expected result фиксируйте до tuning.

ТестОжидаемый результатЧто доказывает
Customer спрашивает о current planТолько current permitted passagesПрименяются scope и lifecycle
Employee просит admin-only procedureContent document не возвращаетсяРаботает role boundary
Query называет retired policyHistorical route или явный no-answerСтарый content не используется молча
Russian вопрос при English-only sourceBounded fallback или reviewLanguage behavior определён явно
Record без tenantIdQuarantine, а не retrievalMissing metadata закрывается безопасно

Логируйте filter values, matched source IDs, source versions, locators, answer route и policy denial. Не сохраняйте raw sensitive text только ради удобства observability. Цель — достаточно evidence для повторения decision, но не второй неуправляемый corpus в logs.

Production acceptance gate

До расширения RAG-пилота подтвердите, что каждый retrieval request несёт trusted scope, а каждый selected passage имеет inspectable provenance. В release gate нужны negative tests: система, которая показывает только happy-path relevance, ещё не проверила свою границу.

ts
require(scope.tenantId && scope.allowedAudiences.length > 0);
require(candidate.metadata.lifecycle === "current");
require(candidate.metadata.sourceVersion && candidate.metadata.locator);
require(candidate.metadata.language && candidate.metadata.audience);

if (!evidence.supportsClaim) route = "no-answer-or-review";
if (!policy.permits(candidate, scope)) route = "deny-without-context";

Результат — не «больше metadata» ради самих metadata. Это retrieval layer, который показывает, почему passage допустим, какая version использована, какая policy его ограничила и когда система обязана не отвечать. Для bounded architecture review начните с гайда по RAG-системам и практических ограничений из кейсов.

CODE_BLOCK.TXT
require(scope.tenantId && scope.allowedAudiences.length > 0);
require(candidate.metadata.lifecycle === "current");
require(candidate.metadata.sourceVersion && candidate.metadata.locator);
require(candidate.metadata.language && candidate.metadata.audience);

if (!evidence.supportsClaim) route = "no-answer-or-review";
if (!policy.permits(candidate, scope)) route = "deny-without-context";