ChatGPTにブログ全エントリを学習させて「おしえてきしださん」を作る

最近「100万件の文章をChatGPTに学習させて応答チャットを作りました」みたいなニュースがあって、違和感があります。
ということで「ChatGPTにブログ全エントリを学習させて「おしえてきしださん」を作る」としたときに、どんな仕組みになっていて、なぜ「ChatGPTに文章を学習させて」ということに違和感があるか見てみます。

とりあえずこんな感じで、質問に対して答えれてるっぽいチャットができました。

まず、Embedding APIを使って、全エントリのベクトルを得てMongoDBに突っ込んでおきます。
このエントリでやってるので、そのまま使います。
GPTのEmbeddingを利用してブログの投稿に対する近いものを探し出す - きしだのHatena

質問が入力されたら、質問文も同じようにEmbeddingでベクトルをとってきます。

var req = EmbeddingRequest.builder()
        .user("dummy")
        .model("text-embedding-ada-002")
        .input(List.of(limitText(text, 4000))).build();
EmbeddingResult res = service.createEmbeddings(req);
List<Double> vec = res.getData().get(0).getEmbedding();

で、質問のベクトルとエントリのベクトルの角度を調べて、一番角度が小さいエントリを選びます。
今回はブルートフォースにやってるけど、実用アプリではなんらかANN(Approximate Nearest Neighbor)アルゴリズムを使いましょう。

double maxScore = 0;
BlogEntry entry = null;
    
for (var e : entries) {
    double score = 0;
    for (int pos = 0; pos < vector.size(); pos++) {
        score += vector.get(pos) * e.vector().get(pos);
    }
    
    if (entry == null || maxScore < score) {
        entry = e;
        maxScore = score;
    }
}
return entry; 

そして、そのエントリを質問の答えになるように要約させます。このときシステムメッセージとして与えるプロンプトは、かなり調整が必要になると思います。

ChatCompletionRequest chatReq = ChatCompletionRequest.builder()
        .user("dummy")
        .model("gpt-3.5-turbo")
        .messages(List.of(
                new ChatMessage(ChatMessageRole.SYSTEM.value(),
                    """
                    次の文章を要約して、文章の執筆者の気持ちでユーザーの質問に答えてください。
                    文章に該当する情報がない場合は、\
                    「該当する文章がありません。質問に答えることができません」としてください。
                    
                    文章:
                    %s
                    """.formatted(limitText(entry.body(), 3000))),
                new ChatMessage(ChatMessageRole.USER.value(), text)))
        .build();

ChatCompletionResult result = service.createChatCompletion(chatReq);

あとは返答を表示して終了。

outputText.append("""
        質問「%s」
        %s

        via:
        %s
        https://nowokay.hatenablog.com/entry/%s

        """.formatted(text,
            result.getChoices().get(0).getMessage().getContent(),
            entry.title(), entry.baseName()));

という感じで、エントリの内容はMongoDBにあって、ぜんぜんChatGPTは学習してないんですよね。
まあマーケティング的には「ChatGPTに学習させて」ってやると食いつきがいいだろうし、このあたりを正しく表すのもめんどいだろうし・・・

ネットの情報を踏まえるのは、こんな感じに質問から検索ワードに分解して検索して内容をダイジェストにしてる感じです。
ChatGPTより賢く質問に答えれるチャットBotを作る(誇張表現) - きしだのHatena

しかし、これ割と楽しいので、みんなも作ろう。
たぶんLangChainとFaissなんか使うと楽にできるはず

ちなみに、サービスとして公開すると楽しいかもしれないけど、メンテがめんどくさそうなのと、他の人に使ってもらえるレベルに作りこむとなると結構たいへんそうなのでやりません・・・

※ 追記 なにか名前をつけるならout-context learningとかがいいのでは、という話をしていた
Embeddingで埋め込みベクトルとってDBに投入して検索するのをout-context learningと呼ぶのはどうか - きしだのHatena

ソース全体はこんな感じ。
https://gist.github.com/kishida/b7c76b650f67eee53e04e36496fa83d3