3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【全世界待望】AIの最新情報を自動でキャッチアップする!

Last updated at Posted at 2025-05-20

はじめに

AIの進歩は目まぐるしいですね!
みなさん、AIの最新情報をキャッチアップできていますか?:dizzy_face:

AIの最新情報キャッチアップのここが大変...

  • 毎日のように情報がアップデートされている
  • 知らない単語が増えてくる
  • キャッチアップするにも何をキャッチアップすればよいかわからない
  • 毎日調べるのは手間がかかる

これ、解消しましょう!!

AIの最新情報を自動でキャッチアップする!

どうやって?

以下の仕組みを考えてみました!

  • AIの最新情報を:arrow_right: Google Trendsから取得
  • 自動で:arrow_right: Azure Logic Appsでワークフロー化
  • キャッチアップする:arrow_right: Amazon Bedrockで要約

後述しますが、Azure Logic Appsを利用しているのは、普段Teamsを利用しており、簡単にTeamsと連携できるためとなります。他のツール(Slackなど)と連携する場合は、Azure Logic Appsに限る必要はありません。

後述しますが、Amazon Bedrockを利用しているのは、簡単にマルチエージェントでWeb検索ができるためとなります。他のやり方でも問題ありません。

構成

ざっくりとした構成は以下:point_down:となります。

image.png

詳細をご説明します。

Google Trendsから取得

  • AWS LambdaからAPIをたたいてGoogle Trendsの関連キーワードを取得する。

image.png

Google Trendsとは

Google Trends(グーグルトレンド)は、Google 検索での上位の検索クエリを分析し、トレンドをグラフで見ることができるツール。
リアルタイムの検索トレンドとして急上昇ワードを見ることができるだけでなく、検索キーワードから1年を振り返るなどで、長いスパンでの検索トレンドから毎日のニュースまで幅広く知ることができる。
https://ja.wikipedia.org/wiki/Google_Trends

検索キーワード「AI」で探す。

image.png

「関連トピック」や「関連キーワード」もわかる。

image.png

AIの最新情報はGoogle Trendsで「AI」の「関連キーワード」から取得します。
情報の取得は SERP API を利用することにしました。

SERP APIでアカウント登録をして、API Keyを取得します。

image.png

アカウント登録で、無料で 100回/月 までAPI実行が可能です。(2025/5/20時点)

他にもGoogle Trendsの情報を取得する以下を確認してみましたが、いずれもGoogleのアクセス制限により情報取得ができませんでした。(2025/5/20時点)

  • Playwright (Web操作を自動で行うツール。)
  • pytrends (Google TrendsをPythonで取得する。)

AWS Lambdaは以下のように実装します。

  • ランタイム Python 3.12
    (追加するレイヤーのrequestsのバージョンに合わせています。)
  • アーキテクチャ x86_64
lambda_function.py
import json
import os
import requests

def lambda_handler(event, context):
    api_key = os.getenv("SERPAPI_API_KEY")
    if not api_key:
        return {
            "statusCode": 500,
            "body": json.dumps({"error": "API key not found"})
        }

    url = "https://serpapi.com/search"
    params = {
        "engine": "google_trends",
        "date": "now 1-d",
        "q": "AI",
        "data_type": "RELATED_QUERIES",
        "hl": "ja",
        "geo": "JP",
        "tz": -540,
        "api_key": api_key
    }

    response = requests.get(url, params=params)
    data = response.json()

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json;charset=UTF-8"},
        "body": json.dumps(rising_queries, ensure_ascii=False)
    }

「設定」タブの「一般設定」で「タイムアウト」を「30秒」に指定します。

image.png


「設定」タブの「関数URL」で「関数URLを作成」を行います。

  • 認証タイプNONE
  • 呼び出しモードBUFFERED(デフォルト)
  • オリジン間リソース共有(CORS)設定チェック
  • 許可オリジン*
  • 許可メソッド*

関数URLをAzure Logic Appsから呼び出すためとなります。

必要に応じて関数URLの認証タイプ等を設定しセキュアにしてください。


「設定」タブの「環境変数」で取得したSERP APIのAPI Keyを指定します。
キー:SERPAPI_API_KEY
値:取得したSERP APIのAPI Key

image.png

今回は 個人利用 かつ 検証のため 環境変数にAPI Keyを設定していますが、本格利用する場合は、AWS Secret Manager等を用いてセキュアに保存しましょう。


「コード」タブの下部より「レイヤーの追加」してrequestsを使用できるようにします。

arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p312-requests:13

image.png


コードの補足説明

(抜粋)lambda_function.py
    params = {
        "engine": "google_trends",
        "date": "now 1-d",
        "q": "AI",
        "data_type": "RELATED_QUERIES",
        "hl": "ja",
        "geo": "JP",
        "tz": -540,
        "api_key": api_key
    }
  • engine:Google Trendsを指定します。google_trends
  • date:「過去1日」を指定します。now 1-d
  • q:検索キーワード「AI」を指定します。AI
  • data_type:検索タイプ「関連キーワード」を指定します。RELATED_QUERIES
  • hl:言語「日本語」を指定します。ja
  • geo:ロケーション「日本」を指定します。JP
  • tz:タイムゾーン「Asia/Tokyo」を指定します。-540
  • api_key:API Keyを指定します。api_key

Amazon Bedrockで要約

  • Google Trendsから取得した関連キーワードをWeb検索して最新情報を取得する。
  • 取得した最新情報をAmazon Bedrockで要約する。

image.png

より柔軟な回答を生成するため、マルチエージェント構成としました。
こちら:point_down:の記事が参考になります:bow:

実装した構成は以下となります。

image.png

AWS Lambdaは以下のように実装します。

  • ランタイム Python 3.13
  • アーキテクチャ x86_64
lambda_function.py
import json
import uuid
import boto3
from botocore.exceptions import ClientError

agent_id = "XXX" # 作成したエージェントIDを指定します。
agent_alias_id = "YYY" # 作成したエージェントエイリアスIDを指定します。

def initialize_session():
    client = boto3.client("bedrock-agent-runtime")
    session_id = str(uuid.uuid4())
    messages = []
    return client, session_id, messages

def invoke_bedrock_agent(client, session_id, prompt):
    try:
        response = client.invoke_agent(
            agentId=agent_id,
            agentAliasId=agent_alias_id,
            sessionId=session_id,
            enableTrace=True,
            inputText=prompt
        )
        return response
    except Exception as e:
        print(f"Error in invoke_bedrock_agent: {str(e)}")
        return None

def handle_agent_response(response):
    try:
        completion_events = response.get("completion", [])
        results = []
        for event in completion_events:
            if "trace" in event:
                trace_data = event.get("trace", {})
                print(f"Trace: {trace_data}")
            if "chunk" in event:
                answer = event["chunk"]["bytes"].decode()
                print(f"Chunk: {answer}")
                results.append(answer)
        return results
    except Exception as e:
        print(f"Error processing response: {str(e)}")
        raise

def lambda_handler(event, context):
    client, session_id, messages = initialize_session()
    # print("Received event:", json.dumps(event, ensure_ascii=False))

    try:
        query = event.get("body")
        input_text = f"""
AI関連キーワードの「{query}」について概要(「{query}」とは?)とその詳細を説明してください。
説明口調で、かつ、見やすいようにマークダウン形式で出力してください。関連リンクは不要です。
        """

        response = invoke_bedrock_agent(client, session_id, input_text)
        results = handle_agent_response(response)
        print("results:", results)

        return {
            "statusCode": 200,
            "headers": {
                'Access-Control-Allow-Headers': 'Authorization, Content-Type',
                'Access-Control-Allow-Origin': '*',
                'Access-Control-Allow-Methods': 'OPTIONS, POST',
                'Content-Type': 'application/json;charset=UTF-8'
            },
            "body": json.dumps({"summary": ", ".join(results)}, ensure_ascii=False)
        }
    except Exception as e:
        print("Lambda Error:", str(e))
        return {
            "statusCode": 500,
            "headers": {
                'Access-Control-Allow-Headers': 'Authorization, Content-Type',
                'Access-Control-Allow-Origin': '*',
                'Access-Control-Allow-Methods': 'OPTIONS, POST',
                'Content-Type': 'application/json;charset=UTF-8'
            },
            "body": json.dumps({"error": str(e)}, ensure_ascii=False)
        }

「設定」タブの「一般設定」で「タイムアウト」を「2分」に指定します。

image.png

マルチエージェント構成のため、1分以上 回答生成に時間がかかります。
また、Azure Logic Appsのタイムアウトが 2分 のため、その値に合わせています。


「設定」タブの「関数URL」で「関数URLを作成」を行います。

  • 認証タイプNONE
  • 呼び出しモードBUFFERED(デフォルト)
  • オリジン間リソース共有(CORS)設定チェック
  • 許可オリジン*
  • 許可メソッド*

関数URLをAzure Logic Appsから呼び出すためとなります。

必要に応じて関数URLの認証タイプ等を設定しセキュアにしてください。


コードの補足説明

(抜粋)lambda_function.py
        input_text = f"""
AI関連キーワードの「{query}」について概要(「{query}」とは?)とその詳細を説明してください。
説明口調で、かつ、見やすいようにマークダウン形式で出力してください。関連リンクは不要です。
        """

上記でプロンプトを指定しています。
思った回答が得られない場合は、こちらのプロンプトを修正してみてください。

Azure Logic Appsでワークフロー化

  • Azure Logic Appsで定期実行する。
  • 生成された情報をTeamsに投稿する。

image.png


Logic Apps ワークフロー補足

【前半部分】

  • Recurrence:毎週 月、火、水、木、金に実行するように指定する。
  • HTTP:Google Trendsから取得するLambdaの関数URLを指定する。
  • チャットまたはチャネルでメッセージを投稿する:取得したGoogle Trendsの結果をTeamsに投稿する。

image.png

【後半部分】

  • For each:Google Trendsで取得したクエリは複数取得している。そのため、並列実行でLogic Appsの処理時間を短縮する。
  • HTTP 1:Google Trendsで取得したクエリに対して、Web検索して要約をするLambdaの関数URLを指定する。
  • チャットまたはチャネルでメッセージを投稿する:生成された要約をTeamsに投稿する。

image.png

ロジックアプリコード(全体)
{
    "definition": {
        "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
        "contentVersion": "1.0.0.0",
        "triggers": {
            "Recurrence": {
                "recurrence": {
                    "interval": 1,
                    "frequency": "Week",
                    "timeZone": "Tokyo Standard Time",
                    "schedule": {
                        "hours": [
                            8
                        ],
                        "minutes": [
                            0
                        ],
                        "weekDays": [
                            "Monday",
                            "Tuesday",
                            "Wednesday",
                            "Thursday",
                            "Friday"
                        ]
                    }
                },
                "evaluatedRecurrence": {
                    "interval": 1,
                    "frequency": "Week",
                    "timeZone": "Tokyo Standard Time",
                    "schedule": {
                        "hours": [
                            8
                        ],
                        "minutes": [
                            0
                        ],
                        "weekDays": [
                            "Monday",
                            "Tuesday",
                            "Wednesday",
                            "Thursday",
                            "Friday"
                        ]
                    }
                },
                "type": "Recurrence"
            }
        },
        "actions": {
            "HTTP": {
                "runAfter": {},
                "type": "Http",
                "inputs": {
                    "uri": "[Google Trendsから取得するLambdaの関数URL]",
                    "method": "POST",
                    "retryPolicy": {
                        "type": "none"
                    }
                },
                "runtimeConfiguration": {
                    "contentTransfer": {
                        "transferMode": "Chunked"
                    }
                }
            },
            "Parse_JSON": {
                "runAfter": {
                    "HTTP": [
                        "Succeeded"
                    ]
                },
                "type": "ParseJson",
                "inputs": {
                    "content": "@body('HTTP')",
                    "schema": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "query": {
                                    "type": "string"
                                },
                                "value": {
                                    "type": "string"
                                }
                            },
                            "required": [
                                "query",
                                "value"
                            ]
                        }
                    }
                }
            },
            "Compose": {
                "runAfter": {
                    "Parse_JSON": [
                        "Succeeded"
                    ]
                },
                "type": "Compose",
                "inputs": "@body('Parse_JSON')"
            },
            "チャットまたはチャネルでメッセージを投稿する": {
                "runAfter": {
                    "Create_HTML_table": [
                        "Succeeded"
                    ]
                },
                "type": "ApiConnection",
                "inputs": {
                    "host": {
                        "connection": {
                            "name": "@parameters('$connections')['teams']['connectionId']"
                        }
                    },
                    "method": "post",
                    "body": {
                        "recipient": "XXX",
                        "messageBody": "<p class=\"editor-paragraph\">■■■■</p><p class=\"editor-paragraph\"><b><strong class=\"editor-text-bold\">【 最新(</strong></b>@{convertFromUtc(utcNow(),'Tokyo Standard Time','yyyy/MM/dd')}<b><strong class=\"editor-text-bold\">)</strong></b><b><strong class=\"editor-text-bold\">のAIトレンド】</strong></b></p><br><p class=\"editor-paragraph\">@{body('Create_HTML_table')}</p>"
                    },
                    "path": "/beta/teams/conversation/message/poster/Flow bot/location/@{encodeURIComponent('Group chat')}"
                }
            },
            "Create_HTML_table": {
                "runAfter": {
                    "Compose": [
                        "Succeeded"
                    ]
                },
                "type": "Table",
                "inputs": {
                    "from": "@outputs('Compose')",
                    "format": "HTML"
                }
            },
            "チャットまたはチャネルでメッセージを投稿する_1": {
                "runAfter": {
                    "Join": [
                        "Succeeded"
                    ]
                },
                "type": "ApiConnection",
                "inputs": {
                    "host": {
                        "connection": {
                            "name": "@parameters('$connections')['teams']['connectionId']"
                        }
                    },
                    "method": "post",
                    "body": {
                        "recipient": "XXX",
                        "messageBody": "<p class=\"editor-paragraph\">■■■■</p><p class=\"editor-paragraph\"><b><strong class=\"editor-text-bold\">【 最新(</strong></b>@{convertFromUtc(utcNow(),'Tokyo Standard Time','yyyy/MM/dd')}<b><strong class=\"editor-text-bold\">)</strong></b><b><strong class=\"editor-text-bold\">の</strong></b><b><strong class=\"editor-text-bold\">AIトレンドの要約】</strong></b></p><br><p class=\"editor-paragraph\">@{body('Join')}</p>"
                    },
                    "path": "/beta/teams/conversation/message/poster/Flow bot/location/@{encodeURIComponent('Group chat')}"
                }
            },
            "For_each": {
                "foreach": "@outputs('Compose')",
                "actions": {
                    "HTTP_1": {
                        "type": "Http",
                        "inputs": {
                            "uri": "https://6fpuzjrqps6guvzmbu7pbhmckm0jprvi.lambda-url.ap-northeast-1.on.aws/",
                            "method": "POST",
                            "body": "@items('For_each')['query']",
                            "retryPolicy": {
                                "type": "none"
                            }
                        },
                        "runtimeConfiguration": {
                            "contentTransfer": {
                                "transferMode": "Chunked"
                            }
                        }
                    },
                    "Append_to_array_variable": {
                        "runAfter": {
                            "Parse_JSON_2": [
                                "Succeeded"
                            ]
                        },
                        "type": "AppendToArrayVariable",
                        "inputs": {
                            "name": "variable1",
                            "value": "@body('Parse_JSON_2')?['summary']"
                        }
                    },
                    "Parse_JSON_2": {
                        "runAfter": {
                            "HTTP_1": [
                                "Succeeded"
                            ]
                        },
                        "type": "ParseJson",
                        "inputs": {
                            "content": "@body('HTTP_1')",
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "summary": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "runAfter": {
                    "Initialize_variables": [
                        "Succeeded"
                    ]
                },
                "type": "Foreach"
            },
            "Initialize_variables": {
                "runAfter": {
                    "チャットまたはチャネルでメッセージを投稿する": [
                        "Succeeded"
                    ]
                },
                "type": "InitializeVariable",
                "inputs": {
                    "variables": [
                        {
                            "name": "variable1",
                            "type": "array"
                        }
                    ]
                }
            },
            "Join": {
                "runAfter": {
                    "For_each": [
                        "Succeeded"
                    ]
                },
                "type": "Join",
                "inputs": {
                    "from": "@variables('variable1')",
                    "joinWith": "<br><br><br><br>==========<br><br><br><br>"
                }
            }
        },
        "outputs": {},
        "parameters": {
            "$connections": {
                "type": "Object",
                "defaultValue": {}
            }
        }
    },
    "parameters": {
        "$connections": {
            "type": "Object",
            "value": {
                "teams": {
                    "id": "XXX",
                    "connectionId": "XXX",
                    "connectionName": "teams"
                }
            }
        }
    }
}

実行結果

定刻になるとTeamsに通知が来ます。
お試しのため、以下は関連キーワード Top 3 のみを取得しています。

image.png

ひとつひとつの関連キーワードについて要約を生成しています。

image.png

おおー!いい感じですね:thumbsup:

おわりに

  • AIの最新情報を毎日自動でキャッチアップできるようになった!
  • Azure、Google、AWSの構成でやってみた。
    Azure:簡単にTeamsと連携できる。
    Google:検索の関連キーワードを取得する。
    AWS:簡単にマルチエージェント構成ができる。

Azure Logic Appsを利用しているのは、普段Teamsを利用しており、簡単にTeamsと連携できるためとなります。他のツール(Slackなど)と連携する場合は、Azure Logic Appsに限る必要はありません。

Amazon Bedrockを利用しているのは、簡単にマルチエージェントでWeb検索ができるためとなります。他のやり方でも問題ありません。

  • この構成は私が検証してみたものですので、もっとセキュアに、もっと効率よく変更してください!
3
4
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
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?