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つのステップがあります:
- 文書の読み込み —— PDF、TXT、Markdown などのファイルを読み込む
- テキストの分割 —— 文書が長すぎるため、検索しやすい小さな断片に分割
- ベクトルの生成 —— エンベディングモデルでテキストを数値ベクトルに変換
- インデックスの保存 —— ベクトルデータベースに保存(ここでは ChromaDB を使用)
- 検索と生成 —— ユーザーの質問時に、関連断片を検索し、モデルに回答させる
以下は完全なコードです。テスト済みで正常に動作します:
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 の思考プロセスが表示され、デバッグに役立ちます。モデルがツール呼び出しの決定プロセスを出力する様子が確認できます。
実測体験:
いくつかのタスクを実行してみましたが、JSON Agent の成功率は約 70-80% です。シンプルなタスク(天気確認、計算)は基本的に問題ありませんが、複雑なタスク(複数ツールの組み合わせ)では時々エラーが発生します。例えば、パラメータ形式が間違っていたり、間違ったツールを選んだりします。これは現在のローカル LLM Agent の共通の問題で、OpenAI ほど安定していません。
Agent の要求が高い場合は、以下の方法を検討してください:
- より強力なモデルを使用(Qwen 2.5 や DeepSeek など)
- タスクフローを簡素化し、ツール数を減らす
- または、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: langchain-ollama パッケージをインストール
インストールコマンドを実行:
```bash
pip install langchain-ollama
```
Ollama がインストールされ、モデルがプルされていることを確認(例:`ollama pull llama3.1:8b`)。 - 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: RAG ナレッジベースを構築
5ステップのフロー:
• 文書の読み込み(TextLoader / PyPDFLoader)
• テキストの分割(RecursiveCharacterTextSplitter)
• ベクトル生成(OllamaEmbeddings)
• インデックス保存(ChromaDB)
• 検索と生成(RAG Chain)
重要パラメータ:chunk_size=1000, k=4, persist_directory は必須。 - 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: 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 の違いは何ですか?
ChatOllama と OllamaLLM はどちらを使うべきですか?
RAG システムで chunk_size と k 値はどう設定しますか?
Ollama のツール呼び出しはなぜ OpenAI ほど安定していませんか?
OpenAI と Ollama を切り替える方法は?
Ollama は本番環境に適していますか?
7 min read · 公開日: 2026年4月7日 · 更新日: 2026年4月8日
関連記事
Ollama Embedding 実践:ローカルベクトル検索と RAG 構築
Ollama Embedding 実践:ローカルベクトル検索と RAG 構築
Ollama マルチモデルデプロイ:Qwen、Llama、DeepSeek の並列実行
Ollama マルチモデルデプロイ:Qwen、Llama、DeepSeek の並列実行
Ollama Modelfile パラメータ詳解:カスタムモデル作成の完全ガイド

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