Метаданные и фильтры в 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

$ 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 / denyRetrieval может вернуть 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:
- IDENTIFY — задать source и owner через стабильные document и passage ID.
- CLASSIFY — указать tenant, audience или role, language, document type и product/business scope.
- VERSION — сохранить effective date, lifecycle, source revision и проверяемый locator.
- AUTHORIZE — установить права caller до выбора context для модели.
- FILTER — применить deterministic conditions до ranking или сразу после retrieval engine, если так устроена его access model.
- VERIFY — проверить, что найденного evidence достаточно, а не только то, что оно допустимо.
- ROUTE — выдать ответ с citation, отправить на review или честно вернуть no-answer.
Названия полей могут отличаться, но их смысл должен пережить ingestion, indexing, query construction, ranking, answer generation и logging. Поле language, исчезающее на этапе chunking, или tenantId, который retrieval adapter не передаёт в query, не является контролем.
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 его проигнорирует.
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 procedure | Content document не возвращается | Работает role boundary |
| Query называет retired policy | Historical route или явный no-answer | Старый content не используется молча |
| Russian вопрос при English-only source | Bounded fallback или review | Language behavior определён явно |
Record без tenantId | Quarantine, а не retrieval | Missing 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, ещё не проверила свою границу.
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-системам и практических ограничений из кейсов.
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";