Insights

AWS S3 Vectors

Revolutionizing AI Data Storage and Vector Search at Scale.

Yantratmika Solutions 33 min read
AWS S3 Vectors

In the rapidly evolving landscape of artificial intelligence and machine learning, the ability to efficiently store, manage, and query vector data has become critical for building intelligent applications. AWS has announced Amazon S3 Vectors, the first cloud storage with native vector support at scale, promising to revolutionize how we approach vector data management while delivering up to 90% cost reduction compared to conventional approaches.

What is AWS S3 Vectors?

Clear Introduction

Amazon S3 Vectors delivers purpose-built, cost-optimized vector storage for your semantic search and AI applications. It represents a fundamental shift in cloud storage architecture by introducing native vector database functionality directly into Amazon S3’s object storage platform.

Deep Conceptual Explanation

AWS S3 Vector is a new storage capability that adds native vector database functionality to Amazon S3, allowing you to store and query vector embeddings directly within S3. Unlike traditional approaches that require separate vector database infrastructure, S3 Vectors enables developers to store numerical vector representations of unstructured data (text, images, audio, video) and perform semantic similarity searches with sub-second query performance.

The service introduces the concept of vector embeddings - numerical representations that preserve semantic relationships between content. For similarity search and AI applications, vectors are created as vector embeddings which are numerical representations that preserve semantic relationships between content (such as text, images, or audio) so similar items are positioned closer together.

Key Benefits

  • Cost Optimization: Up to 90% cost savings compared to traditional vector databases
  • Serverless Architecture: No infrastructure provisioning or management required
  • Native AWS Integration: Seamless integration with Amazon Bedrock, OpenSearch, and SageMaker
  • Scalability: Supporting billions of vectors without provisioning infrastructure
  • Durability: Inherits S3’s 99.999999999% (11 9s) durability

How S3 Vectors Differs from Traditional S3 and Vector Databases

Differences from Traditional S3

Traditional S3 S3 Vectors
Stores unstructured data (documents, images, videos) Stores structured numerical arrays (vectors)
Operations: PUT, GET, DELETE on object keys Specialized vector operations: PutVectors, QueryVectors, ListVectors
File-based access patterns Semantic similarity search patterns
Standard REST API S3 Vectors uses a different service namespace than Amazon S3: the s3vectors namespace

Differences from Vector Databases

Traditional Vector Databases:

  • Require dedicated infrastructure and compute resources
  • High memory requirements for in-memory operations
  • Complex cluster management and scaling
  • Storing 10 million 1536-dimensional vectors with 250,000 queries and 50% overwrites monthly might cost \(300–\)500

S3 Vectors:

  • Serverless, pay-as-you-use model
  • The same workload costs ~\(30–\)50, leveraging S3’s pay-as-you-go model
  • No infrastructure management required
  • Automatic optimization and scaling

Complementary Architecture

Rather than replacing vector databases, S3 Vectors fits into the ecosystem as a complementary piece. Its real future probably lies in working with professional vector databases, not replacing them. Organizations can implement a tiered strategy:

  • Cold Storage: Large, infrequently accessed vectors in S3 Vectors
  • Hot Storage: High-performance, frequently queried vectors in OpenSearch or dedicated vector databases

Architecture and Internal Model

Core Components

S3 Vectors consists of several key components that work together:

1. Vector Buckets

A new bucket type that’s purpose-built to store and query vectors. Vector buckets use a dedicated namespace (s3vectors) separate from traditional S3 buckets.

2. Vector Indexes

A single vector bucket can contain up to 10,000 vector indexes, each holding tens of millions of vectors. Vector indexes organize and logically group vector data within a bucket.

3. Vectors

You store vectors in your vector index. For similarity search and AI applications, vectors are created as vector embeddings. Each vector contains:

  • Key: Unique identifier within the vector index
  • Multi-dimensional vector: Numerical representation (up to 4,096 dimensions)
  • Metadata: Optional key-value pairs for filtering

Internal Architecture

┌─────────────────────────────────────────────────────┐
│                AWS S3 Vectors                       │
├─────────────────────────────────────────────────────┤
│  Vector Bucket (s3vectors namespace)                │
│  ├─ Vector Index 1 (up to 10,000 indexes)           │
│  │  ├─ Vector 1 {key, vector[], metadata{}}         │
│  │  ├─ Vector 2 {key, vector[], metadata{}}         │
│  │  └─ ...                                          │
│  ├─ Vector Index 2                                  │
│  └─ Vector Index N                                  │
├─────────────────────────────────────────────────────┤
│  Integration Layer                                  │
│  ├─ Amazon Bedrock Knowledge Bases                  │
│  ├─ Amazon OpenSearch Service                       │
│  └─ Amazon SageMaker Unified Studio                 │
├─────────────────────────────────────────────────────┤
│  API Layer                                          │
│  ├─ CreateIndex / DeleteIndex                       │
│  ├─ PutVectors / GetVectors / DeleteVectors         │
│  ├─ QueryVectors (similarity search)                │
│  └─ ListVectors / ListIndexes                       │
└─────────────────────────────────────────────────────┘

Search Mechanism

S3 Vector utilizes approximate nearest neighbor (ANN) indexing directly within S3 partitions, supporting both flat and hybrid indices. The system performs similarity searches by:

  1. Computing distances between query vector and stored vectors
  2. Supporting both Cosine similarity and Euclidean distance metrics
  3. Applying metadata filters to narrow results
  4. Returning closest matching vectors with sub-second performance

Consistency Model

Writes to S3 Vectors are strongly consistent, which means that you can immediately access the most recently added data. This ensures that subsequent queries always include recently added data, critical for real-time AI applications.

Key Features & Components

1. Serverless Vector Storage

  • No infrastructure provisioning or management
  • Automatic scaling based on workload demands
  • Pay-per-use pricing model

2. High-Dimensional Vector Support

  • Support for vectors up to 4,096 dimensions
  • Optimized for common embedding model outputs
  • Efficient storage compression

3. Advanced Querying Capabilities

  • Similarity Search: S3 Vectors can perform similarity searches based on semantic meaning rather than exact matching through comparing how close vectors are to each other mathematically
  • Metadata Filtering: Filter results based on attached metadata
  • Batch Operations: Efficient bulk insert and query operations

4. Enterprise Security

  • All Amazon S3 Block Public Access settings are always enabled for vector buckets and cannot be disabled
  • Integration with AWS IAM for fine-grained access control
  • Encryption at rest with KMS support

5. Multi-Service Integration

  • Amazon Bedrock: You can use S3 Vectors in Amazon Bedrock Knowledge Bases to simplify and reduce the cost of vector storage for RAG applications
  • Amazon OpenSearch: Tiered storage strategies for optimal performance
  • Amazon SageMaker: Direct integration for ML workflows

6. Developer-Friendly APIs

Package s3vectors provides the API client, operations, and parameter types for Amazon S3 Vectors with support for:

  • REST APIs
  • AWS SDKs (Python, Java, Go, etc.)
  • AWS CLI integration

Typical Use Cases

1. Semantic Search for E-Commerce

An e-commerce platform wants to enable semantic search, allowing users to find products using natural language queries like “red summer dress” or “wireless headphones with noise cancellation”.

Implementation:

  • Convert product descriptions to vector embeddings using Amazon Titan
  • Store vectors with metadata (category, brand, price)
  • Enable natural language product discovery

2. Medical Image Analysis

Finding similar scenes in a video file, patterns recording in medical imagery becomes economically feasible with S3 Vectors’ cost-effective storage model.

Implementation:

  • Generate embeddings from medical images using specialized models
  • Store embeddings with patient metadata and diagnostic information
  • Enable rapid similarity searches for diagnostic assistance

Finding similar scenes in petabyte-scale video archives demonstrates S3 Vectors’ capability for multimedia content analysis.

Implementation:

  • Extract frame-level or scene-level embeddings from video content
  • Store temporal metadata alongside vector data
  • Enable content-based video retrieval systems

4. Personalized Recommendations

A streaming service wants to recommend movies or music based on user preferences, such as “upbeat pop songs” or “sci-fi thrillers”.

Implementation:

  • Embed user preferences and content metadata as vectors
  • Store interaction history and content features
  • Generate real-time personalized recommendations

5. Retrieval-Augmented Generation (RAG)

Lower the cost of Retrieval Augmented Generation (RAG) by combining S3 Vectors with Amazon Bedrock Knowledge Bases.

Implementation:

  • Store document embeddings in S3 Vectors
  • Integrate with Amazon Bedrock for foundation model access
  • Build cost-effective RAG pipelines for enterprise applications

6. AI Agent Memory Systems

Make your AI agents more intelligent by retaining more context, reasoning with richer data, and building lasting memory from affordable, large-scale vector storage.

Implementation:

  • Store conversation history and context as vectors
  • Maintain long-term agent memory across sessions
  • Enable continuous learning and adaptation

Implementation Guide: Building with AWS S3 Vectors in GoLang

Let’s explore how to build a semantic document search application using AWS S3 Vectors with GoLang. We’ll break down each component and explain its role in the overall architecture.

Application Architecture Overview

Our application follows a clean architecture pattern with these core layers:

┌─────────────────────────────────────────────────────┐
│                 API Layer                           │
│         (HTTP handlers, routing)                    │
├─────────────────────────────────────────────────────┤
│               Service Layer                         │
│        (Business logic, orchestration)              │
├─────────────────────────────────────────────────────┤
│               Integration Layer                     │
│     (S3 Vectors Client, Bedrock Client)             │
├─────────────────────────────────────────────────────┤
│               External Services                     │
│        (AWS S3 Vectors, Amazon Bedrock)             │
└─────────────────────────────────────────────────────┘

Core Dependencies and Project Setup

First, let’s understand the essential dependencies needed for our S3 Vectors implementation:

go.mod essentials:

require (
    github.com/aws/aws-sdk-go-v2 v1.24.0
    github.com/aws/aws-sdk-go-v2/config v1.26.1
    github.com/aws/aws-sdk-go-v2/service/s3vectors v1.0.0
    github.com/aws/aws-sdk-go-v2/service/bedrock-runtime v1.7.3
    github.com/gin-gonic/gin v1.9.1
)

Why these dependencies matter:

  • aws-sdk-go-v2: The latest AWS SDK providing improved performance and better context support
  • s3vectors: The dedicated client for S3 Vectors operations - this is the heart of our vector storage
  • bedrock-runtime: Enables us to generate embeddings using Amazon Titan models
  • gin: Lightweight HTTP framework for our REST API

1. Configuration Management

Configuration is the foundation that makes our application flexible and environment-aware:

type Config struct {
    AWSRegion        string
    VectorBucket     string  // S3 Vector bucket name
    VectorIndex      string  // Index within the bucket
    BedrockModel     string  // Embedding model identifier
    VectorDimensions int32   // Must match your embedding model
    DistanceMetric   string  // COSINE or EUCLIDEAN
}

Key Configuration Insights:

  • VectorDimensions: Critical to match your embedding model. Titan Text Embeddings V2 uses 1024 dimensions
  • DistanceMetric: COSINE works well for text similarity, EUCLIDEAN for numerical data
  • VectorIndex: Logical grouping within a bucket - think of it as a table in a database

Environment-driven configuration:

func Load() *Config {
    return &Config{
        AWSRegion:     getEnvOrDefault("AWS_REGION", "us-east-1"),
        VectorBucket:  getEnvOrDefault("VECTOR_BUCKET", "yantratmika-vectors"),
        // ... other configs
    }
}

This pattern allows seamless deployment across development, staging, and production environments.

2. Data Models and Domain Objects

Our domain models define the contract between different layers:

Document Model:

type Document struct {
    ID        string            `json:"id"`
    Content   string            `json:"content"`
    Title     string            `json:"title"`
    Author    string            `json:"author"`
    Category  string            `json:"category"`
    Metadata  map[string]string `json:"metadata"`
}

Vector Model:

type Vector struct {
    Key      string                 `json:"key"`
    Vector   []float32              `json:"vector"`
    Metadata map[string]interface{} `json:"metadata"`
}

Why this structure matters:

  • Separation of concerns: Documents represent business entities, Vectors represent their mathematical representations
  • Flexible metadata: Allows filtering and categorization without schema changes
  • Type safety: GoLang’s strong typing prevents runtime errors

3. S3 Vectors Client Integration

The S3 Vectors client is our gateway to AWS’s vector storage capabilities:

Client Initialization

type Client struct {
    s3vectorsClient *s3vectors.Client
    bucketName      string
    indexName       string
}

func NewClient(cfg aws.Config, bucketName, indexName string) *Client {
    return &Client{
        s3vectorsClient: s3vectors.NewFromConfig(cfg),
        bucketName:      bucketName,
        indexName:       indexName,
    }
}

Client Design Principles:

  • Encapsulation: Wraps the AWS client with domain-specific logic
  • Configuration injection: Makes testing and environment switching easier
  • Error context: All methods add meaningful error context

Vector Index Creation

func (c *Client) CreateIndex(ctx context.Context, dimensions int32, distanceMetric string) error {
    input := &s3vectors.CreateIndexInput{
        Bucket:            aws.String(c.bucketName),
        Index:             aws.String(c.indexName),
        VectorSizeInBytes: aws.Int32(dimensions * 4), // 4 bytes per float32
        DistanceMetric:    types.DistanceMetricType(distanceMetric),
    }

    _, err := c.s3vectorsClient.CreateIndex(ctx, input)
    return err
}

What this accomplishes:

  • Index initialization: Creates the logical container for your vectors
  • Dimension specification: Ensures all vectors in the index have consistent dimensions
  • Distance metric setup: Defines how similarity calculations are performed

Vector Storage Operations

func (c *Client) PutVectors(ctx context.Context, vectors []models.Vector) error {
    var putVectorItems []types.PutVectorDataType

    for _, vector := range vectors {
        metadataBytes, err := json.Marshal(vector.Metadata)
        if err != nil {
            return fmt.Errorf("failed to marshal metadata: %w", err)
        }

        putVectorItems = append(putVectorItems, types.PutVectorDataType{
            Key:      aws.String(vector.Key),
            Vector:   vector.Vector,
            Metadata: aws.String(string(metadataBytes)),
        })
    }

    // Batch insert for efficiency
    input := &s3vectors.PutVectorsInput{
        Bucket:  aws.String(c.bucketName),
        Index:   aws.String(c.indexName),
        Vectors: putVectorItems,
    }

    _, err := c.s3vectorsClient.PutVectors(ctx, input)
    return err
}

Design patterns demonstrated:

  • Batch operations: More efficient than individual inserts
  • Metadata serialization: JSON encoding for flexible metadata storage
  • Error propagation: Clear error context for debugging

Vector Search Operations

func (c *Client) QueryVectors(ctx context.Context, queryVector []float32, maxResults int32, metadataFilter map[string]string) ([]models.SearchResult, error) {
    input := &s3vectors.QueryVectorsInput{
        Bucket:         aws.String(c.bucketName),
        Index:          aws.String(c.indexName),
        Vector:         queryVector,
        MaxResults:     aws.Int32(maxResults),
        ReturnData:     aws.Bool(false), // Don't return vector data for efficiency
        ReturnMetadata: aws.Bool(true),  // Return metadata for result display
    }

    // Apply metadata filters if provided
    if len(metadataFilter) > 0 {
        filterBytes, _ := json.Marshal(metadataFilter)
        input.MetadataFilter = aws.String(string(filterBytes))
    }

    result, err := c.s3vectorsClient.QueryVectors(ctx, input)
    // ... process results
    return searchResults, nil
}

Query optimization techniques:

  • Selective data return: Only retrieve what you need (metadata, not vector data)
  • Metadata filtering: Pre-filter results to improve relevance
  • Result limiting: Control costs and response times

4. Embedding Generation Service

The embedding service transforms text into numerical vectors using Amazon Bedrock:

Service Architecture

type Service struct {
    bedrockClient *bedrockruntime.Client
    modelID       string
}

type TitanEmbeddingRequest struct {
    InputText string `json:"inputText"`
}

type TitanEmbeddingResponse struct {
    Embedding []float32 `json:"embedding"`
}

Embedding Generation Logic

func (s *Service) GenerateEmbedding(ctx context.Context, text string) ([]float32, error) {
    request := TitanEmbeddingRequest{InputText: text}
    requestBody, _ := json.Marshal(request)

    input := &bedrockruntime.InvokeModelInput{
        ModelId:     aws.String(s.modelID),
        Body:        requestBody,
        ContentType: aws.String("application/json"),
        Accept:      aws.String("application/json"),
    }

    result, err := s.bedrockClient.InvokeModel(ctx, input)
    if err != nil {
        return nil, fmt.Errorf("bedrock invocation failed: %w", err)
    }

    var response TitanEmbeddingResponse
    json.Unmarshal(result.Body, &response)
    return response.Embedding, nil
}

What makes this effective:

  • Model abstraction: Easy to swap embedding models
  • Error handling: Meaningful error messages for troubleshooting
  • Type safety: Structured request/response handling

Batch Embedding Generation

func (s *Service) GenerateBatchEmbeddings(ctx context.Context, texts []string) ([][]float32, error) {
    var embeddings [][]float32

    for _, text := range texts {
        embedding, err := s.GenerateEmbedding(ctx, text)
        if err != nil {
            return nil, fmt.Errorf("batch embedding failed for text: %w", err)
        }
        embeddings = append(embeddings, embedding)
    }

    return embeddings, nil
}

Batch processing benefits:

  • Throughput optimization: Process multiple texts efficiently
  • Error isolation: One failure doesn’t break the entire batch
  • Cost management: Bedrock charges per API call, batching reduces costs

5. Search Service Orchestration

The search service orchestrates the entire workflow from document indexing to retrieval:

Service Dependencies

type Service struct {
    config            *config.Config
    vectorClient      *s3vectors.Client
    embeddingsService *embeddings.Service
}

This dependency injection pattern enables:

  • Testability: Easy to mock dependencies
  • Flexibility: Swap implementations without changing core logic
  • Separation of concerns: Each service has a single responsibility

Document Indexing Workflow

func (s *Service) IndexDocument(ctx context.Context, doc models.Document) (*models.IndexResponse, error) {
    // 1. Prepare text for embedding
    textForEmbedding := fmt.Sprintf("Title: %s\n\nContent: %s", doc.Title, doc.Content)

    // 2. Generate vector embedding
    embedding, err := s.embeddingsService.GenerateEmbedding(ctx, textForEmbedding)
    if err != nil {
        return nil, fmt.Errorf("embedding generation failed: %w", err)
    }

    // 3. Prepare metadata for storage
    metadata := map[string]interface{}{
        "id":         doc.ID,
        "title":      doc.Title,
        "content":    doc.Content, // Store for retrieval
        "indexed_at": time.Now().Format(time.RFC3339),
    }

    // 4. Create and store vector
    vector := models.Vector{
        Key:      doc.ID,
        Vector:   embedding,
        Metadata: metadata,
    }

    return s.vectorClient.PutVectors(ctx, []models.Vector{vector})
}

Workflow explanation:

  1. Text preparation: Combines title and content for richer embeddings
  2. Embedding generation: Converts text to numerical representation
  3. Metadata preparation: Stores searchable and retrievable information
  4. Vector storage: Persists in S3 Vectors for future search

Semantic Search Workflow

func (s *Service) SearchDocuments(ctx context.Context, request models.SearchRequest) (*models.SearchResponse, error) {
    start := time.Now()

    // 1. Generate query embedding
    queryEmbedding, err := s.embeddingsService.GenerateEmbedding(ctx, request.Query)
    if err != nil {
        return nil, fmt.Errorf("query embedding failed: %w", err)
    }

    // 2. Perform vector similarity search
    searchResults, err := s.vectorClient.QueryVectors(ctx, queryEmbedding, request.MaxResults, request.Filters)
    if err != nil {
        return nil, fmt.Errorf("vector search failed: %w", err)
    }

    // 3. Convert distances to similarity scores
    for i := range searchResults {
        searchResults[i].Score = float32(1.0 - searchResults[i].Distance)
    }

    return &models.SearchResponse{
        Results:   searchResults,
        QueryTime: time.Since(start).String(),
    }, nil
}

Search workflow breakdown:

  1. Query embedding: Same model used for indexing ensures consistency
  2. Vector search: S3 Vectors finds most similar documents
  3. Score calculation: Converts mathematical distances to intuitive similarity scores

6. REST API Layer

The API layer exposes our search functionality through HTTP endpoints:

Handler Structure

type Handler struct {
    searchService *search.Service
}

func (h *Handler) IndexDocument(c *gin.Context) {
    var doc models.Document
    if err := c.ShouldBindJSON(&doc); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON"})
        return
    }

    response, err := h.searchService.IndexDocument(c.Request.Context(), doc)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusCreated, response)
}

API design principles:

  • Clear error messages: Help developers debug issues
  • Proper HTTP status codes: Follow REST conventions
  • Context propagation: Pass request context for timeouts and cancellation

Batch Processing Endpoint

func (h *Handler) IndexBatchDocuments(c *gin.Context) {
    var documents []models.Document
    c.ShouldBindJSON(&documents)

    if len(documents) > 100 {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Maximum 100 documents per batch"})
        return
    }

    responses, err := h.searchService.IndexBatchDocuments(c.Request.Context(), documents)
    // ... handle response
}

Batch endpoint benefits:

  • Efficiency: Reduces HTTP overhead for bulk operations
  • Rate limiting: Prevents system overload
  • Atomic operations: All or nothing approach for data integrity

7. Application Bootstrap and Service Wiring

The main application ties everything together:

func main() {
    // 1. Load configuration
    cfg := appconfig.Load()

    // 2. Initialize AWS configuration
    awsConfig, err := config.LoadDefaultConfig(context.TODO(),
        config.WithRegion(cfg.AWSRegion),
    )

    // 3. Wire up services
    vectorClient := s3vectors.NewClient(awsConfig, cfg.VectorBucket, cfg.VectorIndex)
    embeddingsService := embeddings.NewService(awsConfig, cfg.BedrockModel)
    searchService := search.NewService(cfg, vectorClient, embeddingsService)

    // 4. Initialize HTTP server
    handler := api.NewHandler(searchService)
    router := setupRouter(handler)

    // 5. Start with graceful shutdown
    startServer(router, cfg.ServerPort)
}

Bootstrap responsibilities:

  1. Configuration loading: Environment-specific settings
  2. AWS SDK initialization: Authentication and region setup
  3. Dependency injection: Wire services together
  4. HTTP server setup: Route configuration and middleware
  5. Graceful shutdown: Handle termination signals properly

Usage Examples and API Interactions

Now let’s see how to interact with our semantic search API:

Document Indexing

curl -X POST http://localhost:8080/api/v1/documents \
  -H "Content-Type: application/json" \
  -d '{
    "title": "AWS S3 Vectors Guide",
    "content": "Comprehensive guide to using S3 Vectors for AI applications...",
    "author": "Yantratmika Solutions",
    "category": "technology"
  }'
curl -X POST http://localhost:8080/api/v1/search \
  -H "Content-Type: application/json" \
  -d '{
    "query": "vector storage for machine learning",
    "max_results": 5,
    "filters": {"category": "technology"}
  }'

Key Implementation Insights

1. Vector Dimension Consistency All vectors in an index must have the same dimensions. Our application ensures this by:

  • Using a single embedding model throughout
  • Validating dimensions during index creation
  • Consistent text preprocessing

2. Metadata Strategy We store both searchable and retrievable metadata:

  • Searchable: category, author, tags (for filtering)
  • Retrievable: title, content (for displaying results)
  • System: indexed_at, created_at (for auditing)

3. Error Handling Pattern

if err != nil {
    return nil, fmt.Errorf("descriptive context: %w", err)
}

This pattern provides clear error context while preserving the original error for debugging.

4. Context Usage All operations accept context.Context for:

  • Request timeouts
  • Cancellation handling
  • Request tracing

5. Batch Optimization

  • Group multiple vectors into single S3 Vectors calls
  • Generate embeddings in batches when possible
  • Implement reasonable batch size limits

This implementation demonstrates how to build a production-ready semantic search system using AWS S3 Vectors, with proper separation of concerns, error handling, and scalability considerations.

End-to-end RAG Implementation

Building upon our semantic search foundation, let’s implement a complete Retrieval-Augmented Generation (RAG) system that combines S3 Vectors with Amazon Bedrock’s language models.

RAG Architecture Overview

┌─────────────────────────────────────────────────────┐
│                 RAG Pipeline                        │
├─────────────────────────────────────────────────────┤
│  1. Question Processing                             │
│     └─ Generate query embedding                     │
├─────────────────────────────────────────────────────┤
│  2. Document Retrieval                              │
│     └─ S3 Vectors similarity search                 │
├─────────────────────────────────────────────────────┤
│  3. Context Assembly                                │
│     └─ Prepare retrieved docs for LLM              │
├─────────────────────────────────────────────────────┤
│  4. Answer Generation                               │
│     └─ Amazon Bedrock (Claude/Titan)               │
└─────────────────────────────────────────────────────┘

Core RAG Service Structure

The RAG service orchestrates the entire pipeline from question to answer:

import (
    "context"
    "encoding/json"
    "fmt"
    "strings"
    "time"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/service/bedrockruntime"
)

type Service struct {
    searchService *search.Service     // Our existing search functionality
    bedrockClient *bedrockruntime.Client  // For LLM inference
    llmModel      string              // Model identifier (e.g., Claude)
}

Service responsibilities:

  • Question processing: Convert natural language questions to embeddings
  • Document retrieval: Use our search service to find relevant context
  • Context preparation: Format retrieved documents for the LLM
  • Answer generation: Use Bedrock to generate contextual responses

RAG Request and Response Models

Our RAG models define the interface between users and the system:

// RAGRequest represents a RAG query request
type RAGRequest struct {
    Question    string            `json:"question"`
    MaxResults  int32             `json:"max_results,omitempty"`
    Filters     map[string]string `json:"filters,omitempty"`
    Temperature float32           `json:"temperature,omitempty"`
    MaxTokens   int32             `json:"max_tokens,omitempty"`
}

// RAGResponse represents a RAG query response
type RAGResponse struct {
    Answer          string                `json:"answer"`
    Question        string                `json:"question"`
    RetrievedDocs   []models.SearchResult `json:"retrieved_docs"`
    Sources         []string              `json:"sources"`
    QueryTime       string                `json:"query_time"`
    GenerationTime  string                `json:"generation_time"`
    TotalTime       string                `json:"total_time"`
}

Design principles:

  • Transparency: Users can see which documents informed the answer
  • Performance metrics: Timing information for optimization
  • Configurability: Users can tune generation parameters
  • Filtering support: Restrict retrieval to specific document categories

Claude API Integration

For language model integration, we structure our requests according to Bedrock’s Claude API format:

// ClaudeRequest represents the request format for Claude models
type ClaudeRequest struct {
    AnthropicVersion string          `json:"anthropic_version"`
    MaxTokens        int32           `json:"max_tokens"`
    Temperature      float32         `json:"temperature,omitempty"`
    Messages         []ClaudeMessage `json:"messages"`
}

// ClaudeMessage represents a message in Claude format
type ClaudeMessage struct {
    Role    string `json:"role"`    // "user" or "assistant"
    Content string `json:"content"` // The actual message content
}

Why this structure matters:

  • Version compatibility: Ensures we’re using the correct API version
  • Parameter control: Fine-tune response creativity and length
  • Message format: Supports conversation context if needed

Core RAG Generation Logic

The heart of our RAG system orchestrates retrieval and generation:

func (s *Service) GenerateRAGResponse(ctx context.Context, request RAGRequest) (*RAGResponse, error) {
    start := time.Now()

    // Step 1: Retrieve relevant documents using our search service
    searchReq := models.SearchRequest{
        Query:      request.Question,
        MaxResults: request.MaxResults,
        Filters:    request.Filters,
    }

    searchStart := time.Now()
    searchResponse, err := s.searchService.SearchDocuments(ctx, searchReq)
    if err != nil {
        return nil, fmt.Errorf("document retrieval failed: %w", err)
    }
    searchTime := time.Since(searchStart)

    // Step 2: Build context from retrieved documents
    contextString := s.buildContext(searchResponse.Results)

    // Step 3: Generate answer using the LLM
    genStart := time.Now()
    answer, err := s.generateAnswer(ctx, request.Question, contextString, request.Temperature, request.MaxTokens)
    if err != nil {
        return nil, fmt.Errorf("answer generation failed: %w", err)
    }
    genTime := time.Since(genStart)

    // Step 4: Prepare response with all components
    return &RAGResponse{
        Answer:          answer,
        Question:        request.Question,
        RetrievedDocs:   searchResponse.Results,
        Sources:         s.extractSources(searchResponse.Results),
        QueryTime:       searchTime.String(),
        GenerationTime:  genTime.String(),
        TotalTime:       time.Since(start).String(),
    }, nil
}

Pipeline breakdown:

  1. Document retrieval: Leverages our existing S3 Vectors search
  2. Context preparation: Formats documents for optimal LLM comprehension
  3. Answer generation: Uses Bedrock’s LLM with prepared context
  4. Response assembly: Combines answer with metadata and source attribution

Context Building Strategy

The context builder transforms search results into LLM-friendly format:

func (s *Service) buildContext(results []models.SearchResult) string {
    if len(results) == 0 {
        return "No relevant documents found."
    }

    var contextParts []string
    contextParts = append(contextParts, "Based on the following information:")

    for i, result := range results {
        // Format each document with clear structure
        docContext := fmt.Sprintf("\nDocument %d (Relevance Score: %.3f):\nTitle: %s\nContent: %s",
            i+1, result.Score, result.Title, result.Content)

        // Add metadata if available for additional context
        if author, ok := result.Metadata["author"].(string); ok && author != "" {
            docContext += fmt.Sprintf("\nAuthor: %s", author)
        }
        if category, ok := result.Metadata["category"].(string); ok && category != "" {
            docContext += fmt.Sprintf("\nCategory: %s", category)
        }

        contextParts = append(contextParts, docContext)
    }

    return strings.Join(contextParts, "\n")
}

Context optimization techniques:

  • Document ranking: Include relevance scores for the LLM
  • Structured format: Clear delimiters between documents
  • Metadata inclusion: Additional context like authorship and categorization
  • Truncation handling: Could be extended to manage token limits

LLM Answer Generation

The answer generation component interfaces with Amazon Bedrock:

func (s *Service) generateAnswer(ctx context.Context, question, context string, temperature float32, maxTokens int32) (string, error) {
    // Craft a comprehensive prompt for the LLM
    prompt := fmt.Sprintf(`You are a helpful AI assistant. Answer the following question based on the provided context. If the context doesn't contain enough information to answer the question, say so clearly.

Context:
%s

Question: %s

Please provide a comprehensive answer based on the context above. If you reference specific information, mention which document it came from.

Answer:`, context, question)

    // Structure the request for Claude API
    request := ClaudeRequest{
        AnthropicVersion: "bedrock-2023-05-31",
        MaxTokens:        maxTokens,
        Temperature:      temperature,
        Messages: []ClaudeMessage{
            {
                Role:    "user",
                Content: prompt,
            },
        },
    }

    // Make the Bedrock API call
    requestBody, _ := json.Marshal(request)
    input := &bedrockruntime.InvokeModelInput{
        ModelId:     aws.String(s.llmModel),
        Body:        requestBody,
        ContentType: aws.String("application/json"),
        Accept:      aws.String("application/json"),
    }

    result, err := s.bedrockClient.InvokeModel(ctx, input)
    if err != nil {
        return "", fmt.Errorf("bedrock invocation failed: %w", err)
    }

    // Parse the response
    var response ClaudeResponse
    json.Unmarshal(result.Body, &response)

    if len(response.Content) == 0 {
        return "", fmt.Errorf("empty response from model")
    }

    return response.Content[0].Text, nil
}

Prompt engineering insights:

  • Clear instructions: Tell the LLM exactly what to do
  • Context formatting: Structured input for better comprehension
  • Source attribution: Encourage the LLM to cite specific documents
  • Honesty instruction: Ask for clarity when information is insufficient

Source Attribution Logic

Source extraction provides transparency about answer origins:

func (s *Service) extractSources(results []models.SearchResult) []string {
    var sources []string
    for _, result := range results {
        source := result.Title

        // Enhance source information with metadata
        if author, ok := result.Metadata["author"].(string); ok && author != "" {
            source += fmt.Sprintf(" (by %s)", author)
        }

        // Add publication date if available
        if createdAt, ok := result.Metadata["created_at"].(string); ok && createdAt != "" {
            if parsed, err := time.Parse(time.RFC3339, createdAt); err == nil {
                source += fmt.Sprintf(" [%s]", parsed.Format("2006-01-02"))
            }
        }

        sources = append(sources, source)
    }
    return sources
}

Source enhancement benefits:

  • Credibility: Users can verify information sources
  • Timeliness: Show when information was created
  • Authority: Display author information for credibility assessment

RAG API Integration

Adding RAG capabilities to our existing API:

// Add to your API handler
func (h *Handler) GenerateRAGResponse(c *gin.Context) {
    var request rag.RAGRequest
    if err := c.ShouldBindJSON(&request); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request: " + err.Error()})
        return
    }

    if request.Question == "" {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Question is required"})
        return
    }

    // Set reasonable defaults
    if request.MaxResults == 0 {
        request.MaxResults = 5
    }
    if request.MaxTokens == 0 {
        request.MaxTokens = 1000
    }
    if request.Temperature == 0 {
        request.Temperature = 0.7
    }

    response, err := h.ragService.GenerateRAGResponse(c.Request.Context(), request)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "RAG generation failed: " + err.Error()})
        return
    }

    c.JSON(http.StatusOK, response)
}

API design considerations:

  • Input validation: Ensure required fields are present
  • Default values: Provide sensible defaults for optional parameters
  • Error handling: Clear error messages for different failure modes
  • Response structure: Include both answer and supporting metadata

Streaming RAG Implementation

For real-time applications, implement streaming responses:

func (s *Service) StreamRAGResponse(ctx context.Context, request RAGRequest, responseChan chan<- string) error {
    defer close(responseChan)

    // Step 1: Retrieve documents (send progress update)
    responseChan <- "🔍 Searching for relevant documents...\n"

    searchReq := models.SearchRequest{
        Query:      request.Question,
        MaxResults: request.MaxResults,
        Filters:    request.Filters,
    }

    searchResponse, err := s.searchService.SearchDocuments(ctx, searchReq)
    if err != nil {
        responseChan <- fmt.Sprintf("❌ Search failed: %v\n", err)
        return err
    }

    // Step 2: Report findings
    responseChan <- fmt.Sprintf("✅ Found %d relevant documents\n", len(searchResponse.Results))
    for i, result := range searchResponse.Results {
        responseChan <- fmt.Sprintf("📄 %d. %s (score: %.3f)\n", i+1, result.Title, result.Score)
    }

    // Step 3: Generate answer
    responseChan <- "\n🤖 Generating answer...\n\n"

    context := s.buildContext(searchResponse.Results)
    answer, err := s.generateAnswer(ctx, request.Question, context, request.Temperature, request.MaxTokens)
    if err != nil {
        responseChan <- fmt.Sprintf("❌ Generation failed: %v\n", err)
        return err
    }

    // Step 4: Stream the final answer
    responseChan <- answer

    return nil
}

Streaming benefits:

  • Real-time feedback: Users see progress as it happens
  • Better UX: No waiting for long-running operations
  • Debug information: Users can see the retrieval process
  • Progressive disclosure: Information is revealed step by step

RAG Usage Examples

Here are practical examples of using our RAG system:

Basic RAG Query

curl -X POST http://localhost:8080/api/v1/rag/ask \
  -H "Content-Type: application/json" \
  -d '{
    "question": "What are the key benefits of using AWS S3 Vectors for AI applications?",
    "max_results": 3,
    "filters": {
      "category": "technology"
    }
  }'

Advanced RAG Query with Fine-tuning

curl -X POST http://localhost:8080/api/v1/rag/ask \
  -H "Content-Type: application/json" \
  -d '{
    "question": "How does S3 Vectors compare to traditional vector databases in terms of cost and performance?",
    "max_results": 5,
    "temperature": 0.3,
    "max_tokens": 1500,
    "filters": {
      "author": "technical_team"
    }
  }'

Example RAG Response

{
  "answer": "Based on the retrieved documents, AWS S3 Vectors offers several key advantages for AI applications:\n\n1. **Cost Efficiency**: Document 1 indicates that S3 Vectors can reduce costs by up to 90% compared to traditional vector databases...\n\n2. **Scalability**: Document 2 mentions that S3 Vectors inherits S3's virtually unlimited scalability...\n\n3. **Integration**: Document 3 highlights native integration with Amazon Bedrock and OpenSearch...",
  "question": "What are the key benefits of using AWS S3 Vectors for AI applications?",
  "retrieved_docs": [...],
  "sources": [
    "AWS S3 Vectors Cost Analysis (by Yantratmika Solutions) [2024-01-15]",
    "Scalability Patterns in Vector Storage (by Technical Team) [2024-01-10]",
    "AWS Service Integration Guide (by Architecture Team) [2024-01-12]"
  ],
  "query_time": "245ms",
  "generation_time": "1.2s",
  "total_time": "1.445s"
}

RAG Performance Optimization

Key strategies for optimizing RAG performance:

1. Document Chunk Size Management

// Optimize document chunks for better retrieval
func (s *Service) optimizeDocumentChunks(content string, maxChunkSize int) []string {
    // Split content into semantically meaningful chunks
    // Ensure chunks overlap slightly to maintain context
    // Return optimally sized pieces for embedding
}

2. Context Window Management

// Manage token limits for LLM input
func (s *Service) manageLLMContext(context string, maxTokens int32) string {
    // Truncate or summarize context if too long
    // Prioritize higher-scoring documents
    // Maintain document structure and attribution
}

3. Caching Strategies

// Cache embeddings and frequent queries
type RAGCache struct {
    embeddingCache map[string][]float32
    responseCache  map[string]*RAGResponse
}

This RAG implementation demonstrates how S3 Vectors enables cost-effective, scalable question-answering systems that combine the power of vector search with modern language models.

Considerations While Using S3 Vectors

Performance Considerations

  1. Query Latency: S3 Vectors is ideal for workloads where queries are less frequent, with sub-second response times but not optimized for high-QPS scenarios.

  2. Batch Operations: For better performance, use batch operations when possible:

    • Batch document indexing for multiple documents
    • Batch vector insertions to reduce API calls
  3. Vector Dimensions: S3 Vectors prioritizes semantic search performance and cost-effective storage of AI-ready embeddings, supporting up to billions of vectors per index with dimensions up to 4,096.

Cost Considerations

  1. Storage Costs: Vectors are stored as binary data, with costs based on total storage volume
  2. Query Costs: Queries are charged at \(2.5/MM API calls, in addition to a \)/TB charge for data processed
  3. Data Transfer: Standard AWS data transfer charges apply for cross-region access

Architectural Considerations

  1. Tiered Storage Strategy:

    • Use S3 Vectors for “cold” storage of large vector datasets
    • Use Amazon OpenSearch for “hot” storage requiring high-performance queries
    • Implement automated data lifecycle management
  2. Integration Patterns:

    • RAG Applications: Lower the cost of Retrieval Augmented Generation (RAG) by combining S3 Vectors with Amazon Bedrock Knowledge Bases
    • AI Agent Memory: Make your AI agents more intelligent by retaining more context, reasoning with richer data, and building lasting memory from affordable, large-scale vector storage

Security Considerations

  1. Access Control: S3 Vectors uses a different service namespace than Amazon S3: the s3vectors namespace. Therefore, you can design policies specifically for the S3 Vectors service and its resources

  2. Encryption: Support for encryption at rest using AWS KMS

  3. Network Security: VPC endpoints for private network access

  4. Data Privacy: All Amazon S3 Block Public Access settings are always enabled for vector buckets and cannot be disabled

Data Management Considerations

  1. Consistency: Writes to S3 Vectors are strongly consistent, which means that you can immediately access the most recently added data

  2. Backup and Recovery: Leverage S3’s built-in durability and cross-region replication capabilities

  3. Version Management: Implement versioning strategies for embedding models and vector data

  4. Metadata Management: Efficiently use metadata for filtering and organization

Regions, Limits, Quotas and Pricing

Regional Availability

Amazon S3 Vectors preview is now available in the US East (N. Virginia), US East (Ohio), US West (Oregon), Asia Pacific (Sydney), and Europe (Frankfurt) Regions. As the service moves out of preview, expect broader regional availability.

Service Limits and Quotas

Resource Limit
Vector Buckets per Account 100
Vector Indexes per Bucket 10,000 vector indexes
Vectors per Index Tens of millions
Vector Dimensions Up to 4,096
Metadata Size per Vector 64 KB
Batch Size for PutVectors 1,000 vectors per request
Query Results per Request 1,000 vectors

Pricing Structure

AWS S3 Vector follows a pay-as-you-use pricing model similar to other AWS services, charging separately for storage, operations, and data transfer without requiring upfront commitments or infrastructure provisioning.

Storage Pricing

  • Vector storage: Based on GB-month, similar to S3 but optimized for vector data
  • Metadata storage: Included in vector storage costs

Operations Pricing

  • API Operations: $2.5/MM API calls
  • Query Processing: Tiered pricing based on data processed
    • Tier 1 query processing cost: $0.004/TB
    • Tier 2 query processing cost: $0.002/TB

Cost Comparison Example

Traditional Vector Database: Storing 10 million 1536-dimensional vectors with 250,000 queries and 50% overwrites monthly might cost \(300–\)500. S3 Vectors: The same workload costs ~\(30–\)50, leveraging S3’s pay-as-you-go model.

Data Transfer Pricing

  • Same region transfers: Free
  • Cross-region transfers: Standard AWS data transfer rates
  • Internet data transfer: Standard AWS rates

Cost Optimization Strategies

  1. Right-size Vector Dimensions: Use the minimum dimensions required for your use case
  2. Optimize Query Patterns: Batch queries and use metadata filters efficiently
  3. Implement Tiered Storage: Use S3 Vectors for cold storage, OpenSearch for hot queries
  4. Monitor Usage: Use AWS Cost Explorer and CloudWatch for cost monitoring

Best Practices

1. Vector Design and Management

Embedding Strategy

  • Choose appropriate embedding models for your use case
  • Maintain consistency in embedding dimensions across your application
  • Version your embedding models and migrate data when upgrading

Metadata Optimization

// Good: Structured, searchable metadata
metadata := map[string]interface{}{
    "document_type": "article",
    "category":      "technology",
    "author":        "yantratmika",
    "publish_date":  "2024-01-15",
    "tags":          []string{"aws", "vectors", "ai"},
    "language":      "en",
}

// Avoid: Unstructured, non-searchable metadata
metadata := map[string]interface{}{
    "misc_data": "some random information that can't be searched efficiently",
}

2. Performance Optimization

Batch Operations

// Good: Batch multiple vectors together
vectors := make([]models.Vector, 0, 100)
for _, doc := range documents {
    // Process document and create vector
    vectors = append(vectors, vector)
}
client.PutVectors(ctx, vectors)

// Avoid: Individual vector operations
for _, doc := range documents {
    client.PutVectors(ctx, []models.Vector{vector})
}

Query Optimization

// Good: Use metadata filters to narrow results
request := models.SearchRequest{
    Query: "machine learning algorithms",
    MaxResults: 10,
    Filters: map[string]string{
        "category": "technology",
        "language": "en",
    },
}

// Avoid: Broad queries without filters
request := models.SearchRequest{
    Query: "algorithms",
    MaxResults: 100, // Too many results
}

3. Security Best Practices

IAM Policy Example

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3vectors:CreateIndex",
        "s3vectors:PutVectors",
        "s3vectors:QueryVectors",
        "s3vectors:GetVectors",
        "s3vectors:ListVectors"
      ],
      "Resource": [
        "arn:aws:s3vectors:us-east-1:123456789012:bucket/yantratmika-vectors",
        "arn:aws:s3vectors:us-east-1:123456789012:bucket/yantratmika-vectors/index/*"
      ]
    }
  ]
}

Encryption Configuration

// Use KMS encryption for sensitive data
client := s3vectors.NewClient(awsConfig, bucketName, indexName)
// Encryption is handled at the bucket level during creation

4. Monitoring and Observability

CloudWatch Metrics

  • Monitor query latency, error rates, and throughput
  • Set up alarms for unusual patterns
  • Track cost metrics regularly

Application Logging

// Good: Structured logging with context
log.WithFields(log.Fields{
    "operation":    "vector_search",
    "query_time":   duration.Milliseconds(),
    "result_count": len(results),
    "user_id":      userID,
}).Info("Search completed")

// Include error context
log.WithFields(log.Fields{
    "operation": "vector_indexing",
    "error":     err.Error(),
    "doc_id":    docID,
}).Error("Failed to index document")

5. Application Architecture Patterns

Microservices Integration

// Service interface for clean architecture
type VectorSearchService interface {
    IndexDocument(ctx context.Context, doc Document) error
    SearchDocuments(ctx context.Context, query string) ([]SearchResult, error)
    DeleteDocument(ctx context.Context, docID string) error
}

// Implementation with S3 Vectors
type S3VectorService struct {
    client *s3vectors.Client
    embeddings EmbeddingService
}

Error Handling and Retries

// Implement exponential backoff for transient errors
func (s *Service) indexWithRetry(ctx context.Context, vectors []models.Vector) error {
    maxRetries := 3
    baseDelay := time.Second

    for attempt := 0; attempt <= maxRetries; attempt++ {
        err := s.vectorClient.PutVectors(ctx, vectors)
        if err == nil {
            return nil
        }

        // Check if error is retryable
        if !isRetryableError(err) {
            return err
        }

        if attempt < maxRetries {
            delay := baseDelay * time.Duration(1<<attempt)
            time.Sleep(delay)
        }
    }

    return fmt.Errorf("max retries exceeded")
}

6. Data Migration and Versioning

Schema Evolution

// Version your vector schemas
type VectorMetadata struct {
    SchemaVersion string `json:"schema_version"`
    DocumentID    string `json:"document_id"`
    // ... other fields
}

// Handle backward compatibility
func migrateMetadata(metadata map[string]interface{}) map[string]interface{} {
    version, exists := metadata["schema_version"]
    if !exists {
        // Migrate from v1 to v2
        return migrateV1ToV2(metadata)
    }
    return metadata
}

7. Testing Strategies

Unit Testing

func TestVectorSearch(t *testing.T) {
    // Use test doubles for external dependencies
    mockClient := &MockS3VectorClient{}
    service := search.NewService(config, mockClient, mockEmbeddings)

    // Test with known vectors and queries
    result, err := service.SearchDocuments(ctx, testQuery)
    assert.NoError(t, err)
    assert.Equal(t, expectedResults, result)
}

Integration Testing

func TestEndToEndSearch(t *testing.T) {
    // Use test environment with actual S3 Vectors
    testBucket := "test-vectors-" + uuid.New().String()

    // Setup test data
    setupTestData(testBucket)
    defer cleanupTestData(testBucket)

    // Test complete flow
    // ... integration test implementation
}

Conclusion

AWS S3 Vectors represents a paradigm shift in vector data management, offering organizations the ability to build sophisticated AI applications without the complexity and cost of traditional vector database infrastructure. By bringing vector search natively into S3, AWS has essentially merged the worlds of data lake storage and vector databases.

Key Takeaways

  1. Cost Efficiency: Up to 90% cost reduction compared to traditional vector databases makes large-scale vector applications economically feasible.

  2. Seamless Integration: Native integration with Amazon Bedrock, OpenSearch, and SageMaker enables comprehensive AI workflows.

  3. Serverless Simplicity: No infrastructure management required, with automatic scaling and optimization.

  4. Enterprise Ready: Built on S3’s proven durability, security, and compliance capabilities.

Future Outlook

As organizations increasingly adopt AI-driven applications, the demand for cost-effective, scalable vector storage will continue to grow. S3 Vectors positions AWS at the forefront of this trend, enabling everything from simple semantic search applications to sophisticated AI agent memory systems.

For practitioners, this means you can maintain enormous embedding-rich datasets (from images, documents, audio, etc.) in your S3 data lake and immediately unlock semantic search and retrieval capabilities on top of that data — without spinning up new services or worrying about scaling infrastructure.


About Yantratmika Solutions

At Yantratmika Solutions, we specialize in architecting and implementing cutting-edge cloud solutions that drive business transformation. Our expertise spans:

  • Cloud Architecture Design: Scalable, secure, and cost-optimized cloud infrastructures
  • AI/ML Implementation: End-to-end AI solution development and deployment
  • Data Engineering: Modern data platforms and analytics solutions
  • DevOps & Automation: CI/CD pipelines and infrastructure as code

Ready to transform your organization with AWS S3 Vectors?

Contact us at info@yantratmika.com to discuss how we can help you build intelligent, cost-effective AI applications using the latest AWS technologies.

This comprehensive guide demonstrates our deep technical expertise and commitment to helping organizations leverage cutting-edge technologies for competitive advantage.


Last updated: November 2025 | AWS S3 Vectors is currently in preview

Working on something similar?

We help teams take Generative AI, IoT and AWS systems to production. Talk to the engineers who write these articles.