Skip to main content

AWS AI Services

Amazon Bedrock: Practical Guide to Generative AI for Australian Businesses

Comprehensive guide to implementing Amazon Bedrock for generative AI use cases including chatbots, document analysis, and content generation with data sovereignty in mind.

CloudPoint

CloudPoint Team

Amazon Bedrock provides access to leading foundation models through a managed service, eliminating the complexity of hosting and managing AI models. For Australian businesses, Bedrock offers a compliant, scalable path to implementing generative AI capabilities.

What is Amazon Bedrock?

Bedrock is a fully managed service offering foundation models from Amazon and leading AI companies:

Available in Sydney (ap-southeast-2):

  • Data stays in Australia
  • Low latency
  • Privacy Act compliant

Key Benefits:

  • No model hosting required
  • Pay per use
  • Multiple models to choose from
  • Data not used for model training
  • Enterprise security and compliance

Foundation Models

Choose the right model for your use case.

Anthropic Claude 3

Claude 3.5 Sonnet:

  • Most capable model
  • Strong reasoning and analysis
  • Excellent code generation
  • 200K token context window
  • Best for complex tasks

Claude 3 Haiku:

  • Fast and cost-effective
  • Quick responses
  • Lower cost
  • Best for high-volume, simple tasks

Pricing (approximate):

  • Input: $3 per million tokens
  • Output: $15 per million tokens

Amazon Titan

Titan Text models:

  • Text generation
  • Summarization
  • Search and RAG

Titan Embeddings:

  • Semantic search
  • Document clustering
  • Recommendation systems

Pricing: Lower cost than Claude, less capable

Meta Llama

Llama 3 models:

  • Open-source
  • Multiple sizes
  • Cost-effective
  • Good for standard tasks

Common Use Cases

1. Customer Service Chatbot

import boto3
import json

bedrock = boto3.client('bedrock-runtime', region_name='ap-southeast-2')

class CustomerServiceBot:
    def __init__(self, company_context):
        self.company_context = company_context
        self.conversation_history = []

    def generate_response(self, user_message):
        # Build conversation
        self.conversation_history.append({
            "role": "user",
            "content": user_message
        })

        # System prompt
        system_prompt = f"""You are a helpful customer service agent for {self.company_context['name']}.

Company information:
- Products: {', '.join(self.company_context['products'])}
- Support hours: {self.company_context['support_hours']}
- Return policy: {self.company_context['return_policy']}

Provide helpful, professional, and friendly responses. If you don't know something, suggest contacting human support."""

        # Call Bedrock
        response = bedrock.invoke_model(
            modelId='anthropic.claude-3-sonnet-20240229-v1:0',
            body=json.dumps({
                "anthropic_version": "bedrock-2023-05-31",
                "max_tokens": 1024,
                "system": system_prompt,
                "messages": self.conversation_history
            })
        )

        result = json.loads(response['body'].read())
        assistant_message = result['content'][0]['text']

        # Add to history
        self.conversation_history.append({
            "role": "assistant",
            "content": assistant_message
        })

        return assistant_message

# Usage
bot = CustomerServiceBot({
    'name': 'Australian Tech Co',
    'products': ['Software licenses', 'Cloud hosting', 'Support plans'],
    'support_hours': '9 AM - 5 PM AEST Monday-Friday',
    'return_policy': '30-day money-back guarantee'
})

response = bot.generate_response("How do I return a product?")
print(response)

2. Document Analysis and Summarization

def analyze_document(document_text):
    """Extract key insights from documents"""

    prompt = f"""Analyze this business document and provide:
1. Brief summary (2-3 sentences)
2. Key points (bullet list)
3. Action items (if any)
4. Risks or concerns (if any)

Document:
{document_text}

Provide your analysis in a structured format."""

    response = bedrock.invoke_model(
        modelId='anthropic.claude-3-sonnet-20240229-v1:0',
        body=json.dumps({
            "anthropic_version": "bedrock-2023-05-31",
            "max_tokens": 2048,
            "messages": [{
                "role": "user",
                "content": prompt
            }]
        })
    )

    result = json.loads(response['body'].read())
    return result['content'][0]['text']

# Usage
with open('contract.txt', 'r') as f:
    contract_text = f.read()

analysis = analyze_document(contract_text)
print(analysis)

3. Content Generation

def generate_product_description(product_details):
    """Generate compelling product descriptions"""

    prompt = f"""Create an engaging product description for an Australian e-commerce site.

Product details:
- Name: {product_details['name']}
- Features: {', '.join(product_details['features'])}
- Target audience: {product_details['target_audience']}
- Key benefit: {product_details['key_benefit']}

Requirements:
- 100-150 words
- Highlight Australian relevance where applicable
- SEO-friendly
- Compelling and persuasive
- Include call-to-action

Generate the product description:"""

    response = bedrock.invoke_model(
        modelId='anthropic.claude-3-sonnet-20240229-v1:0',
        body=json.dumps({
            "anthropic_version": "bedrock-2023-05-31",
            "max_tokens": 512,
            "messages": [{
                "role": "user",
                "content": prompt
            }]
        })
    )

    result = json.loads(response['body'].read())
    return result['content'][0]['text']

4. RAG (Retrieval Augmented Generation)

Combine Bedrock with your own data for accurate, context-aware responses:

from opensearchpy import OpenSearch

class RAGSystem:
    def __init__(self, opensearch_client, index_name):
        self.search_client = opensearch_client
        self.index_name = index_name
        self.bedrock = boto3.client('bedrock-runtime', region_name='ap-southeast-2')

    def embed_query(self, query):
        """Generate embeddings using Titan"""
        response = self.bedrock.invoke_model(
            modelId='amazon.titan-embed-text-v1',
            body=json.dumps({"inputText": query})
        )
        result = json.loads(response['body'].read())
        return result['embedding']

    def search_documents(self, query, top_k=3):
        """Search for relevant documents"""
        query_embedding = self.embed_query(query)

        search_query = {
            "size": top_k,
            "query": {
                "knn": {
                    "embedding": {
                        "vector": query_embedding,
                        "k": top_k
                    }
                }
            }
        }

        results = self.search_client.search(
            index=self.index_name,
            body=search_query
        )

        return [hit['_source']['text'] for hit in results['hits']['hits']]

    def generate_answer(self, question):
        """Generate answer using retrieved context"""
        # Retrieve relevant documents
        context_docs = self.search_documents(question)
        context = "\n\n".join(context_docs)

        # Generate answer with context
        prompt = f"""Based on the following context, answer the question. If the context doesn't contain the answer, say so.

Context:
{context}

Question: {question}

Answer:"""

        response = self.bedrock.invoke_model(
            modelId='anthropic.claude-3-sonnet-20240229-v1:0',
            body=json.dumps({
                "anthropic_version": "bedrock-2023-05-31",
                "max_tokens": 1024,
                "messages": [{
                    "role": "user",
                    "content": prompt
                }]
            })
        )

        result = json.loads(response['body'].read())
        return result['content'][0]['text']

Best Practices

1. Prompt Engineering

Good prompts yield better results:

Clear Instructions:

Bad: "Write about clouds"
Good: "Write a 200-word technical explanation of AWS cloud computing benefits for Australian small businesses, including cost savings and scalability."

Few-Shot Examples:

prompt = """Generate product categoriesfor Australian hardware store.

Examples:
Input: "Makita 18V drill" → Category: "Power Tools > Drills"
Input: "Dulux ceiling white 10L" → Category: "Paint > Interior > Ceiling"
Input: "Vinyl flooring oak 2m" → Category: "Flooring > Vinyl"

Input: "Stanley hammer 450g"
Category:"""

System Prompts:

{
    "system": "You are a helpful assistant for an Australian real estate company. Always provide property prices in AUD, distances in kilometers, and use Australian terminology (apartment, not flat).",
    "messages": [...]
}

2. Token Management

Tokens = cost. Optimize usage:

Estimate Tokens:

  • ~4 characters = 1 token (English)
  • Input + output tokens both count
  • Context window: Claude 3 supports 200K tokens

Reduce Tokens:

# Bad - includes unnecessary context
full_history = conversation_history  # Last 50 messages

# Good - sliding window
recent_history = conversation_history[-5:]  # Last 5 messages

3. Streaming Responses

For better UX, stream responses:

def stream_response(prompt):
    """Stream responses for real-time feedback"""
    response = bedrock.invoke_model_with_response_stream(
        modelId='anthropic.claude-3-sonnet-20240229-v1:0',
        body=json.dumps({
            "anthropic_version": "bedrock-2023-05-31",
            "max_tokens": 1024,
            "messages": [{
                "role": "user",
                "content": prompt
            }]
        })
    )

    stream = response['body']
    for event in stream:
        chunk = event.get('chunk')
        if chunk:
            delta = json.loads(chunk['bytes'])
            if delta['type'] == 'content_block_delta':
                yield delta['delta']['text']

4. Error Handling

Robust error handling for production:

import time
from botocore.exceptions import ClientError

def call_bedrock_with_retry(prompt, max_retries=3):
    """Call Bedrock with exponential backoff"""
    for attempt in range(max_retries):
        try:
            response = bedrock.invoke_model(
                modelId='anthropic.claude-3-sonnet-20240229-v1:0',
                body=json.dumps({
                    "anthropic_version": "bedrock-2023-05-31",
                    "max_tokens": 1024,
                    "messages": [{"role": "user", "content": prompt}]
                })
            )
            return json.loads(response['body'].read())

        except ClientError as e:
            error_code = e.response['Error']['Code']

            if error_code == 'ThrottlingException':
                # Rate limit hit, backoff and retry
                wait_time = (2 ** attempt) + (random.random() * 0.1)
                time.sleep(wait_time)
                continue

            elif error_code == 'ValidationException':
                # Bad request, don't retry
                raise

            else:
                # Other error, might be transient
                if attempt < max_retries - 1:
                    time.sleep(1)
                    continue
                raise

    raise Exception("Max retries exceeded")

5. Content Moderation

Always validate AI-generated content:

import boto3

comprehend = boto3.client('comprehend', region_name='ap-southeast-2')

def moderate_content(text):
    """Check for inappropriate content"""

    # Toxicity detection
    toxicity = comprehend.detect_toxic_content(
        TextSegments=[{'Text': text}],
        LanguageCode='en'
    )

    for segment in toxicity['ResultList']:
        for label in segment['Labels']:
            if label['Score'] > 0.7:  # High confidence
                return False, f"Inappropriate content detected: {label['Name']}"

    return True, "Content approved"

# Usage
generated_text = generate_content_with_bedrock(prompt)
is_safe, message = moderate_content(generated_text)

if is_safe:
    publish_content(generated_text)
else:
    log_moderation_flag(message)

Cost Optimisation

Choose Right Model

Haiku for:

  • Simple questions
  • High volume
  • Low latency required
  • Cost-sensitive

Sonnet for:

  • Complex reasoning
  • Code generation
  • Detailed analysis
  • Quality over cost

Caching

Cache frequent queries:

import hashlib
from functools import lru_cache

@lru_cache(maxsize=1000)
def get_cached_response(prompt_hash):
    """Cache responses for identical prompts"""
    # This won't call Bedrock if prompt seen before
    return call_bedrock(prompt_hash)

def generate_with_cache(prompt):
    # Hash the prompt
    prompt_hash = hashlib.sha256(prompt.encode()).hexdigest()
    return get_cached_response(prompt_hash)

Batch Processing

Process in batches where possible:

def batch_analyze(texts):
    """Analyze multiple texts in one call"""

    combined_prompt = "Analyze each of these customer reviews and provide sentiment:\n\n"

    for i, text in enumerate(texts, 1):
        combined_prompt += f"Review {i}: {text}\n\n"

    combined_prompt += "Provide analysis for each review:"

    response = call_bedrock(combined_prompt)
    return parse_batch_response(response)

Security and Compliance

Data Handling

Data Residency:

  • Use ap-southeast-2 (Sydney) region
  • Data processed in Australia
  • Complies with Privacy Act

Data Not Used for Training: Your prompts and responses are NOT used to train or improve models.

Encryption

In Transit: TLS 1.2+ At Rest: KMS encryption

Access Control

# IAM policy for Bedrock access
{
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action": [
            "bedrock:InvokeModel",
            "bedrock:InvokeModelWithResponseStream"
        ],
        "Resource": [
            "arn:aws:bedrock:ap-southeast-2::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0"
        ],
        "Condition": {
            "StringEquals": {
                "aws:RequestedRegion": "ap-southeast-2"
            }
        }
    }]
}

Audit Logging

Enable CloudTrail for Bedrock API calls:

  • Who called what model
  • When
  • What prompts (redact if sensitive)
  • Costs

Monitoring

CloudWatch Metrics

Monitor:

  • InvocationCount
  • InvocationLatency
  • ModelInvocationErrors
  • TokensUsed

Cost Tracking

def track_bedrock_usage(prompt, response):
    """Track token usage for cost analysis"""
    cloudwatch = boto3.client('cloudwatch')

    input_tokens = estimate_tokens(prompt)
    output_tokens = estimate_tokens(response)

    cloudwatch.put_metric_data(
        Namespace='BedrockUsage',
        MetricData=[
            {
                'MetricName': 'InputTokens',
                'Value': input_tokens,
                'Unit': 'Count'
            },
            {
                'MetricName': 'OutputTokens',
                'Value': output_tokens,
                'Unit': 'Count'
            }
        ]
    )

Getting Started Checklist

  • Enable Bedrock in Sydney region
  • Request model access (one-time)
  • Set up IAM permissions
  • Implement basic use case
  • Add error handling
  • Implement caching
  • Set up monitoring
  • Add content moderation
  • Implement cost tracking
  • Train team on usage

Conclusion

Amazon Bedrock democratizes access to powerful generative AI capabilities. For Australian businesses, it provides a compliant, scalable, and cost-effective way to implement AI features without managing infrastructure.

Start with a focused use case, implement best practices from the start, and iterate based on results. The combination of multiple foundation models, data sovereignty, and pay-per-use pricing makes Bedrock an excellent choice for Australian businesses exploring generative AI.

CloudPoint helps Australian businesses implement Amazon Bedrock - from use case definition through architecture, implementation, and optimisation. Contact us to discuss your generative AI requirements.


Need Help with Amazon Bedrock?

CloudPoint implements practical Amazon Bedrock solutions for Australian businesses—from document processing to intelligent assistants. Get in touch to discuss your AI needs.

Learn more about our AI Services →