続・Local LLM入門 – 仮想化通信

以前こんな記事を書きました。

tech.virtualtech.jp

内容としてはOllamaと色々なモデルを使って実際の仕事に使ってみるみたいな話でした。
これかこれで面白かったのですが、ただあるものを使っただけだともったいないので、特定のことを聞くと答えてくれるエージェントを目標にどう実装すればいいか考えてみることにしました。

基本的な技術についてはこの本が大変参考になりました。

books.mdn.co.jp

こちらではRAGを作って、RAGを用いることでモデルだけでは足りない情報を付与して応答するような動きになっていました。
うまく動いたのですが、モデル自身にはその情報が付与されないので、このエージェント環境を配布するときはどうすればいいのかなと思って調べることにしました。

試しにClaudeさんに聞いてみたところ、Pythonで割と簡単に実装できるようなのでためしてみました。
まずはPythonの仮想環境を作って、必要なモジュールをインストールします。

python3.13 -m venv venv
source ./venv/bin/activate 

私がインストールしたモジュールは次の通りです。「Pythonで学ぶAI開発入門 ライブラリを活用したAIの作り方」と重複したモジュールも含まれています。

pip install langchain langchain_community langchain-chroma langchain_ollama sentence_transformers langchain-huggingface PyMuPDF

その上で、次にようなコードを実行すると、ソース中に指定されているPDFファイルのデーターを辞書として保管してくれるようです。正確にはModelfileを生成して、モデルファイルにpromptとしてデーターを入力する形になります。コードを実行したときに生成されるModelfileをエディターで開いてみると、PDFに含まれる情報がそのままファイルに転記されていることが分かります。
そのような感じなので、この方法で改造したモデルを配布するのは使うソース次第では関係者限定にしたほうが良いのかもしれません。

import os
from langchain_community.document_loaders import PyMuPDFLoader


pdf_files = [
    'result_2025_2nd_01.pdf',
    'result_2026_1st_01.pdf'
]

all_documents = []
print("📄 PDFファイルを読み込んでいます...")
for pdf_file in pdf_files:
    if not os.path.exists(pdf_file):
        print(f"  ⚠️  {pdf_file} が見つかりません(スキップ)")
        continue

    print(f"  - {pdf_file}")
    try:
        loader = PyMuPDFLoader(pdf_file)
        documents = loader.load()
        all_documents.extend(documents)
        print(f"    ✅ {len(documents)} ページ")
    except Exception as e:
        print(f"    ❌ エラー: {e}")
        continue

if not all_documents:
    print("❌ PDFファイルを読み込めませんでした")
    exit(1)

print(f"\n✅ 合計 {len(all_documents)} ページを読み込みました")


full_text = "\n\n".join([doc.page_content for doc in all_documents])


char_count = len(full_text)
estimated_tokens = char_count // 4  

print(f"\n📊 統計情報:")
print(f"   文字数: {char_count:,}")
print(f"   推定トークン数: {estimated_tokens:,}")


max_tokens = 100000  
if estimated_tokens > max_tokens:
    print(f"\n⚠️  警告: データ量が大きすぎます(推奨: {max_tokens:,}トークン以下)")
    max_chars = max_tokens * 4
    full_text = full_text[:max_chars]
    print(f"   → データを {max_chars:,} 文字(約{max_tokens:,}トークン)に制限しました")


system_prompt = f"""あなたは社内資料専門のAIアシスタントです。

【資料内容】
以下の社内資料を記憶し、この情報に基づいて回答してください:

{full_text}

【回答ルール】
1. 上記の資料に記載されている情報のみを使用して回答すること
2. 資料にない情報については「資料にその情報は記載されていません」と明確に答えること
3. 役員情報や会社情報は資料の冒頭部分に記載されています
4. 具体的な数値やデータを引用する際は、できるだけ正確に答えること
5. 必要に応じて、どのセクションからの情報かを明示すること
"""


modelfile_content = f'''FROM qwen2:7b

PARAMETER temperature 0.3
PARAMETER num_ctx 131072
PARAMETER top_p 0.9
PARAMETER repeat_penalty 1.1

SYSTEM """{system_prompt}"""
'''


with open('Modelfile', 'w', encoding='utf-8') as f:
    f.write(modelfile_content)

print("\n✅ Modelfile が作成されました")
print(f"   保存先: Modelfile")
print(f"   サイズ: {os.path.getsize('Modelfile') / 1024:.2f} KB")

print(f"\n📋 パラメータ設定:")
print(f"   - temperature: 0.3 (より正確な回答)")
print(f"   - num_ctx: 131072 (128kトークン)")
print(f"   - top_p: 0.9")
print(f"   - repeat_penalty: 1.1")

print(f"\n次のステップ:")
print(f"   1. ollama rm company-model  (既存モデルがある場合)")
print(f"   2. ollama create company-model -f Modelfile")
print(f"   3. ollama run company-model")

この後は次のような使い方ができます。

# 作成されたModelfileを使ってモデルを作成
ollama create company-model -f Modelfile
# 作成したカスタムモデルを使って、Ollamaでチャットを実行
ollama run company-model
# モデルをファイルとしてエクスポート
ollama export company-model -o company-model.gguf
# モデルファイルを環境にインポートする
ollama create company-model -f company-model.gguf

試しに、プロジェクトに参加させてもらっているPG-Stromのマニュアルの内容をPDFファイルにまとめたものを学習させてみました。

その結果がこれです。結構簡単にLLMが知らない情報を入力する方法が今回分かって勉強になりました。


備考

PDFファイルはデーターがそのまま入っているものを用意するだけで良いですが、文中に書式(見出し、太字など)が使われていても、うまく良い感じにデーターとして使っていくれるようです。あまり細かいことを考えないでもできるみたいです。

とはいえこの分野はどんどん進化する分野だと思っていますので、色々習得できるように日々アンテナを立てておきたいと思っています。

ちなみにこの実験はつるしのM4 Mac miniで動かしました。全然余裕で動きました。




元の記事を確認する

関連記事