また、BacklogでAzure活用の備忘録です。
普段のお仕事でお客様からのご質問にBacklogを利用して回答するといった事があります。
その質問と回答のセットはナレッジとして非常に有益なのですが、ピンポイントで探しづらく、後から「前同じような質問あったような?」「誰かが同じような対応してたけど...どこだっけ?」とよくなっていました😓
(自分自身の記憶力の問題でもありますが...)
そこで...「これは、自然言語で類似検索できたら良さそう。」という事で作ってみることにしました。ちなみに、この記事で各工程の詳細な解説は致しません。あくまでアイディアとしてこういう使い道もあるんだな~くらいに思って頂ければ幸いです🙌
Backlog APIでチケットの内容を取得 →それらを埋め込み(ベクトル)化 → CosmosDBに保存します。
GitHub Actionsのscheduleトリガーでそれらの処理を月次で走らせ、データのリフレッシュをおこなっています。
と、まあ超シンプルです。
そもそも、とにもかくにも安く済ませたい!と思ってました。Azureでベクトル検索といえばAI Searchが代表的ですが、ここではコスト面を重視でAzure CosmosDB for NoSQLでのベクトルインデックス機能を利用することにしました。詳細は下記のリンクをご参考ください。
※現時点(2024.08) ではパブリックプレビューの段階です
工程
以降は少しハマった点 & ちょっぴり工夫点などを書いていきます👀
ベクトルポリシー
CosmosDB for NoSQLを作成して、新しくデータベースを作成する際にベクトルポリシーを定義します。埋め込みにはtext-embeddings-ada-002
を使うので1536次元です。
AOAIで埋め込みとCosmosDBへの格納処理は以下を参考に書き進めました。
ベクトル検索
検索用のコマンドラインツールもGo言語で書きました。
クロスパーティションクエリ (パーティションキーの指定なしでクエリ) を実行するメソッドやプロパティを探しましたが、 どうも見当たりませんでした。
...
// pkをnullにしても駄目 パーティションキーなしでクエリする方法が分からない
pK := azcosmos.NullPartitionKey
query := "SELECT TOP 10 c.summary, c.description, c.url, VectorDistance(c.vector,@embedding) AS SimilarityScore FROM c ORDER BY VectorDistance(c.vector,@embedding)"
queryOptions := azcosmos.QueryOptions{
QueryParameters: []azcosmos.QueryParameter{
{Name: "@embedding", Value: vector},
},
}
pager := container.NewQueryItemsPager(query, pK, &queryOptions)
Issueを見てると、この機能はGo言語のSDKには現時点(2024.8)で実装されていないようでした。
そこで、パーティションキーを見直すことにしました。
当初 /id
で設定していたのですが、Backlog課題チケットの登録タイプ名で設定する事にしました。
[
{
"id": 1,
"projectId": 1,
"issueKey": "BLG-1",
"keyId": 1,
"issueType": {
"id": 2,
"projectId" :1,
"name": "Q&A", ← この名称でパーティションを分けるようにしてみた
"color": "#7ea800",
"displayOrder": 0
},
"summary": "first issue",
"description": "",
"resolution": null,
"priority": {
"id": 3,
"name": "中"
},
...
この辺は実際に使ってみながら日々改善をしていこうと思ってます。
DBを作り直して実際に検索動作をさせてみた結果はこんな感じです。
Enter your query message >>
Azure FrontDoorから500エラーが返ってきます //検索したいワードを入力
Top 10 similar issues: //以下検索結果
Summary: Azureの障害に関して
URL: https://×××××××××××.backlog.com/view/×××××××××
Summary: フロントドアの5xxエラーが頻発する
URL: https://×××××××××××.backlog.com/view/×××××××××
Summary: フロントドアのバックエンドルーティングの不具合
URL: https://×××××××××××.backlog.com/view/×××××××××××
Summary: Front Door/CDNプロファイルに問題が発生する原因
URL: https://×××××××××××.backlog.com/view/×××××××××××
...
URLはその課題チケットのURLとなっています。
これで類似した問題を探しやすくなりました🙌
Cosmos DBのIP制限とGitHub Actions
CosmosDBには一部のネットワークからしかアクセスできないようにIP制限をかけてます。
しかし、GitHubのIPは固定ではなく、時々変更されるため、ワークフロー実行時のIPを取得してファイアウォールルールを更新するステップを設けることにしました。こちらの記事を参考にさせて頂きました。これだけの為にセルフホステッド用意するの嫌だなあ...と思っていたのでお手軽に済ませられました。
以下はワークフローの全文です。
name: knowledgebase-batch
on:
schedule:
- cron: '0 0 1 * *'
workflow_dispatch:
env:
COMOS_DB_ACCOUNT_NAME: <CosmosDB AccountName>
RESOURCE_GROUP: <Resource_GroupName>
jobs:
knowledgebase-update:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in with Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
# 現在のファイアーウォールルールを取得する
- name: Get IP Address For CosmosDB
id: get-ip-list
uses: azure/CLI@v2
with:
azcliversion: 2.62.0
inlineScript: |
CURRENT_IP_LIST=$(az cosmosdb show --name $COMOS_DB_ACCOUNT_NAME --resource-group $RESOURCE_GROUP --query "ipRules[].ipAddressOrRange" -o tsv | tr '\n' ' ')
echo "CURRENT_IP_LIST=$CURRENT_IP_LIST" >> $GITHUB_ENV
# 実行時のIPアドレスを追加して更新する
- name: Add IP Address For CosmosDB
uses: azure/CLI@v2
with:
azcliversion: 2.62.0
inlineScript: |
NEW_IP=$(az rest --method get --uri http://ipinfo.io/ip)
if [ -z "$CURRENT_IP_LIST" ]; then
NEW_IP_LIST="${NEW_IP}/32"
else
NEW_IP_LIST=$(echo "$CURRENT_IP_LIST ${NEW_IP}/32" | tr ' ' '\n' | sort -u | tr '\n' ',' | sed 's/,$//')
fi
az cosmosdb update --name $COMOS_DB_ACCOUNT_NAME \
--resource-group $RESOURCE_GROUP \
--ip-range-filter "$NEW_IP_LIST"
echo "Updated IP rules: $NEW_IP_LIST"
# Goの処理を実行する
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install dependencies
working-directory: batch/
run: |
go mod tidy
- name: Run Go script
working-directory: batch/
env:
BACKLOG_API_KEY: ${{ secrets.BACKLOG_API_KEY }}
BACKLOG_BASE_URL: ${{ secrets.BACKLOG_BASE_URL }}
AZURE_COSMOS_ENDPOINT: ${{ secrets.AZURE_COSMOS_ENDPOINT }}
AZURE_COSMOS_ACCOUNT_KEY: ${{ secrets.AZURE_COSMOS_ACCOUNT_KEY }}
AZURE_COSMOS_CONTAINER_NAME: <ContainerName>
AZURE_COSMOS_DB_NAME: <DbName>
AOAI_API_KEY: ${{ secrets.AOAI_API_KEY }}
AOAI_ENDPOINT: ${{ secrets.AOAI_ENDPOINT }}
run: |
go run .
# CosmosDBファイアーウォールルールを元に戻す
- name: Reset IP Address For CosmosDB
uses: azure/CLI@v2
with:
azcliversion: 2.62.0
inlineScript: |
RESET_IP_LIST=$(echo "$CURRENT_IP_LIST" | tr ' ' '\n' | sort -u | tr '\n' ',' | sed 's/,$//')
az cosmosdb update --name $COMOS_DB_ACCOUNT_NAME \
--resource-group $RESOURCE_GROUP \
--ip-range-filter "$RESET_IP_LIST"
echo "Updated IP rules: $RESET_IP_LIST"
さいごに
過去の質問と回答例に類似したものが無いかを探す。といったひと手間が楽になりました。
もっと凝れる箇所は色々とあるので、使いながら改善ポイントに手を入れていこうと思います。特に検索精度はもっと良くできそう...🧐
以上、BacklogとAzureを活用した業務改善ネタでした~!