所要時間: 約40分 | 難易度: ★★★☆☆

この記事で作るもの

  • ローカルLLMのQwen2.5を活用し、PDFやマークダウン形式の技術文書から特定の項目(作成日、技術スタック、アーキテクチャの要点など)を正確に抽出してJSON形式で保存するPythonスクリプトを作ります。
  • 外部API(OpenAIなど)へデータを送信せず、すべて自分のPC内で完結させるため、機密性の高い社内ドキュメントも安全に処理できます。
  • 前提知識として、Pythonの基本的な文法(変数、関数、pipでのライブラリインストール)を理解している必要があります。
  • 必要なものは、16GB以上のメモリを搭載したPC(GPU推奨ですがCPUでも動作可能)とPython環境です。

📦 この記事に関連する商品

ASUS RTX 4060 Ti 16GB

VRAM 16GB搭載で、Qwen2.5の7B/14Bモデルをローカルで快適に動かすのに最適な1枚

Amazonで見る 楽天で見る

※アフィリエイトリンクを含みます

なぜこの方法を選ぶのか

AlibabaのAIチームから主要メンバーが離脱したというニュースが話題ですが、実務家としての私の視点は異なります。株価や組織の動向とは裏腹に、彼らが残した「Qwen2.5」というモデル自体の完成度は、現在オープンソース界隈でLlama 3.1を凌駕する場面も多いほど極めて高いです。

特に日本語の処理能力と、指示に従って特定のフォーマット(JSONなど)で出力する能力が、同サイズの他モデルと比べて頭一つ抜けています。SIer時代、私は何千枚ものExcel設計書から項目を手動でデータベースに転記する虚無な作業を何度も見てきました。当時は正規表現で無理やり抽出していましたが、表記揺れに弱く限界がありました。

今回紹介する「Ollama」と「Qwen2.5」を組み合わせた手法は、それらの苦痛を過去のものにします。APIコストを気にせず、何万件もの文書をローカルで高速に処理できる点が最大のメリットです。

Step 1: 環境を整える

まずは、ローカル環境でLLMを動かすためのエンジンである「Ollama」をインストールします。Dockerで構築する方法もありますが、GPUのパススルー設定で時間を溶かすのは本質的ではありません。インストーラーを使うのが最も確実です。

# Ollamaを公式サイト(https://ollama.com/)からダウンロードしてインストールした後、モデルをプルします
ollama run qwen2.5:7b

このコマンドは、Qwen2.5の70億パラメータモデルをダウンロードし、対話モードで起動します。7bモデルは、RTX 3060(12GB VRAM)程度の環境でも非常に軽快に動作します。

次に、PythonからOllamaを制御するためのライブラリと、データ構造を定義するためのPydanticをインストールします。

pip install ollama pydantic

⚠️ 落とし穴: Windows環境でGPUが認識されない場合、NVIDIAのドライバーが古いケースがほとんどです。また、WSL2経由で動かすよりも、現在はWindowsネイティブ版のOllamaを使ったほうが推論速度(トークン生成速度)が安定する傾向にあります。

Step 2: 基本の設定

Pythonスクリプトを作成します。ここでは、抽出したいデータの形を定義します。LLMに「いい感じに抽出して」と頼むのではなく、あらかじめ型を定義しておくことが、システムとして組み込む際の絶対条件です。

import os
import json
from pydantic import BaseModel, Field
from typing import List
import ollama

# 抽出したいデータの構造をクラスで定義
class TechSpec(BaseModel):
    project_name: str = Field(description="プロジェクト名")
    languages: List[str] = Field(description="使用されているプログラミング言語のリスト")
    database: str = Field(description="使用されているデータベース名")
    frameworks: List[str] = Field(description="使用されているフレームワークやライブラリ")
    summary: str = Field(description="システムの概要を30文字以内で要約")

# モデル名の定義。Qwen2.5-7bを使用。
# 精度を重視する場合は 14b や 32b に変更可能ですが、まずは 7b で試すのが定石です。
MODEL_NAME = "qwen2.5:7b"

各項目にField(description=...)を添えるのがコツです。これがそのままLLMへの指示(プロンプト)の一部として機能し、抽出精度を劇的に向上させます。

Step 3: 動かしてみる

定義した構造に基づいて、実際にテキストから情報を抜き出します。Qwen2.5は「JSONモード」をサポートしているため、出力が壊れるリスクを最小限に抑えられます。

def extract_tech_info(text: str):
    prompt = f"以下の技術文書から情報を抽出し、指定されたJSON形式で出力してください。\n\n文書: {text}"

    response = ollama.chat(
        model=MODEL_NAME,
        messages=[
            {'role': 'system', 'content': 'あなたは優秀なテクニカルエディターです。必ず指定されたJSONフォーマットで回答してください。'},
            {'role': 'user', 'content': prompt},
        ],
        format=TechSpec.model_json_schema(), # Pydanticのスキーマを渡す
        options={'temperature': 0} # 決定論的な出力を得るために0に設定
    )

    return response['message']['content']

# テスト用のダミーテキスト
sample_text = """
次世代物流システム「ハコブネ」の開発について。
本プロジェクトでは、バックエンドにPython 3.11とFastAPIを採用し、
データベースにはPostgreSQLを利用します。
また、フロントエンドはTypeScriptとNext.jsで構築され、
キャッシュ戦略としてRedisを導入しています。
"""

result = extract_tech_info(sample_text)
print(result)

期待される出力

{
  "project_name": "ハコブネ",
  "languages": ["Python", "TypeScript"],
  "database": "PostgreSQL",
  "frameworks": ["FastAPI", "Next.js", "Redis"],
  "summary": "次世代物流システムの開発プロジェクト"
}

Qwen2.5は日本語の文脈を正確に捉え、Redisをフレームワーク/ライブラリのカテゴリに分類するなど、賢い判断をしてくれます。temperatureを0にするのは、同じ入力に対して常に同じ結果を出すためです。これはプログラムの一部として組み込む際に必須の設定です。

Step 4: 実用レベルにする

実際の業務では、1つのテキストを処理して終わりではありません。大量のファイルが入ったフォルダを監視し、エラーが起きた際にリトライする仕組みが必要です。

特にローカルLLMは、稀にJSONのパースに失敗する出力を返すことがあります。そのため、Pydanticのmodel_validate_jsonを使って、プログラム側でバリデーションをかけるのが「仕事で使える」コードへの第一歩です。

import time

def robust_extraction(text: str, retries: int = 3):
    for i in range(retries):
        try:
            raw_content = extract_tech_info(text)
            # LLMの出力をパースして、TechSpecクラスのインスタンスを作る
            validated_data = TechSpec.model_validate_json(raw_content)
            return validated_data.model_dump()
        except Exception as e:
            print(f"試行 {i+1} 回目: エラー発生 {e}。再試行します。")
            time.sleep(1)
    return None

# 実用的なバッチ処理の例
documents = [sample_text] # 実際にはファイルから読み込んだリスト
final_results = []

for doc in documents:
    data = robust_extraction(doc)
    if data:
        final_results.append(data)

# 結果をJSONファイルとして保存
with open("extracted_data.json", "w", encoding="utf-8") as f:
    json.dump(final_results, f, ensure_ascii=False, indent=2)

print(f"{len(final_results)} 件のデータを処理しました。")

このように、リトライ処理を入れることで、ローカルLLM特有の不安定さをカバーできます。私が過去に受けた機械学習案件でも、モデル自体の精度向上に時間をかけるより、こうした外側のガードレールをしっかり作る方が、最終的なシステムの信頼性は高まりました。

よくあるトラブルと解決法

エラー内容原因解決策
ollama.ResponseError: model not foundモデルがダウンロードされていないollama pull qwen2.5:7b を実行してください。
抽出結果が空、または不正確プロンプトが曖昧Fielddescriptionをより具体的に書き換えてください。
処理速度が極端に遅いCPUで動作しているPCにNVIDIA製GPUがある場合は、CUDAが認識されているか確認してください。

次のステップ

今回作成したスクリプトは、特定の構造を持ったデータを抽出する「情報の構造化」に特化しています。これをマスターした後は、LangChainなどのフレームワークを導入し、PDFからテキストを抽出する部分の自動化や、抽出したデータをベクトルデータベース(Chromaなど)に格納して検索できるようにする「RAG(検索拡張生成)」へと拡張してみてください。

また、Qwen2.5にはさらに高性能な32bや72bモデルも存在します。RTX 4090などの強力なGPUを持っているなら、ぜひモデルサイズを上げてみてください。7bでは抽出漏れがあった複雑な契約書や、難解な技術論文でも、驚くほどの精度で構造化できるようになります。

AIの開発チームに動きがあっても、公開されたモデルの価値は変わりません。むしろ、使い倒すノウハウを今のうちに蓄積しておくことが、今後のキャリアにおいて大きなアドバンテージになると私は確信しています。

よくある質問

Q1: 7bモデルだと精度が足りない場合はどうすればいいですか?

モデルのサイズを14bまたは32bに上げてください。Ollamaならollama run qwen2.5:14bと打つだけで変更可能です。ただし、14bは16GB以上の、32bは24GB以上のVRAMを持つGPUがないと動作が非常に遅くなります。

Q2: 会社で使う際、プロンプトに機密情報を入れても大丈夫ですか?

はい、この方法は完全にオフラインで動作しているため、データがAlibabaや外部サーバーに送信されることはありません。ネットワークを切断した状態でも動作することを確認してみると、より安心できると思います。

Q3: 日本語の文字化けが発生するのですが。

Pythonでファイルを扱う際は、必ずencoding='utf-8'を指定してください。特にWindowsのデフォルト(cp932)では、LLMが生成した多言語テキストを正しく保存できないケースが多々あります。


あわせて読みたい