← Agent Frameworks 🕐 19 min read
Agent Frameworks

Bedrock Knowledge Bases and RAG Pipeline Cost Governance (2026)

Amazon Bedrock Knowledge Bases is a fully managed RAG orchestration service. The pricing is disaggregated across five independent meters.

Audience: Platform engineers and cloud cost practitioners running enterprise RAG systems on AWS.

TL;DR: AWS Bedrock Knowledge Bases now has four supported vector backends with radically different cost profiles. S3 Vectors (GA December 2025, further cost-reduced June 16 2026) is the correct default for most workloads at roughly $7–10/month per 1M vectors versus $350–700/month for the OpenSearch Serverless floor. The largest cost lever is neither storage nor retrieval — it is the number of tokens delivered to the LLM per query, which is a product of chunk size × k. Cutting k from 5 to 3 with a reranker saves more than switching vector backends for high-QPS workloads. Multi-tenant attribution requires Application Inference Profiles or IAM principal tagging (available April 2026); neither works retroactively.


Amazon Bedrock Knowledge Bases is a fully managed RAG orchestration service. The pricing is disaggregated across five independent meters. Most teams only see the LLM inference line in Cost Explorer; the others require deliberate instrumentation.

1.1 The Five Cost Meters

Meter Pricing Unit Typical Range Notes
Document parsing (Bedrock Data Automation) $0.010/page $10–$100/ingestion run for 1K–10K pages Only charged when BDA is used; parsing PDFs, images, PPT. Text-only S3 files skip this.
Embedding model inference (ingestion) $0.020/1M tokens (Titan V2) $0.02–$20 per run depending on corpus Charged per token across all chunks at ingest and re-ingest
Embedding model inference (query) $0.020/1M tokens (Titan V2) ~$0.000001/query Query embedding is negligible even at 1M queries/day
Vector store (storage + compute) Varies by backend — see §2 $7/mo (S3 Vectors) → $700/mo (AOSS) Dominant cost for low-to-mid QPS; see full breakdown below
Reranking model (optional) $1.00/1,000 queries (Amazon Rerank 1.0) $100–$3,000/mo at typical QPS Each “query” allows up to 100 document chunks; exceeding 100 counts as additional queries

Note: there is no direct per-query retrieval fee charged by the Knowledge Bases API layer itself. You pay the embedding model for the query embedding plus the vector store backend for the similarity search. The LLM call (the actual Claude / Nova / Titan call) is billed separately at the model’s standard on-demand token rates.

1.2 Full Cost Build: Typical Enterprise Deployment

Assumptions: 500K documents (avg 8 pages each = 4M pages), avg chunk size 512 tokens, moderate overlap producing 3M chunks, 100K queries/day, Claude Sonnet 4 at $3/1M input + $15/1M output, k=5, no reranker.

Ingestion (one-time):
  Parsing (4M pages × $0.010)             = $40,000  ← one-time; amortize over KB lifetime
  Embedding (3M chunks × 150 tokens avg)  = 450M tokens × $0.020/1M = $9

Monthly (100K queries/day = 3M/month):
  Query embedding (3M × 50 tokens)        = 150M tokens × $0.020/1M = $3/month
  Vector store (S3 Vectors, ~3M vectors)  = ~$7–15/month
  LLM input (k=5 × 512 tokens + 600 overhead × 3M queries)
    = 3,200 tokens avg × 3M = 9.6B tokens × $3/1M  = $28,800/month
  LLM output (200 tokens avg × 3M)        = 600M × $15/1M = $9,000/month

Total monthly steady-state:               = ~$37,815/month
Document re-parsing on change:            = ~$10/1,000 changed pages

The LLM call accounts for 99%+ of steady-state cost. The vector store is noise by comparison — which explains why vector store selection is a one-time governance decision, not a monthly FinOps priority, while k and chunk size require continuous governance.


2. Vector Store Backend Cost Comparison

2.1 The Four Supported Backends

Backend Storage Cost Query/Compute Cost Idle Floor Scale Ceiling
S3 Vectors $0.06/GB/month Per-query fee (tiered) + $/TB processed $0 2B vectors/index; 20T/bucket
OpenSearch Serverless (AOSS) $0.024/GB/month (separate S3) $0.24/OCU-hour; minimum 2 OCUs ~$350/month (prod) Scales with OCUs
Aurora PostgreSQL pgvector Aurora Serverless v2 ACUs + $0.10/GB storage ACU compute; min 0 ACUs (as of May 2026 auto-pause GA) $0 (auto-pause) or ~$50/month at min ACU ~10M vectors at practical performance
Pinecone (3rd-party) Serverless: ~$0.033/GB/month $2/1M read units $0 (serverless tier) Enterprise scale

2.2 S3 Vectors: The New Default (GA December 2025)

S3 Vectors entered GA in December 2025 with pricing structured as follows:

Write (PUT) pricing: $0.20/GB of logical vector data uploaded. This is a one-time cost per ingestion, not recurring.

Storage pricing: $0.06/GB/month. At typical embedding dimensions:

  • 1024-dim float32 vectors = 4 KB/vector
  • 1M vectors = ~4 GB = $0.24/month storage
  • 10M vectors = ~40 GB = $2.40/month storage

Query pricing (post June 16 2026 reduction): Tiered by index size, with up to 80% reduction for large indexes (>10M vectors). Approximate rates before the June 2026 cut were $2.50/1M queries + $/TB processed. The June 16, 2026 update specifically reduced the data-processed tier for indexes >10M vectors, effectively making large consolidated indexes materially cheaper.

Effective per-1M-vector monthly cost:

1M vectors:
  Storage: 4 GB × $0.06 = $0.24/month
  Write amortized (12 months): 4 GB × $0.20 / 12 = $0.07/month
  Query at 100K queries/day: ~$7/month (varies by index size tier)
  Total: ~$7.30/month per 1M vectors

Compare to the AOSS floor: $350/month minimum, covering up to ~50M vectors at practical performance. S3 Vectors breaks even against AOSS at roughly the $350/month query-volume mark, typically around 4–5M queries/day through a single index.

What S3 Vectors does not support:

  • Complex BM25/keyword hybrid search (requires AOSS or a separate search layer)
  • Full-text aggregations and analytics (DSL queries)
  • Geospatial filtering
  • Index-level access patterns beyond vector similarity + metadata filtering

2.3 OpenSearch Serverless (AOSS): When Still the Right Choice

The $350/month AOSS floor is justified when any of the following apply:

  1. Hybrid search is required. AOSS supports neural_sparse and hybrid query types natively. Hybrid search typically improves recall@3 by 10–20% versus semantic-only, enabling k reduction. The quality gain can outweigh the cost premium for high-value query workloads.
  2. Complex metadata filtering at scale. AOSS DSL supports nested filters, range queries, geo-distance, and aggregations that S3 Vectors does not.
  3. Shared infrastructure with non-RAG workloads. If AOSS already runs a product search or log analytics layer, adding a vector collection shares OCU capacity — the incremental RAG cost is the additional OCU utilization, not the $350 floor.
  4. Compliance or data residency controls. AOSS supports VPC endpoints and fine-grained IAM at the collection level with more mature tooling than S3 Vectors.

AOSS cost reduction within the service: Enable binary vector embeddings (Titan V2 supports 1024-dim binary vectors, announced November 2024). Binary encoding reduces storage by 32× and improves query throughput. At scale, binary vectors cut the AOSS storage bill by ~97% with ~2–5% recall degradation — usually acceptable.

2.4 Aurora PostgreSQL pgvector

Aurora Serverless v2 pgvector is the correct choice when:

  • The knowledge base is small to medium (<5M vectors)
  • The application already runs Aurora as the primary database (no additional infrastructure)
  • True zero idle cost is needed (auto-pause, available without min_capacity floor as of May 28 2026)
  • SQL-native access to the vector store for BI or audit queries is required

At 1M vectors with Aurora Serverless v2 auto-paused at idle:

  • Storage: ~4 GB × $0.10 = $0.40/month
  • Compute: 0.5 ACU minimum when active at ~$0.12/ACU-hour; light RAG workloads might cost $5–15/month
  • Total: $6–20/month — comparable to S3 Vectors for small-scale deployments

Practical limitation: pgvector’s HNSW index degrades at >10M vectors in a single table without partitioning. Above that threshold, AOSS or S3 Vectors scales better.

2.5 Migration Path: OpenSearch Serverless → S3 Vectors

For teams currently paying the AOSS floor with no hybrid search requirement:

Migration sequence:
1. Audit existing KB: does any application call use hybrid_search=True or lexical filters?
   If yes → do not migrate until hybrid need is assessed.
   If no → proceed.

2. Create new S3 Vectors-backed Knowledge Base in Bedrock console.
   (Vector store selection is made at KB creation; cannot be changed in-place.)

3. Trigger full sync of the S3 data source to the new KB.
   Cost: embedding re-inference for all chunks at Titan V2 rates ($0.02/1M tokens).
   For 10M chunks at 150 tokens avg = 1.5B tokens = $30 one-time.

4. Run A/B retrieval quality test: compare Retrieve API responses 
   from old AOSS KB vs new S3 Vectors KB on representative query set.
   Acceptance threshold: recall@5 within 5% of baseline.

5. Cut over application ARN reference from AOSS KB to S3 Vectors KB.
   Update Terraform/CDK resource reference.

6. Keep AOSS KB for 7 days (billing continues during overlap).
   Delete AOSS collection after validation window.

Estimated migration cost for 10M-chunk KB:
  Re-embedding: ~$30
  7-day AOSS overlap: ~$85
  Total one-time migration: ~$115
  Monthly savings: $335–660/month → payback in < 1 week

Performance trade-off to document before migration:

  • S3 Vectors query latency: p50 20–50ms, p99 150–250ms (single index, warm path)
  • AOSS query latency: p50 10–30ms, p99 50–100ms
  • For interactive user-facing RAG, S3 Vectors p99 latency may be noticeable. For backend agentic pipelines, it is inconsequential.

3. Chunking Strategy: How Chunk Count Drives Total Cost

Chunking is a one-time architectural decision that permanently shapes three cost curves simultaneously: ingestion cost, storage cost, and — most importantly — LLM context cost per query.

3.1 Chunk Count Drives Vector Store Size

Every chunk becomes one vector. The relationship is direct:

vector_count = (corpus_token_count / (chunk_size - overlap_tokens)) × (chunk_size / (chunk_size - overlap_tokens))

Simplified: vector_count ≈ corpus_tokens / effective_stride
where effective_stride = chunk_size - overlap

Impact on storage (S3 Vectors, 1024-dim Titan V2 vectors = 4 KB/vector):

Chunk Size Overlap Effective Stride Vectors per 1M Corpus Tokens Storage Cost at S3 Vectors
256 tokens 0 256 ~3,906 vectors negligible
512 tokens 0 512 ~1,953 vectors negligible
512 tokens 100 (20%) 412 ~2,427 vectors negligible
1,024 tokens 0 1,024 ~977 vectors negligible
1,024 tokens 200 (20%) 824 ~1,214 vectors negligible

For a 1B-token corpus:

Chunk Size Overlap Approximate Vector Count S3 Vectors Storage AOSS Impact
256 tokens 50 ~4.9M vectors ~$1.17/month +negligible OCUs
512 tokens 100 ~2.4M vectors ~$0.58/month +negligible OCUs
1,024 tokens 200 ~1.2M vectors ~$0.29/month +negligible OCUs

Storage cost differences are small even at 1B tokens. The real cost impact of chunk size is on LLM context per query, not storage.

3.2 Chunk Size Impact on LLM Inference Cost

At k=5 retrieval:

Chunk Size Tokens per Retrieved Context (k=5) Monthly LLM Input Cost at 100K q/day vs 512-token baseline
256 tokens 1,280 + 600 overhead = 1,880 $16,920/month -$11,880 (41% cheaper)
512 tokens 2,560 + 600 overhead = 3,160 $28,440/month — baseline —
1,024 tokens 5,120 + 600 overhead = 5,720 $51,480/month +$23,040 (81% more expensive)

At k=3 retrieval:

Chunk Size Tokens per Retrieved Context (k=3) Monthly LLM Input Cost at 100K q/day vs k=5 512-token baseline
256 tokens 768 + 600 overhead = 1,368 $12,312/month -$16,128 (-57%)
512 tokens 1,536 + 600 overhead = 2,136 $19,224/month -$9,216 (-32%)
1,024 tokens 3,072 + 600 overhead = 3,672 $33,048/month +$4,608 (+16%)

The optimal cost-quality configuration is typically 512-token chunks at k=3 with a reranker, which costs $9,224/month LLM input versus $28,440/month for the unoptimized k=5 512-token default — a 67% reduction.

3.3 Chunking Strategy Quality vs. Cost

Bedrock Knowledge Bases supports five chunking strategies as of 2026:

Strategy Bedrock Support Chunk Count vs Fixed-Size Quality Signal Cost Impact
Fixed-size Native Baseline Low for heterogeneous docs Predictable, lowest ingestion cost
Semantic chunking Native (via BDA) +10–30% more chunks (boundary detection creates more granular splits) High for prose-heavy corpora +10–30% storage; better retrieval means k can drop
Hierarchical chunking Native 2× chunk count (parent + child stored) Highest for multi-level docs 2× storage; enables parent-doc retrieval pattern
Custom chunking (Lambda) Via Lambda transformer Depends on logic Best for structured/tabular data Additional Lambda execution cost (~negligible)
Default automatic Native Similar to fixed 300 tokens Moderate No tuning required

Hierarchical chunking cost note: Parent-child indexing stores both the small child chunk (used for retrieval) and the large parent chunk (returned to the LLM). This improves context quality for complex questions but doubles the vector count. At S3 Vectors pricing the storage cost increase is minor ($0.50/month per 1M extra vectors); the benefit is that k can often drop from 5 to 2 because each returned parent chunk is already larger and more coherent. Net LLM token cost typically decreases 20–40% despite the apparent overhead.

3.4 Overlap: The Hidden Vector Count Inflator

Overlap is the most commonly overlooked vector count driver. A 20% overlap on a 512-token chunk effectively reduces stride to 410 tokens, increasing vector count by 25% and raising storage proportionally. For S3 Vectors the additional cost is trivial; for AOSS with OCU-based autoscaling, 25% more vectors means 25% longer indexing time and marginally higher OCU utilization during ingestion.

Guidance: Overlap below 15% provides no measurable retrieval benefit for most corpora. Set overlap to 0 for well-structured documents (technical manuals, API docs, financial filings with clear section boundaries). Reserve 10–20% overlap for free-form prose where sentences bridge logical boundaries.


4. Retrieval Cost Optimization

4.1 The k Parameter: Highest-Leverage Retrieval Knob

The k (numberOfResults) parameter directly multiplies LLM input token cost. From the existing rag-vector-db-cost-optimization-2026 research note:

At Claude Sonnet 4 pricing ($3/1M input tokens), k=5 with 512-token chunks adds $0.0077/query in LLM input cost versus $0.0046/query at k=3 — a $0.0031/query differential. At 100K queries/day this is $310/day = $9,300/month saved with no other changes.

The breakeven question is whether k=3 delivers equivalent answer quality to k=5. For most production corpora with hybrid search or a reranker, it does. The correct engineering sequence is:

1. Measure baseline recall@3 and recall@5 on a representative eval set (≥200 queries).
2. If recall@3 / recall@5 ≥ 0.95 → use k=3 immediately.
3. If recall@3 / recall@5 < 0.90 → add reranker at k=20→3 pattern before reducing k.
4. If recall@3 / recall@5 0.90–0.95 → A/B test with 10% traffic to k=3 for 2 weeks.

Bedrock API configuration:

# Bedrock Knowledge Bases Retrieve API — k configuration
response = bedrock_agent_runtime.retrieve(
    knowledgeBaseId='KB_ID',
    retrievalQuery={'text': query},
    retrievalConfiguration={
        'vectorSearchConfiguration': {
            'numberOfResults': 3,  # was 5; saves ~40% LLM input tokens
            'overrideSearchType': 'SEMANTIC'  # or HYBRID when AOSS backend
        }
    }
)

4.2 Hybrid Search: Cost vs. Quality Trade-off

Hybrid search (vector similarity + BM25 keyword) is only available with the AOSS backend. It is not available with S3 Vectors.

Cost impact:

  • No additional AOSS OCU charge for hybrid queries versus pure vector queries
  • Adds 5–15ms query latency (BM25 + vector fusion step)
  • Enables k reduction: hybrid search typically achieves recall@3 equivalent to semantic-only recall@5, saving 40% on LLM context tokens

When hybrid search justifies the AOSS premium:

Monthly AOSS floor cost:          $350
Monthly S3 Vectors cost:          $15
AOSS premium:                     $335/month

k=5 semantic-only LLM cost:       $28,440/month
k=3 hybrid LLM cost:              $19,224/month
Monthly LLM savings from hybrid:  $9,216/month

Net monthly benefit of AOSS/hybrid vs S3 Vectors/semantic:
  $9,216 LLM savings - $335 AOSS premium = +$8,881/month
  → Hybrid with AOSS is the cost-optimal choice at this QPS.

Break-even QPS (where LLM savings > AOSS premium):
  $335 / ($0.0031/query × 30 days) = ~3,600 queries/day
  → Above ~3,600 q/day, AOSS + hybrid beats S3 Vectors + k=5 semantic on total cost.

4.3 Re-ranking: Cost of Adding a Re-ranker vs. Quality Improvement

Amazon Rerank 1.0 is the managed reranking option available natively in Bedrock Knowledge Bases. It costs $1.00/1,000 queries. Each query can rank up to 100 document chunks; larger result sets cost proportionally more.

Two-stage retrieval pattern:

Stage 1: Retrieve k_initial = 20 candidates from vector store (near-zero extra cost)
Stage 2: Rerank 20 candidates → surface top k_final = 3 to LLM

Reranker cost at 100K q/day = 3M/month × $1.00/1,000 = $3,000/month
LLM context savings (k=20→3): 20 chunks × 512 tokens vs 3 chunks × 512 tokens
  = (17 chunks × 512 tokens) × 3M queries × $3/1M = $78,336/month savings
Net monthly benefit: $78,336 - $3,000 = $75,336/month savings

This math is heavily in favor of adding reranking when operating at scale. The reranker cost is almost always recovered through LLM token reduction within the first 24 hours of traffic.

Note on Cohere Rerank 3.5 vs. Amazon Rerank 1.0: Cohere Rerank 3.5 via Bedrock costs ~$2.00/1,000 queries (2× Amazon Rerank) but demonstrates better multilingual and domain-specific performance. For English-only corpora, Amazon Rerank 1.0 is sufficient. Multilingual enterprises with European or APAC content should benchmark Cohere Rerank 3.5 before assuming Amazon Rerank suffices.

4.4 Metadata Filtering to Reduce Retrieved Context

Metadata filters applied at the vector store layer prevent irrelevant chunks from entering the LLM context. Unlike k reduction (which reduces chunk count), metadata filtering reduces irrelevant-chunk contamination without necessarily reducing k.

In practice, metadata filters enable k reduction by improving precision: a k=3 retrieval against a metadata-filtered sub-corpus is often more accurate than k=5 on the full corpus.

Common filter patterns:

# Bedrock KB retrieve with metadata filter
retrievalConfiguration={
    'vectorSearchConfiguration': {
        'numberOfResults': 3,
        'filter': {
            'andAll': [
                {'equals': {'key': 'department', 'value': 'finance'}},
                {'greaterThanOrEquals': {'key': 'doc_version', 'value': 2}},
                {'in': {'key': 'doc_type', 'value': ['policy', 'procedure']}}
            ]
        }
    }
}

Metadata is stored alongside vectors; filtering is applied before similarity scoring in the vector store. It does not add token cost, only a marginal index scan overhead.

Metadata design for cost optimization:

  • department / team → limits search space to relevant content, improves precision
  • doc_version → filter out superseded versions to prevent outdated chunk retrieval
  • language → route to appropriate embedding model output if multilingual KB
  • sensitivity_tier → enforce data governance without application-layer logic

4.5 Query Caching

Two caching tiers exist, with different cost profiles:

Exact-match cache (application layer):

  • Implementation: ElastiCache Redis or DynamoDB with query hash as key
  • Cost: ~$13–50/month for a t4g.small ElastiCache instance
  • Cache hit rate: typically 10–30% for internal enterprise RAG (employee-facing tools have repetitive questions)
  • At 100K q/day with 20% hit rate: 20K queries served from cache at ~$0 vs. full pipeline cost = 20K × $0.0077 LLM cost saved = $154/day = $4,620/month savings

Semantic cache (GPTCache / custom embedding similarity layer):

  • Implementation: store query embedding + response; on new query, compute cosine similarity to cache index; threshold typically 0.95
  • Additional infrastructure cost: ~$30–100/month for the semantic index + embedding compute
  • Cache hit rate: 30–50% for enterprise FAQ-style workloads (employees asking policy questions)
  • Break-even: semantic cache pays back vs. exact-match within 2–3 weeks at moderate QPS

Bedrock-native caching: As of June 2026, Bedrock does not offer a native query-level cache for Knowledge Bases. Prompt caching (Bedrock’s existing feature) applies to the LLM call context and is relevant for long system prompts but does not cache the retrieval step. Application-layer caching remains the only option.


5. Ingestion Pipeline Cost Governance

5.1 Incremental Sync vs. Full Re-index

Bedrock Knowledge Bases supports incremental sync: only new or modified source documents are re-embedded on each sync run. This is controlled by the dataSyncMode parameter (default: INCREMENTAL).

Cost comparison at 1B-token corpus with 5% weekly document churn:

Mode Weekly Embedding Cost Weekly Parsing Cost (BDA) Monthly Total
Incremental (5% churn) 50M tokens × $0.020/1M = $1.00 200K pages × $0.010 = $2,000 ~$8,004
Full re-index 1B tokens × $0.020/1M = $20 4M pages × $0.010 = $40,000 ~$160,080

At this scale, full re-index costs 20× more than incremental. The most common accidental trigger for unintended full re-index is changing the chunking configuration on an existing Knowledge Base. This forces re-embedding of all chunks because the chunk boundaries change. Treat chunking configuration as immutable after the KB goes to production.

5.2 Flex Tier for Ingestion Cost Savings

Bedrock Flex inference tier (launched November 2025) provides ~50% discount on token processing for workloads that can tolerate higher latency and best-effort delivery. Ingestion is the canonical use case: it is asynchronous, non-interactive, and throughput-tolerant.

How to route ingestion to Flex tier: Bedrock Knowledge Bases does not yet expose an explicit inferenceTier parameter on the sync job API. The practical approach as of June 2026 is to use a custom embedding pipeline with explicit model invocation:

import boto3

bedrock = boto3.client('bedrock-runtime')

# Route embedding calls to Flex tier via inference profile ARN
# Flex tier ARN pattern for Titan Embeddings V2 (us-east-1):
FLEX_EMBEDDING_ARN = (
    'arn:aws:bedrock:us-east-1::foundation-model/'
    'amazon.titan-embed-text-v2:0'
    # Note: Flex routing via inference profile requires profile creation
    # with 'inferenceType': 'FLEX' — verify current API support
)

response = bedrock.invoke_model(
    modelId=FLEX_EMBEDDING_ARN,
    body=json.dumps({'inputText': chunk_text, 'dimensions': 1024})
)

Important caveat: As of June 2026, the Flex tier discount applies primarily to text generation models. Verify that embedding model invocations are included in Flex tier pricing before building this into production pipelines. The expected savings if it applies: 50% reduction on the embedding inference cost during ingestion.

Alternative — schedule ingestion during off-peak hours: Even without explicit Flex routing, scheduling ingestion jobs between 02:00–06:00 UTC reduces contention with interactive workloads and allows concurrency throttles to run at higher throughput, reducing clock-time per job.

5.3 Monitoring Ingestion Job Cost in CUR

AWS CUR (Cost and Usage Report) records Bedrock operations with the following relevant fields:

product/ProductCode:        AmazonBedrock
line_item_operation:        IngestDocuments  ← marks KB ingestion runs
line_item_usage_type:       USE1-TitanEmbeddingV2-Text-input-tokens
                            USE1-BedrockDataAutomation-Pages
resource_tags/user:kb_id:   <your-knowledge-base-id>  (if tagged)

To see ingestion cost broken out from query cost in Athena:

-- Monthly ingestion cost vs query cost for all Knowledge Bases
SELECT
  line_item_operation,
  line_item_usage_type,
  DATE_FORMAT(line_item_usage_start_date, '%Y-%m') AS billing_month,
  SUM(line_item_unblended_cost) AS total_cost_usd,
  SUM(line_item_usage_amount) AS total_usage
FROM
  "cur_database"."cur_table"
WHERE
  product_product_code = 'AmazonBedrock'
  AND (
    line_item_operation IN ('IngestDocuments', 'Retrieve', 'RetrieveAndGenerate')
    OR line_item_usage_type LIKE '%TitanEmbedding%'
    OR line_item_usage_type LIKE '%BedrockDataAutomation%'
    OR line_item_usage_type LIKE '%AmazonRerank%'
  )
GROUP BY 1, 2, 3
ORDER BY billing_month DESC, total_cost_usd DESC;

5.4 Deduplication Before Ingestion

Duplicate or near-duplicate documents create redundant vectors that inflate storage, increase embedding cost on every re-ingest, and degrade retrieval (duplicate results consume k slots, reducing effective diversity).

Deduplication approaches before ingestion to S3:

import hashlib
import boto3

s3 = boto3.client('s3')

def get_doc_hash(content: bytes) -> str:
    return hashlib.sha256(content).hexdigest()

def deduplicate_before_sync(bucket: str, prefix: str) -> dict:
    """
    Identify exact duplicates in S3 prefix before KB sync.
    Returns: {hash: [s3_keys_to_delete]}
    """
    paginator = s3.get_paginator('list_objects_v2')
    hash_map = {}
    
    for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
        for obj in page.get('Contents', []):
            # Use ETag as proxy for content hash (MD5 for non-multipart uploads)
            etag = obj['ETag'].strip('"')
            key = obj['Key']
            if etag in hash_map:
                hash_map[etag].append(key)
            else:
                hash_map[etag] = [key]
    
    duplicates = {h: keys for h, keys in hash_map.items() if len(keys) > 1}
    return duplicates

# For near-duplicate detection (e.g., policy v1.0 vs v1.1 with minor edits),
# use MinHash LSH on extracted text before upload.
# Libraries: datasketch, simhash-py

For large document corpora, near-duplicate detection using MinHash or SimHash before ingestion typically removes 5–15% of enterprise document sets (policy revisions, boilerplate-heavy templates, duplicate FAQ entries). At 10% deduplication rate on a 1B-token corpus, this saves $2 on initial embedding and $2/week on incremental sync — modest at the token level, but the retrieval quality improvement is significant.


6. Multi-Tenant RAG Cost Attribution

6.1 Architecture Decision: Separate KB per Tenant vs. Shared KB with Metadata Filtering

Model Cost Isolation Complexity
Separate KB per tenant Vector store floor per tenant; at AOSS: $350 × N tenants Strong data isolation; no cross-contamination risk High: N KB management surfaces
Shared KB, metadata filter One vector store floor; tenantId in metadata Logical isolation only; misconfigured filter = data leak Lower: single KB to manage
Shared KB, S3 Vectors One S3 Vectors bucket; per-query cost only Logical isolation via metadata Lowest cost model

Recommendation for most multi-tenant SaaS platforms:

  • <50 tenants with strict data isolation requirements → separate KB per tenant on S3 Vectors. Cost: ~$7–15/tenant/month (storage + queries).
  • 50–500 tenants, data isolation via logical filtering acceptable → shared KB with mandatory tenantId metadata filter on all Retrieve calls. Enforce at the application layer; audit filter injection with unit tests.
  • 500+ tenants → shared KB on S3 Vectors + application-layer tenant routing. Cost is dominated by query volume, not infrastructure floor.

6.2 Application Inference Profiles for Per-Tenant Retrieval Attribution

Application Inference Profiles (AIPs) are the official mechanism for per-workload cost attribution in Bedrock. Each AIP is a named resource wrapping a specific model ID with attached cost allocation tags.

Creating an AIP per tenant:

import boto3

bedrock = boto3.client('bedrock')

def create_tenant_inference_profile(tenant_id: str, model_id: str) -> str:
    """
    Create an Application Inference Profile for a tenant.
    Returns the profile ARN for use in Retrieve calls.
    """
    response = bedrock.create_inference_profile(
        inferenceProfileName=f'tenant-{tenant_id}-embedding',
        description=f'Embedding profile for tenant {tenant_id}',
        modelSource={
            'copyFrom': f'arn:aws:bedrock:us-east-1::foundation-model/{model_id}'
        },
        tags=[
            {'key': 'tenant_id', 'value': tenant_id},
            {'key': 'cost_center', 'value': f'tenant-{tenant_id}'},
            {'key': 'environment', 'value': 'production'}
        ]
    )
    return response['inferenceProfileArn']

# Use AIP ARN in RetrieveAndGenerate call
def query_kb_for_tenant(
    kb_id: str, 
    query: str, 
    tenant_id: str,
    aip_arn: str
) -> dict:
    client = boto3.client('bedrock-agent-runtime')
    return client.retrieve_and_generate(
        input={'text': query},
        retrieveAndGenerateConfiguration={
            'type': 'KNOWLEDGE_BASE',
            'knowledgeBaseConfiguration': {
                'knowledgeBaseId': kb_id,
                'modelArn': aip_arn,  # AIP ARN carries tenant tags into CUR
                'retrievalConfiguration': {
                    'vectorSearchConfiguration': {
                        'numberOfResults': 3,
                        'filter': {
                            'equals': {'key': 'tenantId', 'value': tenant_id}
                        }
                    }
                }
            }
        }
    )

Important: AIP tags flow into CUR 2.0 and Cost Explorer within 24–48 hours of activation. IAM principal-level cost attribution (available April 2026) provides an alternative: create a separate IAM role per tenant and invoke Bedrock under that role — the IAM principal appears in CUR 2.0’s identity_principal_arn column.

6.3 IAM Principal Tagging for Lightweight Attribution (April 2026)

As of April 2026, Bedrock supports cost allocation by IAM user and role in CUR 2.0. This enables per-tenant attribution without creating a separate AIP per tenant:

# Pattern: STS AssumeRole into tenant-specific role before KB query
import boto3

def get_tenant_bedrock_client(tenant_id: str, base_role_arn: str):
    """
    Assume a tenant-specific IAM role to get Bedrock cost attribution.
    Assumes role naming convention: arn:aws:iam::ACCT:role/bedrock-tenant-<id>
    """
    sts = boto3.client('sts')
    tenant_role_arn = f'arn:aws:iam::123456789012:role/bedrock-tenant-{tenant_id}'
    
    assumed = sts.assume_role(
        RoleArn=tenant_role_arn,
        RoleSessionName=f'tenant-{tenant_id}-rag',
        DurationSeconds=3600
    )
    creds = assumed['Credentials']
    
    return boto3.client(
        'bedrock-agent-runtime',
        aws_access_key_id=creds['AccessKeyId'],
        aws_secret_access_key=creds['SecretAccessKey'],
        aws_session_token=creds['SessionToken']
    )

This approach is operationally simpler than AIPs for pure attribution needs but requires one IAM role per tenant. Both approaches support chargeback via CUR 2.0 queries.

6.4 LiteLLM Virtual Key Routing for Multi-Tenant Attribution

For teams using LiteLLM as an AI gateway in front of Bedrock Knowledge Bases, virtual keys per tenant enable cost attribution at the gateway level:

# litellm_config.yaml
router_settings:
  routing_strategy: latency-based-routing

model_list:
  - model_name: bedrock-kb-proxy
    litellm_params:
      model: bedrock/amazon.titan-embed-text-v2:0
      aws_region_name: us-east-1

# Virtual key config (via LiteLLM admin API):
# POST /key/generate
# {"metadata": {"tenant_id": "acme", "budget_id": "acme-rag"}, "max_budget": 100}

LiteLLM’s spend tracking aggregates per-virtual-key and exposes a /spend endpoint for tenant-level dashboards. Note that LiteLLM tracks token spend on the LLM call but does not see vector store costs — those still require CUR.

6.5 Chargeback Query: Tenant-Level Monthly KB Spend

-- CUR 2.0 Athena query: per-tenant Bedrock KB spend using AIP tags
SELECT
  resource_tags_user_tenant_id AS tenant_id,
  line_item_operation,
  DATE_FORMAT(line_item_usage_start_date, '%Y-%m') AS billing_month,
  SUM(line_item_unblended_cost) AS cost_usd,
  SUM(line_item_usage_amount) AS usage_units,
  line_item_usage_type
FROM
  "cur_database"."cur_table"
WHERE
  product_product_code = 'AmazonBedrock'
  AND resource_tags_user_tenant_id IS NOT NULL
  AND year = '2026'
  AND month IN ('05', '06')
GROUP BY 1, 2, 3, 6
ORDER BY billing_month DESC, cost_usd DESC;

-- Supplement with IAM principal attribution (April 2026+):
SELECT
  identity_principal_arn,
  REGEXP_EXTRACT(identity_principal_arn, 'bedrock-tenant-([^/]+)', 1) AS tenant_id,
  SUM(line_item_unblended_cost) AS cost_usd,
  DATE_FORMAT(line_item_usage_start_date, '%Y-%m') AS billing_month
FROM
  "cur_database"."cur_table"
WHERE
  product_product_code = 'AmazonBedrock'
  AND identity_principal_arn LIKE '%bedrock-tenant-%'
GROUP BY 1, 2, 4
ORDER BY billing_month DESC, cost_usd DESC;

7. CUR Representation for Bedrock Knowledge Bases

7.1 Known line_item_usage_type Patterns

AWS CUR for Bedrock Knowledge Bases does not produce one row per KB query. The finest granularity is per usage type per hour (CUR 2.0) or per day (classic CUR). All Knowledge Base activity aggregates under product/ProductCode = AmazonBedrock.

Usage type patterns observed in production:

Component line_item_operation line_item_usage_type (example, us-east-1) What it measures
KB ingestion — embedding IngestDocuments USE1-TitanEmbeddingV2-Text-input-tokens Tokens embedded during sync job
KB ingestion — parsing IngestDocuments USE1-BedrockDataAutomation-Pages Pages parsed via BDA
KB query — embedding Retrieve or RetrieveAndGenerate USE1-TitanEmbeddingV2-Text-input-tokens Query embedding tokens
KB reranking RetrieveAndGenerate USE1-AmazonRerank-1-0-queries Reranking queries
LLM call (RetrieveAndGenerate) InvokeModel USE1-Claude3-5-Sonnet-InputTokens etc. Full prompt + context tokens
OpenSearch Serverless (if backend) n/a (separate service) AmazonOpenSearchServerless product code OCU hours; separate from Bedrock
S3 Vectors (if backend) n/a (S3 product code) USE1-Vectors-Storage-ByteHrs etc. Storage and query fees

Important architecture note: OpenSearch Serverless and S3 Vectors appear under their own AWS product codes, not under AmazonBedrock. A complete KB cost picture requires joining across at minimum three product codes: AmazonBedrock, AmazonOpenSearchServerless or AmazonS3, and any parser models.

7.2 Athena Query: Full Monthly KB Cost Breakdown by Component

-- Step 1: Bedrock-side KB costs (embedding + reranking + LLM)
WITH bedrock_kb_costs AS (
  SELECT
    DATE_FORMAT(line_item_usage_start_date, '%Y-%m') AS billing_month,
    CASE
      WHEN line_item_operation = 'IngestDocuments' THEN 'ingestion'
      WHEN line_item_operation IN ('Retrieve', 'RetrieveAndGenerate') 
           AND line_item_usage_type LIKE '%Embedding%' THEN 'query_embedding'
      WHEN line_item_usage_type LIKE '%AmazonRerank%' THEN 'reranking'
      WHEN line_item_operation = 'InvokeModel' 
           AND line_item_usage_type LIKE '%-InputTokens' THEN 'llm_input'
      WHEN line_item_operation = 'InvokeModel' 
           AND line_item_usage_type LIKE '%-OutputTokens' THEN 'llm_output'
      ELSE 'other_bedrock'
    END AS cost_component,
    SUM(line_item_unblended_cost) AS cost_usd,
    SUM(line_item_usage_amount) AS usage_amount,
    line_item_usage_type
  FROM "cur_database"."cur_table"
  WHERE
    product_product_code = 'AmazonBedrock'
    AND year = '2026'
    AND month = '06'
  GROUP BY 1, 2, 5
),

-- Step 2: S3 Vectors costs (separate product code)
s3_vector_costs AS (
  SELECT
    DATE_FORMAT(line_item_usage_start_date, '%Y-%m') AS billing_month,
    'vector_store_s3' AS cost_component,
    SUM(line_item_unblended_cost) AS cost_usd,
    SUM(line_item_usage_amount) AS usage_amount,
    line_item_usage_type
  FROM "cur_database"."cur_table"
  WHERE
    product_product_code = 'AmazonS3'
    AND line_item_usage_type LIKE '%Vectors%'
    AND year = '2026'
    AND month = '06'
  GROUP BY 1, 2, 5
),

-- Step 3: OpenSearch Serverless costs (if applicable)
aoss_costs AS (
  SELECT
    DATE_FORMAT(line_item_usage_start_date, '%Y-%m') AS billing_month,
    'vector_store_aoss' AS cost_component,
    SUM(line_item_unblended_cost) AS cost_usd,
    SUM(line_item_usage_amount) AS usage_amount,
    line_item_usage_type
  FROM "cur_database"."cur_table"
  WHERE
    product_product_code = 'AmazonOpenSearchServerless'
    AND year = '2026'
    AND month = '06'
  GROUP BY 1, 2, 5
)

SELECT
  billing_month,
  cost_component,
  ROUND(SUM(cost_usd), 4) AS cost_usd,
  SUM(usage_amount) AS usage_amount,
  line_item_usage_type
FROM (
  SELECT * FROM bedrock_kb_costs
  UNION ALL SELECT * FROM s3_vector_costs
  UNION ALL SELECT * FROM aoss_costs
)
GROUP BY 1, 2, 5
ORDER BY billing_month DESC, cost_usd DESC;

7.3 Tagging Strategy for Isolating KB Costs

Without tags, all Bedrock cost in an account aggregates into a single AmazonBedrock line. Recommended tag structure:

aws:bedrock:knowledgeBaseId = kb-XXXXXXXXXXXXXXXX  (auto-applied by service)
user:application = "rag-{application-name}"
user:environment = "production" | "staging"
user:team = "{owning-team}"
user:cost_center = "{budget-code}"

The aws:bedrock:knowledgeBaseId tag is applied automatically by the service since 2025 and appears in CUR 2.0 under resource_tags_aws_bedrock_knowledge_base_id. This is the primary key for isolating per-KB costs without additional tagging effort.


8. Cost vs. Quality Decision Framework

8.1 Bedrock Knowledge Bases vs. DIY RAG: Build-vs-Buy Analysis

Dimension Bedrock KB (Managed) DIY RAG (LangChain / LlamaIndex + OpenSearch/pgvector)
Time to production Days (S3 source + console config) Weeks to months (chunking, vector store, retrieval logic, monitoring)
Engineering cost ~0.25 FTE ongoing 1–3 FTE build + 0.5 FTE ongoing
Chunking flexibility 5 strategies; semantic + hierarchical via BDA Unlimited; custom Lambda transformers, late chunking, multi-modal
Retrieval patterns Single-hop; agentic retrieval via Bedrock Agents Multi-hop, graph RAG, HyDE, step-back prompting — any pattern
Filtering capability Metadata filtering (flat key-value) Full SQL predicates (pgvector), DSL (OpenSearch)
Observability CUR aggregates; CloudWatch logs; no native trace-per-query cost Full instrumentation possible; LangSmith, Phoenix, custom OTel
Lock-in AWS-only; embedding model tied to KB at creation Portable; swap embedding models, vector stores, retrieval strategies
Monthly cost floor ~$7–15 (S3 Vectors) or ~$350 (AOSS) + LLM $0 (pure Serverless pgvector) + LLM; potentially $0 idle with Aurora auto-pause

Bedrock KB is the right choice when:

  • Time-to-value is the primary constraint
  • The team has <1 dedicated ML/platform engineer
  • The corpus is document-oriented (PDFs, Office files, web pages) with no graph structure
  • Single-hop retrieval covers 90%+ of query patterns
  • Data is already in S3 within the same AWS account/region
  • Single embedding model and chunking strategy are sufficient

DIY RAG is the right choice when:

  • Multi-hop retrieval, graph traversal, or HyDE is required
  • Per-query cost attribution and trace-level observability are mandatory
  • The corpus requires custom preprocessing (financial tables, code, structured databases)
  • Multi-cloud or hybrid deployment is a requirement
  • The embedding model needs to change without re-creating the entire KB
  • Filtering requirements exceed flat metadata (e.g., range queries, geospatial, joins)

8.2 Bedrock KB Limitations That Force DIY

Hard blockers (cannot be worked around within Bedrock KB):

  1. Multi-hop retrieval: Bedrock KB executes a single retrieval step. Iterative or recursive retrieval (retrieve → synthesize → re-retrieve) requires Bedrock Agents, which adds latency and orchestration cost, or a custom multi-hop loop outside the KB API.

  2. Graph RAG: Knowledge graph extraction and traversal are not supported. For enterprise use cases with complex entity relationships (supply chains, org hierarchies, regulatory networks), AWS GraphRAG evaluation toolkit exists but requires custom implementation.

  3. Embedding model lock-in: The embedding model is selected at KB creation and cannot be changed. Switching from Titan V2 to Cohere Embed 3 requires creating a new KB and re-embedding the entire corpus. At 1B tokens, that is $20 in embedding cost and operational downtime.

  4. Custom retrieval logic: BM25 scoring weights, custom similarity thresholds, and retrieval fusion algorithms cannot be tuned within Bedrock KB. The retrieval black box is appropriate for standard workloads but limits precision tuning for specialized domains.

  5. Real-time ingestion: Bedrock KB sync jobs are asynchronous and typically complete in minutes to hours. Use cases requiring sub-minute freshness (live pricing, real-time alerts) require a custom streaming ingestion path outside KB.

8.3 Total Cost of Ownership Comparison

Assumptions: production enterprise RAG, 1B-token corpus, 100K queries/day, 2-engineer team.

Bedrock Knowledge Bases (S3 Vectors + Titan V2 + Claude Sonnet, k=3 + reranker):
  Vector store (S3 Vectors, ~2.4M vectors): $15/month
  Query embedding (3M/month × 50 tokens):  $3/month
  Reranking (3M × $1/1K):                  $3,000/month
  LLM input (k=3 × 512 + 600 overhead):    $19,224/month
  LLM output (200 tokens × 3M):            $9,000/month
  Engineering (0.25 FTE × $200K/yr / 12):  $4,167/month
  Total:                                    ~$35,409/month

DIY RAG (Aurora pgvector + Titan V2 + Claude Sonnet + LangChain, k=3 + Cohere Rerank):
  Vector store (Aurora pgvector auto-pause): $20/month
  Query embedding (same):                   $3/month
  Cohere Rerank 3.5 (3M × $2/1K):          $6,000/month
  LLM input (same):                         $19,224/month
  LLM output (same):                        $9,000/month
  Engineering (1.5 FTE × $200K/yr / 12):   $25,000/month
  Total:                                    ~$59,247/month

Winner at this scale: Bedrock KB — managed saves ~$23,838/month over DIY.

DIY breaks even at the point where engineering leverage reverses:
  Break-even: when DIY saves >$23,838/month on API costs vs. Bedrock KB.
  This requires ~3× more efficient retrieval, which is achievable only with
  highly specialized domain tuning — typically at $10M+/year AI infrastructure spend.

8.4 Decision Tree

Start: Does the corpus require multi-hop, graph, or streaming ingestion?
├─ Yes → DIY RAG required
└─ No → Does the team have >2 dedicated ML engineers?
         ├─ Yes → Evaluate DIY for cost optimization at high scale
         └─ No → Use Bedrock Knowledge Bases
                  └─ Which vector backend?
                     ├─ Need hybrid search or complex filtering? → OpenSearch Serverless
                     ├─ <10M vectors, need SQL access? → Aurora pgvector
                     └─ Default: S3 Vectors (GA December 2025)
                        └─ Retrieval config:
                           ├─ QPS > 3,600/day AND AOSS available? → Hybrid search (see §4.2)
                           └─ All else: k=3 + Amazon Rerank 1.0

9. Summary: Priority Cost Optimization Actions

Ranked by expected monthly savings impact for a 100K q/day enterprise RAG deployment:

Priority Action Monthly Savings Effort
1 Migrate AOSS → S3 Vectors (if no hybrid search requirement) $335–660/month (vector store floor) Medium (one-time KB re-creation, $115 migration cost)
2 Add Amazon Rerank 1.0 + reduce k from 5 to 3 ~$6,216/month net ($9,216 LLM savings - $3,000 reranker cost) Low (config change + eval)
3 Enable hybrid search on AOSS KB + reduce k from 5 to 3 ~$8,881/month net Low (config change + eval)
4 Implement application-layer exact-match cache (20% hit rate) ~$4,620/month Low–Medium (Redis)
5 Reduce chunk size from 1,024 to 512 tokens (for prose docs) ~$18,432/month vs 1,024-chunk baseline at k=5 High (KB re-creation required)
6 Switch from k=5 to k=3 alone (no reranker) ~$9,300/month (if quality is maintained) Low (config change + eval)
7 Schedule ingestion incrementally; avoid full re-index 80–95% ingestion cost reduction vs. weekly full re-index Low (sync mode config)
8 Deduplicate corpus before ingestion 5–15% on embedding + storage Medium (preprocessing pipeline)

The combined effect of items 1 + 2 + 4 for a team currently running AOSS with k=5 and no cache is approximately $10,171–10,496/month in savings — achievable in under two weeks of engineering effort.


Related notes: rag-vector-db-cost-optimization-2026.md (vector store cost anatomy and semantic caching), aws-bedrock-cost-attribution-retroactive-2026.md (CUR retroactive tagging), agentic-ai-cost-explosion-controls-2026.md (agentic loop cost governance)