【Slack連携】OpenAIでYahoo!ニュースの主要トピックス新着記事(RSS)を要約し、Slackに投稿する
手軽に素早く最新ニュースを要約する方法を調べました
性能がどんどん上がっているOpenAIですが、もちろん記事の要約も簡単にできちゃうようです。
今回は試しに、Yahoo!ニュースの主要トピックスの記事のRSSを読み込んで、要約してSlack連携する処理を下記の記事を参考に作ってみました。
まずは、連携の準備として、OpenAIのシークレットキーの発行とSlackのIncomingWebhooksのURLを取得します。
OpenAIのシークレットキーの発行
OpenAIのログイン画面からログインし、下記のようにログイン直後の選択画面に遷移するので、APIを選択します。
その後、表示される画面のサイドメニュー「API keys」を選択します。
API keysの画面に遷移しますので、[+Create new secret key]ボタンでシークレットキーを生成し、コピーしておきましょう。
SlackのIncomingWebhooksのURLを取得
Slackに連携する機能として、IncomingWebhooksがある。
Incoming Webhooks は他のアプリから Slack にメッセージを投稿するためのとてもシンプルな方法です。Incoming Webhook を作成すると、メッセージとその他のオプションを含む JSON ペイロードを送るための一意な URL が払い出されます。
https://slack.dev/java-slack-sdk/guides/ja/incoming-webhooks
このIncomingWebhooksを使っていきます。
Slackアプリの新規作成
Slack アプリ管理画面 にアクセスして、新規でアプリを作ります。
a.[Create New App]をクリックします。
b.From scratchで進めます。
c.アプリの名前とSlackのワークスペースを設定します。
- App Name:アプリの名前を入力します。
- Pick a workspace to develop your app in::Slackのワークスペースを選択します。
そして、[Create App]をクリックします。
WebhookのURLを発行
a.アプリを新規作成後に遷移した画面のサイドメニューのFeatures項目の[Incoming Webhooks]を選択します。
b.Activate Incomig Webhooksを[ON]にします。
c.その後、Settings項目の[Basic Information]を選択し、[Install to Workspace]をクリックします。
d.チャンネルを選び[許可する]をクリックしてインストールします。
e.サイドメニューのFeatures項目の[Incoming Webhooks]を選択します。許可した後は、Webhook URLが作成されています。
ローカルでPythonのスクリプトを実行する
私は、Macを利用しておりますので、その手順となります。
a.Pythonのライブラリインストールします。
※私はすでに、「openai」「requests」はインストールしてありました。適宜必要なライブラリはインストールしてください。
% pip install feedparser
インストール後の確認です。
% pip list
Package Version
------------------ ---------
feedparser 6.0.10
openai 1.3.3
requests 2.31.0
b.Pythonのスクリプトyahoo_openai.pyを準備します。(試しに、参考サイトのスクリプトそのままコピーしました)
import openai
import feedparser
import json
import requests
from typing import List, Dict
from datetime import datetime, timedelta, timezone
import os
# タイムゾーンを日本時間に設定
JST = timezone(timedelta(hours=+9))
# APIキーの設定
openai.api_key = os.environ["OPENAI_API_KEY"]
# Slack Webhook URL
WEBHOOK_URL = "{通知先のWebhook URL}"
# OpenAI API モデル名
OPENAI_MODEL: str = "gpt-3.5-turbo"
# RSSフィードURL(DevelopersIO)
FEED_URL = "https://dev.classmethod.jp/feed/"
SYSTEM_PARAMETER = """```
与えられたフィードの情報を、以下の制約条件をもとに要約を出力してください。
制約条件:
・文章は簡潔にわかりやすく。
・箇条書きで3行で出力。
・要約した文章は日本語へ翻訳。
・最終的な結論を含めること。
期待する出力フォーマット:
1.
2.
3.
```"""
def get_feed_entries() -> List[Dict]:
"""
RSSフィードの取得し、過去1時間以内の更新のみを取得する
"""
updated_since = datetime.now(JST) - timedelta(hours=1)
# RSSフィードの取得
feed = feedparser.parse(FEED_URL)
# 更新日時の閾値よりも新しいエントリーのみを取得
new_entries: List[Dict] = [
entry for entry in feed.entries
if datetime(*entry.updated_parsed[:6], tzinfo=timezone.utc)
.astimezone(JST) > updated_since
]
return new_entries
def generate_summary(feed) -> str:
"""
RSSフィードを元に、OpenAIで要約を実行
"""
text = f"{feed.title}>\n{feed.summary}"
response = openai.ChatCompletion.create(
model=OPENAI_MODEL,
messages=[
{"role": "system", "content": SYSTEM_PARAMETER},
{"role": "user", "content": text},
],
temperature=0.25,
)
summary: str = response.choices[0]["message"]["content"].strip()
return summary
def post_to_slack(message: str, link_url: str, title: str) -> None:
"""
RSSフィードとOpenAIの要約SlackのWebhookURLへPOST
"""
data = {
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"<{link_url}|{title}>",
},
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": message,
},
},
{"type": "divider"},
],
"unfurl_links": False,
}
requests.post(WEBHOOK_URL, data=json.dumps(data))
def handler() -> None:
# RSSフィードから記事を取得し、要約を生成してSlackに投稿する
for entry in get_feed_entries():
summary: str = generate_summary(entry)
post_to_slack(summary, entry.link, entry.title)
if __name__ == "__main__":
handler()
c.OPENAI_API_KEYのエクスポートをします。
% export OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
d.サンプルコードyahoo_openai.pyのSlack Webhook URLとRSSフィードURLを該当のものに変更しましょう。
WEBHOOK_URL = "{通知先のWebhook URL}"
FEED_URL = "https://dev.classmethod.jp/feed/"
e.変更後、スクリプトを実行してみます。
% python3 yahoo_openai.py
..
..
..
openai.RateLimitError: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}
無料で利用できない範囲にいるようですので、5ドルチャージしてみました。。(泣)
チャージ後、再度実行しましたが、エラーでした。
% python3 yahoo_openai.py
..
..
summary: str = response.choices[0]["message"]["content"].strip()
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
TypeError: 'Choice' object is not subscriptable
コードを修正して試しましたが、エラーで進めない感じになったので、一度まっさらな状態から、ライブラリをインストールしてやってみます。Pythonの仮想環境を構築します。
Python仮想環境の構築
a.venvという名前で、Pythonの仮想環境を作成します。
% python3 -m venv venv
実行すると、現在のディレクトリに venv という名前のフォルダが作成され、そのフォルダはPythonの実行ファイルやpipなどのツールのコピー、そしてプロジェクトのための独立したPythonライブラリを置く場所が含まれます。
引用:chatGPTの解説より
python3
は、Python 3.xバージョンのインタープリタを指します。-m venv
は、Pythonに標準で含まれているvenv
モジュールを使用するように指示します。venv
モジュールは、仮想環境を作成するためのものです。venv
は、新しく作成される仮想環境のディレクトリ名です。この名前は任意で変更可能です。
b.作成された仮想環境を使用するためには、それをアクティベートする必要があります。
% source venv/bin/activate
c.openaiをインストールし直します。
% pip install openai
バージョンは1.3.5がインストールされました。
% pip list
Package Version
----------------- ----------
annotated-types 0.6.0
anyio 3.7.1
certifi 2023.11.17
distro 1.8.0
h11 0.14.0
httpcore 1.0.2
httpx 0.25.2
idna 3.5
openai 1.3.5
pip 23.2.1
pydantic 2.5.2
pydantic_core 2.14.5
setuptools 68.1.2
sniffio 1.3.0
tqdm 4.66.1
typing_extensions 4.8.0
d.そしてRSSを読み込むために必要なライブラリを再度インストールします。
% pip install feedparser
バージョンは6.0.10がインストールされました。
% pip list
Package Version
------------------ ----------
annotated-types 0.6.0
anyio 3.7.1
certifi 2023.11.17
charset-normalizer 3.3.2
distro 1.8.0
feedparser 6.0.10
h11 0.14.0
httpcore 1.0.2
httpx 0.25.2
idna 3.5
openai 1.3.5
pip 23.3.1
pydantic 2.5.2
pydantic_core 2.14.5
requests 2.31.0
setuptools 68.1.2
sgmllib3k 1.0.0
sniffio 1.3.0
tqdm 4.66.1
typing_extensions 4.8.0
urllib3 2.1.0
環境構築は完了です。
Pythonスクリプトの作成
改めて、Yahoo!ニュースの主要トピックスの記事のRSSを読み込んで、OpenAIで要約してSlack連携する処理を作成します。
最新のOpenAIのバージョンに合わせて作成したスクリプトは下記です。先程のスクリプトの内容からアップデートした最終版yahoo_openai.pyです。
import os
import feedparser
from openai import OpenAI
import requests
# 環境変数からAPIキーを取得
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
SLACK_WEBHOOK_URL = os.getenv("SLACK_WEBHOOK_URL")
# 使用するモデルを指定(GPT-3.5-turboを仮定していますが、適宜変更してください)
OPENAI_MODEL = "gpt-3.5-turbo"
# RSSフィードURL
FEED_URL = "https://news.yahoo.co.jp/rss/topics/top-picks.xml"
# OpenAIのAPIキーを設定
# openai.api_key = OPENAI_API_KEY
client = OpenAI(api_key = OPENAI_API_KEY)
def fetch_rss_feed(url):
#RSSフィードを取得してパースする
return feedparser.parse(url)
def generate_summary(title, url):
#OpenAIを使用してニュース記事の要約を生成する
prompt_text = f"Please summarize the following news article titled '{title}'. You can find the article at {url}."
chat_completion = client.chat.completions.create(
messages=[
{
"role": "system", "content": "You are an AI trained to summarize news articles.",
"role": "user","content": prompt_text,
}
],
model=OPENAI_MODEL,
)
# choices 属性の内容を確認する
choices = chat_completion.choices
if choices:
# choices がリストの場合、最初の要素の内容を取得する
summary = choices[0].message.content.strip()
else:
# choices が空か、期待したデータ構造でない場合の処理
summary = "No completion found or unexpected data structure."
return summary
def post_to_slack(title, summary):
#Slackにメッセージを投稿する
message = f"Title: {title}\nSummary: {summary}"
requests.post(SLACK_WEBHOOK_URL, json={"text": message})
def main():
# RSSフィードを取得
feed = fetch_rss_feed(FEED_URL)
# 各エントリに対して処理
for entry in feed.entries:
# 要約を生成
summary = generate_summary(entry.title, entry.link)
# Slackに投稿
post_to_slack(entry.title, summary)
if __name__ == "__main__":
main()
実行してみると、Slackに通知が来ました。
たしかに、Yahoo!ニュース・トピックス – 主要 のフィードの内容になってます。
あとは要約が英語なので、日本語に変換して完了です。
プロンプトが英語なので、日本語に修正すると、日本語で要約されます。
..
..
prompt_text = f"次の記事を要約してください。タイトル: '{title}'. 記事はこちらのリンクです: {url}. 日本語でお願いします."
..
..
日本語になりました。