3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Bedrock AgentCore Runtimeを使ってお知らせ文を解析して要約・イベントリマインドするアプリを作った

Last updated at Posted at 2026-01-02

課題意識

  • "AIを活用した開発"に関する知見はだいぶ溜まってきた
    • と言いつつここ数ヶ月ClaudeCodeを使っていなかったので最新潮流に追いつけていないが……
  • 一方で,"AIを組み込んだアプリの開発"に関する知見はまだまだ足りない
    • もちろん,SDKやFW(LangChain etc)を使ってLLMを呼び出すことはできるが,「とりあえず呼び出してみた」の域をなかなか出られていない

そこで,2026年は"AIを組み込んだアプリの開発"にチャレンジして知見を溜めていきたいと思っています.

作ったもの

  1. とあるオンラインゲームの「お知らせ」を取得する.
  2. お知らせ内容の要約をDiscordのフォーラムに投稿する.
  3. お知らせ内容に記載されている各種イベント(例えば◯◯キャンペーン開始,✕✕アイテムの交換期限など)の発生日時をDynamoDBに登録する
  4. 登録したイベントの1日前・3日前・1週間前にDiscordのチャネルにリマインド通知を行う.

この2.と3.の一部(お知らせの要約・イベント日時の抽出)にBedrock AgentCore Runtime上で動くアプリを活用しました.

構成

構成図

for-qiita.png

考え方

いわゆる狭義の「エージェント」的な構成とはせず,EventBridge,Lambda,SQSでワークフローを定義し,その中でLLMを呼び出す形にしています.
そのため,LLM活用という観点から言えば,今回の構成は「Runtime使ってみた」というレベルになります.

この構成にした理由は以下の通りです.

  • 過去に(AgentCore Runtimeを使わずLambdaからOpenAI SDKを使う形式で)同種のアプリを作ったことがあり,その既存のワークフローの資材を流用できる
  • 今回はFWにDSPyを使いたいと考えており,今後プロンプトの最適化も行いたいと考えていたため.
    dspy.ReActとtoolsを用いればDSPyでもエージェントは作れるものの,プロンプト最適化に挑戦する最初の題材としては難しくなりすぎると感じた.)
  • 実現したい処理の流れは非常に直線的であり,LLMに「何をやらせるか考えたい」という分岐要素が無かったため

このように,実現したいことを踏まえると,敢えて「エージェント」的に実現する必要は無いと考えました.

しかし,以下のような観点で,より「エージェント」的な実装に挑戦しても良いと考えています.

  • 例えば,Webhook通知時に「定期メンテナンスに関するお知らせの場合は通知をしない」というような条件分岐を行う場合.
    この場合,ワークフローとして「LLMに「is_about_ordinal_maintenance」か判断させその結果に応じてLambda内で分岐してWebhook実行の有無を切り替える,という考え方もあります.
    一方で,「Webhook通知するツール」をエージェントに与えておき,お知らせをエージェントに渡し,通知するかしないかの判断をLLMに担わせ,通知する場合はツールを実行してもらう,という形でも実現できそうです.
    この形式だと判断・実行をAgent内で完結できるので,アプリの実装としては見通しが良くなるように感じます.
  • 単純に面白そうなのでやってみたい

構築した仕組み・機能

お知らせ検知機能

  • EventBridge+Lambdaで実現
  • お知らせの一覧を確認し,取得した情報をDynamoDBに保存
  • 過去に保存したレコードを取得し,新規お知らせがないかを確認
    (ここにはLLMは一切使っていません)
  • 「新規お知らせ」がある場合は,Discord Forumに対するWebhookを実行し,スレッドを作成
    • 本来は,後続のサマリ生成後の投稿時にスレッドを作成しても良いのですが,各お知らせに関する情報をDynamoDBに保存する処理を集約したかったので,このような構成とした
  • 「新規お知らせ」がある場合は,そのお知らせIDと,作成したDiscord ForumのスレッドIDをSQSに詰め,後続の「お知らせ要約・イベント抽出」に渡す

ここはLLMも使っておらず,Lambda上でシンプルなPythonの処理を書いているだけです.
ポイントらしいポイントが特にあるわけではありませんが,Webhookをpostする際に

https://discord.com/api/webhooks/*********/*******?wait=true

のように?wait=trueを付与しないと,レスポンスからスレッドIDを取得することができないことに注意が必要です.
→(参考)https://discord.com/developers/docs/resources/webhook

お知らせ要約・イベント抽出機能

前述の「お知らせ検知」機能から渡されたデータを元に,以下の処理を行います.

  1. お知らせの内容を要約
  2. お知らせに記載されている各種イベントの発生日時を抽出し,DynamoDBに保存
  3. 要約した内容とイベント発生日時をDiscord Forumのスレッドに投稿

ここで1. および 2.の実現のためにBedrock AgentCore Runtimeを利用しています.
Bedrock AgentCoreを使うにあたり,以下の記事を大いに参考にしました.
(ハンズオンイベント自体にも参加させてもらいました.)

自分なりに変更した点は2つあります.

  1. pip -> uv
  2. Strands -> DSPy
1. pip -> uv

まず上記のハンズオンではpipを使っていますが,自分はPythonを使う際はuvを使いたいので,uvを使うように見直しをしています.
と言っても,agentcore configureする際にpyproject.tomlがあれば自動でそれを検知してくれるようなので,agentcoreの資材があるフォルダでuv initし,パッケージのインストール時にはpip installではなくuv addする以外,特に大きくやることに違いはありません.
uv pip compile pyproject.toml -o requirements.txtとかする必要は無い)

2. Strands -> DSPy

使用するFWをStrandsではなくDSPyに変更しました.
こちらもuv add strandsする代わりにuv add dspyした上で,あとは普通にDSPyを使った実装をすれば良いだけです.
以下のようなSignatureを定義して,LLMに対するI/Oを定義しています.

class NoticeSummary(dspy.Signature):
    """Summarise notifications for the online game and extract necessary information."""

    notice_contents_html = dspy.InputField(desc="HTML content of the notice")
    thumbnail_image_url: str = dspy.OutputField(
        desc="URL of an image suitable as a thumbnail for this announcement"
    )
    summary: str = dspy.OutputField(desc="Summary of the notice in Japanese.")
    obtainable_items: List[str] = dspy.OutputField(
        desc="The items indicated in this announcement as available to users."
    )
    events: List[Dict[str, datetime.datetime]] = dspy.OutputField(
        desc="Event names and dates and times of events announced in this notice (e.g., maintenance start, maintenance end, campaign start, campaign end). The event name should be in Japanese. The event name must be understandable to people who do not read this notice."
    )

ポイントとしては,eventsのdescにThe event name must be understandable to people who do not read this notice.と明記したことです.
こうしないと,例えばお知らせのタイトルが「新春◯◯キャンペーン」で本文に「1月7日13時イベント開始!」とあると,eventsには

[{'イベント開始', '2026-01-07 13:00:00'}]

のような出力がなされ,後から見た時に「何のイベントなのか?」がわからなくなってしまいます.
それを避けるために,上記のようなdescの明記を追記しています.
今回はDSPyでのプロンプト最適化までは行っていませんが,最適化余地が大きそうなのはこの部分だと感じました.
(とは言え,上記の指示だけでかなり精度は上がっています.)

Runtimeでは,上記のSignatureを以下のように呼び出しています.

@app.entrypoint
async def invoke_agent(payload, context):
    summariser = dspy.ChainOfThought(NoticeSummary)
    response = summariser(notice_contents_html=payload.get("notice_contents_html", ""))

    # datetimeオブジェクトをISO形式の文字列に変換
    events_iso = convert_datetime_to_iso(response.events)

    # summaryを返す(JSON serializable形式で)
    return {
        "thumbnail_image_url": response.thumbnail_image_url,
        "summary": response.summary,
        "obtainable_items": response.obtainable_items,
        "events": events_iso,
    }


app.run()

上記を見ても分かる通り,AgentCoreで実装するからこうだ,と特に気にしなければいけないポイントはほぼありませんでした.
過去にLambda上でFWやSDKを呼び出したことがある人は,特に引っかかるポイントもなく実装できると思います(アノテーションつけるのを忘れずに,くらい).

イベントリマインド機能

こちらはEventBridge+Lambda+DynamoDBの構成です.
特にLLMは使っていません.

その他,今回初挑戦した事項

  • AWS CDK
    • これまで何となくCDKに苦手意識があり,SAMやCloudFormationを使うことが多かったのですが初挑戦してみた
    • 今まで何故苦手意識があったのかわからないくらいサクッと書けた
    • IaCはコーディングエージェントと相性が良いので今後積極的に使っていきたい
    • この記事の構成図は,CDKをinputにしてClaudeCodeに書いてもらったものをベースにした.
      AWS Diagram MCPを使って図を作ってもらったあと,それをDraw.io形式に書き直してもらいましたが,AWS Diagram MCPの生成物が正直「うーん……」というレベルだったので,最初からMCPを使わずにDraw.ioで書いてもらったほうが良かったかも.
    • AWS Diagram MCPによる生成図
      AWS Diagram MCPによる生成図
  • Ruff
    • これまで,Pythonは書き捨てで終わることが多かったのでLinter・Formatterを使っていなかったが,今後Pythonを使うときの知見蓄積の意味も込めて,Ruffを使ってみた
    • Biome同様,LinterとFormatterが統合されており開発体験が良いと感じた

今後に向けて

前述の通り,「これを"エージェント"として実現するにはどのような構成が良いだろうか?」というのは勉強の題材としては良い題材と感じています.
(AgentCoreのツール等を有効活用できそうです)
また,それ以外の題材についても,今後Bedrock AgentCoreやそれ以外のサービスも使いながら,AIを使った機能の導入に挑戦していきたいと思います.

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?