【UiPath】ベクトルDB対応アクティビティで独自ナレッジへの質疑応答を実装する

# はじめに
本記事ではベクトルDBとして [Pinecone](https://www.pinecone.io/) を利用して独自ナレッジを登録し、その内容を元に質疑応答するワークフローをUiPath Automation Cloudで実装します。

# 全体構成
- **Pinecone**: Free版でもOK
- **Azure OpenAI Service**: GPTモデルとEmbedding(埋め込み)モデルを利用
- **UiPath Automation Cloud**: Community版でもOK
    - Integration Service: サービス間を連携
    - Studio Web: ワークフローを作成

![00a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/e7bb5dc8-9ebf-e12e-d2e6-a9ef21296750.png)

# 概念の説明
今回実装するワークフローで重要となる概念について簡単に説明します。
- **Embedding(埋め込み)モデル** は、テキストなどのデータを数値ベクトルに変換するためのモデルで、これによりデータを機械学習アルゴリズムで利用しやすくします。このモデルは文脈を考慮して単語やフレーズの意味を捉え、それらを数値化します。たとえば意味合いが近いフレーズはお互いの距離が近く、意味合いが遠いフレーズは距離が遠くなるようにベクトル化されます。このようなモデルは、テキストの類似性を計算したり、文脈に基づいて単語を予測したりするのに有用です。
- **ベクトルDB** は、ベクトル形式のデータを保存および検索するためのデータベースです。これは類似性検索や最近傍探索などベクトル間の関係に基づいた操作を高速に行うのに適しています。テキストをEmbeddingモデルによって数値表現された時、ベクトルDBはその情報を効率的に検索しアクセスするのに役立ちます。今回利用するPineconeは人気のあるベクトルDBの一つです。
- EmbeddingモデルとベクトルDBを組み合わせると、大量のテキストデータから意味的に関連する情報を迅速に抽出できます。例えば特定の質問に最も適した回答を検索したり、特定のテキストに類似した他のテキストを見つけるなど、単純なキーワード一致では実現が難しい検索処理を効率的に実行できます。生成AIシステムの構築において、独自データをあらかじめベクトルDBなどに格納して最適な回答を可能にする手法は **Grounding / RAG(Retrieval-Augmented Generation)** と呼ばれます。Azure環境にRAGアーキテクチャをデプロイする手順については  [「UiPathドキュメントポータルの内容をチャットで問い合わせるシステムを作る」](https://qiita.com/hidecha/items/5ba2ad685748d9ba7418) の記事でも説明しておりますが、今回はより手軽に実装する手順について説明したいと思います。

# 構築手順
大まかな構築の流れ
1. PineconeのインデックスとAPIキー作成
2. Azure OpenAI Serviceのデプロイ
3. Integration Serviceのコネクター設定
4. Studio Webで独自ナレッジをPineconeに登録するワークフロー実装
5. Studio WebでPineconeを参照して回答するワークフロー実装

## 1. PineconeのインデックスとAPIキー作成
- [Pinecone](https://www.pinecone.io/) にアクセスします。初めて利用する時は [Sign Up Free] をクリックします。
    ![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/dd8cde0b-f83b-c7a4-e55d-db82f81aa1a8.png)

- サインアップ方式(Google, GitHub, Microsoftアカウントまたはメールアドレスなど)を適宜選択し、アカウントを作成します。
    ![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/8022b035-5763-6be9-6718-b24d6326dcfa.png)

- 今回は検証目的のためFreeプランで進めます。本番利用の際には [利用規約](https://www.pinecone.io/thin-client-user-agreement/) を参照し、 [適切な料金プラン](https://www.pinecone.io/pricing/) を選択してください。
- アカウントやプロジェクトなどを登録します。
- Indexesメニューを選択し [Create Index] をクリックしてインデックスを作成します。
    ![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/cc33b245-1601-6754-2d5c-3fcf0b6cec1a.png)

    - **Name**: インデックス名は何でも良いですが、本記事では `uipath-test` とします。後で使用するためインデックス名をメモします。
    - **Dimensions**: `1536` (Embeddingモデルで使用される次元)
    - **Metric**: `cosine`
    - **Capacity Mode**: Freeプランでは選択できないため既定値のままとします
    - [Create Index] をクリックしてインデックスを作成します。
    ![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/d83a22ef-2999-0e0f-e37f-13b1f5ae88cf.png)

- API Keysメニューを選択し [Copy Key Value] アイコンをクリックしてAPIキーをコピーします。後で使用するためにメモします。
    ![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/60de1927-4f8e-b93c-51c2-2d8a749732b6.png)

## 2. Azure OpenAI Serviceのデプロイ
- Azure OpenAI Serviceのデプロイ手順は次の記事をご参照ください。

https://qiita.com/hidecha/items/774f009ddda1f760402a

- Azure OpenAI Serviceの **キー1** と **リソース名** をそれぞれコピーしてメモします。
    ![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/1d39a648-f977-c3d0-cfd3-0341f967ef84.png)
- [Azure OpenAI Studio](https://oai.azure.com/portal) にて次の2つのモデルをデプロイします。
    - GPTモデル
        - **モデル**: `gpt-4` (もしくは `gpt-35-turbo`)
        - **モデルバージョン**: `0613` (もしくはより新しいバージョン)
        - **デプロイ名**: `gpt-4`
        - **詳細設定オプション**: 既定値

            ![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/694718c9-e7c5-9aae-9763-4bf3d21cb43e.png)
    - 埋め込みモデル
        - **モデル**: `text-embedding-ada-002`
        - **モデルバージョン**: `2`
        - **デプロイ名**: `text-embedding-ada-002` (モデル名と同じにする！)
        - **詳細設定オプション**: 既定値
            ![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/c070fc5c-003b-e0bb-3843-c3fbc4b68cca.png)
    :::note warn
    GPTモデルのデプロイ名は別名でも良いですが、埋め込みモデルのデプロイ名は必ずモデル名と同じにしてください。(理由は後述)
:::

## 3. Integration Serviceのコネクター設定
- [Automation Cloud](https://cloud.uipath.com) にてPineconeおよびAzure OpenAI Serviceと連携する設定を行います。
- 左メニューにて Integration Service のアイコンをクリックします。
    ![11b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/c4be5e38-e602-181c-2702-a5c0073f94cf.png)

### Pinecone
- コネクターとしてPineconeを選択し [Pineconeに接続] をクリックします。
    ![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/d4efde3b-73ef-18f0-6e0d-d276d23bf281.png)

- 先ほどメモしたAPIキーとインデックス名を指定して [接続] をクリックします。
    ![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/38d8df01-0a48-fe54-8f76-dca04798056b.png)

### Azure OpenAI Service
- コネクターとしてAzure OpenAIを選択し [Microsoft Azure OpenAIに接続] をクリックします。
    ![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/20dc6522-5f6e-bf32-77f7-794f370ed183.png)

- 先ほどメモしたAzure OpenAI Serviceのキーとリソース名を指定して [接続] をクリックします。
    ![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/c933d8d8-91f2-54d5-af29-c2ecfa014a6b.png)

## 4. Studio Webで独自ナレッジをPineconeに登録するワークフロー実装
- [Automation Cloud](https://cloud.uipath.com)  の左メニューにて Studio のアイコンをクリックします。
    ![21a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/8d4abb27-064c-49a2-f6aa-7676ce2dd933.png)

- プロジェクト > [新しいプロジェクト] をクリックします。
    ![22a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/1c911dfc-e40e-09ea-0607-527f40c9ed53.png)

- トリガーは [手動トリガー] を選択します。左上の編集アイコンでプロジェクト名を編集して「Pinecone登録」とします。
- [引数を追加] をクリックし、独自ナレッジとして追加するテキストを定義します。
    - **名前**: `in_text`
    - **方向**: `入力`
    - **型**: `テキスト`
    - **既定値**: 本来は空白として実行時に引数として独自ナレッジのコンテンツを渡すのが望ましいですが、簡易的に検証を行うために値をセットしておきます。今回は [UiPath用語集](https://docs.uipath.com/ja/overview/other/latest/overview/uipath-glossary) のコンテンツをコピー&ペーストで入力します。
    - [作成] をクリックします。
    ![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/57d1e8c8-802c-8350-ae04-4f21cde93e43.png)

- (+) をクリックし、Microsoft Azure OpenAIの **[埋め込みを作成](https://docs.uipath.com/ja/activities/other/latest/integration-service/uipath-microsoft-azureopenai-generate-embeddings)** アクティビティを選択します。
    ![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/57848bfe-1247-0879-0ca8-5f0941030c21.png)

- 先ほど作成したAzure OpenAIのコネクションが表示されることを確認し、各フィールドを設定します。
    - **モデル**: `text-embedding-ada-002` を選択します。
    - **入力**: 引数 `in_text` を指定します。    
    ![15a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/497d6ada-7f27-3354-b274-f0af54fb0e63.png)

- (+) をクリックし、Pineconeの **[埋め込みを更新/挿入](https://docs.uipath.com/ja/activities/other/latest/integration-service/uipath-pinecone-pinecone-vectors-upsert)** アクティビティを選択します。
    ![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/c42da9b2-b269-ad50-133f-fda0abfde4fc.png)

- 先ほど作成したPineconeのコネクションが表示されることを確認し、各フィールドを設定します。
    - **埋め込み**: 右の[その他を表示]アイコン→[変数を使用]→[埋め込みを作成]→ `埋め込み` を順にクリックして変数を選択します。
    - **メタデータ**: 引数 `in_text` を指定します。
    - **Namespace**: Pineconeでインデックス内のレコードを論理的に分離する時に使用します。今回は空白(Default)のままとします。
    ![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/ae80e8ae-4c1f-6866-4be9-f24df71a17fb.png)

- [テスト] をクリックして実行してエラーが発生しないことを確認します。
    ![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/f3bf03f8-2a54-e52d-4b6f-0bfbe8ac33f2.png)

- Pineconeの管理画面にアクセスし、 `uipath-test` インデックスにて　[Query] をクリックし、先ほど作成したレコードが表示されることを確認します。
    ![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/72d1237f-736d-1cd0-df9e-16439bf30150.png)

## 5. Studio WebでPineconeを参照して回答するワークフロー実装
- Studio Webにて新しいワークフローのプロジェクトを作成します。今回も手動トリガーを選択します。プロジェクト名は「Pineconeチャット」とします。
    ![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/5210b353-cdc8-9f95-f1f2-70d39a050fd6.png)

- [引数を追加] をクリックし、独自ナレッジに問い合わせる質問文を定義します。
    - **名前**: `in_question`
    - **方向**: `入力`
    - **型**: `テキスト`
    - **既定値**: 本来は空白として実行時に質問内容を引数として渡すのが望ましいですが、簡易的に検証を行うために値をセットしておきます。今回は「Studioで使用されるローカルフィードとは何ですか？」と入力してみます。
    - [作成] をクリックします。
    ![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/b97da4f1-60ca-7ac5-119d-a8396e37d9c6.png)

### 独自ナレッジを参照しない問い合わせ
- 比較のため、まずは独自ナレッジを参照せずにGPT-4に問い合わせをしてみます。
    - Microsoft Azure OpenAIの **[チャット補完を生成](https://docs.uipath.com/ja/activities/other/latest/integration-service/uipath-microsoft-azureopenai-generate-chat-completion)** アクティビティを選択します。
    ![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/1f9e4989-6b79-40c9-bfc3-2ed32085f9bf.png)

    - Azure OpenAIのコネクションが表示されることを確認し、各フィールドに値をセットします。
        - **プロンプト**: 引数 `in_question`
        - **コンテキスト**: 空白
        - **モデル**: `gpt-4` (Azure OpenAI ServiceでのGPTモデルのデプロイ名)
        ![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/e0d96823-a6bd-2e54-3c94-a73a5e0e50b0.png)
        
    - (+) をクリックし、 **メッセージをログ** アクティビティを選択します。
        ![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/baa2cbc7-0503-1d83-692a-8e0e5fc43c77.png)

        - **メッセージ**: チャット補完を生成 → `上位の生成テキスト` 変数を選択します。
        - **ログレベル**: `Info`
        ![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/b644bfb9-2fa3-76b3-538b-d4afb4281905.png)
        
    - 左上の [テスト] をクリックして実行します。質問内容では単に"Studio"と述べていますのでUiPath Studioと結びつかない回答が返ってきます。

    ```
    Studioでは、`ローカルフィード`とは、あなたの興味に基づくコンテンツが配信されるフィードのことを
    指します。ユーザーが最も頻繁に閲覧または操作する種類のコンテンツほど優先的に表示されます。文章の
    作成や編集、コメントの投稿などの行動は、このフィードに表示されるコンテンツをカスタマイズする上で
    影響を及ぼします。ユーザーに関連するお気に入りのプロジェクトや最新のニュース、ヒントなどの情報も
    含まれます。
    
    ただし、OpenAIのGPT-3などのAIモデルは、個々のユーザーやユーザーのプライバシー情報にアクセスする
    能力はありません。入力される情報を特定のユーザーや以前のリクエストと関連付けることはありません。
    ```

### 独自ナレッジを参照する問い合わせ
- 次に独自ナレッジを参照してGPT-4に問い合わせをしてみます。
    - **チャット補完を生成** アクティビティの縦三点リーダーをクリックし、無効化します。
    ![26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/5939442c-a1d0-78ce-2477-dc1d81058150.png)
    
    - (+) をクリックし、Microsoft Azure OpenAI の **[埋め込みを含むチャット補完を生成](https://docs.uipath.com/ja/activities/other/latest/integration-service/uipath-microsoft-azureopenai-generate-chat-completion-with-embedding)** をクリックします。
    ![27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/86a7a6d1-ade0-1023-ae82-878097e6af20.png)
    
    - Azure OpenAIのコネクションが表示されることを確認し、各フィールドに値をセットします。
        - **入力**: 引数 `in_question`
        - **モデル**: `gpt-4` (Azure OpenAI ServiceでのGPTモデルのデプロイ名)
        - **Pinecone APIキー**: メモしたPinecone APIキー
        - **Pinecone環境**: `gcp-starter`
        - **Pineconeのインデックス**: `uipath-test`
        - **Pinecone namespace**: 空白
        ![28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/04358bbb-a296-dcbc-4913-081f542e007b.png)

    :::note warn
    現在のところ下記の制約があります。(今後の改善に期待したいと思います)
    - Pineconeのコネクションは選択できないためAPIキーなどを手動で指定します。
    - Azure OpenAI ServiceでのEmbeddingモデルのデプロイ名は指定できません。デプロイ名はモデル名と同じ `text-embedding-ada-002` として作成しておく必要があります。別名にすると実行時にエラーになります。
:::

- **メッセージをログ** アクティビティのメッセージを 埋め込みを含むチャット補完を生成 → `上位の生成テキスト` 変数に変更します。

- 左上の [テスト] をクリックして実行します。今回は独自ナレッジとしてUiPath用語集を参照するワークフローになっていますのでUiPath Studioに関連した回答が返ってきます。
    ```
    ローカルフィードは、アクティビティパッケージのローカルリポジトリを指します。これは、Studio
    またはRobotによってデフォルトで「%ProgramFiles(x86)%\UiPath\Studio\Packages」にインストール
    されます。このフィードは、インストールしないように選択することも、Studioの「パッケージを管理」
    ウィンドウで後から無効化することも可能です。
    ```

# 応用例

## PDFファイルの登録
- 先ほどの実装例では独自ナレッジのテキストを入力引数で直接指定しましたが、OneDriveに配置されたPDFファイルからテキストを読み込むこともできます。これを実装するには「Pinecone登録」ワークフローを下記のように変更します。
- Microsoft 365の **ファイルをダウンロード** アクティビティを使用します。
    - Microsoft 365のコネクターを作成します。
    - OneDriveに配置したPDFファイルを選択します。ここでは日本ディープラーニング協会(JDLA)が公開している [生成AIの利用ガイドライン](https://www.jdla.org/document/#ai-guideline) → 「生成AIの利用ガイドライン【条項のみ】(第1.1版, 2023年10月公開)」をPDF化したものを使用します。
    :::note warn
    読み込みに使用するPDFファイルの文字数には注意が必要です。今回使用している [Embeddingモデルの最大トークン数は8,191](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/models#embeddings-models) です。日本語ではおおよそ1文字あたり1トークンとなります (正確には [Tokenizer](https://platform.openai.com/tokenizer) を使って調べることができます)。よって約8,000文字を超えるPDFファイルは一度には扱えないため分割(チャンク)して処理する必要があります。    
:::

- **PDFのテキストを抽出** アクティビティを使用し、ファイルをダウンロード → [ダウンロードされるファイル] 変数を指定します。
    ![29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/a8bf39de-401c-d2e2-9e5c-241b2a22f0f1.png)

- **埋め込みを作成** アクティビティの入力フィールドに、PDFのテキストを抽出 → `抽出テキスト` 変数を指定します。
- **埋め込みを更新/挿入** アクティビティの入力フィールドにも同様に、PDFのテキストを抽出 → `抽出テキスト` 変数を指定します。
    ![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/efed2e46-9500-675f-ddda-caf4e660f7ae.png)

- [テスト] をクリックして実行し、Pineconeのインデックスに新たにレコードが追加されていることを確認します。
- 「Pineconeチャット」ワークフローにて生成AIガイドラインの内容に沿った質問 (例:「生成AIによる生成物が著作権侵害に該当しないようにするためにはどのようなことに留意すれば良いですか？」) を実行します。もしトークンの上限に達してしまう場合にはAzure OpenAI Serviceにて `gpt-4-32k` など多くのトークンを利用できるモデルをデプロイして利用します。

## Studioでの実装
- 今回は主にStudio Webで実装しましたが、UiPath Studioでももちろん実装できますので、既存の自動化処理に組み込むことも可能になります。
    - Integration Serviceアクティビティのバージョンは現時点で最新の `1.2.0` を使用します。
    - 下記のアクティビティをそれぞれ使用します。
        - Microsoft Azure OpenAI → **[埋め込みを作成](https://docs.uipath.com/ja/activities/other/latest/integration-service/uipath-microsoft-azureopenai-generate-embeddings)**, **[埋め込みを含むチャット補完を生成](https://docs.uipath.com/ja/activities/other/latest/integration-service/uipath-microsoft-azureopenai-generate-chat-completion-with-embedding)**
        - Pinecone → **[埋め込みを更新/挿入](https://docs.uipath.com/ja/activities/other/latest/integration-service/uipath-pinecone-pinecone-vectors-upsert)**

    ![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/24862f2d-79be-3894-6817-d31ee1957d48.png)

# おわりに
今回はPineconeをベクトルDBとして独自ナレッジの登録と問い合わせを行うワークフローの実装例について説明しました。社内の独自ナレッジを登録して文脈に即した回答が可能になりますので、ぜひ様々なシステムの自動化に組み込んで利用してみてください。現在のところはまだ他のベクトルDB (Azure AI Search・Chromaなど) は対応しておりませんが、今後の拡張に期待したいと思います。