こんにちは!第一開発事業部の大瀧優杏です!🎀

Slackのスレッド、長くなりすぎて追うの大変な時ってありますよね!😣

そこで今回は、Amazon Bedrockを使って
「スレッドの内容を3行に要約してくれるSlack Bot」を作ってみました!

本記事では、

  • Botの作成手順
  • 実際の動作イメージ
  • 実装のポイント
    についてまとめました!

これからSlack Botを作成する方、Slack APIやBedrockを触る方の参考になれば嬉しいです🙌
ぜひ最後までご覧ください!

使用した技術スタック

  • 言語: Python 3.12
  • インフラ管理: AWS SAM (Serverless Application Model)
  • コンピューティング: AWS Lambda
  • モデル: Amazon Bedrock (Anthropic Claude 3 Haiku)
  • API連携: Amazon API Gateway, Slack API (slack_sdk)

Slackアプリの作成と権限設定

Slack API画面にアクセスし、「Create New App」からアプリを作成します。

OAuth & Permissions メニューを開き、以下の Bot Token Scopes を追加します。

OAuth & Permissionsメニューでスコープを設定します。
「Install to Workspace」を実行し、発行された Bot User OAuth Tokenを控えておきます。

  • app_mentions:read (メンションされたイベントを取得するため)
  • chat:write (要約結果をチャンネルに送信するため)
  • channels:history (スレッドの会話履歴を取得するため)

AWS SAMテンプレートの作成

AWS SAMを使用してLambda関数を定義します。
API Gatewayを使用して、Slackからのリクエストを受け取るためのエンドポイントを作成します。
template.yaml を以下の内容で作成します。
コードに自信がなかったので、今回はGeminiを利用して生成しました😆

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  SlackEventsApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod

  SummaryFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: .
      Handler: lambda_function.lambda_handler
      Runtime: python3.12
      Timeout: 60 
      MemorySize: 512
      Events:
        SlackEvents:
          Type: Api
          Properties:
            RestApiId: !Ref SlackEventsApi
            Path: /slack/events
            Method: post
      Policies:
        - Statement:
            - Effect: Allow
              Action: "bedrock:InvokeModel"
              Resource: "*"

Lambda関数の実装

こちらもGeminiを使ってコードを作成しました!

プロジェクト直下にlambda_function.pyを作成します。
処理の流れは以下の通りです。

  • SlackのURL検証(Challenge)への応答
  • ボット自身の発言による無限ループの防止処理
  • Slack APIによるスレッド履歴の取得
  • Amazon Bedrock(Claude 3 Haiku)による要約処理
  • Slackへの結果送信
import os
import json
import boto3
from slack_sdk import WebClient

SLACK_TOKEN = os.environ['SLACK_BOT_TOKEN']
client = WebClient(token=SLACK_TOKEN)
bedrock = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')

def lambda_handler(event, context):
    # event['body'] が辞書型か文字列型か判定してパースする
    body_data = event.get('body')
    if isinstance(body_data, str):
        body = json.loads(body_data)
    else:
        body = body_data

    # SlackのURL検証用(Event Subscriptions設定時に必須)
    if 'challenge' in body:
        return {'statusCode': 200, 'body': body['challenge']}

    slack_event = body.get('event', {})

    # ボット自身の発言による無限ループを防止
    if 'bot_id' in slack_event:
        return {'statusCode': 200}

    channel = slack_event.get('channel')
    thread_ts = slack_event.get('thread_ts') or slack_event.get('ts')

    try:
        # 1. スレッドの履歴を取得
        replies = client.conversations_replies(channel=channel, ts=thread_ts)
        # ボットの発言を除外してテキストを結合
        messages = [m['text'] for m in replies['messages'] if 'bot_id' not in m]
        context_text = "\n".join(messages)

        # 2. Bedrock (Claude 3 Haiku) で要約
        prompt = f"以下のSlackスレッドを、重要点に絞って3行で要約してください。\n\n{context_text}"

        native_request = {
            "anthropic_version": "bedrock-2023-05-31",
            "max_tokens": 512,
            "messages": [{"role": "user", "content": prompt}]
        }

        response = bedrock.invoke_model(
            modelId="anthropic.claude-3-haiku-20240307-v1:0",
            body=json.dumps(native_request)
        )
        result = json.loads(response['body'].read())
        summary = result['content'][0]['text']

        # 3. Slackに返信
        client.chat_postMessage(channel=channel, thread_ts=thread_ts, text=f"要約しました!\n\n{summary}")

    except Exception as e:
        print(f"Error: {e}")
        return {'statusCode': 500}

    return {'statusCode': 200}

ビルドとデプロイ

ターミナルで以下のコマンドを実行し、AWS環境へデプロイします。

sam build
sam deploy --guided

デプロイ完了後、AWSマネジメントコンソールから対象のLambda関数を開き、以下の設定を行います。
環境変数: キーにSLACK_BOT_TOKENを追加し、Slack appで取得したトークンを設定します。

SlackのEvent Subscriptions設定

Slack API画面に戻り、Event Subscriptions を開きます。
「Enable Events」をオンにし、先ほど用意したAPI GatewayのURLを Request URL に貼り付けます。

検証が通り Verified になることを確認します。
Subscribe to bot events に app_mention を追加して保存し、ワークスペースに再インストールを行います。

これで、Slack上でボット宛にメンションを飛ばすと、スレッドを要約して返信してくれます。

実際の使用レポ

実際の会話の一部

@SummaryBotをメンションすると…

成功しました✨✨🥳👏🏻
スレッドの内容が綺麗に3行にまとまりました!

しかし……
実は……

一度のメンションで数回メッセージが送信される仕様になってしまっていました…😭

今後の課題

SummaryBotの重複送信
現在の実装だと、ボットから要約メッセージが2回(場合によっては3回)送信されてしまうという問題が発生していました。

原因を調査したところ、Slack APIの3秒ルールが原因でした。

Slackの仕様として、リクエスト送信後3秒以内にHTTP 200 OKが返らない場合、「失敗」とみなされ、自動的にリトライ(再送)されます。
Amazon Bedrockによる要約処理には数秒〜十数秒かかるため、1回目の処理が終わってリクエストを返却する前にSlackからリトライが届いてしまい、結果としてLambdaが複数回起動して重複送信が起きていたようです。😭
そのため、同一イベントが複数回処理されないような冪等性の考慮も必要になります。

次回の記事では、Lambda関数をSlackへの応答用とBedrockでの要約処理用の2つに分割し、非同期処理を行うアーキテクチャへの改修を行っていきます!
具体的には、API Gatewayからのリクエストには即座に200 OKを返し、その後の要約処理は非同期で別Lambdaに委譲する構成を検討しています。
お楽しみに😆✨