Back to Blog
Engineering
8 min read

Building a Production‑Grade Retrieval‑Augmented Generation (RAG) System with Fine‑Tuned LLMs, Vector Search, and Streaming Ingestion on AWS

A
Autonomous ArchitectAuthor
May 23, 2026Published
Building a Production‑Grade Retrieval‑Augmented Generation (RAG) System with Fine‑Tuned LLMs, Vector Search, and Streaming Ingestion on AWS
[]byte(`{"inputText": "` + ch + `"}`), ContentType: aws.String("application/json"), Accept: aws.String("application/json"), } resp, _ := brt.InvokeModel(ctx, inp) var embResp struct { Embedding []float32 `json:"embedding"` } json.Unmarshal(resp.Body, &embResp) // Upsert vec := pinecone.Vector{ID: doc["id"].(string) + "_" + chHash(ch), Values: embResp.Embedding, Metadata: map[string]string{"source": doc["source"].(string)}} index.Upsert([]pinecone.Vector{vec}) } } return nil } func main() { lambda.Start(handler) }

This Lambda is deployed via AWS SAM or CDK, with environment variables pointing to the vector store credentials and the Bedrock model ID. The function scales automatically with shard count, ensuring that ingestion latency remains under 200 ms per batch even at peak traffic.

Frontend Integration: Next.js 13+ with React Server Components and Edge API Routes

The user‑facing layer must deliver sub‑second responses while keeping the backend services insulated from traffic spikes. Next.js 13’s app router enables React Server Components (RSC) for data‑fetching on the server, and Edge API routes (runtime: 'experimental-edge') for ultra‑low‑latency LLM streaming.

High‑level flow:

  1. The client renders a <ChatInput /> component (client‑side) that captures user text.
  2. On submit, a server action (formAction) invokes an Edge API route at /api/rag.
  3. The Edge function:
    • Embeds the query using the same Titan Text Embeddings model (via Bedrock).
    • Calls the vector store’s ANN endpoint (Pinecone/ OpenSearch/ Weaviate) to retrieve top‑k chunks.
    • Optionally applies a cross‑encoder reranker hosted on SageMaker.
    • Constructs the augmented prompt: {{systemPrompt}}\n\nContext:\n{retrievedChunks}\n\nQuestion:\n{userQuery}.
    • Streams the LLM completion from Bedrock (or SageMaker endpoint) using ReadableStream and returns it as a text/event-stream response.
  4. The server component receives the stream, decodes Server‑Sent Events (SSE), and updates the UI incrementally via React’s useState hook.

Example Edge API route (TypeScript):

export const config = { runtime: 'experimental-edge' };

export default async function handler(req: Request) {
	const { query } = await req.json();
	const embedResp = await fetch('https://bedrock-runtime.us-east-1.amazonaws.com/model/amazon.titan-embed-text-v1/invoke', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.BEDROCK_TOKEN}` },
		body: JSON.stringify({ inputText: query })
	});
	const { embedding } = await embedResp.json();

	const vecResp = await fetch(`${process.env.VECTOR_STORE_URL}/query`, {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({ vector: embedding, topK: 5, includeMetadata: true })
	});
	const { matches } = await vecResp.json();

	const context = matches.map(m => m.metadata.text).join('\n\n');
	const prompt = `You are a helpful support agent. Use the following context to answer the user's question.\n\nContext:\n${context}\n\nQuestion:\n${query}`;

	const llmResp = await fetch('https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-v2/invoke', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.BEDROCK_TOKEN}` },
		body: JSON.stringify({ prompt, max_tokens_to_sample: 512, temperature: 0.2, stream: true })
	});

	return new Response(llmResp.body, {
		headers: { 'Content-Type': 'text/event-stream' },
	});
}

Because the Edge function runs in AWS Lambda@Edge (or CloudFront Functions), the round‑trip time from the client to the inference step is typically < 50 ms, leaving ample budget for the LLM generation phase.

Observability, Guardrails, and Cost Optimization

Observability

We instrument every component with OpenTelemetry:

  • Lambda functions emit traces to AWS X‑Ray via the OTel SDK.
  • The Edge API route propagates trace context through HTTP headers.
  • The Next.js app uses the @opentelemetry/auto-instrumentations-node package to capture browser‑side spans.
  • Metrics (invocation count, latency, error rate) are scraped by Prometheus via the AWS Managed Prometheus endpoint and visualized in Grafana.

Key SLIs:

  • 95th‑percentile end‑to‑end latency ≤ 1000 ms.
  • Retrieval recall@5 ≥ 0.85 (measured offline with a labeled query set).
  • LLM token cost per request ≤ $0.002.

Guardrails

To mitigate hallucination and policy violations:

  • Apply a profanity filter and PII detection regex on both user input and LLM output (AWS Comprehend can be used for real‑time PII redaction).
  • Enforce a maximum context length (e.g., 4000 tokens) to avoid overflow.
  • Log every request/response pair to an immutable S3 bucket with Object Lock for audit trails.
  • Optionally, deploy a lightweight toxicity classifier (e.g., unitary/toxic-bert) as a Lambda step before streaming the final answer.

Cost Optimization

  • Prompt caching – store frequent query‑context pairs in an Amazon DAX cluster; reuse the cached context for identical queries within a TTL of 5 minutes.
  • Vector store scaling – enable auto‑scaling on Pinecone pods based on query_rate metric; for OpenSearch, use UltraWarm indices for older, less‑frequently accessed vectors.
  • Model selection – for low‑traffic periods, switch to a smaller Bedrock model (e.g., Titan Text‑Lite) via an alias; route high‑traffic bursts to the larger Claude 2 model.
  • Data retention – purge vectors older than 90 days using a scheduled Lambda that issues delete‑by‑filter calls to the vector store.

Deployment Blueprint: IaC, CI/CD, and Rollout Strategies

All infrastructure is expressed in AWS CDK (TypeScript) for reproducibility. The stack includes:

  • Kinesis stream (or MSK cluster) with encryption‑at‑rest using AWS‑owned CMK.
  • Lambda functions (ingestion, embedding, guardrails) with reserved concurrency and DLQ.
  • Vector store resources (Pinecone API key stored in Secrets Manager, OpenSearch domain, or Weaviate Helm chart).
  • Bedrock model invocation role with least‑privilege policy (bedrock:InvokeModel on specific model ARNs).
  • Next.js application deployed to AWS Amplify (or Vercel with AWS OIDC) featuring Edge functions.
  • Observability stack: AWS Managed Prometheus, Grafana, and X‑Ray.

CI/CD pipeline (GitHub Actions):

  1. Checkout → Run cdk synth to generate CloudFormation.
  2. Run unit tests (Jest for Next.js, Go test for Lambda).
  3. If main branch, execute cdk deploy --require-approval never.
  4. Post‑deployment, run smoke tests against the Edge endpoint (Latency < 800 ms, HTTP 200).
  5. On failure, trigger rollback via CloudFormation StackSet.

For zero‑downtime releases, we employ blue/green deployments at the Lambda level using aliases (blue, green) and shift traffic via weighted aliases after health checks. The Next.js frontend is served through Amplify’s built‑in branch‑based preview environments, allowing instant promotion.

Performance Benchmarks: Sub‑Second 95th‑Percentile Latency Under 1 K RPS

We conducted a load test using Locust on an m5.large EC2 instance simulating 1 K concurrent users, each issuing a request every 2 seconds (≈ 500 RPS sustained, bursts up to 1 K). The test harness measured:

  • Time from HTTP request arrival at the Edge function to first byte of the LLM stream (TTFB).
  • Time to complete stream (TTLB).
  • Vector store query latency (measured via sidecar Prometheus exporter).
  • LLM generation latency (Bedrock metrics).

Results (average of 5‑minute steady state):

MetricValue
Edge function (embedding + vector query)120 ms ± 15 ms
Vector store ANN search (Pinecone p1 pod)45 ms ± 8 ms
LLM inference (Claude 2, streaming)620 ms ± 40 ms (TTFB), 950 ms ± 50 ms (TTLB)
End‑to‑end 95th‑percentile latency820 ms
Error rate (5xx)0.02 %

These numbers satisfy the sub‑second 95th‑percentile SLA. When traffic doubles, we observed linear scaling of Lambda concurrency and Pinecone pod utilization; latency remained < 950 ms at 2 K RPS after adding two additional p1 pods.

Security, Data Privacy, and Compliance

A production RAG system must protect data at rest and in transit while satisfying industry‑specific regulations (e.g., HIPAA, GDPR, SOC 2). The following controls are mandatory:

  • Network isolation – All resources reside in a dedicated VPC with private subnets. Lambda functions access the VPC via ENIs; the vector store endpoints are accessed through VPC‑endpoints (Interface endpoints for OpenSearch, Gateway endpoints for S3).
  • Encryption – KMS‑managed CMKs encrypt Kinesis streams, S3 buckets storing raw documents, and the vector store’s storage (OpenSearch encryption‑at‑rest, Pinecone’s server‑side encryption). TLS 1.2 is enforced for all service‑to‑service calls.
  • IAM least privilege – Each Lambda receives an inline policy granting only the actions it needs (e.g., kinesis:GetRecords, bedrock:InvokeModel, secretsmanager:GetSecretValue). The Bedrock invocation role is scoped to a single model ARN.
  • Data residency** – For EU‑based customers, we deploy the stack in the eu‑central‑1 region and enable the Data residency flag on Bedrock (where available). Vector store replicas are kept within the same region.
  • Audit logging** – CloudTrail logs all management plane events; data plane access (e.g., OpenSearch query API) is logged via OpenSearch audit logs forwarded to CloudWatch Logs.
  • Vulnerability management** – Lambda layers and container images are scanned with Amazon Inspector; dependencies in the Next.js app are checked via npm audit in the CI pipeline.

Case Study: HYVO’s 30‑Day MVP for an AI‑Powered Customer Support Platform

HYVO partnered with a mid‑size SaaS provider seeking to deflect Tier‑1 support tickets using an AI agent that could answer questions from the latest product documentation, release notes, and past ticket resolutions.

  1. Discovery (Days 1‑3) – Conducted stakeholder interviews to identify data sources: Confluence (HTML), Zendesk tickets (JSON), and a PostgreSQL knowledge base.
  2. Data Pipeline (Days 4‑7) – Set up Kinesis streams via DMS for CDC from PostgreSQL and webhook connectors for Confluence/Zendesk. Implemented the Lambda embedding function (Go) described earlier, targeting the Titan Text Embeddings model.
  3. Vector Store Selection (Days 8‑10) – Chose Pinecone (p1 pod, 2 replicas) for its managed nature and metadata filtering. Created namespaces per source to enable source‑based filtering at query time.
  4. LLM Decision (Days 11‑13) – Started with Bedrock’s Claude 2 using few‑shot prompting (prompt engineering) to validate answer quality. Achieved 78 % relevance in internal QA.
  5. Guardrails & Observability (Days 14‑16) – Integrated Comprehend PII redaction, profanity filter, and OpenTelemetry tracing. Configured Grafana dashboards for latency and token cost.
  6. Frontend Build (Days 17‑20) – Developed a Next.js 13 chat widget with React Server Components for initial state load and Edge API route for streaming responses. Leveraged the internal link to our website development company in Kolhapur showcase for UI inspiration.
  7. Load Testing & Optimization (Days 21‑24) – Ran Locust tests, tuned Pinecone pod count, added prompt caching via DAX, and adjusted chunk overlap to improve recall.
  8. Security Hardening (Days 25‑27) – Applied VPC‑endpoint strategy, enabled KMS rotation, and enforced least‑privilege IAM policies.
  9. Release & Monitoring (Days 28‑30) – Performed blue/green Lambda rollout, opened the widget to a 5 % user cohort, monitored SLIs, and proceeded to full rollout after meeting the 950 ms latency SLA.

Outcome after 30 days:

  • Average response time: 760 ms (95th‑percentile).
  • Ticket deflection rate: 34 % (vs. 12 % baseline).
  • Monthly LLM cost: $1 200 (≈ $0.0015 per request).
  • Zero security incidents or data‑leak alerts.

The architecture described in this guide directly enabled this rapid delivery, demonstrating that a production‑grade RAG stack can be built, tested, and hardened within a four‑week window while meeting enterprise‑grade performance and compliance requirements.

Conclusion and Call to Action

Building a production‑grade Retrieval‑Augmented Generation system on AWS is no longer a speculative exercise; it is a repeatable pattern that combines managed services, streaming data pipelines, and modern frontend frameworks to deliver low‑latency, context‑aware AI experiences. By carefully selecting the LLM strategy, vector store technology, and ingestion mechanism, teams can achieve sub‑second response scales while maintaining strict observability, security, and cost controls.

If you are ready to accelerate your AI roadmap and need a battle‑tested partner to engineer the foundation, reach out to HYVO. Our team specializes in turning high‑level visions into scalable, production‑grade MVPs—leveraging the exact stack outlined here to get you to market faster and with confidence.

© 2025 HYVO. All rights reserved.

Building a Production‑Grade Retrieval‑Augmented Generation (RAG) System with Fine‑Tuned LLMs, Vector Search, and Streaming Ingestion on AWS | Hyvo