News API + ChatGPT で1日のニュースを要約してメール送信する

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、この間ニュースを見ていたら「チャット型 AI のユーザー数が減少している」という内容を見ました。

どうやら「幻滅期に突入か!?」とまで言われているようですが、私はとても便利に感じていて、もう業務だけでなくプライベートでも使っている状態です。

そしてそんな中、「Artifact」という海外のニュースアプリで便利な機能があると聞きつけました。

それが・・・・・・

AI によるニュースの要約

です。

こんなカンジです。
↓↓↓

コレいいですよね。👍

正直なところ、たくさんのニュース全部読むのには時間がかかります。なので私は「うーん、コレ長っげぇ記事だな…😅」と感じたときはChatGPTに以下のように聞いてました。

#ChatGPTプロンプト

以下のページを「結局何が言いたいの?」という疑問に答えるように箇条書きで三行に要約してください。

(ここにURL)

※ ちなみにこのプロンプトは私が運営している wordpave だとより簡単に使えます!

ただし、ChatGPTの要約能力はとても満足しているのですが、毎回コピペする作業がめんどうなんです。

そこで❗

今回は「1日に1回、 News API + ChatGPT API でニュースの要約をメール送信する」という機能をつくってみることにしました。

ぜひ何かの参考になりましたら嬉しいです。😊✨

「たまに ChatGPT をベタ褒めすると、
ありがとうございます!😊
と絵文字付きになるので、すごくかわいいです」

開発環境: Laravel 10.x

News API のキーを取得する

まずNews APIにアクセスするためにはAPIキーと呼ばれる「パスワードのようなもの」を取得する必要があります。

実際に手順をご紹介します。(2023.8.25 現在)

まずは News APIのトップページ にアクセスします。

すると、ページ右上に「Get API key」と書かれたボタンがあるのでこれをクリック。

ページ移動して以下のフォームが表示されるので中身をそれぞれ入力&チェックして「Submit」ボタンをクリックしましょう。

  • First name: 名前
  • Email address: メールアドレス
  • Choose a password: パスワード
  • You are…: 個人での利用かビジネスでの利用か選択。なお、ビジネスの場合は有料です。※
  • (ロボットじゃありませんをチェック)
  • 利用規約を読んで、同意するならボックスにチェックを入れる

※ 開発するためなら無料。ただし、ニュースは24時間遅れるなどの制限があります。詳しくは こちら をご覧ください。

すると、(メールアドレスチェックがあると思いきや、いきなり😯)APIキーが表示されます。

では、忘れないようにLaravel.envに以下のように登録しておきましょう。

.env

NEWS_API_KEY=****************************

これで、News APIへの登録は完了です❗

ChatGPT APIの登録

ChatGPT APIOpenAI)への登録は過去に記事として公開していますので、以下2つを参考にしてみてください。

⚠ 注意: つまり、OpenAIに事前支払いが発生します。料金はこちら。ただ、今回のプログラムを実行したところ、1セントでしたので安かったです。

では、こっちのAPIキーも登録しておきましょう。

.env

OPENAI_API_KEY="*********************************"

※ ハイフンを含んでいたのでダブルクォートで囲っています。(不要??🤔)

要約したニュースを送信する手順

次に、実際のプログラムに行く前に要約ニュースをメール送信する手順を確認しておきましょう。

  1. New API を使って各ニュース本文を取得
  2. 各ニュースを結合して1つのテキストを作成
  3. これを ChatGPT API を使って要約
  4. 要約したニュースをメールで送信

つまり、「ニュースを取ってきて ChatGPT に投げる」をプログラムにしただけのシンプルなものになっています。

パッケージをインストールする

では、プログラムする際に必要となるパッケージをインストールしておきましょう。

以下のコマンドを実行してください。

composer require openai-php/laravel

※ もしエラーが出る場合は、composer.lockを削除してからコマンドを実行してください。

そして、コンフィグ・ファイルをLaravel側へコピーします。

php artisan vendor:publish --provider="OpenAI\Laravel\ServiceProvider"

ではここからが本格的なプログラムのはじまりです❗

メール送信部分をつくる

次の項目で本体をつくることになりますが、先に部品になる「メール送信する部分」をつくっておきましょう。

以下のコマンドを実行してください。

php artisan make:mail NewsSummerized

するとファイルが作成されるので、中身を次のようにします。(太字が変更した部分です)

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class NewsSummerized extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     */
    public function __construct(private string $summary_text)
    {}

    /**
     * Get the message envelope.
     */
    public function envelope(): Envelope
    {
        $today = today();
        $subject = $today->format('Y/m/d') . 'のニュースまとめ';

        return new Envelope(
            subject: $subject,
        );
    }

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            htmlString: nl2br($this->summary_text), // 今回はめんどうなのでビューを使わずに直接 HTML を指定しています
        );
    }

    /**
     * Get the attachments for the message.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [];
    }
}

これで、以下のようにすることでメール送信ができるようになります。

Mail::to($to)->send(new NewsSummerized($text));

ChatGPT のプロンプト部分をつくる

続いて、ChatGPTのプロンプト部分をつくります。
正直なところ、コード内に「ヒアドキュメント」などで直接書いてもいいのですが、チェックしにくいので、あえてBladeを使って実装することにします。

以下のファイルを作成してください。

resources/views/summary_news_prompt.blade.php

以下のテキストから1日の流れがわかるように要約してください。

なお、条件は以下のとおりです。

・箇条書きにする
・おおむね5分以内で読めるような文字数にする
・似ているニュースはひとつにまとめる
・箇条書きだけで回答する(説明文は不要)
・英語で考えて日本語で出力する

#テキスト
{{ $text }}

※ 「英語で〜」の部分はX(旧ツイッター)のタイムラインに流れてきた情報で、とても有益でした。感謝!

つまり、以下のようにすることでこのテキストを自由に取得することができるようになります。

$prompt = view('summary_news_prompt', [
    'text' => '(ここに要約する前のテキスト)'
])->render();

ニュース要約を送信するコマンドを作成する

やっと本体までたどり着きました。

今回は独自のArtisanコマンドを実行することで要約したニュースをメール送信しますので、以下のコマンドを実行してください。

php artisan make:command SendNewsSummaryCommand

すると、ファイルが作成されるので中身を以下のように変更します。

app/Console/Commands/SendNewsSummaryCommand.php

<?php

namespace App\Console\Commands;

use App\Mail\NewsSummerized;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use OpenAI\Laravel\Facades\OpenAI;

class SendNewsSummaryCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'send:news_summary';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send news summary with News API & ChatGPT API';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $description_text = $this->getNewsDescriptionText();
        $summary_text = $this->getSummaryText($description_text);

        $to = 'test@example.com';
        Mail::to($to)->send(new NewsSummerized($summary_text));

        return Command::SUCCESS;
    }

    private function getNewsDescriptionText()
    {
        $new_api_url = 'https://newsapi.org/v2/top-headlines?country=jp&apiKey='. env('NEWS_API_KEY'); // 本来は config から取得すべきです

        try {

            $response = Http::get($new_api_url);

        } catch (\Throwable $th) {

            $this->error($th->getMessage());
            throw $th;

        }

        $news_data = $response->json();
        $description_texts = [];

        foreach ($news_data['articles'] as $article) {

            $article_description = $article['description'];

            if(Str::length($article_description) > 0) {

                $description_texts[] = $article_description;

            }

        }

        return implode("\n\n", $description_texts);
    }

    private function getSummaryText($original_text)
    {
        $prompt = view('summary_news_prompt', [
            'text' => $original_text
        ])->render();

        try {

            $result = OpenAI::chat()->create([
                'model' => 'gpt-3.5-turbo',
                'messages' => [
                    ['role' => 'user', 'content' => $prompt],
                ],
            ]);

        } catch (\Throwable $th) {

            $this->error($th->getMessage());
            throw $th;

        }

        return Arr::get($result, 'choices.0.message.content');
    }
}

これで作業はすべて完了です。
お疲れ様でした。😊✨

テストしてみる

では、実際にテストしてみましょう❗

ちなみに私の場合、メールのテストはmailcatcherを使いますが、みなさんはMAILGUNやログなどお好きなものを使ってください。

以下のコマンドを実行します。

php artisan send:news_summary

すると・・・・・・

はい❗
メッセージが届きました。

では中身はどうなっているでしょうか・・・・・・

どうでしょう❗
なかなかいい要約になっているんじゃないでしょうか。

(ただ、これを見る限り普通に元のニュース・タイトルだけでもいい気もするんですが、実際にURLにアクセスしてから元テキストをつくると、より情報量が大きくなるかもしれません)

ちなみに・・・・・・

今回OpenAIに10ドルを課金してから実行してみたのですが、1回につき1セント(=0.01ドル0.0423円:2023.8.26 現在)でした。

思ったより安いですね👍

企業様へのご提案

今回の機能を応用させると、以下のようなことが実現できます。

  • 特定の業界に関連するニュースだけを ChatGPT にフィルタリングさせ、それらのニュースだけをメール送信する
  • 過去のニュースを貯めていき、その流れから投資すべきエリアを ChatGPT に提案させる
  • 同じく過去ニュースから時代の流れを読み取り、ChatGPT に未来を予測させ新商品や新業態への知見を得る

もしこういった機能をご希望でしたら、いつでもお気軽にお問い合わせからご相談ください。

お待ちしております。😊✨

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回は「News API + ChatGPT API」を使ってニュース要約をしてみました。

ただ、使い方に寄ってはChatGPT APIの料金が高くなるかもなので、ちょっと不安なところではありますが、どうやらChatGPTはめちゃくちゃ赤字らしいので、あまり無理は言えませんね(笑)

ちなみに(個人的なプロジェクトだけは)プログラミングもChatGPTにお願いしたりもしますが、感想としては「最初の70%までは素晴らしい、でもその後も使ったらエライ目に合う…」といったカンジです。

つまり、「ここ変えといて」と言って変えてもらっても、その次の「あ、ここも間違ってるから変えてね」と言うと、さっき直した所を無視したりして「いや、さっきのどこ行ったの💢」ということが多発しました。

なので、イメージとしては「現在地から●●駅周辺には自動で行くけど、そこからは自分で運転しなきゃダメ」みたいなカンジでしょうか。

ただし、「GPT-5」が商標登録の申請がされたそうですし、今後また精度が上がるかもですね。今後も注目していきます。

ではでは〜❗

「久しぶりに
ヒクソン・グレイシー vs 髙田延彦
の試合を観ました🥊」

このエントリーをはてなブックマークに追加       follow us in feedly