これは時代が変わるぞという実感がやってきました。
ChatGPTが新着論文を要約し毎朝メールしてくれる仕組みの作り方 | Antaa Slide @antaa__jp #Antaa https://slide.antaa.jp/article/view/d0de1bd8ea414420
Pubmedというアメリカの論文サイトで検索して、その結果をChatGPTに送って、要約・翻訳した結果をまとめてメールするという仕組みです。
-スライドにあったGASをコピー
-ChatGPTのAPIを取得
-API、メアド、検索キーワード、タイトルを変更
-毎朝実行する
(3時間)
新着論文が和訳されてメールがきて、おおーっ、やばいくらい感動しました!
ものすごく苦労してきた事が簡単に・・・
せっかくなので、メールだけではなく、スプレッドシートに内容をストック(3時間)
宛先を個人ではなく、ブログ宛に変更。(3時間)
それにしてもプログラミングって時間どろぼうですね。
test.js
// OpenAI の API keys (https://platform.openai.com/account/api-keys)
const OPENAI_API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// ChatGPT に渡す命令
const PROMPT_PREFIX = "あなたはADHDに詳しい心理学者です。以下の論文を、タイトルと要約の2点をそれぞれ改行で分けて日本語で説明してください。要点は箇条書きで。";
// 結果メールの送信先
const EMAIL_RECIPIENT = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// 結果メールのタイトル
const EMAIL_SUBJECT = "PubMedの新着論文の要約";
// 結果メールの送信者の名前
const EMAIL_SENDER = "PubMed要約bot";
// PubMed の検索クエリ
const PUBMED_QUERY = "ADHD";
// PubMed の対象の記事タイプ(以下のいずれかのタイプとマッチする論文のみをヒットさせる ※ 期待したように記事タイプが判定されない場合も多い)
const PUBMED_PUBTYPES = ["Journal Article", "Books and Documents", "Clinical Trial", "Meta-Analysis", "Randomized Controlled Trial", "Review", "Systematic Review"];
// PubMed の検索対象日数
const PUBMED_TERM = 1;
// PubMed の検索時のヒット論文で要約する論文の本数の上限(多ければ多いだけ、ChatGPT API の token を多く消費する)
const MAX_PAPER_COUNT = 100;
//記録用のスプレッドシート
const sheet = SpreadsheetApp.getActiveSheet();
function main() {
if (!OPENAI_API_KEY) {
console.log("ERROR: OPEN_API_KEY を指定してください");
return;
}
const today = new Date();
const yesterday = new Date(today.getFullYear(), today.getMonth(), today.getDate() - PUBMED_TERM);
const ids = getPaperIDsOn(yesterday);
let output = "[:contents]\n\n";
let paperCount = 0;
for (let i = 0; i < ids.length; i++) {
Utilities.sleep(1000);
const id = ids[i];
const pubmedUrl = `https://pubmed.ncbi.nlm.nih.gov/${id}`;
const summary = getPaperSummaryByID(id);
const title = summary.title;
console.log(`id: ${id}, pubtype: ${summary.pubtype.join(",")}`);
if (!checkPubtype(summary.pubtype)) {
console.log("pubtype: NG");
continue;
}
console.log("pubtype: OK");
if (++paperCount > MAX_PAPER_COUNT) break;
const abstract = getPaperAbstractByID(id);
const input = "\n" + "title: " + title + "\n" + "abstract: " + abstract;
const res = callChatGPT(input);
console.log(res);
const paragraphs = res.choices.map((c) => c.message.content.trim());
//はてぶ記法で先頭に"*"を追記
output += "*" + `${paragraphs.join("\n")}\n\n${pubmedUrl}\n\n\n`;
//スプレッドシートに追記
sendSheet(paragraphs,pubmedUrl);
}
if (paperCount === 0) {
output += "検索条件に合致した新着論文はありませんでした。";
}
output = output.trim();
sendEmail(output);
}
function toYYYYMMDD(date) {
return [date.getFullYear(), date.getMonth() + 1, date.getDate()].join("/");
}
function getPaperIDsOn(date) {
const query = encodeURIComponent(PUBMED_QUERY);
const url = `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&retmode=json&sort=pub_date&term=${query}&mindate=${toYYYYMMDD(date)}&maxdate=${toYYYYMMDD(date)}`;
console.log(url);
const res = JSON.parse(UrlFetchApp.fetch(url).getContentText());
return res.esearchresult.idlist;
}
function getPaperSummaryByID(id) {
const url = `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&retmode=json&id=${id}`;
console.log(url);
const res = JSON.parse(UrlFetchApp.fetch(url).getContentText());
return res.result[id];
}
function getPaperAbstractByID(id) {
const url = `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&retmode=xml&id=${id}`;
const xml = UrlFetchApp.fetch(url).getContentText();
const match = xml.match(/<Abstract>(.*?)<\/Abstract>/);
return match ? match[1] : "";
}
function checkPubtype(pubtypes) {
const common = pubtypes.filter(x => PUBMED_PUBTYPES.indexOf(x) !== -1);
return common.length > 0;
}
function callChatGPT(input) {
const messages = [
{
role: "user",
content: PROMPT_PREFIX + "\n" + input,
},
];
const url = "https://api.openai.com/v1/chat/completions";
const options = {
"method": "post",
"headers": {
"Authorization": `Bearer ${OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
"payload": JSON.stringify({
model: "gpt-3.5-turbo",
messages,
}),
};
return JSON.parse(UrlFetchApp.fetch(url, options).getContentText());
}
function sendEmail(body) {
const options = { name: EMAIL_SENDER };
//件名に日付をいれたい
const date1 = new Date().toLocaleDateString("ja-JP", {year: "numeric",month: "2-digit",
day: "2-digit"});
GmailApp.sendEmail(EMAIL_RECIPIENT, date1 +" "+ EMAIL_SUBJECT, body, options);
}
function sendSheet(body,url) {
let date = new Date();
value = [date,url,body];
value = value.flat();
//スプレッドシートの最終行に挿入
sheet.appendRow(value);
}