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([
"1つ目のテキスト",
"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 を一から説明し始めるかもしれず、ユーザー体験が途切れてしまいます。
ストリーミング出力:レスポンスに「生きている」感じを
ストリーミング出力の利点は、ユーザーが真っ白な画面で結果を待たずに済むことです——文字が1つずつ飛び出してきて、誰かがタイピングしているように見えます。この体験は長い回答ほど重要です。
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 アプリのシーンのひとつです。簡単に言えば、まずドキュメントベースから関連内容を検索し、その内容をもとにモデルに回答を生成させます。こうすることで、モデルは学習データに含まれていない情報を「知る」ことができます。
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. 検索器(retriever)を構築 ===
# 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 公式がひとつの方式を示しています:JSON ベースの 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 を1行変えるだけです:
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 を使うべきか?主な違いを1枚の表にまとめました:
| 比較項目 | OpenAI (GPT-4) | Ollama (Llama 3.1) |
|---|---|---|
| コスト | $0.03/1K input tokens | 無料(ローカル GPU の電気代を消費) |
| プライバシー | データをクラウドにアップロード、コンプライアンスに敏感なシーンは要注意 | ローカルで処理、データは外に出ない |
| ツール呼び出し | ネイティブ対応、安定して信頼できる | JSON Agent が必要、成功率 70〜80% |
| レスポンス速度 | 速い(クラウド最適化、最初の1文字まで 1〜3 秒) | ローカル GPU 次第(3〜10 秒とばらつく) |
| モデル性能 | GPT-4 は現状最強クラスのひとつ | 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 は本番環境に向いている?
8分で読めます · 公開日: 2026年4月7日 · 更新日: 2026年6月15日
Ollama ローカル LLM 実践ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Ollama API 実践:Python と Node.js クライアント開発ガイド
Ollama API の呼び出し方法を詳しく解説。Python と Node.js SDK のネイティブ呼び出し、ストリーミングレスポンス処理、ツール呼び出しの Agent Loop、thinking モード、OpenAI 互換方式の比較まで網羅します
第 14 / 19 記事
次の記事
Ollama Embedding 実践:ローカル ベクトル検索と RAG 構築
Ollama でローカル RAG システムを構築:mxbai-embed-large と nomic-embed-text の比較、ChromaDB/FAISS/Milvus ベクトルデータベースの選び方、完全な Python コード実践
第 16 / 19 記事
関連記事
Ollama 入門:ローカルで大規模言語モデルを動かす第一歩
Ollama 入門:ローカルで大規模言語モデルを動かす第一歩
Ollama モデル管理:ダウンロード、切り替え、削除とバージョン管理の完全ガイド
Ollama モデル管理:ダウンロード、切り替え、削除とバージョン管理の完全ガイド
Ollama バージョンロールバック実践:90% の開発者が見落とす 3 つの重要ステップ
コメント
GitHubアカウントでログインしてコメントできます