Skip to content

Commit

Permalink
Merge pull request #1466 from tubone24/cms/blog/2024-12-30-Langfuse-v…
Browse files Browse the repository at this point in the history
…3をAWSマネージドサービスで作る

Langfuse v3をAWSマネージドサービスで作る
  • Loading branch information
tubone24 authored Dec 31, 2024
2 parents c72275c + ff2cf3b commit 47e22e9
Show file tree
Hide file tree
Showing 3 changed files with 510 additions and 47 deletions.
30 changes: 13 additions & 17 deletions src/components/Content/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,40 +73,36 @@
blockquote {
position: relative;
box-sizing: border-box;
padding: 10px 12px;
margin-top: 40px;
margin-bottom: 40px;
margin-left: -5px;
padding: 35px 15px 10px;
font-style: italic;
color: #464646;
border: solid 3px #3ca5d4;
border-left-width: 50px;
color: #777;
background: #f5f5f5;
border-left: 4px solid #9dd4ff;
box-shadow: 0 2px 4px rgb(0 0 0 / 14%);
}

blockquote::before {
position: absolute;
top: 50%;
left: -40px;
top: 5px;
left: 3px;
display: inline-block;
//font-family: sans-serif;
font-size: 32px;
font-family: sans-serif;
font-size: 90px;
line-height: 1;
color: $white;
content: ">";
color: #9dd4ff;
content: "";
}

blockquote p {
position: relative;
z-index: 3;
padding: 0;
margin: 10px 0;
margin: 7px 0;
line-height: 1.7;
}

blockquote cite {
display: block;
font-size: 0.9em;
color: #545454;
color: #888;
text-align: right;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ templateKey: blog-post
おはようございます!!こんにちは!!
この記事は**AWS re:invent開催のラスベガス**からお送りしてますが、一切AWS関係ないです....!!!

![](https://i.imgur.com/wuH4vwW.jpg)
![w](https://i.imgur.com/wuH4vwW.jpg)

アドベントカレンダーを**re:invent**期間中に書こうという魂胆が裏目にでました!

Expand All @@ -34,11 +34,11 @@ templateKey: blog-post

あと、この記事は[KDDIアジャイル開発センター(KAG) Advent Calendar 2024](https://qiita.com/advent-calendar/2024/kag)の5日目の記事です!

## 企業検索窓のサジェスト機能が作りたいよー
## 企業検索窓のサジェスト機能が作りたいよー

突然ですが、こんな感じの**企業名を入力することでどんどん企業名の候補がサジェストで出てくるようなUI**を作ってほしいとクライアントやプロダクトマネージャーから依頼されたらどうしますか...?

![](https://i.imgur.com/1UvKYKu.gif)
![w](https://i.imgur.com/1UvKYKu.gif)

企業名のサジェスト機能を提供しているAPIや実装に組み込めるSDKで提供しているSaaSを探したり、(このブログの検索窓でも採用してますが)[Algolia](https://www.algolia.com/)などの全文検索SaaSを活用するなどを検討することでしょう。

Expand All @@ -52,7 +52,7 @@ templateKey: blog-post

今回は、 AWSなどのクラウドサービスにスクラッチでサジェスト機能の全文検索の仕組みとフロントエンドを作成し、しかもそのコストをできるだけ下げていく、というなんともマニアックな要件に対して挑んだ軌跡を記載していきたいと思います。

### ちなみに...
### ちなみに

日本の企業名に関しては、国税庁が提供する[法人番号システム Web-API](https://www.houjin-bangou.nta.go.jp/pc/webapi/riyokiyaku.html)というものがあります。

Expand Down Expand Up @@ -84,7 +84,7 @@ templateKey: blog-post
- **Elasticsearch**のような全文検索エンジンを構築し、サジェストしたい単語(今回は企業名)をインデックスとして登録しておく
- フロントエンドから**直接上記のエンジンにクエリを投げ込んで**検索結果にアクセスする

![](https://i.imgur.com/OO7JYK3.jpg)
![w](https://i.imgur.com/OO7JYK3.jpg)

## データソースはどうする?

Expand All @@ -104,7 +104,7 @@ templateKey: blog-post

上記の例ではoptionsに映画情報のサジェストの候補を突っ込むだけでよくあるサジェスト機能が作れます。

![](https://i.imgur.com/1WX3Roe.gif)
![w](https://i.imgur.com/1WX3Roe.gif)

### デバウンス処理

Expand All @@ -126,11 +126,11 @@ templateKey: blog-post

**300ms**

![](https://i.imgur.com/TFdYdcS.gif)
![w](https://i.imgur.com/TFdYdcS.gif)

**50ms**

![](https://i.imgur.com/fQe66X3.gif)
![w](https://i.imgur.com/fQe66X3.gif)

デバウンス処理は、lodashのdebounceとReact Hooksを組み合わせて次のように比較的かんたんに実装ができます。

Expand Down Expand Up @@ -168,13 +168,13 @@ import debounce from "lodash/debounce";
もちろん実運用だと可用性を考え冗長な構成を取ったりする必要はあるので**あくまでもケースバイケース**ということはご承知いただくのと、お金があるのであれば運用コストを考えてOpenSearchなどのマネージドサービスに乗っかったほうが100%よいとは思います。
| デプロイメントタイプ | インスタンスタイプ | 月額コスト |
| ------------------------ | ------------------- | ---------- |
| 通常のOpenSearch Service | t3.small.search | $40.992 |
| Serverless(レプリカ有) | 2 OCU (0.5 × 4) | $488.976 |
| Serverless(レプリカ無) | 1 OCU (0.5 × 2) | $244.488 |
| ECS Fargate | (vCPU0.5 / Memory 1G) * 1タスク | $17.77 |
| ECS Fargate spot | (vCPU0.5 / Memory 1G) * 1タスク | $5.47 |
| デプロイメントタイプ | インスタンスタイプ | 月額コスト |
| ------------------------ | ------------------- | ---------- |
| 通常のOpenSearch Service | t3.small.search | $40.992 |
| Serverless(レプリカ有) | 2 OCU (0.5 × 4) | $488.976 |
| Serverless(レプリカ無) | 1 OCU (0.5 × 2) | $244.488 |
| ECS Fargate | (vCPU0.5 / Memory 1G) * 1タスク | $17.77 |
| ECS Fargate spot | (vCPU0.5 / Memory 1G) * 1タスク | $5.47 |
このあと紹介する方法でインデックス作成・クエリ作成していくと、上記のECSのような小さなサイズのタスクでもそこそこいいパフォーマンスで動きます。
Expand All @@ -200,7 +200,7 @@ import debounce from "lodash/debounce";
N-gramとは検索ワードを転置インデックスに配置する際に**単語を単純に文字数ごとに分割**し登録していく方法です。
![](https://i.imgur.com/qH1P0nD.jpg)
![w](https://i.imgur.com/qH1P0nD.jpg)
こうすることで、「天」や「天下」と入力したタイミングで該当がヒットする(可能性が出てくる)ためすごくサジェストと相性がいいです。
Expand All @@ -218,15 +218,15 @@ N-gramとは検索ワードを転置インデックスに配置する際に**単
また、入力途中の値が入ってくるパターンと完全に変換しきった文字列が入り乱れるサジェストでは、このN-Gram対象のフィールドを分けて**マルチマッチング検索**を使い、各結果に対して重み付けをことでよりサジェストチックな検索体験が実現できます。
![](https://i.imgur.com/1HLTLQL.jpg)
![w](https://i.imgur.com/1HLTLQL.jpg)
インデックスおよびクエリの具体的な指定方法については後ほど細かく見ていきます。
### インフラ構成
今回はこんな感じでECSにすっごいちっちゃいちっちゃいコンテナを立ち上げ、そこであらかじめ企業情報(企業名・住所など)をインデックスしておいたElasticsearchを立ち上げる構成を取ります。
![](https://i.imgur.com/ioPuQHy.jpg)
![w](https://i.imgur.com/ioPuQHy.jpg)
バックエンドAPIとは切り離しかなり乱暴ですが**直接フロントエンドからElasticsearchのクエリを実行**させるようにさせてパフォーマンスと追加のバックエンドサーバー構築コストを抑えます。
Expand All @@ -242,7 +242,7 @@ N-gramとは検索ワードを転置インデックスに配置する際に**単
つまり、コンテナ作成時に追加したインデックスを**フリーズして使う**、ということです。
ローカル or CI/CDでElasticsearchのイメージを立ち上げ、インデックスを作成後、Docker Commitを使ってイメージを固めてECRなどにPushする構成を取ります。
ローカルor CI/CDでElasticsearchのイメージを立ち上げ、インデックスを作成後、Docker Commitを使ってイメージを固めてECRなどにPushする構成を取ります。
### ECS FargateでElasticsearchを動かすTips
Expand All @@ -252,7 +252,7 @@ N-gramとは検索ワードを転置インデックスに配置する際に**単
もしinitProcessEnabledをtrueにしたままコンテナを立ち上げると、
```
```bash
To fix the problem, use the -s option or set the environment variable TINI_SUBREAPER to register Tini as a child subreaper, or run Tini as PID 1.
```
Expand Down Expand Up @@ -302,8 +302,6 @@ docker run -d \
/bin/bash /elasticsearch-entrypoint.sh
```
### スロークエリ監視
おまたせしました!事前準備は終わりです。
Expand Down Expand Up @@ -353,7 +351,6 @@ es.indices.put_index_template(name="default", body=refresh_template)
ちょっと長くなりますが、先に今回のインデックス・マッピング設定を貼ります。
```python
from elasticsearch import Elasticsearch

Expand Down Expand Up @@ -486,7 +483,7 @@ if es.indices.exists(index="companies"):
補足的な話ですが簡単にElasticsearchのフィルター処理の簡単な流れを図示します。
![](https://i.imgur.com/Nuvz5Ik.png)
![w](https://i.imgur.com/Nuvz5Ik.png)
このように最初に**Character Filter**、そのあとに**Tokenizer**が入り最後に**Token Filter**が入る構成です。
Expand All @@ -495,6 +492,7 @@ if es.indices.exists(index="companies"):
### アナライザーについて
今回は次の3つで構成されています。
- kuromoji_analyzer
- N-gram
- Edge N-gram
Expand All @@ -516,7 +514,6 @@ ICU normalizerなどのプラグインのほか、今回のために独自で作
- normalize_charset_filter
- 全角英数記号を半角英数記号に正規化
#### ngram_analyzerとedge_ngram_analyzer
日本語形態素解析ベースで動くkuromoji analyzerの他、ngram、edge ngramのanalyzerも設定します。
Expand Down Expand Up @@ -550,7 +547,6 @@ response = es.indices.put_settings(index="companies", body=settings_body)
検索クエリでは、さまざまな形式で登録したフィールドに対してマルチマッチで検索を実施します。
- 検索対象
- company_name (通常の会社名)
- company_name.ngram (N-gram解析された会社名)
Expand All @@ -575,7 +571,7 @@ response = es.indices.put_settings(index="companies", body=settings_body)
}
```
合わせて次のように各フィールドに重み付けを行うことで、あいまい検索より前方一致、前方一致より完全一致を優先的に順位付けするようになります。
合わせて次のように各フィールドに重み付けを行なうことで、あいまい検索より前方一致、前方一致より完全一致を優先的に順位付けするようになります。
```json
{
Expand Down Expand Up @@ -626,13 +622,10 @@ response = es.indices.put_settings(index="companies", body=settings_body)
こうすることでより体験のよいサジェストを提供できるはずです。
## おわりに
本記事では、限られたリソースとコストのなかで企業名サジェスト機能を実装する方法について、小さい小さいコンテナを使った実装とそのインデックス例を取り上げました。
貧乏開発が必ずしもいいとは限りませんが、常にコスト意識を持つことの重要性を感じることができました。
時差ボケが抜けず、たぶん駄文になりましたが以上です。
Loading

0 comments on commit 47e22e9

Please sign in to comment.