スマートフォン・タブレットからインターネットサーバーオペレーション

APPW.jp
 

ConoHa VPS(メモリ2GB / 3コア)でllama.cppによるRAGの実装実験手順

0. RAG(Retrieval-Augmented Generation)とは

RAGは、ユーザーの質問に対して、まず関連する文書をベクトル検索で取得し、その文書を大規模言語モデル(LLM)に渡して回答を生成する手法です。これにより、モデル単体では持ち得ない最新情報やドメイン知識を活用した高精度な応答が可能になります。

1. 構成概要

RAGの基本構成は以下の通りです。

  • ユーザー質問 → 埋め込みモデルでベクトル化
  • ベクトルDBで関連文書を検索
  • llama.cpp LLMで回答生成
  • 回答出力

2. 環境準備

依存パッケージのインストール
必要なパッケージをインストールします。

sudo apt update && sudo apt install -y build-essential cmake git python3-pip python3-venv

llama.cppのビルド
LLMをCPUで動かすための軽量フレームワーク「llama.cpp」をビルドします。


git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
cmake -B build
cmake --build build --config Release -j3
    

Python仮想環境の作成と必要ライブラリのインストール


python3 -m venv rag-env
source rag-env/bin/activate
pip install llama-cpp-python sentence-transformers chromadb
    

3. モデルのダウンロード

Hugging Face CLIを使って、実験用のLLMモデル(例:Gemma)をダウンロードします。


pip install huggingface-hub
hf download unsloth/gemma-4-E2B-it-GGUF gemma-4-E2B-it-Q4_K_M.gguf --local-dir ~/models
    

解説:モデルは用途やVPSのスペックに合わせて選択してください。

4. RAGの実装

主要ライブラリのインポートと設定


python3 -m venv rag-env
source rag-env/bin/activate
pip install llama-cpp-python sentence-transformers chromadb
    

llama-cpp-pythonでLLMを、sentence-transformersで埋め込みモデルを、chromadbでベクトルDBを扱います。

サンプルコードのポイント:

  • テキストを分割(chunk)してベクトル化し、DBに登録
  • ユーザー質問をベクトル化して関連文書を検索
  • 検索結果をプロンプトに組み込み、LLMで回答生成

# rag.py
from llama_cpp import Llama
from sentence_transformers import SentenceTransformer
import chromadb
import numpy as np

# ---- 設定 ----
MODEL_PATH = "(#MODELのPATH#)/gemma-4-E2B-it-Q4_K_M.gguf"
N_CTX = 2048
N_THREADS = 3      # CPUのコア数

# ---- 初期化 ----
# LLM(llama.cpp)
llm = Llama(
    model_path=MODEL_PATH,
    n_ctx=N_CTX,
    n_threads=N_THREADS,
    n_gpu_layers=0,   # CPU only
    verbose=False
)

# 埋め込みモデル(軽量なものを選ぶ)
embedder = SentenceTransformer("intfloat/multilingual-e5-small")

# ベクトルDB(ChromaDB をローカルで使用)
chroma_client = chromadb.PersistentClient(path="./chroma_db")
collection = chroma_client.get_or_create_collection("documents")


def chunk_text(text: str, chunk_size: int = 300, overlap: int = 50) -> list[str]:
    """長文を重複付きで分割"""
    words = text.split()
    chunks, i = [], 0
    while i < len(words):
        chunk = " ".join(words[i:i + chunk_size])
        chunks.append(chunk)
        i += chunk_size - overlap
    return chunks

# ---- ドキュメント登録 ----
def add_documents(docs: list[dict]):
    texts = [d["text"] for d in docs]
    ids   = [d["id"]   for d in docs]
    # 空の場合は source キーをデフォルトで入れる
    metas = [d.get("metadata") or {"source": "unknown"} for d in docs]

    embeddings = embedder.encode(texts, normalize_embeddings=True).tolist()
    collection.upsert(documents=texts, embeddings=embeddings,
                      ids=ids, metadatas=metas)
    print(f"{len(docs)} 件登録完了")


# ---- 検索 ----
def retrieve(query: str, top_k: int = 3) -> list[str]:
    q_emb = embedder.encode([query], normalize_embeddings=True).tolist()
    results = collection.query(query_embeddings=q_emb, n_results=top_k)
    return results["documents"][0]   # テキストのリスト


# ---- RAG 推論 ----
def rag_query(question: str) -> str:
    # 1. 関連文書を取得
    contexts = retrieve(question)
    context_text = "\n\n".join(f"[{i+1}] {c}" for i, c in enumerate(contexts))

    # 2. プロンプト構築
    prompt = f"""以下のコンテキストを参考に質問に答えてください。

### コンテキスト
{context_text}

### 質問
{question}

### 回答
"""

    # 3. LLM で生成
    output = llm(
        prompt,
        max_tokens=512,
        temperature=0.1,
        stop=["###", "\n\n\n"]
    )
    return output["choices"][0]["text"].strip()


# ---- メイン ----
if __name__ == "__main__":
    # サンプルドキュメント登録
    sample_docs = [
        {"id": "1", "text": "ConoHa VPS の CPU 3 コア / メモリ 2GB のプランを利用している。",
        "metadata": {"source": "rpi_doc"}},
        {"id": "2", "text": "llama.cpp は CPU のみでも LLM を動かせる軽量フレームワークだ。",
         "metadata": {"source": "llamacpp_doc"}},
        {"id": "3", "text": "RAG はベクトル検索で関連文書を取得し、LLM に渡す手法だ。",
         "metadata": {"source": "rag_doc"}},
    ]
    add_documents(sample_docs)

    # 質問
    question = "ConoHa VPS で LLM を動かすには何が必要ですか?"
    answer = rag_query(question)
    print(f"\nQ: {question}\nA: {answer}")

解説:

  • add_documents関数で文書をベクトルDBに登録
  • retrieve関数で質問に関連する文書を検索
  • rag_query関数でプロンプトを組み立て、LLMで回答生成

5. サンプル実行

仮想環境の有効化と実行


source rag-env/bin/activate
python rag.py
    

出力例:


Q: ConoHa VPS で LLM を動かすには何が必要ですか?
A: ConoHa VPS で LLM を動かすには、以下の要素が必要です。

1. **ハードウェアリソース**:
    * **CPU**: 3 コア
    * **メモリ**: 2GB
2. **ソフトウェア**:
    * **軽量フレームワーク**: `llama.cpp` のような、CPU のみで動作可能な軽量フレームワーク。
3. **LLM モデル**:
    * 実行したいLLMモデル。
4. **(オプション)高度な手法**:
    * **RAG**: ベクトル検索を用いて関連文書を取得し、LLM に渡す手法を利用する場合。

**まとめると、ConoHa VPS 上でLLMを動かすためには、VPSのスペック(CPU/メモリ)を考慮し、`llama.cpp`などの軽量なフレームワークを選び、実行したいモデルを用意することが必要です。**
    

『ConoHa VPS(メモリ2GB / 3コア)でllama.cppによるRAGの実装実験手順』を公開しました。