皆様、Bedrockを使うとこんな素敵なことができるようです!(すごい)
Amazon BedrockのClaude 3で、先人に倣って5ch風スレッドを作れないかと試していますが、本当に凄い pic.twitter.com/uOabApcFR6
— nasuuu (@nasuvit_z) April 17, 2024
AWS WAFについて語る5ch風スレッドをClaude 3 Opusで作らせたら、自称サイバーセキュリティニキがなんJのノリでWAFの解説を始めた
— nasuuu (@nasuvit_z) April 21, 2024
しかし何でこんなに頭に入ってきやすいのか https://t.co/4uyfGDK9mt pic.twitter.com/ouU8F4e5DX
これに触発され、AWSのWhat's Newをわかりやすく解説できるアプリをStreamlitで作ってみました。
解説
処理1:What's NewのRSSを取得する
AWSのWhat's NewはRSSが公開されているので、こいつを取得します。RSSの取得はrss-parser
を使用します。
requests
で取得したHTTPレスポンスをrss-parser
に渡すとパースしてくれます。
rss_url = "https://aws.amazon.com/about-aws/whats-new/recent/feed/"
response = requests.get(rss_url)
rss = RSSParser.parse(response.text)
items = rss.channel.items
for item in items:
print(item.title.content) # タイトル
print(item.pub_date.content) # pub_date
print(item.link.content) # Link
処理2:What's Newの本文を取得する
RSSで取得したURLから、What's Newの本文を取得します。本文の取得はBeautifulSoup4
を使用します。
What's NewのHTMLを解析したところ、section
タグ内のテキストを取得するといい感じであることがわかりました。
link = "https://*****" # What's NewのURL
response = requests.get(link) # requestsでHTMLを取得
soup = BeautifulSoup(response.text, features="html.parser")
contents = soup.section.get_text() # <section>タグ内のテキストを取得
処理3:Claude 3で会話形式の解説を生成する
試行錯誤の結果、以下のようなプロンプトとしました。
AWSのWhat's newを提示しますので、注意深く読み込んでください。
<AWSのWhat's new>
{{What's new}}
</AWSのWhat's new>
タスク
会話形式でわかりやすく解説してください。
登場人物
- user1: AWSスペシャリスト
- user2: 新入社員
ルール
- 回答は必ず日本語で行ってください。
- ハルシネーションは許されないので、What\'s newの内容からのみ回答を生成してください。
- また、回答付不要な出力は含めず、登場人物の会話のみを出力してください。
- 回答は以下のJSON形式としてください。
{
"conversations": [
{
"actor": "user1",
"message": "..."
},
{
"actor": "user2",
"message": "..."
},
{
"actor": "user1",
"message": "..."
}
]
}
テクニック的には以下のようなものを考慮しながら考えました。
- ドキュメントを先に示す
- ルールを示す
- 例示する
結果的には適当にやってもいい感じになりましたね
処理4:Streamlitで画面UIを作成する
サイドバーに一覧を表示して、選択したWhat's Newを右側に表示します。
選択したらこんな感じ
ソースコード全体
import json
import boto3
import requests
import streamlit as st
from bs4 import BeautifulSoup
from rss_parser import RSSParser
if "rss" not in st.session_state:
rss_url = "https://aws.amazon.com/about-aws/whats-new/recent/feed/"
response = requests.get(rss_url)
st.session_state["rss"] = RSSParser.parse(response.text)
rss = st.session_state["rss"]
items = rss.channel.items
def f1(**kwargs):
st.session_state["selection"] = kwargs
st.title("AWSのWhat's Newを解説くん")
st.write("←から選んでね")
with st.sidebar:
for item in items:
with st.container(border=True):
st.write(item.title.content)
kwargs = {
"title": item.title.content,
"pub_date": item.pub_date.content,
"link": item.link.content,
}
st.button("おしえて", key=item.guid.content, on_click=f1, kwargs=kwargs)
prompt = """
AWSのWhat's newを提示しますので、注意深く読み込んでください。
<AWSのWhat's new>
{{What's new}}
</AWSのWhat's new>
タスク
会話形式でわかりやすく解説してください。
登場人物
- user1: AWSスペシャリスト
- user2: 新入社員
ルール
- 回答は必ず日本語で行ってください。
- ハルシネーションは許されないので、What\'s newの内容からのみ回答を生成してください。
- また、回答付不要な出力は含めず、登場人物の会話のみを出力してください。
- 回答は以下のJSON形式としてください。
{
"conversations": [
{
"actor": "user1",
"message": "..."
},
{
"actor": "user2",
"message": "..."
},
{
"actor": "user1",
"message": "..."
}
]
}
"""
if "selection" in st.session_state:
kwargs = st.session_state["selection"]
response = requests.get(kwargs["link"])
soup = BeautifulSoup(response.text, features="html.parser")
contents = soup.section.get_text()
st.subheader(kwargs["title"])
st.write(kwargs["pub_date"])
with st.spinner("ちょっとまってね"):
client = boto3.client("bedrock-runtime")
model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
response = client.invoke_model(
modelId=model_id,
body=json.dumps(
{
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": prompt.replace("{{What's new}}", contents),
}
],
}
],
"temperature": 0.5,
}
),
)
try:
response_body = json.loads(response.get("body").read())
conversations = json.loads(response_body["content"][0]["text"])
for c in conversations["conversations"]:
if c["actor"] == "user1":
with st.chat_message("ai"):
st.write(c["message"])
if c["actor"] == "user2":
with st.chat_message("human"):
st.write(c["message"])
except Exception as e:
st.write("ごめんなさい。エラーです。")
with st.expander("Haikuの回答:"):
st.write(response_body)
st.link_button("ブログを確認", kwargs["link"])
デモ
(諸事情によりBedrockは使わず、AnthripicのAPIを使用しています)