生成AI - 考えるエンジニア
FC2ブログ

RAGを使う(5)

 DifyでRAG(Retrieval-Augmented Generation: 検索拡張生成)が利用できるようなので、試してみました。Difyについては過去のブログを参照ください。

 利用したシステム推論モデルはGemma2:2b、埋め込みモデルはmxbai-embed-largeです。費用がかからないモデルの組み合わせです。RAGに読み込ませる独自データとして今回も以前使用した「私の過去のブログのテキスト文章」を用いました。

以下、操作を順を追って説明します。

 Difyを立ち上げた後に「ナレッジ」を選択して「知識を作成」をクリックします。Dify_RAG_1_241104.pngデータソースの選択画面が出ますので「テキストファイルからインポート」で、読み込ませるファイルをドラッグ&ドロップし、「次へ」をクリックすると、Dify_RAG_2_241104.png「テキストの前処理とクリーニング」画面が出て、右側にそのファイルのプレビューが確認できました。チャンクの設定は「自動」にして、Dify_RAG_3_241104.pngインデックスモードは「高品質」にしました。Dify_RAG_4_241104.png検索設定は3パターン選べますが「ベクトル検索」を選択し、「保存して処理」ボタンを押すと、Dify_RAG_5_241104.pngナレッジが作成されました。「ドキュメントに移動」をクリックすると、Dify_RAG_6_241104.png有効なナレッジが表示されました。ナレッジの準備が完了したので「スタジオ」ボタンを押して、元々作成していた「Gemma2:2b」のチャット設定画面に戻ります。Dify_RAG_7_241104.pngここで「コンテキスト」の「+追加」を押して、今作成したナレッジファイルを選択し「追加」ボタンを押すと、チャットでナレッジが利用できるようになります。Dify_RAG_8_241104.png加えて、「Orchestrate」に意図的にコンテキストの内容を回答するように指示しました。Dify_RAG_9_241104.png
 実際に動かしてみます。プロンプトは以前のブログで試したものと同じです。回答は貧弱ですが、読み込まれたコンテキストを元に回答していることが分かりました。Dify_RAG_10_241104.pngただし、期待している回答が得られないことがあるのも分かりました。Dify_RAG_11_241104.png
 今回、費用がかからないGemma2:2bとmxbai-embed-largeを用いて、DifyでRAGの検証を行いました。操作自体は簡単にできますが、RAGの性能をいかに上げるかが問題点として認識できました。引き続き試行錯誤していこうと思います。

DifyでOllamaを使う(2)

 今回は、OllamaでインストールしたローカルLLMをDifyのAPIを使う試みです。

以下の順でお話しします。
 1. Dify上でのAPI利用の準備
 2. PythonプログラムからのAPI利用
 3. curlコマンドからの利用
 4. .NET8(C#)プログラムからのAPI利用

 Ollama、Difyは前回のブログでお話ししたUbuntu Server(本体はRaspberry Pi 5)で実行しました。

1. Dify上でのAPI利用の準備
 APIはモデルプロバイダに登録したモデルのgemma2:2bを利用しました。Dify_API_setup1_241102.pnggemma2:2bを使ったチャットボットを作成した後に、スタジオの「公開する」から「APIリファレンスにアクセス」をクリックし、Dify_API_setup2_241102.png「APIキー」ボタンを押しました。ここで、APIがアクセスするBase URLが「http://192.168.11.11/v1」と表示されていることを確認。Dify_API_setup3_241102.png「+新しいシークレットキーを作成」をクリックすると、Dify_API_setup4_241102.pngAPIシークレットキーが作成されました。キーは紛失しないようにコピーして保管しました。「OK」ボタンを押すとDify_API_setup5_241102.png登録されていることが確認できました。Dify_API_setup6_241102.pngAPI実行に必要なBase URLとAPIシークレットキーが作成されたので、準備完了です。

2. PythonプログラムからのAPI利用
 1.で準備をしたBase URLとAPIシークレットキーを使って、PythonプログラムからAPIを利用しました。プログラムは以下の通りです。
import requests
import json

# APIキーとベースURLの設定
API_KEY = 'My-API-Key'
BASE_URL = 'http://192.168.11.11/v1'

# ヘッダーの設定
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}

# 質問を送信する関数
def send_question(query, user, conversation_id=None):
url = f'{BASE_URL}/chat-messages'

# リクエストボディの作成
data = {
'query': query,
'inputs': {},
'response_mode': 'blocking',
'user': user,
'auto_generate_name': True
}

if conversation_id:
data['conversation_id'] = conversation_id

# リクエストの送信
response = requests.post(url, headers=headers, json=data)
return response.json()

# 使用例
if __name__ == "__main__":
query = "日本の新幹線について教えてください。"
user = "DrBobT"

response = send_question(query, user)

print("Message ID: ", response.get('message_id'))
print("Conversation ID: ", response.get('conversation_id'))
print("Answer: ", response.get('answer'))
print("Retriever Resources: ", json.dumps(response.get('metadata', {}).get('retriever_resources'), indent=2, ensure_ascii=False))
実行結果は以下の通りです。正常に動作しました。Dify_API_run1_241102.png3. curlコマンドからの利用
 curlコマンドはMacMiniからUbuntu Server(本体はRaspberry Pi 5)に投げることで実行しました。response_modeとして、結果が全て取得できて表示をする「blocking」と、結果を逐次表示しながら表示する「streaming」があります。
 まずは「blocking」モードです。実行結果は以下の通り。Dify_API_run2_241102.png「{ "event": 」以降が回答です。分かりにくいので整頓すると、Dify_API_run3_241102.png赤枠の部分が回答の内容であることが分かります。Unicodeで返ってきているので、文字変換をすると正しく会話ができていることが分かりました。Dify_API_run4_241102.png 次に「streaming」モードです。実行させると、画面に文字が立て続けに出力されました。Dify_API_run5_241102.png詳細を見ると、赤枠部分が回答となりますが、細切れに入っていることが分かりました。Dify_API_run6_241102.pngこの回答もUnicodeなので、変換すると、結果は以下の通りで、問題ないことが分かりました。Dify_API_run7_241102.png
4. .NET8(C#)プログラムからのAPI利用
 2.のPythonプログラムをGPT-4oでC#にしてもらいました。プログラムは以下の通り。
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

class Program
{
private static readonly string ApiKey = "My-API-Key";
private static readonly string BaseUrl = "http://192.168.11.11/v1";

static async Task Main(string[] args)
{
string query = "こんにちは";
string user = "DrBobT";

var response = await SendQuestionAsync(query, user);

Console.WriteLine("Message ID: " + response["message_id"]);
Console.WriteLine("Conversation ID: " + response["conversation_id"]);
Console.WriteLine("Answer: " + response["answer"]);
Console.WriteLine("Retriever Resources: " + response["metadata"]?["retriever_resources"]?.ToString(Formatting.Indented) ?? "N/A");
}

public static async Task<JObject> SendQuestionAsync(string query, string user, string? conversationId = null)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

string url = $"{BaseUrl}/chat-messages";

var data = new
{
query = query,
inputs = new { },
response_mode = "blocking",
user = user,
auto_generate_name = true,
conversation_id = conversationId
};

string jsonData = JsonConvert.SerializeObject(data);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");

HttpResponseMessage response = await client.PostAsync(url, content);

if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return JObject.Parse(result);
}
else
{
throw new Exception($"Error: {response.StatusCode}");
}
}
}
}
結果も問題なしでした。Dify_API_run8_241102.png
 今回、OllamaでインストールしたローカルLLMをDifyのAPIを使って利用できることが分かりました。WebAPIなのでPython、C#のどちらからも利用できるのが良いですね。何かに使っていこうと思います。

DifyでOllamaを使う(1)

 先日のブログでローカルLLMの実行ツール「ollama」をインストールして、Microsoftが開発した小規模言語モデル(SLM)のphi3を実行させるお話をしました。今回は、Difyからphi3を実行させるお話です。

 手順を説明する前に、Difyとphi3がインストールされているハードウェアは2台あり、それぞれ利用形態が異なります。この2つについて説明します。
1. Ubuntu Server(本体はRaspberry Pi 5)
   Mac MiniからWebブラウザ経由でリモート操作する。
2. Mac Mini
   Mac MiniのlocalhostからWebブラウザ経由で操作する。

まずは難儀した1.から説明します。

1. Ubuntu Server(本体はRaspberry Pi 5)
 まず、以前の手順に従って、Difyのモデルプロバイダの「Ollama」に「phi3」を登録しようした時に、以下のエラーが出ました。ポート11434の接続が確立できない不具合です。SettingError_241026.png設定時の状況はターミナルからollama(phi3)を起動させた状態でした。Check1_241026.pngネット情報やGPT-4oに質問をしながら、ポート番号 11434 を使用しているプロセスを確認すると、正常にollamaが利用していることが分かりました。Check2_241026.pngちなみに、リモート元のMac MiniからのUbuntu Server(192.168.11.41)への通信の確認を行うと、ポート80はOK、ポート11434はNGでした。Check3_241026.pngファイアウォールが原因であると疑って、許可しましたが、結果は変わらず、Mac Miniからの通信はエラーでした。
sudo ufw allow 11434
sudo ufw allow 11434/tcp
ネットで原因調査をしている中で、参考になるブログにヒットしました。最終的に「/etc/systemd/system」フォルダの「ollama.service」ファイルを修正することでエラーは解決しました。11行目をコメントアウトし、12、13行目を追加・保存して、Ubuntu Serverを再起動させました。Check4_241026.png設定に登録した情報は以下の通りです。Base URLはUbuntu ServerのServer名ではなく、IPアドレスで記入しました。Check5_241026.pngモデルプロバイダに正常に登録できていることも確認しました。Check6_241026.png
 それでは実際にphi3でボットを作成しましょう!

「最初から作成」-「チャットボット」を選択して、アプリの名前と説明を記入し「作成する」をクリックしました。MakeBot1_241026.png「公開する」をクリックし、「更新」ボタンを押した後に、「アプリを実行」をクリックしました。MakeBot2_241026.png回答内容と回答スピードは、GPT-4oには劣りますが、動作は問題なしです。MakeBot3_241026.png
2. Mac Mini
 Mac Mini内でlocalhost環境で利用する場合は、1.のリモート利用に比べて設定が楽です。Base URLのみ設定が異なります。ollamaの設定ファイル等は触る必要はありませんでした。Check7_241026.png回答内容は同じモデルを利用しているので変わりませんが、回答スピードはハードウェアの能力の差でMac Miniの方が圧倒的に速いです。
 
 今回、Difyからphi3を正常に動作させることができました。phi3は利用にお金がかからないので、いろいろなことが試せますね。ネタを考えてみようと思います。

Azure AI SearchでRAGを使う(1)

 Azure OpenAI(例えば、GPT-4o等)を利用してRAG(Retrieval-Augmented Generation)を実施したい場合に、「Azure AI Search」サービスを利用すると良いというネット記事を多く見かけます。実際どのようなものなのか、どのくらいの実力があるのかについて、大変興味を持ちましたので、味見をしてみました。

 とはいえ、正常に動作するまで、大変難儀しましたので、備忘録を残しておこうと思います。Azure側の設定が非常に面倒だったので、後日整理してお話しします。

 今回は、GPT-4oのチャットプレイグラウンド上で、独自データの「読み込ませあり/なし(RAGあり/なし)」で結果の違いを確認しようと思います。

 まず、独自データとして、ある工場における各勤務毎の設備状態、設備異常のメモを準備しました。datalist_241021.pngこのデータ自身は「架空のもの」でGPT-4oに作成してもらいました。このようなダミーデータの作成をGPT-4oに頼むとすぐに作ってくれるので重宝しますね。
 独自データはAzure AI Searchサービスに事前登録した上で評価スタートです。

 以下、実行結果です。

1. RAGなし
 独自データの中の中島健太郎さんについて、質問すると、当然のことながら「データベースに含まれていない」との回答でした。GPT-4o_noRAG1_241021.png もう一つ、設備異常の中で「制御関係の不具合」を聞いてみると、一般的な回答が得られました。想定内の回答です。GPT-4o_noRAG2_241021.png
2. RAGあり
 チャットプレイグラウンドのセットアップの「データを追加する」に設定を入れることにより、RAGが機能するようになります。設定内容の詳細は後日お話しします。RAGなしの時と同じ質問をすると、独自データから引き出された回答をしてくれました。Citation(引用)も付いており、回答の根拠を示してくれました。GPT-4o_RAG1_241021.png独自データと答え合わせを行うと正解でした。Ans_RAG1_241021.png もう一つの質問についても、独自データに基づく回答がありました。GPT-4o_RAG2_241021.pngこちらも答え合わせで正解でした。Ans_RAG2_241021.png
 今回、Azure AI Searchサービスを使って、独自データを使ったRAG評価を行いました。深掘りはしていませんが、感触としては、結果は問題なさそうです。正常動作させるまでに大変難儀をした件は次回以降にお話しします。

Semantic Kernelを使う(7)

 前回、Semantic KernelのMemory機能でRAG(Retrieval-Augmented Generation)を実行しようとして、一つの事例でうまく行きましたが、異なる事例のデータを使うとうまく行かない問題があり、中途半端な状態で終わってしまいましたので、引き続き調査しました。

同じプログラムでテキストファイルを読み込んでいる点は同じですが、結果が異なるということなので、原因として
 1. ファイル容量(文字数)が多いこと
 2. ファイルの中身の細部の形式が異なること
の2つを考えました。

結論から言うと、1.ではなく、2.がヒットしていました。結果をまとめると以下の通りです。Result_241013.png 改めて、詳細を追って行きます。

 1.のファイル容量については確かに、エラーを吐いたブログ記事(A)の方が、正常に結果出力したもの(注文の多い料理店)に比べ、10倍以上の容量がありました。そこで、ブログ記事(A)から一部を抜粋したテキストファイル(B)を作成して実行させました。Token数も注文の多い料理店のテキストよりも少ないファイルです。しかし、結果は同じ「INFO NOT FOUND」でした。このことから、1.のファイル容量は関係のないことが分かりました。

 次に、2.のファイルの中身の細部の形式ですが、注文の多い料理店のテキストファイルは、1文(句点)毎に改行が入っており、かつ無駄なスペースが含まれていないものでした。一方、ブログ記事は段落毎にしか改行が入っていません。そこで、テキストファイル(B)について、無駄なスペースを省き、一文毎に改行を入れるようなテキストファイル(C)を作成しました。実行すると、正常に動作しました。Answer1_241013.png 最後に、ファイル容量とは関係ないことを裏付けるために、元々のブログ記事のテキストファイル(A)についても、1文毎に改行を入れたテキストファイル(D)を作成しました。実行すると、データ量が多いので「15秒程度」の待ち時間はありましたが、正常に回答が戻ってきました。Answer2_241013.png 前回、不明で中途半端だったことが、今回、原因が分かり明確になりました。Semantic KernelのMemory機能に入力するデータは1文を1行として、整理整頓しておく必要があることが良く分かりました。次回から気を付けようと思います。

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

Author: Dr.BobT
興味のおもむくままに生涯考え続けるエンジニアでありたい。

月別アーカイブ

メールフォーム

名前:
メール:
件名:
本文: