15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Amazon Q Developerと作る!] 社会人一年目エンジニアが作る録音を行い自動で要約してHTMLレポート化するサーバーレスシステムを構築してみた

Last updated at Posted at 2025-07-06

はじめに

こんにちは!Tarekatsuです!

最近AWSのコミュニティであるJaws-ugによく参加しているのですが、LTを聞いていて話しもメモも同時に取るのは難しいと思い、後で要約されたものが見られるようになればいいなと思い、Amazon Q Developerと一緒にサーバーレスシステムを作ってみました!(一緒にと書いたが、ほとんど自分は作業してない:sweat_smile:)ただ、録音した音声データを使うので、良くないと思われる方がいらっしゃると思うのでとりあえず使う予定はないです。

音声ファイルをアップロードするだけで、自動的にテキスト化→AI要約→HTMLレポート生成まで行うシステムが完成しました。AWS Step Functions、Lambda、Transcribe、Bedrockを組み合わせた完全自動化システムの構築過程を紹介します。

Amazon Q Developerの役割

今回のシステム構築では、Amazon Q Developerが大活躍しました:

  • 録音PC: Pythonで音声録音 → S3アップロード
  • コード生成: Lambda関数のコードを自動生成
  • デバッグ支援: エラー解決とトラブルシューティング
  • ファイル操作: JSON設定ファイルの作成と編集
  • AWS CLI実行: デプロイメントコマンドの実行

指示を与えていただけですが、初心者の私でも、Q Developerのサポートでスムーズに構築できました!

システム構成

録音アプリにより、S3にファイルがアップロードされるとイベント通知により、一連の処理が自動で実行されるようになっています。最後に静的Webホスティングの設定がされている、S3に出力されるようになっており、すぐにブラウザ画面から確認できます。

音声要約.png

使用サービス

  • Amazon S3: ファイル保存・静的ウェブホスティング
  • AWS Lambda: サーバーレス処理
  • AWS Step Functions: ワークフロー管理
  • Amazon Transcribe: 音声認識
  • Amazon Bedrock (Claude 3 Sonnet): AI要約生成

構築手順

1. Lambda関数の作成

S3イベントトリガー関数

こちらでS3イベント通知からStepfunctionのワークフローを呼び出しています。

import boto3
import json
import urllib.parse

stepfunctions = boto3.client('stepfunctions')

def lambda_handler(event, context):
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = urllib.parse.unquote_plus(record['s3']['object']['key'])
        
        input_data = {
            'bucket': bucket,
            'key': key
        }
        
        stepfunctions.start_execution(
            stateMachineArn='arn:aws:states"ステートマシンワークフロー"',
            name=f"execution-{key.replace('/', '-').replace('.', '-')}-{context.aws_request_id[:8]}",
            input=json.dumps(input_data)
        )
    
    return {'statusCode': 200}

ファイル種別判定関数

こちらに関しては、判定する必要はないと思いますが、自分で後で作るシステムで使おうと思っているので、ファイルの種類の判定をしております。

def lambda_handler(event, context):
    key = event['key']
    
    if key.lower().endswith('.mp3'):
        return {
            'bucket': event['bucket'],
            'key': key,
            'fileType': 'mp3'
        }
    else:
        return "File processing skipped"

音声認識関数

こちらで、bucketの中のmp3ファイルをTranscribeで要約して、outputバケットの中に出力しております。

import boto3
import json
import time
import uuid

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

def lambda_handler(event, context):
    bucket = event['bucket']
    key = event['key']
    
    job_name = f"transcribe-{uuid.uuid4().hex[:8]}"
    media_uri = f"s3://{bucket}/{key}"
    
    # Transcribeジョブ開始
    transcribe.start_transcription_job(
        TranscriptionJobName=job_name,
        Media={'MediaFileUri': media_uri},
        MediaFormat='mp3',
        LanguageCode='ja-JP'
    )
    
    # ジョブ完了待機
    while True:
        response = transcribe.get_transcription_job(TranscriptionJobName=job_name)
        status = response['TranscriptionJob']['TranscriptionJobStatus']
        
        if status == 'COMPLETED':
            break
        elif status == 'FAILED':
            raise Exception(f"Transcription failed")
        
        time.sleep(10)
    
    # 結果取得・保存
    transcript_uri = response['TranscriptionJob']['Transcript']['TranscriptFileUri']
    
    import urllib.request
    with urllib.request.urlopen(transcript_uri) as response_data:
        transcript_data = json.loads(response_data.read().decode('utf-8'))
    
    transcript_text = transcript_data['results']['transcripts'][0]['transcript']
    
    # テキストファイル保存
    txt_key = f"tmp/{key.replace('.mp3', '.txt')}"
    utf8_bom = b'\\xef\\xbb\\xbf'
    text_with_bom = utf8_bom + transcript_text.encode('utf-8')
    
    s3.put_object(
        Bucket='output-bucket',
        Key=txt_key,
        Body=text_with_bom,
        ContentType='text/plain; charset=utf-8'
    )
    
    transcribe.delete_transcription_job(TranscriptionJobName=job_name)
    
    return {
        'bucket': 'output-bucket',
        'key': txt_key,
        'fileType': 'txt',
        'originalKey': key
    }

AI要約・HTML生成関数

こちらで、テキスト形式になったファイルからBedrockを使用して、要約した文を生成しております。要約した文を静的Webホスティングで扱うためにHTML形式で出力するようにしています。

  • 注意点:Q Devloperのコードの生成したコード大体そのまま使っているのですが、claude 3 sonnetを使用しております。生成されたコードが、少し古いデータを扱って生成されるので、新しいモデルを使いたい場合は、自分で修正する必要があると思いました。
import boto3
import json

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

def lambda_handler(event, context):
    bucket = event['bucket']
    key = event['key']
    
    # テキストファイル読み込み
    response = s3.get_object(Bucket=bucket, Key=key)
    text = response['Body'].read().decode('utf-8')
    
    # AI要約生成
    summary = generate_summary(text)
    
    # HTML生成
    html_content = f"""
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI要約 - {key.replace('tmp/', '').replace('.txt', '')}</title>
    <style>
        body {{
            font-family: 'Hiragino Sans', 'Yu Gothic', 'Meiryo', sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            line-height: 1.6;
            background-color: #f5f5f5;
        }}
        .container {{
            background-color: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }}
        h1 {{
            color: #2c3e50;
            border-bottom: 3px solid #3498db;
            padding-bottom: 10px;
        }}
        .meta {{
            background-color: #ecf0f1;
            padding: 15px;
            border-radius: 5px;
            margin: 20px 0;
            color: #7f8c8d;
        }}
        .summary {{
            background-color: #fff;
            padding: 20px;
            border-left: 4px solid #3498db;
            margin: 20px 0;
        }}
    </style>
</head>
<body>
    <div class="container">
        <h1>🤖 AI要約レポート</h1>
        
        <div class="meta">
            <strong>📄 元ファイル:</strong> {key}<br>
            <strong>📅 処理日時:</strong> {__import__('datetime').datetime.now().strftime('%Y年%m月%d日 %H:%M:%S')}<br>
            <strong>📊 文字数:</strong> {len(text):,}文字
        </div>
        
        <div class="summary">
            <h2>📝 要約内容</h2>
            {summary.replace(chr(10), '<br>')}
        </div>
        
        <div class="footer">
            Generated by Amazon Bedrock Claude 3 Sonnet
        </div>
    </div>
</body>
</html>
    """
    
    # HTML保存
    output_key = f"html/{key.replace('.txt', '_summary.html').replace('tmp/', '')}"
    s3.put_object(
        Bucket='output-bucket',
        Key=output_key,
        Body=html_content.encode('utf-8'),
        ContentType='text/html; charset=utf-8'
    )
    
    return {
        'bucket': 'output-bucket',
        'key': output_key,
        'status': 'completed'
    }

def generate_summary(text):
    body = json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 1200,
        "messages": [{
            "role": "user",
            "content": f"以下のテキストを読んで、内容を分かりやすく要約してください。重要なポイントを整理して、詳細な要約を作成してください:\\n\\n{text[:8000]}"
        }]
    })
    
    response = bedrock.invoke_model(
        body=body,
        modelId="apac.anthropic.claude-3-sonnet-20240229-v1:0",
        accept="application/json",
        contentType="application/json"
    )
    
    result = json.loads(response.get('body').read())
    return result['content'][0]['text']

2. Step Functions定義

ステップファンクションの流れは以下のようになっています。基本的にはLambdaを順番で使うようになっています。ファイルチェックの際にmp3以外の場合はそのまま終了するようになっています。

スクリーンショット 2025-07-06 144521.png

3. 録音+S3アップロードシステム

簡単なシステムになっており、真ん中のボタンを押すと録音が開始され、もう一度押すとファイルがwav形式からmp3に変換されて、S3にアップロードされます。

スクリーンショット 2025-07-06 15.38.23.png

テスト

以下の画像のような環境でテストしてみました。

IMG_8348.jpg

以下の文章をPollyに読み上げてもらいました。ただ、2500文字程度あるので見たい人はコピーしてみてください。

クラウドコンピューティングの進化と未来への展望について説明します。現代のit業界において、クラウドコンピューティングは革命的な変化をもたらしました。従来のオンプレミス環境からインターネットを通じて、コンピューティングリソースを提供するクラウドサービスへの移行は、企業の運営方法を根本的に変えています。クラウドコンピューティングの最大の利点は、スケーラビリティと柔軟。です。企業は必要に応じて、リソースを増減でき、初期投資を大幅に削減できます。amazonウェブサービス、マイクロソフトアズール、googleクラウドプラットフォームなどの主要プロバイダーは、様々なサービスを提供し、企業のデジタル変革を支援しています。人工知能と機械学習の分野では、クラウドプラットフォームが重要な役割を果たしています。大量のデータ処理と、複雑な計算を必要とするaiアプリケーションは、クラウドの強力なコンピューティング能力なしには実現困難です。自然言語処理、画像認識、予測分析などの技術が、クラウドベースのサービスとして提供されています。セキュリティの観点から見ると、クラウドサービスは従来の懸念を払拭し、むしろ高度なセキュリティ機能を提供しています。暗号化、アクセス、制御、監査ログなどの機能により、データの保護が強化されています。また、災害復旧とバックアップの自動化により、事業継続性も向上しています。エッジコンピューティングの台頭により、クラウドアーキテクチャはさらに進化しています。iotデバイスの普及に伴い、データ処理をエッジで行い、レイテンシを削減する需要が高まっています。ファイブg通信技術との組み合わ、により、リアルタイム処理が可能になり、自動運転車、スマートシティ、産業オートメーションなどの分野で革新が期待されています。サーバーレスコンピューティングは、開発者の生産性を大幅に向上させています。awsラムダ、アズールファンクションズ、googleクラウドファンクションズなどのサービスにより、インフラストラクチャーの管理から解放され、ビジネスロジックの開発に集中できます。

生成されるHTMLページ

出力されたファイルは以下のようになりました。少し長くなったような気もするので、プロンプトを少し変えればその要約対象に適したファイルが出力されるかもしれないですね。

スクリーンショット 2025-07-06 153746.png

まとめと反省点

まとめ

今回は録音+要約システムをQ Developerを使用して作成しました。社会人一年目にすると、知識はある程度ある方かも知れないですが、経験の浅い私でもこのようなシステムを作ることができました。まだ、プロンプトや長い音声、細かい設定の部分などは修正が必要な箇所はあるのですが、AIコーディングエージェントを使うことで、短時間でこのようなシステムを構築できると思います。形にしてみるのは楽しいのでぜひ試してみてください!

反省点

最後に注意書きであり反省点ではあるのですが、完全にわからない部分をAIに頼ったままにするのは良くないと感じました。今回はコードの生成から構築まで、かなりQ Developerに頼って構築しました。Lambdaのコードや各AIサービスの使い方などは自分も見返してわかる部分はまだ大丈夫だとしても、Stepfunctionsに関しては、設定方法などあまり理解しないままAIにやってもらいました。作ってもらったモノから逆に勉強することや、ある程度勉強した上で、その部分をAIに任せるのが良いのではないかと思います。

15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?