arxivの論文を、自動要約・翻訳してDiscordに飛ばす
毎時決まった時間帯に、AI論文を自動翻訳/要約して、Discordへ流す機能を実装する。
GASとDiscordのWebhook機能を使う
Discordの設定
自分専用のDiscordサーバーがない方は、こちらの記事を参照
ボットから情報を受け取りたいチャネル(paperチャネル)を右クリックし、チャネルの編集(Edit channel)を行う
連携サービス(integration) -> create webhook
https://scrapbox.io/files/6629dfa27f78d10025f3edd6.png
「新しいウェブフック」を選択し、作成されたウェブフックをURLをコピー(あとで使うので、どこかへ保存)
GASの設定
Google Driveの画面へ行く
任意のフォルダを作成(例えば、scriptsフォルダ)し、右クリック -> その他 -> Google Apps Scriptを押す
https://scrapbox.io/files/6629e0aace6915002455cae7.png
以下の画面が開くので、コードを書いていく。
https://scrapbox.io/files/6629e0e3d6c7ce00243a0432.png
GASとDiscordを連結する
まずは、接続を確認
code:js
function myFunction() {
const payload =
{
"content": "Hello, World!"
};
const options =
{
"method": "post",
"payload": payload
};
UrlFetchApp.fetch(WEBHOOK_URL, options);
}
command Sで保存して、コードを実行
https://scrapbox.io/files/6629e2b78dbb5500246e46ae.png
権限を確認 -> gmailアカウントクリック -> 詳細 -> 安全ではないページに移動をクリック -> 許可
https://scrapbox.io/files/6629e2f9225b0800257971c8.png
GASの実行ログとともに、DiscordへもHello Worldが届いていることが確認される。
GASでarxivの論文を取得する
以下のコードを貼り付ける
code:js
// スクリプトプロパティにAPIキーを保存するには、スクリプトエディタで「ファイル」>「プロジェクトのプロパティ」>「スクリプトプロパティ」を選択し、OPENAI_API_KEYとしてAPIキーを追加してください。
const OPENAI_API_KEY = PropertiesService.getScriptProperties().getProperty("OPENAI_API_KEY");
const OPENAI_API_MODEL = "gpt-3.5-turbo-0125"
const WEBHOOK_URL = "" // 上でコピーしたWebhook urlを貼り付けてください。
// 検索クエリ
const QUERY = "ChatGPT OR GPTs OR OpenAI";
// 検索対象日数
const TERM = 1;
// 検索時のヒット論文で要約する論文の本数の上限
const MAX_PAPER_COUNT = 3;
// ChatGPT に渡す命令
const PROMPT_PREFIX = "あなたは情報教育、テクノロジーに詳しい教師です。\n以下の論文を、タイトルと要約の2点をそれぞれ改行で分けて、専門用語を使わず、簡素で平易な日本語で説明してください。\n要点は箇条書きでお願いします。\n## 出力形式 \nタイトル: {タイトル}\n要約: {要約}";
function getDateBeforeDays(days) {
const date = new Date();
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
date.setDate(date.getDate() - days);
return date.toISOString().split("T")0; }
function getArxivPapers(query, fromDate, toDate) {
const options = {
muteHttpExceptions: true,
};
const url = http://export.arxiv.org/api/query?search_query=${query}&start=0&max_results=20&sortBy=submittedDate&sortOrder=descending
const xmlText = UrlFetchApp.fetch(url, options).getContentText();
const document = XmlService.parse(xmlText);
const root = document.getRootElement();
const papers = entries
.map((entry) => {
return {
title: title,
abstract: abstract,
url: id,
published: published,
};
}).filter((paper) => {
const publishedDate = new Date(paper.published);
const from = new Date(fromDate);
const to = new Date(toDate);
to.setDate(to.getDate() + 1);
return publishedDate >= from && publishedDate < to;
});
console.log(フィルタリング後の論文数: ${papers.length});
return papers;
}
function callChatGPT(input) {
const messages = [
{
role: "user",
content: PROMPT_PREFIX + "\n" + input,
},
];
const options = {
"method": "post",
"headers": {
"Authorization": Bearer ${OPENAI_API_KEY},
"Content-Type": "application/json",
},
"payload": JSON.stringify({
model: OPENAI_API_MODEL,
messages,
}),
};
return JSON.parse(UrlFetchApp.fetch(OPENAI_API_ENDPOINT, options).getContentText());
}
function main() {
if (!OPENAI_API_KEY) {
console.log("ERROR: OPEN_API_KEY を指定してください");
return;
}
const fromDate = getDateBeforeDays(TERM);
const toDate = getDateBeforeDays(0);
console.log(検索範囲の開始日: ${fromDate});
console.log(検索範囲の終了日: ${toDate});
const arxivPapers = getArxivPapers(QUERY, fromDate, toDate);
console.log(取得された論文数: ${arxivPapers.length});
const papers = arxivPapers;
let output = "新着論文のお知らせ\n\n";
let paperCount = 0;
if (papers.length === 0) {
output += "指定された期間内に、検索クエリに一致する新しい論文は見つかりませんでした。\n\n";
} else {
for (const paper of papers) {
if (++paperCount > MAX_PAPER_COUNT) break;
console.log(論文${paperCount}: ${paper.title});
const title = paper.title;
const abstract = paper.abstract;
const url = paper.url;
const input = "\n" + "title: " + title + "\n" + "abstract: " + abstract;
const res = callChatGPT(input);
console.log(ChatGPTからのレスポンス: ${JSON.stringify(res, null, 2)});
const paragraphs = res.choices.map((c) => c.message.content.trim());
output += ${paragraphs.join("\n")}\n\n${url}\n\n\n;
}
}
output = output.trim();
console.log(最終結果: ${output});
const payload =
{
"content": output
};
const options =
{
"method": "post",
"payload": payload
};
UrlFetchApp.fetch(WEBHOOK_URL, options);
}
WEBHOOK_URLに、上で設定した値を貼り付ける
検索をカスタマイズをしたい時
arxivのAPIマニュアルを見ながら、以下のコードを修正する。
code:js
// 検索クエリ
const QUERY = "ChatGPT";
// 検索対象日数
const TERM = 1;
// 検索時のヒット論文で要約する論文の本数の上限
const MAX_PAPER_COUNT = 2;
// ChatGPT に渡す命令
const PROMPT_PREFIX = "あなたは情報教育、テクノロジーに詳しい教師です。\n以下の論文を、タイトルと要約の2点をそれぞれ改行で分けて、専門用語を使わず、簡素で平易な日本語で説明してください。\n要点は箇条書きでお願いします。\n## 出力形式 \nタイトル: {タイトル}\n要約: {要約}";
例えば、AND OR検索、著者別検索、カテゴリー検索などができる
code:js
const QUERY = "ChatGPT OR GPTs OR OpenAI";
const QUERY = "au:"Grisha Perelman"; //著者がGrisha Perelmanさん
const QUERY = "cat:cs.AI"; //カテゴリーがAI
なお、自分はこの設定とする。
code:js
// 検索クエリ
const QUERY = "ChatGPT OR GPTs OR OpenAI";
// 検索対象日数
const TERM = 1;
// 検索時のヒット論文で要約する論文の本数の上限
const MAX_PAPER_COUNT = 3;
// ChatGPT に渡す命令
const PROMPT_PREFIX = "あなたは情報教育、テクノロジーに詳しい教師です。\n以下の論文を、タイトルと要約の2点をそれぞれ改行で分けて、専門用語を使わず、簡素で平易な日本語で説明してください。\n要点は箇条書きでお願いします。\n## 出力形式 \nタイトル: {タイトル}\n要約: {要約}";
続いて、OpenAIのAPI keyを設定する。
API keyはこちらのリンクから取得する。
サイドバーのプロジェクトの設定をクリック
スクロールするとスクリプトプロパティの項目が表示されるので、「スクリプトプロパティを追加」を選択。
プロパティに「OPENAI_API_KEY」と入れて、値に事前に取得したChatGPTのAPIキーをコピペし、「スクリプトプロパティを保存」をクリック。
https://scrapbox.io/files/6629ea36460b8c0025e572c3.png
正常動作するか確認するために、エディタに戻り、main関数を実行する
https://scrapbox.io/files/6629eac8d8323c0025e6f1f5.png
通信がうまくいっていれば、このようになる。
https://scrapbox.io/files/6629eb08a4fbe9002595e20d.png
GASでトリガーを設定し、定期実行する
毎日決まったタイミングで処理を行うために、設定を行う。
💡これが便利よね。cloud functionsとか書かなくても、簡単に実行できて楽ちん
左のサイドバーのトリガーを選択
右下のトリガーの追加から、以下の画面を設定する。(一例)
これで、毎朝午前7時-8時に、論文の翻訳/要約が届くようになった。
最後に、エディターからデプロイ -> ウェブアプリを選択して完成です。
お疲れ様でした!
関連
参考
/icons/hr.icon
もっと、たくさんのAI情報を知りたいあなたへ...
現在週に1度の頻度で、AIの最新情報や活用ノウハウを、メルマガ限定で発信しております。
一人でも多くの方にとって、「AI人材としてのスキルアップ」につながれば幸いです^^
また、現在、登録者限定で「AIをフル活用した新しい知的生産管理」の実演解説動画を配布中です 🎁
※ ご登録完了のメールに、URlのリンクを添付いたします。
以下のリンクから、ぜひお受け取りください👇