続・LlamaIndexを使って独自データをQ&AするだけならOpenAI API使わなくてもいいでない？

# はじめに

どうも、レガシー組込みエンジニアの@yagisawaです。

以前[LlamaIndexを使って独自データをQ&AするだけならOpenAI API使わなくてもいいでない？](https://qiita.com/yagisawa/items/b212460d440e4b240fc3)という記事を書きました。
ひょんなことからAzure OpenAIを使えるようになったので久しぶりにLlamaIndexを使ったところ、だいぶパッケージ構成等が変わっているようなので情報をアップデートしようと思い記事にしました。

# 環境

- CPU
    - 11世代 Core i5
- メモリ
    - 8GB
- OS
    - Windows 11 Pro 23H2(64bit)
- Pythonライブラリ
  ```text:requirements.txt
  fugashi==1.4.0
  langchain==0.3.13
  langchain-community==0.3.13
  llama-index==0.12.8
  llama-index-embeddings-langchain==0.3.0
  llama-index-llms-langchain==0.5.0
  llama_cpp_python==0.3.5
  protobuf==5.29.2
  sentence-transformers==3.3.1
  unidic-lite==1.0.8
  ```
- LLM
    - [ELYZA-japanese-Llama-2-7b](https://huggingface.co/QuantFactory/ELYZA-japanese-Llama-2-7b-instruct-GGUF)
    - [ruri-large](https://huggingface.co/cl-nagoya/ruri-large)

# とりあえず結論

以下のコードで動きました。

```python:main.py
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)

from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.llms import LlamaCpp
from llama_index.core import PromptTemplate, Settings, SimpleDirectoryReader, VectorStoreIndex
from llama_index.embeddings.langchain import LangchainEmbedding

Settings.llm         = LlamaCpp(model_path=f'./models/ELYZA-japanese-Llama-2-7b-instruct.Q5_K_M.gguf', temperature=0, n_ctx=4096)
Settings.embed_model = LangchainEmbedding(HuggingFaceEmbeddings(model_name="cl-nagoya/ruri-large"))

# ドキュメントのインデックス化
documents = SimpleDirectoryReader('./inputs').load_data()
index     = VectorStoreIndex.from_documents(documents)

# 質問
temp = """
 

あなたは誠実で優秀な日本人のアシスタントです。



以下の「コンテキスト情報」と「制約条件」を元に次の質問に回答してください: {query_str}

# コンテキスト情報

---------------------
{context_str}
---------------------

# 制約条件

- コンテキスト情報はマークダウン形式で書かれています。
- コンテキスト情報に無い情報は絶対に回答に含めないでください。
- コンテキスト情報の内容を丸投げするのではなく、ちゃんと文章にして回答してください。
- 質問の答えを知らない場合は、誤った情報を共有しないでください。
"""
query_engine = index.as_query_engine(text_qa_template=PromptTemplate(temp))
while True:
    req_msg = input('\n## Question: ')
    res_msg = query_engine.query(req_msg)
    print('\n##', str(res_msg).strip())
```

使っているクラスやインポート元が変わった程度で大枠は変わりません。

埋め込みモデルは以前`multilingual-e5-large`というモデルを使っていましたが、[Ruri: 日本語に特化した汎用テキスト埋め込みモデル](https://zenn.dev/hpp/articles/b5132c64c40d24)という記事を見かけて`ruri-large`というモデルを使っています。今回のような小規模な独自データではあまり効果を感じませんが、別でやっている大規模な独自データではより的確なコンテキスト情報をピックアップしているように感じます。

# 細かい話

## ディレクトリ構成

以下のような構成となっています。

```text
root/
  ├ inputs/
  │  └ 洗濯機トラブルシューティング.md
  ├ models/
  │  └ ELYZA-japanese-Llama-2-7b-instruct.Q5_K_M.gguf
  └ main.py
```

## 独自データ

以前の記事と同じ、以下のような内容を学習させています。

```markdown:洗濯機トラブルシューティング.md
# 洗濯機トラブルシューティング

## こんなときは

### 乾きが悪い

- 湿り気が残っている場合は、乾燥時間を設定して追加乾燥してください。
- 乾燥フィルターに糸くずが溜まっていませんか。
- 排水口に糸くずなどが溜まっていませんか。

### 臭いがする

- 乾燥機能の使い始めに、ゴムの臭いがすることがあります。
- 排水口にたまった汚れの臭いが逆流することがあります。排水口は定期的に掃除してください。

### 電源が入らない

- 電源を切ってから約3秒間は電源「入」を受付けません。
- 電源を一切受け付けない時は、電源プラグを抜き、1分ほどしてから指し直してください。

## エラー一覧

| エラー表示 | エラー内容         |
| ---------- | ------------------ |
| E01        | 給水できません     |
| E02        | ドアが開いています |
| E03        | 排水できません     |
| E04        | 脱水できません     |
```

## 注意点

`HuggingFaceEmbeddings`を使ってモデルをダウンロードするときに

```
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
```

というエラーが出る場合があります。Hugging Faceの証明書に問題があるということらしいのですが、コードの先頭に

```python
import requests
from huggingface_hub import configure_http_backend

def backend_factory() -> requests.Session:
	session = requests.Session()
	session.verify = False
	return session

configure_http_backend(backend_factory=backend_factory)
```

と記載することで解消できます(解消というより証明書の検証をスキップするのですが)。

# おわりに

以前の記事を書いたときも、記事を書いている間にパッケージがアップデートされてコードを書き換えたりとかしていましたが、AI界隈の技術はまだまだ進化が止まりませんね。

今回は急ぎ足で記事を書いたので全然調べられていませんが、優秀な日本語LLMが色々と登場しているみたいなので、機会があったら性能比較とかしてみたいと思っています。(既にあるのかもしれませんが)1bit LLMが気になっているので、LlamaCPPで動くモデルとか出てきたら試してみたいですね。