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件
- アクセス方法
- 住所
結合したファイルのイメージ
3. Pinecone Assistantへのアップロード
3.1 アシスタントの作成
Pineconeのコンソールから"Create an Assistant"ボタンを押下して作成します。
3.2 ファイルアップロード
作成したホテル情報のデータファイルをPinecone Assistantにアップロードします。プログラムからでもアップロードできますが、ブラウザからファイルアップロードしました。画面に右端の"Drag and Drop"の領域にファイルをドラッグすることでアップロードできます。
3.3 確認
データ登録完了後、Pinecone Assistantのコンソールから質問と回答を確認することができます。
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;
};
})());
-
メッセージ受信の準備
- Socket.IOの'message'イベントをリッスンします
- 受信したデータに応じて適切な処理を行います
-
メッセージの種類別処理
- ストリーミング開始 (\$\$start\$\$):
- 既存のローディング表示を削除します
- 新しいメッセージ表示用のdiv要素を作成し、チャットコンテナに追加します
- メッセージ累積用の文字列を初期化します
- ストリーミング中のメッセージ内容
- 受信したデータをメッセージ文字列に追加
- Markdown形式に変換し、安全にHTMLとして表示
- ストリーミング終了 (\$\$stop\$\$)
- 使用していた変数をクリアします
- 使用していた変数をクリアします
- ストリーミング開始 (\$\$start\$\$):
-
表示の更新:
- 新しいメッセージが追加されるたびに、チャットコンテナを自動スクロールします
5. おわりに
Pinecone Assistantを活用すると、複雑な設定なしで、ファイルをアップロードするだけで簡単にRAGを構築できることを実感しました。また、応答でストリームを選択すると、リアルタイムの表示が可能です。楽しく遊べたので無料版のトークン数の上限をもうちょっと上げてくれたらと思いました。