2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

余計なクラウド環境などを使わずに、とりあえずローカルからPythonOpenAIのAPIだけでRAGする。

そもそもRAGとはなんだったか

LLMは何でも知っている訳ではない。例えば特定の会社の規則などの、社外秘情報は知らない。
なので、まずはそれらを検索できるデータベースなど使って、ユーザーの質問から、質問に関する情報を取得する。
そして質問質問に関する情報を一緒にLLMに渡してやることで、与えた情報に基づいて回答を生成させる。

必要なライブラリを入れる

pip install openai
pip install numpy

データベースを作成する

データベースから検索したい文章について、ベクトルを生成する。
検索させたい単位毎にベクトルを生成する。

ここでは、コード中の配列に設定した文字列データを入力として、embed.jsonという名称のファイルに、ベクトル化したデータを書き出す。これをデータベースとする。

from openai import OpenAI
import json

OPEN_AI_API_KEY = 'xxxx'

data = {
    '日本国憲法 第1章 天皇 第1条': {
        'body': '天皇は、日本国の象徴であり日本国民統合の象徴であつて、この地位は、主権の存する日本国民の総意に基く。'
    },
    '日本国憲法 第1章 天皇 第2条': {
        'body': '皇位は、世襲のものであつて、国会の議決した皇室典範の定めるところにより、これを継承する。'
    }
}

openai_client = OpenAI(api_key=OPEN_AI_API_KEY)

# ベクトルを保存する配列
index = []

# 全データに対して繰り返し
for title, item in data.items():
    body = item['body']
    input = title + '\n' + body

    # ベクトル化
    response = openai_client.embeddings.create(
        model="text-embedding-3-small",
        input=input
    )
    embedding = response.data[0].embedding

    # ベクトルを配列に追加
    index.append({
        'title': title,
        'body': body,
        'embedding': embedding
    })

# ファイルに書き出し
with open('./embed.json', 'w') as f:
    json.dump(index, f)

ユーザーの質問からデータベースを検索する

ユーザーの質問もベクトル化する。
そしてembed.jsonから、質問ベクトルに近いベクトルを検索する。

ベクトルの近さはコサイン類似度によって計算する。

import json
from openai import OpenAI
from numpy import dot
from numpy.linalg import norm

OPEN_AI_API_KEY = 'xxxx'

openai_client = OpenAI(api_key=OPEN_AI_API_KEY)

# 準備したベクトルデータベースを読み込み
with open('./embed.json') as f:
    index = json.load(f)

# コサイン類似度を計算する関数    
def cosine_similarity(lhs, rhs):
    return dot(lhs, rhs) / (norm(lhs) * norm(rhs))

# 準備したベクトルデータベースから、近いものを検索する関数
def search(vec, k=3):
    results = map(
        lambda i: {
            'title': i['title'],
            'body': i['body'],
            'similarity': cosine_similarity(i['embedding'], vec)
        },
        index
    )

    # 類似度の大きいもの順にソート
    results = sorted(results, key=lambda i: i['similarity'], reverse=True)

    # 類似度の大きいものからk件を返す
    return results[:k]


# --------------------------------


# LLMへの質問
input = '皇位は世襲ですか?'

# 質問をベクトル化
response = openai_client.embeddings.create(
    model='text-embedding-3-small',
    input=input
)
vec = response.data[0].embedding

# 近いデータを検索
data = search(vec)

print(data)

実行結果

[{'title': '日本国憲法 第1章 天皇 第2条', 'body': '皇位は、世襲のものであつて、国会の議決した皇室典範の定めるところにより、これを継承する。', 'similarity': np.float64(0.5653466695377146)}, {'title': '日本国憲法 第1章 天皇 第1条', 'body': '天皇は、日本国の象徴であり日本国民統合の象徴であつて、この地位は、主権の存する日本国民の総意に基く。', 'similarity': np.float64(0.37133550037315366)}]

プロンプトを生成する

検索したデータから、プロンプトを準備する。
データからLLMに渡す文字列を作成するだけ。

# プロンプトのテンプレート
prompt_template = '''
ユーザーの質問に対する回答を作成してください。

回答に必要であれば、以下のinformationを参照してください。
informationに示す文章には、0から1の範囲の数値が、ユーザーの発言に対する類似度として付与されています。類似度は1が最もユーザーの発言に近いという意味です。

# information

{information}
'''

# プロンプトに埋め込むテキストを検索したデータから作成
information = '\n\n'.join(map(lambda e: f"=====\n類似度 {e['similarity']}\n\n{e['body']}", data))

# プロンプト生成
prompt = prompt_template.format(
    information=information,
).strip()

print(prompt)

実行結果

ユーザーの質問に対する回答を作成してください。

回答に必要であれば、以下のinformationを参照してください。
informationに示す文章には、0から1の範囲の数値が、ユーザーの発言に対する類似度として付与されています。類似度は1が最もユーザーの発言に近いという意味です。

# information

=====
類似度 0.5653466695377146

皇位は、世襲のものであつて、国会の議決した皇室典範の定めるところにより、これを継承する。

=====
類似度 0.37133550037315366

天皇は、日本国の象徴であり日本国民統合の象徴であつて、この地位は、主権の存する日本国民の総意に基く。

LLMに回答を生成させる

準備したプロンプトとユーザーの質問をLLMに渡して、回答を生成させる。

response = openai_client.chat.completions.create(
    messages=[
        {
            "role": "system",
            "content": prompt, # プロンプトを渡す
        },
        {
            "role": "user",
            "content": input, # 質問を渡す
        }
    ],
    model="gpt-4o",
    n=1,
    temperature=1.0,
    frequency_penalty=0.5,
)
content = response.choices[0].message.content

print(content)

実行結果

はい、皇位は世襲であり、国会の議決した皇室典範の定めるところにより継承されます。

おまけ

PythonAWSを使用して、Slackからのチャットによる問い合わせにRAGで回答するボットの、サンプルコードを作成しました。

よろしければ、ご自由にご利用ください🤗

2
3
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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?