利用无服务器架构实时转录与翻译
通过简单事件驱动的无服务器架构,利用 Amazon Translate 和 Amazon Transcribe 实现实时转录与翻译。
开始使用 Amazon Translate 和 Amazon Transcribe
我们先来看看如何通过简单事件驱动的无服务器架构来上手操作 Amazon Translate 和 Amazon Transcribe。我们将创建一个简单编排。
- 当音频文件以 /audio 前缀上传至 Amazon Simple Storage Service (S3) 存储桶时
- 我们会触发一个 AWS Lambda 函数,它将运用 Amazon Transcribe 从音频文件中提取文本。该函数会将转录文本以 /transcriptions 前缀写入同一个存储桶
- 接着,当在 /transcriptions 目录下创建一个文件时,另一个 AWS Lambda 函数会被触发以执行翻译任务。为简化说明,我们假设录音为英语,希望将其翻译为意大利语。这个 AWS Lambda 函数会将翻译结果写入 /translations 目录
编排非常适用于创建小型原型和概念验证。在这个例子中,我们也将 Amazon S3 用作消息总线。如果要将该架构投产,推荐使用更复杂的编配工具,如 AWS StepFunction。
重要提示:在使用 Amazon S3、Amazon S3 前缀和 AWS Lambda 实现编排时,务必要非常小心,不要导致 AWS Lambda 函数出现递归调用,否则会产生不必要的费用。请留意函数写入 Amazon S3 的路径。例如,如果此示例中的两个函数之一写入 /audio 目录,就会陷入无限循环!如果出现这种情况,请确保限制函数的调用频率,并可能为所有的编排工具添加紧急停止开关。一个常见的策略是为每个编排工具提供一个 Amazon S3 路径的拒绝列表。如果在触发函数的事件中检测到上述路径之一,该函数可能会限制自己的调用频率或忽略事件,将事件发到死信队列,并向相关通知系统发送警报。其他人可能更喜欢为每个阶段保留单独的存储桶,但请注意您的亚马逊云科技账户限额,如果需要更多存储桶,可申请增加服务配额。
创建 AWS CDK 堆栈
1. 安装 AWS CDK CLI
如果您还没有安装 AWS CDK,请使用 npm 进行安装:
npm install -g aws-cdk
2. 新建 AWS CDK 项目
为您的 AWS CDK 项目新建一个目录,并使用下列命令对其初始化
mkdir transcribe-translate
cd transcribe-translate
cdk init app --language typescript
3. 添加所需的 AWS CDK 依赖项
npm install @aws-cdk/aws-s3 @aws-cdk/aws-lambda @aws-cdk/aws-iam @aws-cdk/aws-s3-notifications @aws-cdk/aws-lambda-nodejs
创建基础架构
编辑 lib/transcribe-translate-stack.ts 文件以定义资源:
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as s3n from 'aws-cdk-lib/aws-s3-notifications';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as path from 'path';
export class TranscribeTranslateStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create the S3 bucket
const bucket = new s3.Bucket(this, 'UploadBucket', {
removalPolicy: cdk.RemovalPolicy.DESTROY, // this is OK only for dev
autoDeleteObjects: true, // this is OK only for dev
});
// Create the Transcribe Lambda function
const transcribeFunction = new NodejsFunction(this, 'TranscribeFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
entry: path.join(__dirname, '../lambda/transcribe.mjs'),
handler: 'handler',
environment: {
BUCKET_NAME: bucket.bucketName,
},
timeout: cdk.Duration.seconds(900),
});
// Grant read/write permissions on S3 objects for the transcribe function
bucket.grantReadWrite(transcribeFunction, 'transcriptions/*');
// Additional permissions for Transcribe
transcribeFunction.addToRolePolicy(new iam.PolicyStatement({
actions: ['transcribe:StartTranscriptionJob', 'transcribe:GetTranscriptionJob'],
resources: ['*'],
}));
// Add S3 event notification to trigger Transcribe function
bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(transcribeFunction), {
prefix: 'audio/',
});
// Create the Translate Lambda function
const translateFunction = new NodejsFunction(this, 'TranslateFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
entry: path.join(__dirname, '../lambda/translate.mjs'),
handler: 'handler',
environment: {
BUCKET_NAME: bucket.bucketName,
},
timeout: cdk.Duration.seconds(900),
});
// Grant read/write permissions on specific S3 objects for the translate function
bucket.grantRead(translateFunction, 'transcriptions/*');
bucket.grantReadWrite(translateFunction,'translations/*');
// Additional permissions for Translate
translateFunction.addToRolePolicy(new iam.PolicyStatement({
actions: ['translate:TranslateText'],
resources: ['*'],
}));
// Add S3 event notification to trigger Translate function
bucket.addEventNotification(s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(translateFunction), {
prefix: 'transcriptions/',
});
}
}
触发转录任务
在以下示例中,我们将触发转录任务,但不会监控其是否成功完成。这种乐观方法只有在创建原型时才会有效!如果投产,建议是轮询 Amazon Transcribe API ,以便您知道任务何时成功完成。
在本地代码仓库创建一个 lambda/transcribe.mjs 文件
import { TranscribeClient, StartTranscriptionJobCommand } from '@aws-sdk/client-transcribe';
const transcribeClient = new TranscribeClient();
export const handler = async (event) => {
const bucket = process.env.BUCKET_NAME;
const key = event.Records[0].s3.object.key;
const transcribeParams = {
TranscriptionJobName: `TranscribeJob-${Date.now()}`,
LanguageCode: 'en-US',
Media: {
MediaFileUri: `s3://${bucket}/${key}`
},
OutputBucketName: bucket,
OutputKey: `transcriptions/${key.split("/").pop()}.json`
};
try {
await transcribeClient.send(new StartTranscriptionJobCommand(transcribeParams));
console.log('Transcription job started');
} catch (err) {
console.error('Error starting transcription job', err);
return;
}
};
任务完成后会在 /transcriptions 下新建一个对象。这将触发编排的下一阶段。
触发翻译任务
创建一个 lambda/translate.mjs 文件
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import { TranslateClient, TranslateTextCommand } from '@aws-sdk/client-translate';
const s3Client = new S3Client();
const translateClient = new TranslateClient();
export const handler = async (event) => {
const bucket = process.env.BUCKET_NAME;
const key = event.Records[0].s3.object.key;
// Get the transcription text from S3
let transcriptionData;
try {
transcriptionData = await s3Client.send(new GetObjectCommand({
Bucket: bucket,
Key: key
}));
} catch (err) {
console.error('Error getting transcription from S3', err);
return;
}
const { results } = JSON.parse(await streamToString(transcriptionData.Body));
const transcriptionText = results.transcripts.reduce(
(acc, curr) => acc + curr.transcript, ''
);
console.log('Transcription text:', transcriptionText);
// Translate the transcription
const translateParams = {
SourceLanguageCode: 'en',
TargetLanguageCode: 'it',
Text: transcriptionText
};
console.log(translateParams);
let translation;
try{
translation = await translateClient.send(new TranslateTextCommand(translateParams));
console.log('Translation:', translation.TranslatedText);
}catch(err){
console.error('Error translating text', err);
return;
}
// Save the translation result to S3
const translationResult = {
originalText: transcriptionText,
translatedText: translation.TranslatedText
};
try{
await s3Client.send(new PutObjectCommand({
Bucket: bucket,
Key: `translations/${key.split("/").pop()}.it.txt`,
Body: translationResult.translatedText,
ContentType: 'text/plain'
}));
}catch(err){
console.error('Error saving translation to S3', err);
return;
}
console.log('Translation saved');
};
// Helper function to convert a stream to a string
const streamToString = (stream) => {
return new Promise((resolve, reject) => {
const chunks = [];
stream.on('data', (chunk) => chunks.push(chunk));
stream.on('error', reject);
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
});
};
部署
cdk bootstrap # if you haven't bootstrapped in this region before
cdk deploy
完成部署后,您应该能看到一个输出列表。在这里可以找到您的 Amazon S3 存储桶名称。
测试
要进行测试,只需将 mp3 文件上传到 audio/ 输出所得的 Amazon S3 存储桶中。您可以使用下列 Amazon CLI
aws s3 cp ./path/to/your/audio/file s3://your-bucket-here/audio/file.mp3
或者转到亚马逊云科技控制台,向 audio/ 上传 mp3 文件。稍后您便可得到 transcriptions/ 和 translations/,在这两个文件夹中可以找到每个类别的编排结果。
清理
任务完成后,用下列命令拆解基础架构
cdk destroy