はじめに
JAWS-UG をはじめ、多くの技術コミュニティが connpass でイベントを公開しています。
グループのメンバーになっていればメール通知が届きますが、他のメールに埋もれたり、メンバーになっていないグループのイベントには気づけなかったりで、「面白そう!」と思った頃にはもう満席…を何度も繰り返していました。
普段利用しているSlack で定期的に確認できれば見逃しも減ると思い、connpass のイベントを毎朝 AI が検索・要約して Slack に届けてくれるエージェントを作りました。
本記事では、システム概要・アーキテクチャ・実装のポイントを紹介します。
本記事の情報は 2026 年 6 月時点のものです。
最新情報については公式ドキュメントをご確認ください。
この記事でわかること
- connpass のイベントを AI が検索・要約して Slack に届ける仕組みの全体像
- Amazon Bedrock AgentCore + Strands Agents SDK を使った AI エージェントの作り方
- AWS CDK でエージェント基盤(AgentCore Runtime + EventBridge + Lambda)をデプロイする方法
ターゲット
- AWS Lambda・CDK を触ったことがある方
- AI エージェントで何か実用的なものを作ってみたい方
- connpass のイベント情報を効率よく集めたい方
- Amazon Bedrock AgentCore や Strands Agents SDK に興味がある方
1. ツール概要
本ツールは、connpass API v2 からキーワードに合致するイベントを検索し、AI(Amazon Nova)が要約してくれるサーバーレスアプリケーションです。毎朝の自動配信と、Slack 上でメンションして呼び出す対話的な検索の2つの導線でイベント情報を届けます。
主な機能は以下のとおりです。
- AI による要約: 検索結果を LLM が読みやすく整理(タイトル・日時・場所・参加者数・URL を箇条書き)
- 毎朝のダイジェスト配信: EventBridge で毎朝 JST 9:00 に自動実行
-
複数キーワード対応:
TypeScript,AWS,AIのようにカンマ区切りで興味分野を指定 - レート制限遵守: connpass API の 1 req/sec 制限を Throttle ユーティリティで自動遵守
-
対話的な検索: Slack で
@connpass-scout TypeScript の勉強会教えてのようにメンションすると、その場で検索してスレッドに返信。同じスレッド内で話しかければ会話を継続できる
単純なキーワードマッチ通知との違いは、AI がイベントの中身を読んで要約してくれるところです。リンクを開かなくても Slack 上でざっと読んで「これ行きたい」と判断できるので、朝の情報収集が格段に楽になりました。
2. 使用する主な技術・サービス
| 技術/サービス | 役割 | 参考リンク |
|---|---|---|
| Amazon Bedrock AgentCore | AI エージェントのマネージド実行環境 | https://aws.amazon.com/bedrock/agentcore/ |
| Strands Agents SDK | エージェント構築フレームワーク | https://strandsagents.com/ |
| connpass API v2 | イベント情報の検索・取得 | https://connpass.com/about/api/v2/ |
| SSM Parameter Store | 認証情報の暗号化保管(SecureString) | https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html |
| AWS CDK | インフラのコード管理・デプロイ | https://docs.aws.amazon.com/cdk/v2/guide/home.html |
connpass API v2 を利用するには事前に API キーの申請が必要です。connpass API 利用申請フォームから申請し、審査通過後にキーが発行されます。申請から発行まで数日かかる場合があるため、構築を始める前に申請しておくことをおすすめします。
3. システム構成図
以下が全体像です。「毎朝の自動配信」と「対話的な検索」の2系統が、同じ Bedrock AgentCore Runtime を共有しています。connpass API キー・Slack Bot Token・Signing Secret はどちらの系統からも参照するため、SSM Parameter Store にまとめて保管しています。
3.1 毎朝の自動配信フロー
① EventBridge Rule が cron スケジュール(毎朝 JST 9:00)で Scheduler Lambda を起動
② Lambda が SSM Parameter Store から Slack Bot Token を取得し、「🔍 検索中...」をチャンネルに投稿
③ Lambda が Bedrock AgentCore Runtime に InvokeAgentRuntime でイベント検索プロンプトを送信
④ AgentCore 上の Strands Agent が SSM から connpass API キーを取得
⑤ Agent が connpass API v2 を呼び出してイベントを検索
⑥ Agent が検索結果を Slack mrkdwn 形式で要約し、Lambda にイベント結果(概要・URL)を返す
⑦ Lambda が chat.postMessage / chat.update でプレースホルダーを要約結果に差し替えて Slack に投稿
3.2 対話的な検索フロー
① ユーザーが Slack でボットにメンション(例: @connpass-scout JAWS-UG の直近のイベント3件教えて!)
② Slack の Event Subscriptions(app_mention)が API Gateway にイベントを送信
③ API Gateway が POST /slack/events で slack-verify Lambda を呼び出す
④ slack-verify が Signing Secret でリクエスト署名を検証し、SQS に SendMessage でエンキュー(即座に HTTP 200 OK を返す)
⑤ SQS から slack-process Lambda がポーリング取得(Event Source Mapping, batchSize: 1)
⑥ slack-process が Bedrock AgentCore Runtime を InvokeAgentRuntime で呼び出す(session = thread_ts)
⑦ Agent が connpass API v2 を呼び出してイベントを検索
⑧ Agent の応答(イベント結果・概要・URL)が slack-process に返る
⑨ slack-process が Slack スレッドに chat.postMessage で返信
4. 構築方法
前提条件
- AWS アカウントが作成済みであること
- AWS CLI が設定済みであること
- Node.js
22.x以上がインストール済みであること - connpass API v2 のキーを取得済みであること
- Slack App を作成し、Bot Token を取得済みであること
手順
1. Slack App の作成
https://api.slack.com/apps → Create New App → From scratch を選択します。
-
App Name:
connpass-scout-agent(任意) - Workspace: デプロイ先のワークスペースを選択
2. Bot Token Scopes の設定
左メニュー OAuth & Permissions → Scopes → Bot Token Scopes に以下を追加します。
| Scope | 用途 |
|---|---|
chat:write |
チャンネル/スレッドへのメッセージ投稿 |
app_mentions:read |
メンションイベントの受信(対話機能で使用) |
3. ワークスペースへインストール
同ページ上部の Install to Workspace → 許可します。
xoxb- で始まる Bot User OAuth Token が表示されるのでメモしておきます(後で SSM に登録します)。
4. Signing Secret の取得
左メニュー Basic Information → App Credentials → Signing Secret をメモします(こちらも SSM に登録します)。
5. Bot をチャンネルに招待 & チャンネル ID を取得
投稿先チャンネルで以下を実行して Bot を招待します。
/invite @connpass-scout-agent(Slack App名)
また、デプロイ時に チャンネル ID が必要です。Slack でチャンネル名を右クリック → 「チャンネル詳細を表示」の最下部、または URL(https://app.slack.com/client/TXXXXX/C01234ABCDE)の末尾部分が C で始まるチャンネル ID です。これもメモしておきます。
6. connpass API キーの取得
connpass の API利用申請フォーム から申請します(個人利用の場合は無料)。
審査通過後に API キーがもらえます。
7. リポジトリのフォークとクローン
GitHub でリポジトリをフォークし、自分のアカウントにコピーしてから作業します。
# GitHub 上で Fork ボタンを押した後
git clone https://github.com/<あなたのユーザー名>/connpass-scout-agent.git
cd connpass-scout-agent
npm install
8. SSM Parameter Store に認証情報を登録
# connpass API キー
aws ssm put-parameter \
--name /connpass-scout-agent/connpass/api-key \
--type SecureString \
--value "<あなたの connpass API キー>"
# Slack Bot Token
aws ssm put-parameter \
--name /connpass-scout-agent/slack/bot-token \
--type SecureString \
--value "xoxb-xxxx-xxxx-xxxx"
# Slack Signing Secret
aws ssm put-parameter \
--name /connpass-scout-agent/slack/signing-secret \
--type SecureString \
--value "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
API キーや Token が漏洩すると第三者に悪用される可能性があります。
SecureString で暗号化して管理し、リポジトリには絶対にコミットしないでください。
9. エージェントのビルド
AgentCore Runtime にデプロイするためのパッケージを生成します。
npm run package --workspace=@connpass-scout-agent/agent
このコマンドで packages/agent/deploy/ にデプロイ用のアーティファクトが生成されます。CDK はこのディレクトリを zip にして AgentCore にアップロードします。
10. CDK Bootstrap(初回のみ)
cd packages/infra
npx cdk bootstrap
11. デプロイ
npx cdk deploy --all -c slackChannelId=C01234ABCDE
検索キーワードもデプロイ時に指定できます。
npx cdk deploy --all \
-c slackChannelId=C01234ABCDE \
-c searchKeywords="TypeScript,React,AWS,AI"
設定可能な主な Context パラメータは以下のとおりです。
| パラメータ | 説明 | デフォルト値 |
|---|---|---|
| slackChannelId | Slack チャンネル ID | C00000000 |
| searchKeywords | 検索キーワード(カンマ区切り) | JAWS,AWS,AI |
CDK スタックで作成される AWS リソース:
- Bedrock AgentCore Runtime: Node.js 22.x / Strands Agent + connpass 検索ツール
- Lambda(Scheduler): Node.js 22.x / ARM64 / メモリ 256 MB / タイムアウト 5 分
- EventBridge Rule: cron(0 0 * * ? *) — 毎日 UTC 0:00 = JST 9:00
-
IAM Policy:
bedrock-agentcore:InvokeAgentRuntime,ssm:GetParameter(対象パスのみ限定) -
IAM Policy(AgentCore):
bedrock:InvokeModel,bedrock:InvokeModelWithResponseStream(Nova モデルファミリーのみに限定)
12. Event Subscriptions の設定(デプロイ後)
デプロイ完了後、CDK の出力に含まれる SlackEventsEndpoint の URL を使って Slack App の Event Subscriptions を設定します。
-
Slack App 管理画面 → 左メニュー
Event Subscriptions→Enable Events: On -
Request URL にデプロイ出力の
SlackEventsEndpointを貼り付け -
Subscribe to bot eventsでapp_mentionを追加 Save Changes
5. 動作確認
定期配信(Scheduler)
デプロイできたら、Lambda コンソールから手動テストしてみます。
- AWS マネジメントコンソールで Scheduler Lambda 関数を開く
-
Testボタンをクリック(ペイロードは空{}で OK) - Slack チャンネルにイベントダイジェストが届けば成功 🎉
AgentCore Runtime を直接叩いて疎通確認することもできます。
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-arn "<デプロイ時に出力された AgentRuntimeArn>" \
--payload '{"prompt": "TypeScript の勉強会を探して"}' \
--content-type application/json \
/tmp/response.json \
&& cat /tmp/response.json
{"reply": "..."} 形式でイベント情報が返ってくれば OK です。
@メンションによる対話検索
Event Subscriptions の設定が完了していれば、Slack から直接メンションして検索もできます。
@connpass-scout-agent TypeScript の勉強会を探して
6. 実装のポイント
Strands Agents SDK でエージェントを定義する
エージェントの定義はかなりシンプルです。モデル・プロンプト・ツールを渡すだけ。
import { Agent, BedrockModel } from "@strands-agents/sdk";
const agent = new Agent({
name: "connpass-scout-agent",
model: new BedrockModel({
modelId: "global.amazon.nova-2-lite-v1:0",
region: "ap-northeast-1",
}),
systemPrompt: `あなたは connpass のイベント情報から
ユーザーの興味分野に合うものを探して紹介するアシスタントです。
## ルール
- 推測でイベント情報を作り出さず、ツールの検索結果のみを根拠に回答してください
- connpass API のレート制限があるため、複数キーワードの検索は1つずつ順番に実行してください
## 出力フォーマット(Slack mrkdwn 形式)
- 各イベントは <URL|タイトル> のリンク形式で出力
- 📅 日時 👥 参加者/定員 の絵文字付きで情報を整理
- description を元にイベント内容を30文字以内で要約
...`,
tools: [connpassSearchTool],
});
-
軽量モデルを選択: 「検索して要約する」程度のタスクなので コスト重視で
nova-2-liteを選定 -
出力形式を Slack mrkdwn で指定: エージェントの応答をそのまま Slack に投稿するため、
<URL|タイトル>のリンク形式や絵文字を使った書式をシステムプロンプトで指示しています。これにより、LLM の出力を後加工せずそのまま Block Kit の mrkdwn セクションに流せます
Tool 定義は Zod でスキーマを書く
connpass 検索ツールの入力スキーマを Zod で定義しておくと、LLM が適切な引数でツールを呼び出してくれます。
import { tool } from "@strands-agents/sdk";
import { z } from "zod";
const connpassSearchTool = tool({
name: "search_connpass_events",
description: "connpass からキーワードに合致する技術コミュニティイベントを検索する",
inputSchema: z.object({
keywords: z.array(z.string().min(1)).min(1)
.describe('検索キーワードの配列(AND検索)。例: ["TypeScript", "React"]'),
count: z.number().int().min(1).max(100).optional()
.describe("取得する最大件数(省略時は10)"),
}),
callback: async (input) => {
const result = await client.searchEvents({
keyword: input.keywords,
count: input.count ?? 10,
order: 2, // 開催日時順
});
return result.events.map(toDigestEvent);
},
});
LLM に返すデータは description の HTML をストリップした先頭 400 文字と、タイトル・日時・場所・参加者数(キャンセル待ち含む)・URL に絞っています。トークンの無駄遣いを防ぎつつ、要約に必要な情報だけ渡すようにしています。
connpass API のレート制限とリトライ
connpass API v2 は 1 リクエスト/秒の制限があります。複数キーワードで検索するとリクエストが連続するので、Throttle ユーティリティで前回リクエストから 2 秒間隔を空けるようにしています。
const response = await throttle(() =>
fetchImpl(url, {
headers: {
"X-API-Key": options.apiKey,
"User-Agent": options.userAgent,
},
})
);
さらに、万が一 429(Too Many Requests)が返ってきた場合に備えて、エクスポネンシャルバックオフ(2s → 4s → 8s)でリトライする仕組みも入れています。connpass API はレート制限が厳しめなので、余裕を持ったスロットル間隔 + リトライの二段構えにしました。
Slack への投稿を「検索中...」→ 結果差し替えにする
ユーザー体験として、エージェントの応答を待つ間何も表示されないのは不安なので、まず「🔍 検索中...」というメッセージを投稿し、エージェントの応答が返ったら chat.update で本文に差し替える擬似ストリーミング方式にしています。
投稿メッセージも Slack の Block Kit(mrkdwn セクション)で構造化しているので、太字やリンクが効いた読みやすい形式で届きます。
ハマりポイント: SSM 動的参照が使えない
当初は cdk.SecretValue.ssmSecure() で SSM SecureString の動的参照({{resolve:ssm-secure:...}})を使おうとしましたが、AWS::BedrockAgentCore::Runtime の EnvironmentVariables ではこの動的参照がサポートされておらず、デプロイ時にエラーになりました。
そのため、環境変数にはパラメータ名だけを渡し、Runtime のコード側で SSM から API キーを取得する方式に変更しています。Runtime の実行ロールには ssm:GetParameter 権限を付与します。
// Runtime ロールに SSM 読み取り権限を追加
runtime.addToRolePolicy(
new iam.PolicyStatement({
sid: "ReadConnpassApiKey",
actions: ["ssm:GetParameter"],
resources: [
cdk.Arn.format({
service: "ssm",
resource: "parameter",
resourceName: props.connpassApiKeyParameterName.replace(/^\//, ""),
}, this),
],
}),
);
AgentCore には AgentCore Identity + Token Vault という仕組みがあり、外部 API の認証情報を AgentCore 側で安全に管理できます。本来はこちらに寄せるのが推奨構成ですが、今回のツールは個人利用を前提としているため、コスト面を考えて SSM Parameter Store を使うシンプルな方式を採用しています。
まとめ
Amazon Bedrock AgentCore と Strands Agents SDK を使って、connpass のイベントを毎朝 AI が要約して Slack に届けてくれるエージェントを作りました。
毎朝 Slack を開くと「今月こんな勉強会あるよ」と教えてくれるので、イベントを見逃すことが少なくなったと実感しています。気になるキーワードがあればその場でメンションして深掘りもできるようになりました。運用しながら知見が溜まってきたら、また記事にしたいと思います。










