日本語はまだ勉強中ですから、変などころあったら、ぜひコメントしてください。
RAGとは
RAGはRetrieval augmented generationの略です。
大規模言語モデル内容を生成する前に、promptによる外部のデーターベースに知識を検索、元のpromptと組み合わせて、アウトプットを改善するための技術です。
sequenceDiagram
actor ユーザ
ユーザ->>サーバー: prompt: 今日の注目<br/>の新聞は何ですか?
サーバー->>ベクトルデータベース: promptをキーワードとして<br/>知識を検索
ベクトルデータベース--)サーバー: 知識を返す
サーバー->>llm: promptと知識を組み合わせて
llm-->>サーバー: 生成する内容を返す
サーバー->>ユーザ: 内容を返す
例えば:
ユーザ:今日の注目の新聞は何ですか?
gemini:
今日の注目の新聞記事については、リアルタイムで変化するため、特定の新聞社名を挙げて「今日の注目の記事はこれです」と断言することは難しいです。
chatgpt:
本日(2024年11月6日)の日本の注目ニュースは次のような内容が含まれている可能性があります:
アメリカ大統領選挙の最新動向 - 特に若い世代の投票行動や政策への関心が注目されています。
国内経済とインフレの動き - 日本の物価高騰が続き、家計への影響や政府の対応が議論されています。
アジアの地政学的緊張 - 日本周辺地域の安全保障に関連する動向が続いています。
以上の内容が注目ニュースとして考えられますが、詳細については直接ニュースソースで確認されるとよいでしょう。
geminiはリアルタイムによっての内容は回答できません。
chatgptはRAGか、Function Callingか、どちらかを使っていると思います。
今回はgolang+langchaingo+ollama+qdrantでRAGを実験してみます。
準備
ollamaをインストール
curl -fsSL https://ollama.com/install.sh | sh
qdrantのコンテナを実行
mkdir -p ~/.config/qdrant
docker run -p 6333:6333 -p 6334:6334 \
-v ~/.config/qdrant:/qdrant/storage:z \
qdrant/qdrant
bge-large-en-v1.5をollamaで実行
知識をベクトルデータベースに保存するにはText Embedingモデルが必要です。今回はbge-large-en-v1.5を使うことになりました。
ollamaのモデルライブラリにはbge-large-en-v1.5がありませんので、bge-large-en-v1.5をGGUFに変換することが必要です。
git-lfsとcmakeをインストール
sudo apt install git-lfs cmake
git lfs install
git clone https://huggingface.co/BAAI/bge-large-en-v1.5
llama.cppをクローン
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp/
# python仮想環境はおすすめです
sudo apt install python3.12-venv
python3 -m venv python-venv
./python-venv/bin/pip3 install -r requirements.txt
GGUFに変換
./llama.cpp/python-venv/bin/python3 ./llama.cpp/convert_hf_to_gguf.py ./bge-large-en-v1.5/
cd bge-large-en-v1.5/
echo "FROM ./bge-large-en-v1.5-F16.gguf" > Modelfile
ollama create bge-large-en-v1.5-F16 -f Modelfile
# embedingモデルはollamaで直接実行はできませんです
# Error: "bge-large-en-v1.5-F16" does not support generate
# ここは試してみてだけです
ollama run bge-large-en-v1.5-F16
Qwen2.5をollamaで実行
# 32bは20GBのGPUメモリ(VRAM)が必要です
# CPUもできるですが、非常に重いですから
# 3bや7bや14bなどもぜひ使ってみてください
ollama run qwen2.5:32b-instruct
実現
text embedder
import (
"github.com/tmc/langchaingo/embeddings"
"github.com/tmc/langchaingo/llms/ollama"
"github.com/tmc/langchaingo/vectorstores/qdrant"
)
llm, err := ollama.New(
ollama.WithModel("bge-large-en-v1.5-F16"),
ollama.WithServerURL("http://localhost:11434"),
)
if err != nil {
panic(err)
}
embeder, err := embeddings.NewEmbedder(llm)
if err != nil {
panic(err)
}
qu,err := url.Parse("http://localhost:6333")
if err != nil {
panic(err)
}
qd, err := qdrant.New(
qdrant.WithURL(*qu),
qdrant.WithCollectionName("test"),
qdrant.WithEmbedder(embeder),
)
if err != nil {
return nil, err
}
テキストを導入
// TextToChunks テキストを分割
func TextToChunks(r io.Reader, chunkSize, chunkOverlap int) ([]schema.Document, error) {
// 新たなドキュメンタリーローダーを作成
docLoaded := documentloaders.NewText(r)
// テキストを再帰的に分割し方法
split := textsplitter.NewRecursiveCharacter()
// 分割サイズを設定
split.ChunkSize = chunkSize
// 重なるサイズを設定
split.ChunkOverlap = chunkOverlap
// ロードして分割する
docs, err := docLoaded.LoadAndSplit(context.Background(), split)
if err != nil {
panic(err)
}
return docs, nil
}
docs, err := TextToChunks(strings.NewReader(`テストテキスト`), 300, 20)
if err != nil {
panic(err)
}
// ベクトルデーターベースに導入
_, err := qd.AddDocuments(context.TODO(), docs)
if err != nil {
panic(err)
}
RAG
知識を検索
optionsVector := []vectorstores.Option{
vectorstores.WithScoreThreshold(0.80),
}
retriever := vectorstores.ToRetriever(qd, 10, optionsVector...)
docRetrieved, err := retriever.GetRelevantDocuments(context.Background(), prompt)
if err != nil {
panic(err)
}
llmで予測する
llm, err := ollama.New(
ollama.WithModel("qwen2.5:32b-instruct"),
ollama.WithServerURL("http://localhost:11434"),
)
if err != nil {
panic(err)
}
var msgs []llms.MessageContent
for _, doc := range docRetrieved {
msgs = append(msgs, llms.TextParts(llms.ChatMessageTypeAI, doc.PageContent))
}
msgs = append(msgs, llms.TextParts(llms.ChatMessageTypeHuman, prompt))
_, err := llm.GenerateContent(ctx, msgs,
llms.WithTemperature(0.3),
llms.WithStreamingFunc(func(ctx context.Context, chunk []byte) error {
// 結果をストリームで標準出力に出力して
fmt.Print(string(chunk))
return nil
}),
)
if err != nil {
panic(err)
}