Bedrock Knowledge Base Metadata: Filter Retrieval by Language and Repo

Bedrock Knowledge Base Metadata: Filter Retrieval by Language and Repo

A monorepo Knowledge Base without metadata filters will happily retrieve Python ETL snippets while you’re editing a TypeScript Lambda. Attach language, repository, service, and owner metadata at ingest, then pass retrieval filters from the IDE context so Bedrock Retrieve only searches the slice that matters.

⚡ TL;DR: Emit sidecar metadata JSON (or inline attributes) per chunk at sync time; map filterable fields in the KB; call Retrieve/RetrieveAndGenerate with vectorSearchConfiguration.filter; derive filters from open files / git root in the client. See Bedrock Retrieval Filters (tenants) and KB Sync webhooks.

Metadata at ingest

// s3://code-kb/payments/src/getOrder.ts.metadata.json
{
  "metadataAttributes": {
    "repo": "acme/backend",
    "service": "payments",
    "language": "typescript",
    "owner": "payments-platform",
    "path": "services/payments/src/getOrder.ts",
    "visibility": "internal"
  }
}
# sync_chunk.py — write chunk + metadata for Bedrock KB S3 data source
def write_chunk(bucket: str, key: str, text: str, meta: dict):
    s3.put_object(Bucket=bucket, Key=key, Body=text.encode("utf-8"))
    s3.put_object(
        Bucket=bucket,
        Key=f"{key}.metadata.json",
        Body=json.dumps({"metadataAttributes": meta}).encode("utf-8"),
        ContentType="application/json",
    )

Derive language from extension + package.json / pyproject.toml presence; derive service from path conventions (services/<name>/). Fail the sync job if required keys are missing—unfiltered docs are a production bug.

Filter at Retrieve time

import boto3
bedrock_agent = boto3.client("bedrock-agent-runtime")

def retrieve_for_ide(query: str, *, repo: str, language: str | None, service: str | None):
    and_all = [{"equals": {"key": "repo", "value": repo}}]
    if language:
        and_all.append({"equals": {"key": "language", "value": language}})
    if service:
        and_all.append({"equals": {"key": "service", "value": service}})

    return bedrock_agent.retrieve(
        knowledgeBaseId=KB_ID,
        retrievalQuery={"text": query},
        retrievalConfiguration={
            "vectorSearchConfiguration": {
                "numberOfResults": 8,
                "filter": {"andAll": and_all},
            }
        },
    )

Default filters from the active editor (languageId, nearest service root). Do not use global search as the default for “where is X implemented?” inside a 40-service monorepo.

Client context wiring

function filtersFromEditor(ctx: IdeContext) {
  return {
    repo: ctx.gitRemoteSlug,           // acme/backend
    language: mapVscodeLang(ctx.languageId),
    service: detectService(ctx.filePath), // services/payments/...
  };
}

Offer an explicit “search all services” toggle for cross-cutting questions (auth, platform libs). Log which filter mode was used for eval slices.

Pitfalls

Pitfall Result Fix
Metadata only on parent folder Chunks inherit wrong service Per-chunk sidecars
Stringly-typed language (ts vs typescript) Empty results Canonical enum at sync
Over-filtering (repo+service+path prefix) Zero hits Relax stepwise; never silently drop filters without UX
No visibility attribute Secrets/docs leak across teams Filter visibility=internal + IAM

Closing checklist

Dos
– Require repo, language, service metadata on every chunk
– Pass andAll filters from IDE context by default
– Canonicalize enum values at sync time
– Provide an explicit broad-search mode
– Include filter mode in RAG eval dimensions

Donts
– Do not ingest without sidecars “to move faster”
– Do not rely on path substrings in the query text instead of filters
– Do not mix tenant/customer data without tenant isolation filters
– Do not fail open (ignore filter errors)
– Do not forget to re-sync metadata after service renames

Related reading

Last updated on September 11, 2026


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply