Azure OpenAI Service ワークショップに参加したので復習してみる(Cosmos DB×AI Search部分)

# はじめに
リソースがなんとか作成できたので、インデックスを作成するFunctionsの作成に入ります。

前回の記事

https://qiita.com/usomaru/items/cdda29fd83c2c95a69de

# そもそもどういう構成か

アーキテクチャをそのまま引用していいかわからなかったので、すごく簡単に今回のワークショップで作成した構成をかきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/73e3a82a-5be3-42dd-d13a-8f1100c05039.png)


まず、自身が持っているデータ(社内ナレッジとか)をCosmosDBに登録します。
登録したデータはAzure Open AIを使ってベクター化され、AI Searchにデータとともに登録されるイメージです。

そのデータを基におれおれChatGPTで社内ナレッジとかが検索できるようになる！というものでした。

# 今回実装してみるところ
この記事では、特に私が触ったことのなかった、Cosmos DBのChange Feedの機能を使ってAI Searchにインデックスを登録するところまでをピックアップしてまとめたいと思います。(上の図の赤枠で囲っているところ)

# 環境
- windows11
- Visual Studio Code
- Python v3.10.11
- Azure Core Tools v4.0.5455(x64)

# リソース作成

以下のリソースを作成します。
- CosmosDB
- AI Search(プランはBasic)
- Azure Functions(ローカルに作成)

:::note warn
Azure Functionsのリソースに関しては今回はローカル実行のみで発行はしてません。
:::

# AI Searchでインデックスを作成

インデックスを以下のような設定で作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/ee1d9e9f-477a-a0bf-58b3-293da138c1a6.png)



|フィールド名|型|取得可能|フィルター可能|ソート可能|ファセット可能|検索可能|アナライザー|
|--|--|--|--|--|--|--|--|
title|Edm.String|✅|-|-|-|✅|日本語 - Microsoft|
category|Edm.String|✅|✅|-|✅|✅|日本語 - Microsoft|
content|Edm.String|✅|-|-|-|✅|日本語 - Microsoft|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/5319768b-3246-4566-7125-1ad77e7751bb.png)

各項目について( ..)φメモメモ
|項目名|説明メモ|
|--|--|
|取得可能|チェックが入っていないとSELECTできない|
|フィルター可能|完全一致の検索|
|ソート可能|数字とか日付でソート|
|ファセット可能|ドリルダウンできる|
|検索可能|フルテキスト検索|

おまけ
管理者キーはインデックスの更新とかができる
クエリキーはインデックスの読み込みだけができる

# Funcionsを実装

Cosmos DB Triggerを使うことででリアルタイムに AI Search のインデックスを更新できます。

```json:local.setting.json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
    "COSMOS_CONNECTION": "",
    "AI_SEARCH_ENDPOINT": "",
    "AI_SEARCH_ADMIN_KEY": ""
  }
}
```

`COSMOS_CONNECTION`の値👇

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/bc4913d9-4e0f-4095-2560-1305a5e72b11.png)


`AI_SEARCH_ENDPOINT`の値👇

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/5f41638b-9dd3-d438-14d3-06efacab13b2.png)

`AI_SEARCH_ADMIN_KEY`の値👇

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/b36b9b3a-0e44-62b8-ad0a-7f78109cb07e.png)


```indexer.py
import azure.functions as func
import logging
import os

from azure.core.credentials import AzureKeyCredential
from azure.search.documents import SearchClient

app = func.FunctionApp()

COSMOS_DATABASE_NAME = ""
COSMOS_CONTAINER_NAME = ""
AI_SEARCH_INDEX_NAME = ""

search_client = SearchClient(os.getenv("AI_SEARCH_ENDPOINT"),
                             AI_SEARCH_INDEX_NAME,
                             AzureKeyCredential(os.getenv("AI_SEARCH_ADMIN_KEY")))

@app.cosmos_db_trigger(arg_name="items",
                       connection="COSMOS_CONNECTION",
                       database_name=COSMOS_DATABASE_NAME, 
                       container_name=COSMOS_CONTAINER_NAME, 
                       create_lease_container_if_not_exists=False,
                       feed_poll_delay=5000,
                       lease_container_name="leases")
def indexer(items: func.DocumentList):
    documents_to_upsert = []

    for item in items:

        documents_to_upsert.append({
            "id": item["id"],
            "title": item["title"],
            "category": item["category"],
            "content": item["content"]})

    search_client.merge_or_upload_documents(documents_to_upsert)
    logging.info(f"{len(documents_to_upsert)} document(s) uploaded")
```

# 試してみる
CosmosDBにデータを登録してみてインデックスが登録されるかを見てみます。

以下のデータをデータエクスプローラーを使って登録してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/7b31faed-ca46-5772-d4cd-f9f437f48c54.png)


登録されたら、AI Searchから作成したインデックスを見てみます。
検索ボタンを押すと、先ほど登録したデータがこちらにも反映されていることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/0efca91a-de3e-b528-2a11-81ce9b1bb85a.png)


# おわりに
今回初めてしっかり試したのですが、リアルタイムでデータを反映できるChange Feed機能は便利だなと思いました。
データが追加・更新されるたびに勝手にインデックスまで登録してくれるので、データの管理も楽ですね。