0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pinecone Assistantを使用してみた

Posted at

1. はじめに

Pinecone Assistantを利用してchatBotを作成してみました。Pinecone Assistantは、テキストファイルやPDFからRAG(Retrieval-Augmented Generation、検索拡張生成)を構築できるサービスです。今回は、楽天APIを使用してホテル情報を取得し、そのデータを元にRAGを構築してみました。FlaskサーバーからPinecone Assistantをストリーミングで呼び出し、ユーザーからの質問に対してchatBotが応答できるようにしました。

2. データファイルの作成

楽天のAPIを使用して、8,890件のホテルデータを抽出しました。各ホテルの情報を1件1ファイルとして取得するプログラムを作成しましたが、Pinecone Assistantの無料版では最大10ファイルまでしかアップロードできないため、最終的にはすべてのデータを1つのファイルに結合しました。

以下は、ホテル情報を取得するためのPythonコードです。

#
#  省略
#
    # URLはAPIのエンドポイント
    response = requests.get(URL)
    if response.status_code == 200:
        data = response.json()
        hotelinfomations = data["hotels"]
        for hotel in hotelinfomations:
            info = hotel["hotel"][0]["hotelBasicInfo"]
            text = createTextFile(info)

            with open(f"{info['hotelNo']}.txt", "w", encoding="utf-8") as f:
                f.write(text)
    else:
        print(response.status_code)
#
#  データの編集
#
def createTextFile(info):
    text = f"hotelName:{info["hotelName"]}\n"
    text += f"hotlInfo:{info["hotelSpecial"]}\n" 
    text += f"useReview:{info["userReview"]}\n"
    text += f"access:{info['access']}\n"
    text += f"address:{info['address1']}{info['address2']}\n" 
    
    return text  

取得した情報

各ホテルごとにAPIの戻り値から次の情報を取得しました。ユーザレビューをもっと取得したかったのですが、力不足であきらめました。

  1. ホテル名
  2. ホテル情報
  3. 最新のユーザレビュー1件
  4. アクセス方法
  5. 住所

結合したファイルのイメージ

各ホテルごとのファイルを結合したイメージは次のとおりです。
image.png

3. Pinecone Assistantへのアップロード

3.1 アシスタントの作成

Pineconeのコンソールから"Create an Assistant"ボタンを押下して作成します。
image.png

3.2 ファイルアップロード

作成したホテル情報のデータファイルをPinecone Assistantにアップロードします。プログラムからでもアップロードできますが、ブラウザからファイルアップロードしました。画面に右端の"Drag and Drop"の領域にファイルをドラッグすることでアップロードできます。

image.png

3.3 確認

データ登録完了後、Pinecone Assistantのコンソールから質問と回答を確認することができます。

image.png

4. chatBotの実装について

前提として、PineconeのAPIキーが必要です。Pinecone Assistantを使用して、ユーザの任意の質問をもとにホテル情報を検索するchatBotを実装しました。

4.1 プログラムについて

Flaskを使用してサーバ側の処理を作成しました。

Pinecone Assistant Clientの初期化

PineconeのAPIキーと、作成したPinecone Assistantの名前(hotelsearchassistant)を使用してPinecone Assistantのclientを初期化します。

    pc = Pinecone(api_key=pinecone_key)
    # Pinecone Assistant名の設定
    pinecone_assistant = "hotelsearchassistant"
#
#  省略
#
def get_assistant():
    assistant = pc.assistant.Assistant(
        assistant_name= pinecone_assistant, 
    )   
    return assistant

Pinecone Assistantとの応答(サーバ側処理)

クライアントからの質問を受信し、Pinecone Assistantと接続し、回答をストリーミング形式で受信・処理します。回答はJSON形式でも受信可能です。

from flask import Flask, request, jsonify, render_template
from pinecone_plugins.assistant.models.chat import Message
from flask_socketio import SocketIO

app = Flask(__name__)
socketio = SocketIO(app)
#
# 途中のコードを省略
#
@app.route('/chat', methods=['POST'])
def chat():
    try:
        message = request.json['message']
        assistant = get_assistant()
        msg = Message(content=message)
 
        chunks = assistant.chat(messages=[msg],stream=True)
        
        for chunk in chunks:
            if chunk.type == "content_chunk" :
                socketio.emit('message', chunk.delta.content)
            elif chunk.type == "message_start" :
                socketio.emit('message', "$$start$$")
            elif chunk.type == "message_end" :
                socketio.emit('message', "$$stop$$")
        
        # 
        answer = "end" 
                
        return jsonify({
            'answer': answer,
        })

    except Exception as e:
        print(e)
        return jsonify({'error': str(e)}), 500
  • ストリーミング処理: Pinecone Assistantから受け取ったデータを、その種類に応じて適切に処理し、Socket.IOを通じてクライアントにリアルタイムで送信しています。ストリーミングの開始と終了時には、それぞれ$$start$$$$stop$$をクライアントに返しています

  • レスポンス: ストリーミングが完了した後、単純な完了メッセージ("end")を返していますが、これはリクエストの終わりを示すものです

クライアント側の処理(サーバーからの回答を受ける部分)

Socket.IOを使用してサーバーからストリーミングされるメッセージを処理し、画面にリアルタイムで表示させました。

        socket.on('message', (() => {
            let streamDiv, streamString;
        
            return data => {
                const chatContainer = document.getElementById('chat-container');
        
                if (data === "$$start$$") {
                    chatContainer.querySelector('.bot-message:last-child .loading')?.parentElement.remove();
                    streamDiv = document.createElement('div');
                    streamDiv.className = "message bot-message";
                    chatContainer.appendChild(streamDiv);
                    streamString = "";
                } else if (data === "$$stop$$") {
                    streamDiv = streamString = null;
                } else if (streamDiv) {
                    streamString += data;
                    streamDiv.innerHTML = DOMPurify.sanitize(marked.parse(streamString));
                }
        
                chatContainer.scrollTop = chatContainer.scrollHeight;
            };
        })());
  1. メッセージ受信の準備

    • Socket.IOの'message'イベントをリッスンします
    • 受信したデータに応じて適切な処理を行います
  2. メッセージの種類別処理

    • ストリーミング開始 (\$\$start\$\$):
      • 既存のローディング表示を削除します
      • 新しいメッセージ表示用のdiv要素を作成し、チャットコンテナに追加します
      • メッセージ累積用の文字列を初期化します
    • ストリーミング中のメッセージ内容
      • 受信したデータをメッセージ文字列に追加
      • Markdown形式に変換し、安全にHTMLとして表示
    • ストリーミング終了 (\$\$stop\$\$)
      • 使用していた変数をクリアします
         
  3. 表示の更新:

    • 新しいメッセージが追加されるたびに、チャットコンテナを自動スクロールします

5. おわりに

Pinecone Assistantを活用すると、複雑な設定なしで、ファイルをアップロードするだけで簡単にRAGを構築できることを実感しました。また、応答でストリームを選択すると、リアルタイムの表示が可能です。楽しく遊べたので無料版のトークン数の上限をもうちょっと上げてくれたらと思いました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?