言語を切り替える
テーマを切り替える

LangChain + Ollama 統合ガイド:ローカル LLM アプリ開発完全マニュアル

先月、OpenAI の請求書を見てみたら $52.3 でした。正直なところ、当時は少しショックを受けました。個人開発者として、たまに AI を触る程度でこれほどの金額を使っていたとは。そこで思い出したのが、ローカルで動いている Ollama です。無料の Llama 3.1 がそこで待っています。

しかし問題がありました。Ollama API を直接呼び出してアプリを書くには、コード量が少し多くなります。リクエスト形式、レスポンス解析、エラー処理……毎回書くのが面倒に感じていました。そこで LangChain が役立ちます。ラップされたインターフェースで、Chat、RAG、Agent すべてに対応し、さらに1行でモデルを切り替えることもできます。

本記事は Ollama ローカル LLM 実践ガイド シリーズのフレームワーク統合編です。langchain-ollama パッケージの基礎から始め、Chat、RAG、Agent の3つの実践シナリオまで解説します。シリーズの前回(API 呼び出し、マルチモデルデプロイ)を読んでいれば、今回は断片的な知識を完全な開発フレームワークとしてまとめられます。

langchain-ollama パッケージ入門

まず、私が踏んだ落とし穴についてお話しします。以前は langchain_community.llms.Ollama を使っていました。コードは問題なく動くのですが、どこか違和感がありました。ドキュメントを見るたびに「langchain-ollama」というパッケージが表示されるのです。後で分かったのですが、LangChain 公式が Ollama 統合を独立パッケージとして分離していました。それが langchain-ollama です。

なぜ公式パッケージを推奨するのか?

型ヒントがより充実しており、IDE の自動補完がより使いやすくなります。メンテナンスのサイクルも LangChain メインバージョンと同期しているため、互換性の問題を心配する必要がありません。また、コミュニティパッケージはいつ廃止されるか分かりませんが、公式パッケージは長期的なソリューションです。以前、コミュニティパッケージが廃止された経験があるので、この点は重要です。

インストールは1行で完了します:

pip install langchain-ollama

インストール後、このパッケージには3つのコアクラスがあり、それぞれ異なるシーンに対応していることが分かります:

クラス名用途典型的なシーン
ChatOllama会話モデル複数ターンのチャット、Q&A システム
OllamaLLMテキスト補完単発の生成、テキストの続き
OllamaEmbeddingsベクトルエンベディングRAG、意味検索

実際に使ってみたところ、90%のシーンで ChatOllama だけで十分でした。会話モデルは複数ターンのやり取りに対応し、ストリーミング出力も可能です。文字が1つずつ表示されるあのエフェクトは、ユーザー体験が一気に返ってくるよりもずっと良くなります。

最小限のサンプルで、どれほどシンプルか体験してみましょう:

from langchain_ollama import ChatOllama

# モデルを初期化
llm = ChatOllama(
    model="llama3.1:8b",  # モデル名。事前に Ollama でプルが必要
    temperature=0.7       # ランダム性パラメータ。0-1 の間
)

# メッセージを送信
response = llm.invoke("こんにちは、自己紹介をお願いします")
print(response.content)

このコードを実行する前に、ollama pull llama3.1:8b でモデルをプルしていることを確認してください。まだ Ollama をインストールしていない場合は、シリーズ第1回の入門記事を参照してください。

エンベディングモデル OllamaEmbeddings も使い方は似ており、主にテキストをベクトルに変換するのに使います。後の RAG パートで詳しく使いますが、ここでは簡単なサンプルを示します:

from langchain_ollama import OllamaEmbeddings

embeddings = OllamaEmbeddings(model="nomic-embed-text")

# 単一テキストのエンベディング
vector = embeddings.embed_query("これはテストテキストです")
print(f"ベクトル次元: {len(vector)}")  # 通常 768 以上を出力

# 複数テキストの一括エンベディング
vectors = embeddings.embed_documents([
    "最初のテキスト",
    "2番目のテキスト"
])

nomic-embed-text は現在主流のエンベディングモデルで、意味検索のために設計されています。ベクトル次元が高く(通常 768+)、検索効果は汎用モデルよりもはるかに優れています。

Chat アプリ実践:複数ターンの会話とストリーミング出力

API を1回呼び出すだけならシンプルですが、実際のチャットシーンはもっと複雑です。ユーザーは連続して質問し、モデルは前の会話内容を覚えている必要があります。LangChain はメッセージリストを使ってこの問題を処理します。

複数ターンの会話実装

LangChain のメッセージタイプには3種類あります:

  • SystemMessage:モデルの役割と振る舞いを設定(例:「あなたはプロのプログラマーアシスタントです」)
  • HumanMessage:ユーザー入力
  • AIMessage:モデルの返答

コードを見てみましょう:

from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

llm = ChatOllama(model="llama3.1:8b", temperature=0.7)

# 会話履歴を構築
messages = [
    SystemMessage(content="あなたは技術概念を説明するのが得意な開発者アシスタントです。回答は簡潔で分かりやすく。"),
    HumanMessage(content="REST API とは何ですか?"),
    AIMessage(content="REST API はネットワークサービスインターフェースの設計スタイルで、HTTP メソッド(GET/POST/PUT/DELETE)を使ってリソースを操作します。簡単に言えば、URL でデータにアクセスする方法です。"),
    HumanMessage(content="では、GraphQL との違いは何ですか?")
]

# モデルは会話履歴全体に基づいて返答を生成
response = llm.invoke(messages)
print(response.content)

このコードでは、モデルは前の回答を見ることができ、ユーザーが GraphQL と REST の違いについて追及していることを理解しています。もし AIMessage のレコードがなければ、モデルは GraphQL を最初から説明するかもしれません。ユーザー体験が途切れてしまいます。

ストリーミング出力:レスポンスに「生きている」感じを持たせる

ストリーミング出力のメリットは、ユーザーが空白画面を凝視して結果を待つ必要がないことです。文字が次々と表示され、誰かがタイプしているような感覚です。この体験は長い回答には特に重要です。

from langchain_ollama import ChatOllama

llm = ChatOllama(model="llama3.1:8b")

# ストリーミング出力
print("モデルの回答:", end="", flush=True)
for chunk in llm.stream("Python でクイックソートアルゴリズムを書き、原理を説明してください"):
    print(chunk.content, end="", flush=True)
print()  # 最後に改行

stream() メソッドはイテレータを返し、各 chunk には小さなテキスト片が含まれています。flush=True を指定することで、コンテンツが即座に表示され、バッファに蓄積されません。

実際にテストしてみましたが、ストリーミング出力の遅延感は一括返却よりもかなり低いです。特に回答が100文字を超える場合、ユーザーはシステムが「思考中」だと感じ、「フリーズした」ではなくなります。

RAG アプリ実践:ローカルナレッジベース検索

RAG(Retrieval-Augmented Generation、検索拡張生成)は現在、最も実用的な LLM アプリケーションシナリオの1つです。簡単に言うと、まず文書ライブラリから関連コンテンツを検索し、そのコンテンツに基づいてモデルに回答を生成させます。これにより、モデルは学習データに含まれていない情報を「知る」ことができます。

RAG フローの分解

完全な RAG システムには5つのステップがあります:

  1. 文書の読み込み —— PDF、TXT、Markdown などのファイルを読み込む
  2. テキストの分割 —— 文書が長すぎるため、検索しやすい小さな断片に分割
  3. ベクトルの生成 —— エンベディングモデルでテキストを数値ベクトルに変換
  4. インデックスの保存 —— ベクトルデータベースに保存(ここでは ChromaDB を使用)
  5. 検索と生成 —— ユーザーの質問時に、関連断片を検索し、モデルに回答させる

以下は完全なコードです。テスト済みで正常に動作します:

from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_chroma import Chroma
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

# === 1. 文書の読み込み ===
# PDF、TXT、Markdown など複数の形式に対応
loader = TextLoader("./my_document.txt")  # 文書パスに置き換えてください
docs = loader.load()

# === 2. テキストの分割 ===
# chunk_size=1000 は一般的なパラメータ。各断片は約1000文字
# chunk_overlap=200 で断片間に重複を設け、情報の断絶を防ぐ
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200
)
splits = text_splitter.split_documents(docs)

# === 3 & 4. ベクトル生成と保存 ===
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma.from_documents(
    documents=splits,
    embedding=embeddings,
    persist_directory="./chroma_db"  # 永続化ストレージパス
)

# === 5. リトリーバーの構築 ===
# search_kwargs={"k": 4} は最も関連性の高い4つの断片を検索
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# === 6. RAG Chain の構築 ===
template = """以下のコンテキストに基づいて質問に答えてください。コンテキストに関連情報がない場合は、「文書に関連情報がありません」と明確に述べてください。

コンテキスト:
{context}

質問:{question}
"""
prompt = ChatPromptTemplate.from_template(template)

llm = ChatOllama(model="llama3.1:8b")

# LangChain の LCEL 構文。| 記号で各コンポーネントを接続
rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# === 7. クエリ ===
response = rag_chain.invoke("文書の主な内容は何ですか?")
print(response)

このコードは少し長く見えますが、分解してみると非常に明確です。重要なのは rag_chain の構築です。LangChain の LCEL(LangChain Expression Language)構文を使って、リトリーバー、プロンプトテンプレート、モデル、出力パーサーを連結しています。

実用的なパラメータ調整のヒント

chunk_size は、文書の内容が密(技術文書)の場合は 800 に調整できます。エッセイ的な内容なら 1000-1500 でも構いません。

k 値(検索断片数)は通常 3-5 個で十分です。多すぎると関連性が薄まり、少なすぎると重要な情報を見逃す可能性があります。

persist_directory は必ず設定してください。設定しないと、再起動のたびにベクトルデータベースを再構築する必要があり、時間がかかります。

初めて RAG を実行したとき、永続化を設定していませんでした。その結果、コードを変更するたびにエンベディングを再実行する必要があり、遅すぎてイライラしました。後で persist_directory を追加したところ、構築済みのベクトルデータベースを直接読み込めるようになり、数秒で起動できるようになりました。

Agent アプリ実践:JSON ベースのツール呼び出し

Agent と通常の会話の最大の違いは、Agent が外部ツールを呼び出せることです。

例えば、ユーザーが「北京の今日の天気はどうですか」と聞くと、通常の会話モデルは適当な答えをでっち上げるしかありません。Agent はまず天気取得ツールを呼び出し、実際のデータを取得してから回答します。

Ollama ツール呼び出しの落とし穴

ここで正直に言うべき問題があります。Ollama のツール呼び出しサポートは OpenAI ほど成熟していません。OpenAI のモデルは function calling をネイティブサポートし、いつツールを呼び出すべきか、パラメータをどう渡すかを正確に認識できます。Ollama のモデル(Llama 3.1 を含む)は、この点ではまだ未熟です。

どうすればいいでしょうか?LangChain 公方は1つのソリューションを提供しています:JSON-based Agent

考え方は、モデルに構造化された JSON を出力させ、Agent フレームワークがこの JSON を解析してどのツールを呼び出すかを決定します。実際にテストしてみたところ、効果は悪くありません。OpenAI のネイティブツール呼び出しほどスムーズではありませんが、基本的なタスクは完了できます。

カスタムツールのサンプル

まず、いくつかのシンプルなツール関数を定義します:

from langchain_ollama import ChatOllama
from langchain_core.tools import tool

# ツールを定義
@tool
def get_weather(city: str) -> str:
    """指定された都市の天気情報を取得"""
    # ここはモックデータ。実際は天気 API に接続可能
    weather_data = {
        "北京": "晴れ、25°C、空気質良好",
        "上海": "曇り、22°C、小雨の可能性あり",
        "深セン": "暑い、30°C、紫外線が強い"
    }
    return weather_data.get(city, f"{city} の天気データが見つかりません")

@tool
def calculate(expression: str) -> str:
    """数学計算を実行"""
    try:
        result = eval(expression)  # 注意:本番環境ではより安全な実装が必要
        return f"計算結果:{result}"
    except:
        return "計算エラー。式を確認してください"

@tool
def search_local_docs(query: str) -> str:
    """ローカル文書ライブラリを検索"""
    # ここでは前述の RAG パートのリトリーバーに接続可能
    return f"'{query}' の検索結果:3件の関連レコードが見つかりました"

@tool デコレータは通常の関数を LangChain ツールに変換します。関数の docstring は自動的にツールの説明になり、モデルはこの説明に基づいていつどのツールを使うべきかを判断します。

次に JSON Agent を作成します:

from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate

llm = ChatOllama(model="llama3.1:8b")
tools = [get_weather, calculate, search_local_docs]

# プロンプトテンプレートを作成
prompt = ChatPromptTemplate.from_messages([
    ("system", "あなたは役立つアシスタントで、ツールを使ってタスクを完了できます。"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

# Agent を作成
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# タスクを実行
response = agent_executor.invoke({
    "input": "北京の今日の天気を調べて、それから 23 + 45 を計算して"
})

print(response["output"])

verbose=True を指定すると、Agent の思考プロセスが表示され、デバッグに役立ちます。モデルがツール呼び出しの決定プロセスを出力する様子が確認できます。

実測体験

70-80%
JSON Agent 成功率
来源: 著者の実測データ

いくつかのタスクを実行してみましたが、JSON Agent の成功率は約 70-80% です。シンプルなタスク(天気確認、計算)は基本的に問題ありませんが、複雑なタスク(複数ツールの組み合わせ)では時々エラーが発生します。例えば、パラメータ形式が間違っていたり、間違ったツールを選んだりします。これは現在のローカル LLM Agent の共通の問題で、OpenAI ほど安定していません。

Agent の要求が高い場合は、以下の方法を検討してください:

  1. より強力なモデルを使用(Qwen 2.5 や DeepSeek など)
  2. タスクフローを簡素化し、ツール数を減らす
  3. または、OpenAI のネイティブツール呼び出しを使う。コストは高いですが、安定性ははるかに優れています

OpenAI vs Ollama:1行のコードで切り替え

多くの人から聞かれます。LangChain でコードを書いたのですが、OpenAI も Ollama も両方使えますか?答えは「はい、しかも信じられないほどシンプル」です。

切り替え方法1:import を変更

OpenAI を使ったコードがあるとします:

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4", temperature=0.7)
response = llm.invoke("量子コンピューティングを説明して")

Ollama に切り替えるには、import 行を変更するだけです:

from langchain_ollama import ChatOllama

llm = ChatOllama(model="llama3.1:8b", temperature=0.7)
response = llm.invoke("量子コンピューティングを説明して")

他のコード(プロンプトテンプレート、Chain 構築、出力解析)は一切変更不要です。LangChain の抽象化レイヤーが十分に優れており、モデル切り替えはビジネスロジックにほぼ透明です。

切り替え方法2:OpenAI-Compatible API

Ollama は「自分を OpenAI だと見せかける」方法も提供しています。OpenAI-Compatible API です。メリットは import さえ変更する必要がないことです:

from langchain_openai import ChatOpenAI

# base_url と api_key だけを変更。他はそのまま
llm = ChatOpenAI(
    model="llama3.1:8b",
    base_url="http://localhost:11434/v1",  # Ollama の OpenAI 互換エンドポイント
    api_key="ollama"  # 適当に入力。Ollama は検証しない
)

response = llm.invoke("量子コンピューティングを説明して")

この方法はどのシーンに適しているでしょうか。プロジェクトですでに大量に ChatOpenAI を使っていて、コード構造を変更したくないが、ローカルモデルの効果をテストしたい場合です。

比較まとめ

これほど多くの切り替え方法を紹介しましたが、いつ Ollama を使い、いつ OpenAI を使うべきでしょうか?主な違いを表にまとめました:

比較項目OpenAI (GPT-4)Ollama (Llama 3.1)
コスト$0.03/1K input tokens無料(ローカル GPU の電力を消費)
プライバシーデータはクラウドにアップロード。コンプライアンスに敏感なシーンでは注意が必要ローカル処理。データは外に出ない
ツール呼び出しネイティブサポート。安定して信頼性が高いJSON Agent が必要。成功率 70-80%
レスポンス速度高速(クラウド最適化、1-3秒でファーストバイト)ローカル GPU に依存(3-10秒と変動)
モデル能力GPT-4 は現在最高峰の1つLlama 3.1 8B は中程度の強さ。十分だが GPT-4 ほどではない

私の提案は:

  • 個人学習、プロトタイプ開発:Ollama を使用。コストを抑え、自由に試せる
  • 本番環境、高同時アクセス:OpenAI を使用。安定性とレスポンス速度が保証される
  • プライバシーに敏感なデータ:Ollama を使用。データはローカルから出ない
  • 複雑な Agent タスク:OpenAI を使用。ツール呼び出しがより安定

理想的な状態は両方を持つことです。開発段階では Ollama でコストを抑え、本番では OpenAI で安定性を確保する。切り替えコストはたった1行のコードです。なぜやらないのでしょうか。

まとめ

ここまで、LangChain + Ollama の統合パスについて完全な地図を提供しました。

langchain-ollama パッケージから始め、ChatOllama、OllamaLLM、OllamaEmbeddings の3つのコアクラスを理解しました。次に、3つのシーンを実践しました。Chat 複数ターンの会話(ストリーミング出力で体験をスムーズに)、RAG ナレッジベース検索(ローカル文書をインテリジェントQ&Aに変換)、Agent ツール呼び出し(JSON Agent は現在の妥協案)。最後に、OpenAI と Ollama の切り替え戦略を比較しました。1行のコードで切り替え、コストは月 $50 から無料に。

いつ Ollama を選ぶべきか?

一言で言うと、コストを抑えたい、プライバシーを守りたい、または単に LLM 開発を学びたいときです。ローカルで動かし、自由に試せて、請求書が爆発することを心配する必要がありません。

いつ OpenAI を使うべきか?

複雑な Agent タスク、高同時アクセスの本番環境、レスポンス速度と安定性が高い要求があるシーンです。現在のローカル LLM はまだクラウドの体験に代わるものではありません。

まだ試していない場合は、まず Chat シーンから始めることをお勧めします。コードが最もシンプルで、効果が最も直感的です。うまくいったら RAG を試し、ローカル文書を接続して、「モデルがあなたの資料を読める」驚きを体験してください。Agent は後回しで構いません。ツール呼び出しの落とし穴がまだ多く、忍耐強いデバッグが必要です。

シリーズの後半では、さらに多くのコンテンツがあります。マルチモデルデプロイ(LangChain で異なるモデルを切り替える方法)、パフォーマンスチューニング(ローカル LLM をより速く動かす)、本番デプロイ(ローカルアプリを利用可能なサービスに変換)。興味があれば、引き続きフォローしてください。

質問があればコメント欄で議論するか、直接 GitHub で探してください。コードサンプルはすべてテスト済みで、正常に動作するはずです。エラーが発生した場合は、モデルがプルされていないか依存関係がインストールされていない可能性が高いです。エラーメッセージに従ってトラブルシューティングしてください。

LangChain + Ollama 統合開発

インストール設定から Chat、RAG、Agent の3つの実践シナリオまで、ワンストップでローカル LLM アプリ開発を習得

⏱️ 目安時間: 60 分

  1. 1

    ステップ1: langchain-ollama パッケージをインストール

    インストールコマンドを実行:

    ```bash
    pip install langchain-ollama
    ```

    Ollama がインストールされ、モデルがプルされていることを確認(例:`ollama pull llama3.1:8b`)。
  2. 2

    ステップ2: Chat アプリを作成

    ChatOllama を初期化し、メッセージを送信:

    ```python
    from langchain_ollama import ChatOllama

    llm = ChatOllama(model="llama3.1:8b", temperature=0.7)
    response = llm.invoke("こんにちは")
    print(response.content)
    ```

    複数ターンの会話とストリーミング出力に対応。
  3. 3

    ステップ3: RAG ナレッジベースを構築

    5ステップのフロー:

    • 文書の読み込み(TextLoader / PyPDFLoader)
    • テキストの分割(RecursiveCharacterTextSplitter)
    • ベクトル生成(OllamaEmbeddings)
    • インデックス保存(ChromaDB)
    • 検索と生成(RAG Chain)

    重要パラメータ:chunk_size=1000, k=4, persist_directory は必須。
  4. 4

    ステップ4: Agent ツール呼び出しを実装

    ツール関数を定義し、JSON Agent を作成:

    ```python
    @tool
    def get_weather(city: str) -> str:
    """天気情報を取得"""
    ...

    agent = create_tool_calling_agent(llm, tools, prompt)
    agent_executor = AgentExecutor(agent=agent, tools=tools)
    ```

    成功率は約 70-80%。複雑なタスクでは OpenAI を推奨。
  5. 5

    ステップ5: OpenAI / Ollama を切り替え

    方法1:import を変更

    ```python
    from langchain_ollama import ChatOllama # Ollama を使用
    from langchain_openai import ChatOpenAI # OpenAI を使用
    ```

    方法2:OpenAI-Compatible API(import は変更しない)

    ```python
    llm = ChatOpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
    )
    ```

FAQ

langchain-ollama と langchain_community.llms.Ollama の違いは何ですか?
langchain-ollama は公式の独立パッケージで、型ヒントがより充実しており、メンテナンスサイクルはメインバージョンと同期しています。langchain_community.llms.Ollama はコミュニティパッケージで、いつ廃止されるか分かりません。公式パッケージ langchain-ollama の使用を推奨します。
ChatOllama と OllamaLLM はどちらを使うべきですか?
90%のシーンで ChatOllama だけで十分です。複数ターンの会話、ストリーミング出力、メッセージ履歴に対応しています。OllamaLLM は単発のテキスト生成や続きの作成に適しています。
RAG システムで chunk_size と k 値はどう設定しますか?
chunk_size:技術文書は 800、エッセイ的な内容は 1000-1500 がおすすめ。k 値(検索断片数)は通常 3-5 個で、多すぎると関連性が薄まり、少なすぎると重要な情報を見逃す可能性があります。persist_directory を設定してベクトルデータベースを永続化することを必須とします。
Ollama のツール呼び出しはなぜ OpenAI ほど安定していませんか?
Ollama モデル(Llama 3.1 を含む)はネイティブの function calling サポートがないため、JSON Agent ソリューションを使ってモデルに構造化 JSON を出力させる必要があります。成功率は約 70-80% です。OpenAI のネイティブツール呼び出しはより安定しており、複雑な Agent タスクでは OpenAI の使用を推奨します。
OpenAI と Ollama を切り替える方法は?
方法1:import を変更(ChatOpenAI を ChatOllama に変更)。方法2:OpenAI-Compatible API を使用し、base_url と api_key だけを変更。他のコード(プロンプトテンプレート、Chain、出力解析)は一切変更不要です。
Ollama は本番環境に適していますか?
シーンによります。個人学習、プロトタイプ開発、プライバシーに敏感なデータには Ollama が適しています。高同時アクセスの本番環境、複雑な Agent タスク、レスポンス速度が高い要求には OpenAI を推奨します。理想的なソリューションは、開発時に Ollama でコストを抑え、本番では OpenAI で安定性を確保することです。

7 min read · 公開日: 2026年4月7日 · 更新日: 2026年4月8日

コメント

GitHubアカウントでログインしてコメントできます

関連記事