概要
2024年6月にAWSによって公開された生成AIでAWSアップデートを効率的にキャッチアップ!の記事内で、What’s New Summary Notifierという、AWSのニュース記事を要約してSlackに通知してくれる生成 AI アプリケーションのサンプル実装が公開されております。
そこで、本記事では弊社が運営するオウンドメディア「iret.media」の公開記事のレビューにこの仕組みを転用できないか、を検証してみました。
What’s New Summary Notifierの中身を見てみる
前段として、What’s New Summary Notifierの中身がどうなっているかを確認してみます。
インフラ構築には、AWS CDKが採用されています。
(生成AIでAWSアップデートを効率的にキャッチアップ!から引用)
全体像はこんな感じです。
①Lambda関数(RSSNewsCrawler)が1時間おきに起動し、1時間ごとにWebサイトのRSSを取得(EventBridge)
②最新の記事(1週間以内に公開された記事)が、DynamoDBに登録される
下記のような形で、公開記事のurlをパーティションキーとして、notifier_name(RSS配信元のサイト)・ category(公開記事の種類)・pubtime(記事の公開時刻)・title(記事のタイトル)が登録されています。
※notifier_name
について
ここでは深くは触れませんが、複数のRSS配信サイトをソースとして通知をしたい場合に、配信元を識別するのに有効です。
③DynamoDBへのレコード登録をトリガーに、ニュースを取得して要約するLambda関数(NotifyNewEntry)が起動、Slack(またはTeams)に通知される。
検証スタート
今回の検証では、生成AIに記事の要約ではなく、記事の内容チェックをさせようということになっただけなので、生成AIに与えるプロンプト(指示)を変えるだけで、簡単にカスタマイズができそうです。
Slack通知用のWebhookの設定
Slackのワークフロービルダーにて、ワークフローを作成します。
以下の4つの変数を作成し、
ウェブリクエストのURLを控えておきます。
控えたURLは、こちらの手順を参考にAWS Systems Manager Parameter Storeに保存します。
続いて、ワークフロービルダー画面の右側にあるステップ
>メッセージ
から、チャンネルへメッセージを送信する
を選択し、ステップを追加します。ステップ2の、スレッドでメッセージに返信する
も同様の手順で追加します。
追加する際、それぞれ変数は下記のように挿入してください。
設定値の変更
What’s New Summary Notifierのリポジトリをcloneし、設定値の追加・修正を行います。
cdk.json
"context"
キー配下の設定値を、一部変更します。
デフォルトでは、"modelId"
が "modelId": "anthropic.claude-3-sonnet-20240229-v1:0"
となっており、Claude3 Sonnetが指定されています。今回は、より高い性能を持つと言われているClaude3.5 SonnetのmodelIdを指定してみます。(Claude3.5の性能に関する詳細はこちらを参照)
※Claude3.5利用にあたっては、モデルアクセスを行い、モデルが利用可能になっていることが前提です。
また、
"AwsSolutionsArchitectJapanese": { "outputLanguage": "Japanese. Each sentence must be output in polite and formal desu/masu style", "persona": "solutions architect in AWS" },
の記載の下に、下記を追加します。
"IretEmployeeJapanese": { "outputLanguage": "日本語。各文は丁寧で正式なです/ます調で出力してください。", "persona": "アイレット株式会社(iret, Inc.)の社員" }
なお、ここで指定する "outputLanguage"
はレビューを出力させる言語、"persona"
は回答させるAIモデルに指定するペルソナです。
サンプルの実装では、英語で記載がされていましたが、今回の検証では日本語で指定をしてみました。
最終的に"context"
キー以下の設定値はこのようになっています。
"context": { "modelRegion": "us-east-1", "modelId": "anthropic.claude-3-5-sonnet-20240620-v1:0", "summarizers": { "AwsSolutionsArchitectEnglish": { "outputLanguage": "English.", "persona": "solutions architect in AWS" }, "AwsSolutionsArchitectJapanese": { "outputLanguage": "Japanese. Each sentence must be output in polite and formal desu/masu style", "persona": "solutions architect in AWS" }, "IretEmployeeJapanese": { "outputLanguage": "日本語。各文は丁寧で正式なです/ます調で出力してください。", "persona": "アイレット株式会社(iret, Inc.)の社員" } },
さらに、"notifiers"
キー以下の部分を、下記に変更します。"summarizerName"
の値を先ほど追加したIretEmployeeJapanese
にし、(名前はなんでも可)、"rssUrl"
の部分に、弊社のRSSを指定します。
"notifiers": { "IretMediaBlogs": { "destination": "slack", "summarizerName": "IretEmployeeJapanese", "webhookUrlParameterName": "/WhatsNew/URL", "rssUrl": { "IretMedia": "https://iret.media/feed/" } } },
index.py(whats-new-summary/notify-to-app/index.py)
summarize_blog
関数内の、
変数prompt_data
の中身 を、以下のように書き換えます。
prompt_data = f""" <input>{blog_body}</input> <persona>あなたはプロフェッショナルな{persona}です。</persona> <instruction>以下の観点について、<input></input>タグ内の記事をレビューしてください: - 文法的なエラー、タイポ、および明確さ。 - 適切な見出しやサブ見出しの有無。 - 必要に応じて、図表やスクリーンショットが使用されているか。 - 機能しないコードの回避。ライブラリのバージョンや実行環境の詳細が指定されているか。 - 記事のタイトルと内容の一貫性。 - サービスや製品名が正式名称で記載されているか。 - クレデンシャルが公開されていないか。 - 極端に短いセクションや内容が乏しい部分がないか。 - ライセンス要件と著作権への配慮: - すべての第三者コンテンツ(画像やコードスニペットなど)がライセンスに従って使用されているか確認する。 - 必要に応じて、適切な帰属が提供されているか確認する。 - 記事が著作権や知的財産権を侵害していないか確認する。 修正点と提案を箇条書きで提供してください。重大な問題が見つからない場合は、記事がよく書かれていることを示すフィードバックを提供するか、さらに改善が可能な部分を提案してください。すべてのフィードバックが明確で実行可能であることを確認してください。</instruction> <outputLanguage>{language}</outputLanguage> <summaryRule>記事で特定された主なエラーの種類について要約してください。出力形式は<outputFormat></outputFormat>タグで定義されています。</summaryRule> <outputFormat><thinking>(エラーや提案の箇条書き、または問題が見つからない場合のフィードバック)</thinking><summary>(最終的な要約)</summary></outputFormat> """
※ 2024/10/30追記:AIにレビューしてもらう観点をS3に外出しすることもできます。こうすることで、レビュー観点を修正したい、となった場合、ソースコードに変更を加えずに修正を行うことができます。(いちいちcdk deploy を叩く必要がなくなる)
その場合、S3バケットからレビュー観点を取得する処理及び、index.pyの変数prompt_dataの修正、whats-new-summary-notifier-stack.ts内のnotifyNewEntryRoleにおいて、S3 GetObjectを付与する実装が追加で必要です。それぞれ以下をご参照ください。
【S3バケットからレビュー観点を取得する処理】
source_bucket = "(バケット名)" source_key = "(オブジェクトのキー名)" s3_client = boto3.client('s3') try: instruction_data_from_s3 = s3_client.get_object(Bucket=source_bucket, Key=source_key) instruction_data = instruction_data_from_s3['Body'].read().decode('utf-8') print(instruction_data) except ClientError as e: print(f"Error fetching prompt data from S3: {e}") return None, None
【変数prompt_dataの修正】
prompt_data = f""" <input>{blog_body}</input> <persona>あなたはプロフェッショナルな{persona}です。</persona> <instruction>{instruction_data}</instruction> <outputLanguage>{language}</outputLanguage> <summaryRule>記事で特定された主なエラーの種類について要約してください。出力形式は<outputFormat></outputFormat>タグで定義されています。</summaryRule> <outputFormat><thinking>(エラーや提案の箇条書き、または問題が見つからない場合のフィードバック)</thinking><summary>(最終的な要約)</summary></outputFormat>
【whats-new-summary-notifier-stack.ts内のnotifyNewEntryRoleにおいて、S3 GetObjectを付与する実装】
// 43行目の下に下記を追記 // S3 GetObject権限を追加 new PolicyStatement({ actions: ['s3:GetObject'], effect: Effect.ALLOW, resources: (S3に配置した、レビュー観点として参照させたいドキュメントのarn), }),
デプロイ
※デプロイ前に下記コマンドを叩きます。
npm ci
cdk bootstrap(デプロイを実施するリージョンでbootstrap済みであれば実施不要
cdk deploy
コマンドを叩いてデプロイします。成功すれば下記のような表示が出るかと思います。
maeno1:whats-new-summary-notifier maeno$ cdk deploy --profile XXXXX ・・・・・・(長いので割愛) ・・・・・・ ・・・・・・ ↓ ↓ 成功した場合 ✅ WhatsNewSummaryNotifierStack ✨ Deployment time: 33.86s Stack ARN: arn:aws:cloudformation:<region>:<account id>:stack/WhatsNewSummaryNotifierStack/XXXXXXXXX ✨ Total time: 51.96s
検証結果
Slackに、iret.mediaの投稿レビューがされていることを確認できました🙌
プロンプトで指定した観点に沿って、記事をレビューすることができているようです。
必要に応じて、プロンプト内の、
タグ内で指定しているレビュー観点を調整すれば、AIによるFBの内容は調整できそうですね。
↓
記事レビューの内容
番外編:他にもできそうなこと
まだ試すことはできていないですが、下記のようなこともできそうです。
RSS取得間隔の調整
デフォルトでは、1時間に1回実行されるようになっていますが(マネジメントコンソールの画像参照)、
EventBridgeの設定をいじる事で、RSSの取得間隔を変更することができるようです。
//whats-new-summary-notifier-stack.ts(一部抜粋) //ここでcron式に指定する値を設定 for (const notifierName in notifiers) { const notifier = notifiers[notifierName]; // const cron is a cronOption defined in a notifier. if it is not defined, set default schedule (every hour) const schedule: CronOptions = notifier['schedule'] || { minute: '0', hour: '*', day: '*', month: '*', year: '*', };
レビュー対象記事の範囲の調節
デフォルトでは、過去7日以内に公開された記事がDynamoDBに書き込まれるようになっています。
具体的には、whats-new-summary/rss-crawler/index.pyの、recently_published
関数で実装されています。
# whats-new-summary/rss-crawler/index.py def recently_published(pubdate): """Check if the publication date is recent Args: pubdate (str): The publication date and time """ elapsed_time = datetime.datetime.now() - str2datetime(pubdate) print(elapsed_time) if elapsed_time.days > 7: return False return True
if文における日数の指定を任意の数字に変えることで、書き込まれる対象の記事(通知対象の記事)
の範囲を広げたり、狭めたりすることができます。
# whats-new-summary/rss-crawler/index.py if elapsed_time.days > 7: #この数字を変える return False
以下は、RSSを取得し、DynamoDBにデータを登録する関数の全体のコードです。
# whats-new-summary/rss-crawler/index.py def handler(event, context): notifier_name, notifier = event.values() rss_urls = notifier["rssUrl"] for rss_name, rss_url in rss_urls.items(): rss_result = feedparser.parse(rss_url) print(json.dumps(rss_result)) print("RSS updated " + rss_result["feed"]["updated"]) if not recently_published(rss_result["feed"]["updated"]): # Do not process RSS feeds that have not been updated for a certain period of time. # If you want to retrieve from the past, change this number of days and re-import. print("Skip RSS " + rss_name) continue add_blog(rss_name, rss_result["entries"], notifier_name)
If you want to retrieve from the past, change this number of days and re-import.
というコメントにもあるように、過去の記事も取得できるようにするためには、数字を変えて、データを再インポート(データの洗い替え?)を行う必要があるようです。
まとめ/所感
今回は、「iret.mediaの記事を、AIにレビューを行わせて、Slackに通知する」という検証を行いました。
これまでの業務でAWS CDKは使ったことがありませんでしたが、カスタマイズ自体は簡単に行うことができました。アイデア次第で、色々なカスタマイズができそうですね。
また、今回の検証を通して、OSSをただ試して使うだけでなく、「どう自分の中/自社の業務効率化に活用できるかを考える」視点も大事にしていきたいな、と感じました。