エンジニアのはしがき

プログラミングの日々の知見を書き連ねているブログです

Spring(Java)で雑にOpenAI APIにニュースを要約させてみた

日頃技術系のニュースを追う際に、よく見るサイトの記事を俯瞰して見たいので毎日スクレイピングした記事をDBに格納して、自作アプリから見れるようにしています。

↓こんな感じ

https://birds-eye.ts-soda.net/news

特に大きな問題はなく運用できていたんですが、記事の内容をOpenAI APIで要約できれば時間がない時でもざっくりニュースを追えてより便利になりそうだったのでやってみました。

やったこと

Springで構築したWebAPIでニュースサイトをスクレイピングして、OpenAI APIに要約させて自作アプリ上から見えるようにしてみました。

実装

以下はスクレイピングした文字列をOpenAI APIに要約させている処理を抜粋したものです。

package com.birdseyeapi.birdseyeapi.AI.Summarize;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.ArrayList;
import java.util.List;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class OpenAISummarizer implements AISummarizer {
    private static final String OpenAIEndpoint = System.getenv("OPENAI_CHAT_ENDPOINT");
    private static final String OpenAIAPIKey = System.getenv("OPENAI_API_KEY");
    private static final String OpenAIModel = "gpt-3.5-turbo";
    private static final int MaxPromptTextLength = 3000;
    private final HttpClient httpClient;
    private final ObjectMapper mapper = new ObjectMapper();

    public OpenAISummarizer() {
        this.httpClient = HttpClient.newHttpClient();
    }

    @Override
    public String summarize(String text) throws IOException, InterruptedException {
        List<OpenAISummarizeMessage> messages = new ArrayList<>();
        String prompt = String.format("""
            次の文章を日本語で要約してください。
            なお、要約結果の文章は200文字以内に収まるように調整してください。
            ---
            %s
        """, text);
        if (prompt.length() > MaxPromptTextLength) {
            prompt = prompt.substring(0, MaxPromptTextLength);
        }
        messages.add(new OpenAISummarizeMessage(OpenAIMessageRole.User.GetStrName(), prompt));
        OpenAISummarizeRequest reqBody = new OpenAISummarizeRequest(OpenAIModel, messages);

        final HttpRequest req = HttpRequest
            .newBuilder(URI.create(OpenAIEndpoint))
            .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + OpenAIAPIKey)
            .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(reqBody)))
            .build();
        
        final HttpResponse<String> res = httpClient.send(req, BodyHandlers.ofString());
        final OpenAISummarizeResult result = mapper.readValue(res.body(), OpenAISummarizeResult.class);
        return result.getChoices().get(0).getMessage().getContent();
    }   
}

summarizeメソッドの引数にはスクレイピングした結果の文字列が渡されるようになっています。

httpClientでOpenAI APIを呼び出し、スクレイピングした文字列を要約させています。

↓OpenAI APIの仕様は以下を参照

https://platform.openai.com/docs/api-reference/chat/create

OpenAIのプロンプトについてはあまり詳しくないですが、突貫で上記のようにプロンプトを書いてみたところそれなりに良い感じに要約してくれました。

要約してみた結果

例えば以下の記事をスクレイピングしてOpenAI APIで要約させてみます。

japan.zdnet.com

OpenAI APIのレスポンスは以下のようになりました。

AIの普及により、職場の業務やキャリアに影響が出る可能性があることが明らかになった。Indeedの調査によると、生成AIの能力によって職務の大部分を実行できる場合は、影響を受ける可能性が高く、半分以下の場合は低いとされている。若い世代では影響を受ける可能性が最も低く、年齢が上がるほど影響を受けやすくなる傾向がある。特に中堅層のプロフェッショナルは生成AIの影響を受ける可能性が高い。一方、55歳以上の高齢労働者は再び影響を受けにくくなる。この影響は、従業員の役割やスキルによって異なるが、管理職や特定の分野での経験を持つ労働者ほど影響を受けやすいとされている。

結構良い感じに要約してくれました。(たったこれだけのプロンプトで要約できちゃうのが凄いところですね)

あとがき

要約された文章的には問題なさそうですが、改行がないのでちょっと読むのがしんどいところが1つ課題かなーと思いました。 「適宜改行を入れてね」的な文をプロンプトに入れて試してみていますが、あんまりうまくいかないですね…。もうちょっと考える必要がありそうです。