見出し画像

Azure OpenAI x Pinecone x TeamsでFAQ検索ボットを作成する~pineconeにqueryを投げる+LLMで回答を作成する編

この記事について

この記事ではApp Serviceを利用してpinecone.queryがアップロードしたpinecone.Indexに対してqueryするところまでを解説しています。ところどころソースは載せていますが、常に最新とはいかないので、参考までにしてください。なお、使用しているframeworkはflaskです。

手順

1.queryを受け付けるapp.pyを記述する

# app.py
import openai
import pinecone
from flask import Flask, request
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy


# pineconeに検索を投げるため、質問もvector化する
def search(pindex,keyword):
    embeddings = embedded.embedding(keyword)
    embeds = [record['embedding'] for record in embeddings['data']]
    response = pindex.query(
        top_k=3,
        vector=embeds
    )
    return(response)

# ChatCompetionで回答を生成する
def llm(question,assitant):
    openai.api_type = 'azure'
    openai.api_base = '自分のurl'
    openai.api_version = '2023-03-15-preview'
    openai.api_key = '自分のAPIキー'

    response = openai.ChatCompletion.create(
        engine='gpt-35-turbo',
        messages = [
            {"role":"system","content":"assistantからの情報のみで答えること"},
            {"role":"assistant","content": assitant},
            {"role":"user","content": question},
        ],
        temperature=0.7,
        max_tokens=800,
        top_p=0.95,
        frequency_penalty=0,
        presence_penalty=0,
        stop=None
    )
    return(response.choices[0]['message']['content'].strip())


@app.route('/chat', methods=['POST'])
def post_chat():
    try:     
        chat = request.json["chat"]
    except:
        return('{"Status":"Nothing"}')

    pinecone.init(
        api_key= '自分のAPIキー',
        environment='自分の環境'
    )
    # 既存のindexへの接続を行う
    # search関数に渡したいので、意味がないかもしれないが、pcnindexに入れてみる
    pcnindex = pinecone.Index(index_name='index_of_faq')
    # pineconeからはtop_k=3分の3件が返ってくる
    results = search(pcnindex,chat)

    reschat = ""
    urls = []
    titles = []
    num = 0
    # json形式で戻ってきているmatchesをループする
    for res in results["matches"]:
        id = res['id']
        # postgresqlにidを投げてオリジナルのテキストデータを取得する
        count = db.session.query(faqdata).filter_by(id=id).count()
        if(count != 0):
            result = db.session.query(faqdata).filter_by(id=id).first()
            urls.append(result.url)
            titles.append(result.title)
            #metadataの取り出しサンプル
            print(res["metadata"]["text"],res["metadata"]["url"])

            #最初の一件だけ処理をする
       if num == 0:
                reschat = llm(chat,result.textdata)
                num += 1
  # Teamsにそのまま投げられるようにhtmlをある程度作成してresponseすることにする
    answer  = ""
    answer += reschat
    answer += "\n\n<br><br>他にも候補があります。<br>\n"
    row = 0
    for url,title in zip(urls,titles):
        answer += f'\n<br><a href="{url}">"{title}"</a><br>\n'
        row += 1
    return(answer)

embedded.pyは再掲します。

#embedded.py

import openai

openai.api_type = 'azure'
openai.api_key = '自分のキー'
openai.api_base = '自分のエンドポイント'
openai.api_version = "2023-05-15"

def embedding(textdata):
    response = openai.Embedding.create(
        input=textdata,
        engine = 'text-embedding-ada-002',
        
    )
    return(response)

このままコピペされても動かないと思います。申し訳ございません。
処理の流れとしては、jsonで受け付けたキーワードをembeddingでvector化して、それをpineconeのqueryに渡します。結果が返ってくるのですが、pineconeのidがオンプレのDBのFAQのユニークキーになっているため、postgresqlに検索をかけて、FAQのurlとタイトルを持ってこれるというわけです。pineconeのidが何でもいいみたいなので、そういう使い方にしました。そして、そのオリジナルの回答をプロンプトのassistantデータにして、Chat-GPTに投げて回答を生成させています。
Teamsにはhtmlで直接投稿ができるので、App Service側で加工をしてresponseしています。
(2023/06/20追記)
pineconeのmetadataが取り出せることが判明したのでソースにサンプルを追加しました。

相互リンク

概要編

Pineconeアップロード編

postgresqlアップロード編

Logic App作成編


いいなと思ったら応援しよう!