素人が大会を盛り上げるチャットボットに日本語Rerankerを使って知識を埋め込んでみた

# はじめに
[技術チャレンジ部](https://challenge-club.connpass.com/)のとも(Tomo)です。チームTPACで[自動運転AIチャレンジ2024](https://www.jsae.or.jp/jaaic/2024ver/summary/)に[参加中](https://www.jsae.or.jp/jaaic/2024ver/contest/)です。
[こちら](https://qiita.com/tomo-v/items/6aeec8c27cc5586d4fa8)や[こちら](https://qiita.com/tomo-v/items/3b3a743b96f5bd038cda)の記事で作ってみたチャットボットに、Difyを使用して本大会の知識を埋め込んでみました。
素人なので、変なことを言っていたらすいません!

# 実現したいこと
「本大会の予選の締め切りっていつでしたっけ?」と聞いたら答えてくれる、締め切り駆動型開発の必須機能。

# 環境
- チャットボットで使った環境そのまま = 自動運転AIチャレンジのGPU環境がベース
  - [ZBOX E](https://qiita.com/air-gh/items/d0721ec05abdab28fe01)
    - RTX 4070 Laptop GPU搭載
  - Ubuntu 22.04 Desktop
  - Docker Engine
  - NVIDIA Container Toolkit
    - [自動運転AIチャレンジ2024の描画あり環境でインストール](https://automotiveaichallenge.github.io/aichallenge-documentation-2024/setup/visible-simulation.html)していた
  - self hostedなDify
  - OpenAI API
- 今回追加したもの
  - git lfs (念のため)
  - [Hugging FaceのText Embeddings Inference](https://github.com/huggingface/text-embeddings-inference)
  - [hotchpotch/japanese-reranker-cross-encoder-small-v1](https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-small-v1)

# Reranker
## DifyのナレッジとRerankモデル
Difyから使用できる外部サービスはたくさんあり、このチャットボットではOpenAIのLLMを使用していました。
Difyのナレッジで設定できるモデルとしては、埋め込みモデルとRerankモデルがあります。このうち、埋め込みモデルは、OpenAI APIでも提供されるため、そのまま使えましたが、Rerankモデルがありません。
Difyのナレッジの検索設定では、Rerankモデルを使用しない設定もできるため、最初これで試していたのですが、なかなかうまく答えてくれません。外部のRerankサービスを別途使えるようにすることも考えたのですが、重い腰が上がりませんでした(←アカウント作るだけだろ)。

## 日本語対応Reranker
うまく答えてくれない理由を色々考えていたのですが、そもそもRerankerはどの程度日本語に対応しているのか、気になって検索してみたところ、今年4月のこちらの記事に出会えました。

https://secon.dev/entry/2024/04/02/070000-japanese-reranker-release/

> 日本語を適切に学習させたRerankerファミリー

きっと今の私に必要なものです。このようなモデルを公開されていることに、圧倒的感謝です。

# DifyからのRerankモデルの使用
ところが、このモデルをどうやって使用するのか、さっぱり分かりません。素人なので…。
Difyの 設定 -> モデルプロバイダー の画面を見ていると、`RERANK`と書かれたものがいくつかありますが、その中にサービスのアイコンがないものがあります。Text Embedding Inferenceです。
このモデルプロバイダー設定では、Server urlが設定できるので、ローカルでモデルを実行するサービスを作れば、結合できそうです。

<img width="450" alt="text-embedding-intference.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3764521/b7b2ba4b-311a-79ca-3ca4-f8818ef5e3e0.png">

# HuggingfaceのText Embedding Inference

Text Embedding Inferenceで検索すると、以下のリポジトリがあるようです。

https://github.com/huggingface/text-embeddings-inference

[Using Re-rankers models](https://github.com/huggingface/text-embeddings-inference?tab=readme-ov-file#using-re-rankers-models)という章もあり、docker化された実行環境に対し、curlでアクセスできるようです。

これは、ローカルでサービスとしてモデルを実行できるのでは…? そしてDifyと結合できるのでは…?

# Rerankerサービス
## 起動
先ほどの[Using Re-rankers models](https://github.com/huggingface/text-embeddings-inference?tab=readme-ov-file#using-re-rankers-models)に書かれていた起動方法をベースに、以下を変更しました。
- モデル名: `hotchpotch/japanese-reranker-cross-encoder-small-v1`
- デーモン化: `-d`
- Docker Image: `ghcr.io/huggingface/text-embeddings-inference:89-1.5`

結果として、以下を入力。

```shell
sudo apt install git-lfs
mkdir data
model=hotchpotch/japanese-reranker-cross-encoder-small-v1
volume=$PWD/data
docker run -d --gpus all -p 8080:80 -v $volume:/data --pull always ghcr.io/huggingface/text-embeddings-inference:89-1.5 --model-id $model
```

モデル名に少し戸惑いました。モデルをローカルに持ってきて指定するのかと思いましたが、Hugging Faceから取ってきてくれるようなので、Hugging Face上のパスを指定します。
ただ、その時にtokenizer.jsonが存在することが必要なようで、japanese-reranker-cross-encoder-small-v1にはこれがあったため、使用してみました。チャットボットであることを考えると、smallが合っていそうな気もします。
Docker ImageはGPUの種類によって変えるようですが、今回の環境は[ここ](https://github.com/huggingface/text-embeddings-inference?tab=readme-ov-file#docker-images)にある「Ada Lovelace (RTX 4000 series, ...)」に該当するため、text-embeddings-inference:89-1.5を指定しました。あってよかった。

これだけでサービスが起動できるの、ほんと楽…。

## 動作確認
こちらも[Using Re-rankers models](https://github.com/huggingface/text-embeddings-inference?tab=readme-ov-file#using-re-rankers-models)に書かれていた通りに実行したら、scoreが取得できました。英語ですが…。

```shell
curl 127.0.0.1:8080/rerank \
    -X POST \
    -d '{"query":"What is Deep Learning?", "texts": ["Deep Learning is not...", "Deep learning is..."]}' \
    -H 'Content-Type: application/json'
```

Difyから接続すると、/infoにアクセスするようなので、こちらも確認しておきます。

```shell
curl 127.0.0.1:8080/info
```

# Dify
## Rerankerサービスへの接続
先ほどのDifyの 設定 -> モデルプロバイダー -> Text Embedding Inferenceにて、以下を設定しました。

- Model Name: `japanese-reranker-cross-encoder-small-v1`
- Server url: `http://host.docker.internal:8080`

host.docker.internalは、[Firecrawlを使えるようにする際に設定](https://qiita.com/tomo-v/items/6aeec8c27cc5586d4fa8#firecrawl%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AEdocker%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E8%A8%AD%E5%AE%9A)していました。

接続すると、以下のように、モデルが表示されます。やった。

<img width="600" alt="text-embedding-inference-model.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3764521/1854f95a-fb54-bdd0-5249-8b9204b36ccd.png">

## ナレッジの作成
Difyでナレッジを作成し、以下のように設定しました。
埋め込みモデルは、コストパフォーマンスが良さそうな`text-embedding-3-small`を指定。
検索設定は、おすすめとあるハイブリッド検索にて、Rerankモデルが設定できるようになったので、`japanese-reranker-cross-encoder-small-v1`を指定。

<img width="500" alt="knowledge-setting.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3764521/d6e9523b-0a5d-4251-bb47-0a915a33450d.png">

ナレッジのドキュメントは、若干の試行錯誤の結果、HTMLよりはMarkdownのほうが良さそうだったので、一旦は本大会の情報をMarkdownで入力し、セグメント設定のチャンク設定は自動としました。

## LLMブロック
LLMブロックでは、元の「Knowledge Retreival + Chatbot」テンプレートをそのまま活用してコンテキストを埋め込みました。


<img width="300" alt="llm-block.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3764521/31d66bb0-dbd3-7c1c-6616-6f1adf9a8bac.png">

# 結果

![practice.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3764521/a0767f25-b63e-ade7-199d-f8ca44189a89.png)

ヤバい!(締め切りが)

# 今後に向けて
上記以外にも、結構うまく答えてくれる場面があり、可能性を感じています。開発の現状をインプットするなど、知識と質問の方法によっては、チームを支援してくれる頼れる仲間になってもらえる気がしています。
うまく答えてもらえないこともまだまだありますが、現状は動かしてみただけ状態で、比較評価ができていません。
各種設定やモデルなどを変えながら、効果を確認していければと思います。