Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
build/
cdk.out/
*.js
*.d.ts
95 changes: 95 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---

Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0

---

# Lambda to Bedrock Advanced Prompt Optimization (CDK)

This pattern deploys a Lambda function that creates an Amazon Bedrock Advanced Prompt Optimization job to automatically optimize prompts for your target model(s). The function uploads evaluation data to S3, submits the optimization job, polls for completion, and returns results.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-bedrock-prompt-optimization-cdk

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

- [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
- [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed
- [Node.js](https://nodejs.org/) 20.x or later
- [Amazon Bedrock model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) enabled for your target model (Anthropic Claude Sonnet is the default)

## Deployment Instructions

1. Create a new directory, navigate to the directory, and clone the repository:
```bash
git clone https://github.com/aws-samples/serverless-patterns
cd serverless-patterns/lambda-bedrock-prompt-optimization-cdk
```

2. Install dependencies:
```bash
npm install
```

3. Deploy the stack:
```bash
cdk deploy
```

## How it works

1. The Lambda function receives a prompt template and evaluation samples (or uses defaults).
2. It uploads the input data as JSONL to an S3 bucket.
3. It calls `CreateAdvancedPromptOptimizationJob` with the input S3 URI, output S3 URI, and target model configuration.
4. The service runs iterative inference-evaluate-rewrite loops to optimize the prompt.
5. The Lambda polls `GetAdvancedPromptOptimizationJob` every 10 seconds until completion.
6. Results (optimized prompts with evaluation metrics) are written to S3 and returned in the response.

## Architecture

```
┌──────────┐ ┌─────────────────┐ ┌────────┐
│ Lambda │────────▶│ Bedrock AdvPO │────────▶│ S3 │
│ Function │◀────────│ (Optimization) │ │ Bucket │
└──────────┘ poll └─────────────────┘ └────────┘
```

## Testing

Invoke the Lambda function with a test event:

```bash
aws lambda invoke \
--function-name <FunctionName from stack output> \
--payload '{"promptTemplate": "Answer concisely: {{question}}", "modelId": "us.anthropic.claude-sonnet-4-20250514-v1:0"}' \
--cli-binary-format raw-in-base64-out \
output.json

cat output.json
```

Or use the default prompt template and samples:

```bash
aws lambda invoke \
--function-name <FunctionName from stack output> \
--payload '{}' \
--cli-binary-format raw-in-base64-out \
output.json
```

## Cleanup

```bash
cdk destroy
```

---

## Author

- Nithin Chandran R - [LinkedIn](https://www.linkedin.com/in/nithin-chandran-r/)
12 changes: 12 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { LambdaBedrockPromptOptimizationStack } from '../lib/lambda-bedrock-prompt-optimization-stack';

const app = new cdk.App();
new LambdaBedrockPromptOptimizationStack(app, 'LambdaBedrockPromptOptimizationStack', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
});
7 changes: 7 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"app": "npx ts-node --prefer-ts-exts bin/app.ts",
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:stackRelativeExports": true
}
}
56 changes: 56 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"title": "Lambda to Bedrock Advanced Prompt Optimization",
"description": "Optimize prompts across multiple models using Amazon Bedrock Advanced Prompt Optimization, triggered by AWS Lambda. Deployed with AWS CDK.",
"language": "Python",
"level": "200",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"This pattern deploys a Lambda function that creates an Amazon Bedrock Advanced Prompt Optimization job.",
"The job takes a prompt template with evaluation samples, optimizes it for your target model(s), and writes results to S3.",
"The Lambda function polls the job status and returns the optimization results, enabling automated prompt engineering pipelines."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-bedrock-prompt-optimization-cdk",
"templateURL": "serverless-patterns/lambda-bedrock-prompt-optimization-cdk",
"projectFolder": "lambda-bedrock-prompt-optimization-cdk",
"templateFile": "lib/lambda-bedrock-prompt-optimization-stack.ts"
}
},
"resources": {
"bullets": [
{
"text": "Amazon Bedrock Advanced Prompt Optimization",
"link": "https://docs.aws.amazon.com/bedrock/latest/userguide/advanced-prompt-optimization-jobs.html"
},
{
"text": "Bedrock Advanced Prompt Optimization Prerequisites",
"link": "https://docs.aws.amazon.com/bedrock/latest/userguide/advanced-prompt-optimization-prereqs.html"
}
]
},
"deploy": {
"text": ["cdk deploy"]
},
"testing": {
"text": ["See the README for testing instructions."]
},
"cleanup": {
"text": ["cdk destroy"]
},
"authors": [
{
"name": "Nithin Chandran R",
"bio": "Technical Account Manager at AWS",
"linkedin": "nithin-chandran-r"
}
],
"patternArch": {
"icon1": { "x": 20, "y": 50, "service": "lambda", "label": "AWS Lambda" },
"icon2": { "x": 80, "y": 50, "service": "bedrock", "label": "Amazon Bedrock" },
"line1": { "from": "icon1", "to": "icon2" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3 from 'aws-cdk-lib/aws-s3';

export class LambdaBedrockPromptOptimizationStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const bucket = new s3.Bucket(this, 'PromptOptBucket', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});

const fn = new lambda.Function(this, 'PromptOptFunction', {
runtime: lambda.Runtime.PYTHON_3_12,
handler: 'index.handler',
code: lambda.Code.fromAsset('src/lambda', {
bundling: {
image: lambda.Runtime.PYTHON_3_12.bundlingImage,
command: [
'bash', '-c',
'pip install -r requirements.txt -t /asset-output && cp -au . /asset-output',
],
},
}),
timeout: cdk.Duration.minutes(15),
memorySize: 256,
environment: {
BUCKET_NAME: bucket.bucketName,
},
});

bucket.grantReadWrite(fn);

fn.addToRolePolicy(new iam.PolicyStatement({
actions: [
'bedrock:CreateAdvancedPromptOptimizationJob',
'bedrock:GetAdvancedPromptOptimizationJob',
'bedrock:ListAdvancedPromptOptimizationJobs',
],
resources: [
`arn:aws:bedrock:${this.region}:${this.account}:advanced-prompt-optimization-job/*`,
`arn:aws:bedrock:*:${this.account}:inference-profile/*`,
'arn:aws:bedrock:*::foundation-model/*',
],
}));

fn.addToRolePolicy(new iam.PolicyStatement({
actions: [
'bedrock:InvokeModel',
'bedrock:InvokeModelWithResponseStream',
],
resources: [
'arn:aws:bedrock:*::foundation-model/*',
`arn:aws:bedrock:*:${this.account}:inference-profile/*`,
],
}));

new cdk.CfnOutput(this, 'FunctionName', { value: fn.functionName });
new cdk.CfnOutput(this, 'BucketName', { value: bucket.bucketName });
}
}
20 changes: 20 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "lambda-bedrock-prompt-optimization-cdk",
"version": "1.0.0",
"bin": { "app": "bin/app.ts" },
"scripts": {
"build": "tsc",
"cdk": "cdk"
},
"dependencies": {
"aws-cdk-lib": "^2.170.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.21"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "~5.4.0",
"aws-cdk": "^2.170.0",
"ts-node": "^10.9.0"
}
}
63 changes: 63 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/src/lambda/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import os
import time
import boto3

bedrock = boto3.client('bedrock')
s3 = boto3.client('s3')

BUCKET = os.environ['BUCKET_NAME']


def handler(event, context):
"""Create an Advanced Prompt Optimization job, poll until complete, return results."""
prompt_template = event.get('promptTemplate', 'You are a helpful assistant. Answer the following question concisely and accurately: {{question}}')
template_id = event.get('templateId', 'qa-template-v1')
model_id = event.get('modelId', 'us.anthropic.claude-sonnet-4-20250514-v1:0')
samples = event.get('evaluationSamples', [
{'inputVariables': [{'question': 'What is cloud computing?'}], 'referenceResponse': 'Cloud computing is the on-demand delivery of IT resources over the Internet with pay-as-you-go pricing.'},
{'inputVariables': [{'question': 'What is serverless?'}], 'referenceResponse': 'Serverless is a cloud execution model where the provider manages infrastructure, scaling automatically and charging only for actual usage.'},
{'inputVariables': [{'question': 'What is Amazon Bedrock?'}], 'referenceResponse': 'Amazon Bedrock is a fully managed service that offers foundation models from leading AI companies through a single API.'},
])

# Build JSONL input (one line per template)
input_entry = {
'version': 'bedrock-2026-05-14',
'templateId': template_id,
'promptTemplate': prompt_template,
'steeringCriteria': ['ACCURATE', 'CONCISE'],
'evaluationSamples': samples,
}
input_key = f'input/{context.aws_request_id}.jsonl'
s3.put_object(Bucket=BUCKET, Key=input_key, Body=json.dumps(input_entry))

# Create optimization job
job = bedrock.create_advanced_prompt_optimization_job(
jobName=f'opt-{context.aws_request_id[:8]}',
inputConfig={'s3Uri': f's3://{BUCKET}/{input_key}'},
outputConfig={'s3Uri': f's3://{BUCKET}/output/{context.aws_request_id}/'},
modelConfigurations=[{'modelId': model_id}],
)
job_arn = job['jobArn']

# Poll for completion (max ~14 min with 15 min Lambda timeout)
for _ in range(84):
time.sleep(10)
status = bedrock.get_advanced_prompt_optimization_job(jobIdentifier=job_arn)
state = status.get('jobStatus', status.get('status', 'UNKNOWN'))
if state in ('COMPLETED', 'SUCCEEDED'):
return {
'statusCode': 200,
'jobArn': job_arn,
'status': state,
'outputUri': f's3://{BUCKET}/output/{context.aws_request_id}/',
}
if state in ('FAILED', 'STOPPED'):
return {
'statusCode': 500,
'jobArn': job_arn,
'status': state,
'failureMessage': status.get('failureMessage', 'Unknown error'),
}

return {'statusCode': 408, 'jobArn': job_arn, 'status': 'TIMEOUT', 'message': 'Job did not complete within Lambda timeout'}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
boto3>=1.42.0
18 changes: 18 additions & 0 deletions lambda-bedrock-prompt-optimization-cdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["es2020"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"outDir": "./build",
"rootDir": ".",
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"exclude": ["node_modules", "build"]
}