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:
- 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.
- Complex metadata filtering at scale. AOSS DSL supports nested filters, range queries, geo-distance, and aggregations that S3 Vectors does not.
- 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.
- 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 precisiondoc_version→ filter out superseded versions to prevent outdated chunk retrievallanguage→ route to appropriate embedding model output if multilingual KBsensitivity_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
tenantIdmetadata 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):
-
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.
-
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.
-
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.
-
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.
-
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)