質問1つで社員マスター！ - Weaviate・RAG・GPTを活用した検索の実装-

## はじめに
本格的にプログラミングを学び始めてから、早くも1年が経過しました！

社会人となった私は、社内のDX（デジタルトランスフォーメーション）推進に携わる機会を得て、生成AIの勉強に取り組み始めました。
その過程で、**WeaviateとRAG（Retrieval-Augmented Generation）を用いた簡易的な検索システムの実装**に挑戦しましたが、予想以上の難関に直面しました。。。

この記事では、その奮闘の記録を共有したいと思います。
同じような挑戦をしている方々の参考になれば幸いです！！！

## 実現したいこと
**ユーザーの自然な問いかけに対して、適切な社員情報を提供すること**

例えば、
特定の社員に関する質問：
「〇〇さんについて教えてください」という問いかけに対し、「〇〇さんは△△事業部に所属しており、〜」といった具体的なプロフィール情報を返答する

属性や趣味に基づく社員検索：
「ラーメン好きな人はいますか？」のような問いかけに対して、「ラーメンが好きなのは□□さんです。〇〇さんの趣味は〜」といった形で、関連する社員情報を提供する

このように、単なるキーワード検索を超えて、文脈を理解し、関連性の高い情報を自然な形で提供することを目指しました！

※本記事ではOpenAIとWeaviate CloudのAPIを使用しています

##  早速やっていく！
本記事では簡易的な実装を目指したので、サーバーを設定する手間を省くことのできるWeaviateのインスタンスをクラウド上でホスティングするサービスである**Weaviate Cloud** (https://auth.wcs.api.weaviate.io/auth/realms/SeMI/login-actions/registration?client_id=wcs-frontend&tab_id=XnCZlD8eBjU) を用いました！

弊社では「ourly」というプロフィールサービスを導入しており、社員のプロフィール情報はourlyに集約されています。

最初の計画では、ourlyのページをスクレイピングすることを考えていました。
しかし、ourlyへのログインが必要であることを完全に忘れていました。。。

現在の私の技術力では、ログイン問題を簡単に解決することが難しいと判断し、方針を変更しました。
代わりに、データを直接Weaviateのデータベースへ登録することにしました！！！


### 1, Weaviate Cloudで無料のsandbox作成
作成するsandboxとはWeaviateのデータベースの実行環境のことです！
sandboxを作成するには会員登録が必要なので会員登録を行なってください。

![スクリーンショット 2024-07-01 18.03.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617571/9f8e9b82-111c-473a-91ec-e1d0059c8a09.png)
無事ログインできたら、赤枠で囲っている部分の「+ Create cluster」からsandboxを作成します。
（ボタンはclusterとなっていますが、clusterを作成するにはお金がかかってしまいますので注意してください！）
sandboxは14日後に使えなくなってしまいますが、２つまで無料で作ることができるので安心して作成してください！



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617571/291c287f-2fe5-a8ba-4952-37b7907b9d99.png)

適当な名前を入力したら「Create」ボタンを押してsandboxの作成は完了です!
![スクリーンショット 2024-07-06 21.45.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617571/047ae999-51cf-cd2c-c74f-669bf19a157f.png)
「＋Create cluster」のボタンがあったページに設定した名前のsandboxが追加されていたら成功！



### 2, Schemaを作成

先ほど作成したsandboxのURLとAPI Keyが必要になるので、確認方法を説明していきます！

まず、画像の赤色の枠で囲った下矢印ボタンを押します。
![スクリーンショット 2024-07-06 21.45.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617571/04fd3a54-7b05-30ac-e7f9-12196cca94b7.png)

すると画面がにゅっと下に伸びて必要な情報が出てきます！
赤枠線の部分がURL、青枠線の部分でAPI Keyを確認できます！
![スクリーンショット 2024-07-06 22.03.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617571/01b3d99b-2029-8bc4-41e9-dae69f5b5bdd.png)

ご自身のそれぞれの情報をコピーして、以下のコードの# Weaviateクライアントの設定の**url=""にURL**を、**auth_client_secret=AuthApiKey(api_key="")にAPI Key**を貼り付けてください！
**additional_headers={'X-OpenAI-Api-Key': ""}にはOpenAIのAPI Key**を入れてください〜！

```python
import os
import weaviate
from weaviate.auth import AuthApiKey

# Weaviateクライアントの設定
client = weaviate.Client(
    url="",
    auth_client_secret=AuthApiKey(api_key=""),
    additional_headers={'X-OpenAI-Api-Key': ""}
)

# 既存のクラスを削除
if client.schema.exists("Profile"):
    client.schema.delete_class("Profile")

# スキーマの定義
schema = {
    "classes": [
        {
            "class": "Profile",
            "description": "profile",
            "vectorizer": "text2vec-openai",
            "properties": [
                {
                    "name": "text",
                    "description": "text of profile",
                    "dataType": ["text"]
                }
            ]
        }
    ]
}

# スキーマを追加
client.schema.create(schema)
```

本記事では一旦構造化せずに、全文をそのままテキストとしてデータベースに格納することにしました！

Weaviateクライアントの設定はのバージョンによって書き方が異なるので注意してください!
（上記のコードの書き方は1.34.0です！）
```
pip show openai
```
このコマンドを実行することでバージョンを確認することができますので、確認してみてください！
私はこの書き方に一番苦戦しました。。。


### 3, Weaviateにデータを追加する

```python
import os
import weaviate
from weaviate.auth import AuthApiKey

# Weaviateクライアントの設定
client = weaviate.Client(
    url="",
    auth_client_secret=AuthApiKey(api_key=""),
    additional_headers={'X-OpenAI-Api-Key': ""}
)

# サンプルテキスト
data = [
    {
        "text": ""
    },
    {
        "text": ""
    },
    {
        "text": ""
    },
    
]

# データをインポート
for item in data:
    client.data_object.create(
        {
            "text": item["text"],
        },
        "Profile"  # スキーマのクラス
    )

print("Data import completed.") 
```

このtextの部分に社員のプロフィール情報をまるごと入れました！
（ファイルを分けているので毎回importやWeaviateクライアントの設定をしています）


### 4, ベクトル検索しGPTで回答を生成
```python
import json
import os
import openai
from openai import OpenAI
import weaviate
from weaviate.auth import AuthApiKey

# Weaviateクライアントの設定
client = weaviate.Client(
    url="",
    auth_client_secret=AuthApiKey(api_key=""),
    additional_headers={'X-OpenAI-Api-Key': ""}
)

text_input = input("質問を入力してください: ")

#　ベクトル検索
response = (
        client.query
        .get("Profile", ["text"])
        .with_near_text({"concepts": [text_input], "certainty": 0.7})
        .with_limit(1)
        .do()
    )
    

#　プロフィールのテキストを取得
profile_text = response['data']['Get']['Profile'][0]['text']

# GPTで回答を生成
def generate_answer(context, question):
    client = OpenAI(api_key='')
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": """あなたは社員のプロフィール情報をたくさん知っています。
                                質問を送ってくる人は社員について知りたがっているので、質問には丁寧に答えてください。"""
            },
            {
                "role": "user",
                "content": f"質問{question}に一問一答形式ではなく、つながった文章で10文程度で答えてください。"
            },
            {
                "role": "assistant",
                "content": f"{context}の情報に基づいて回答します。"
            }
        ],
        temperature=0.7,
    )

    return response.choices[0].message.content

#　回答を生成
answer = generate_answer(profile_text, text_input)
print(answer)

```

### 5, 実行結果
「〇〇さんについて知りたい！」と投げた結果、以下のようになりました！
![スクリーンショット 2024-06-30 17.59.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617571/5acfeb7e-6666-e009-c514-f89c3b1a6fa4.png)

「ラーメン好きな人は？？？」と投げた結果は、以下のようになりました！
![スクリーンショット 2024-06-30 18.07.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617571/4f5803e0-2a59-973b-0709-797c13305898.png)

すごい、、、、！！

## 最後に
このように、WeaviateとRAG、そしてGPTを組み合わせることで、単純なキーワード検索を超えた、文脈を理解した高度な情報検索システムを実現することができました！

技術の進歩は日々めざましく、特に生成AIの分野では新しい可能性が次々と生まれています。
この記事が、同じように挑戦している方々の参考になれば幸いです！