Ollama: LangChain統合
LangChainはAIアプリケーションの指揮者——モデルは役者、チェーンは台本、エージェントは監督。
💡 ヒント: LangChainの中核はエージェント/チェーン/ツールの3層アーキテクチャ——チェーンは固定パイプライン(プロンプト→LLM→パーサー)、ツールは呼び出し可能な外部機能(データベース検索、文書検索)、エージェントはどのツールを呼び出すかを自律的に決定する意思決定者。90%のシナリオはチェーンで十分;動的な意思決定が必要な場合のみエージェントを使用。
📋 前提条件: まず以下を習得していること
- レッスン5: REST APIの基礎
- レッスン7: Python SDK統合
1. 学べること
- LangChain + Ollama接続設定
- プロンプトテンプレート + チェーンによるチェーンプロンプトエンジニアリング
- 出力パーサーによる構造化出力
- ツール呼び出しエージェント
- AliceのSaaSメトリクス分析エージェント
2. スタートアップCTOのリアルな事例
(1) ペインポイント:毎月50のメトリクスレポートを手動分析
AliceのSaaS企業は50のビジネスメトリクスを追跡し、毎月手動で傾向分析とレポート作成が必要。1人のアナリストが3日間かけて作業を完了し、重要な異常シグナルを見逃すことが多い。
(2) ソリューション:LangChainエージェントで自動分析
LangChain + OllamaでAIエージェントを構築し、メトリクスデータを自動読み取り、異常を特定し、分析レポートを生成:
PYTHON
from langchain_ollama import ChatOllama
llm = ChatOllama(model="qwen2.5", temperature=0.2)
result = llm.invoke("Analyze the 20% drop in MRR last month")
3. LangChain + Ollama接続
💡 ヒント:
ChatOllamaのbase_urlパラメータはデフォルトでhttp://localhost:11434。Ollamaが別のホストやDockerコンテナにデプロイされている場合、base_url="http://ollama:11434"または該当アドレスを明示的に設定する必要がある。
(1) インストールと初期化
BASH
pip install langchain langchain-ollama langchain-community
(2) 2つのコアコンポーネント
| コンポーネント | クラス | 目的 |
|---|---|---|
| チャットモデル | ChatOllama | 対話生成 |
| 埋め込み | OllamaEmbeddings | テキストベクトル化 |
flowchart TD
A[LangChainアプリ] --> B[ChatOllama<br/>qwen2.5 / llama3.2]
A --> C[OllamaEmbeddings<br/>nomic-embed-text]
B --> D[Ollamaサーバー<br/>localhost:11434]
C --> D
| ChatOllamaパラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
| model | str | — | モデル名 |
| temperature | float | 0.8 | ランダム性 |
| base_url | str | http://localhost:11434 | サーバーアドレス |
| num_ctx | int | 2048 | コンテキストウィンドウ |
(3) ▶ サンプル:ChatOllama基本呼び出し
PYTHON
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, SystemMessage
# Initialize ChatOllama
llm = ChatOllama(model="qwen2.5", temperature=0.3)
# Simple invocation
response = llm.invoke("What is RAG?")
print(response.content)
# With system message
response = llm.invoke([
SystemMessage(content="You are a data analyst. Be concise."),
HumanMessage(content="What causes MRR to drop?")
])
print(response.content)
出力:
TEXT
# Execution successful
4. プロンプトテンプレート + チェーン
(1) チェーンプロンプトエンジニアリング
プロンプトテンプレートにより、プロンプトをパラメータ化、再利用、合成可能に:
flowchart LR
A[入力変数] --> B[PromptTemplate]
B --> C[ChatOllama]
C --> D[出力パーサー]
D --> E[構造化結果]
| チェーンタイプ | 説明 | ユースケース |
|---|---|---|
| LLMChain | プロンプト → LLM → 出力 | 単一段階推論 |
| SequentialChain | 複数チェーンの直列 | 多段階分析 |
| RouterChain | 入力に基づくルーティング | 分岐処理 |
(2) ▶ サンプル:プロンプトテンプレート + チェーン
PYTHON
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatOllama(model="qwen2.5", temperature=0.3)
# Define prompt template
prompt = ChatPromptTemplate.from_messages([
("system", "You are a {role}. Be concise and professional."),
("human", "{input}")
])
# Build chain: prompt -> llm -> parser
chain = prompt | llm | StrOutputParser()
# Invoke with variables
result = chain.invoke({
"role": "customer service agent for e-commerce",
"input": "Customer wants to cancel order #12345"
})
print(result)
出力:
TEXT
# Execution successful
5. 出力パーサーによる構造化出力
⚠️ 警告: 小規模モデル(3Bパラメータ未満)は不安定なJSON構造を生成し、
PydanticOutputParserのパース失敗を招く。8B以上のモデルでformat="json"を使用し、コードにtry/exceptリトライロジックを追加すること。
(1) 一般的な出力パーサー
| パーサー | 目的 | 出力形式 |
|---|---|---|
| StrOutputParser | プレーンテキスト | string |
| JsonOutputParser | JSONオブジェクト | dict |
| PydanticOutputParser | Pydanticモデル | 型付きオブジェクト |
| CommaSeparatedListOutputParser | カンマ区切りリスト | list[str] |
(2) Pydantic構造化出力の比較
| アプローチ | 利点 | 欠点 |
|---|---|---|
| format=json | シンプル | 型検証なし |
| JsonOutputParser | スキーマヒントあり | パース失敗の可能性 |
| PydanticOutputParser | 完全な型検証 | モデルがスキーマに従う必要がある |
(3) ▶ サンプル:Pydantic構造化出力
PYTHON
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
# Define output schema
class CustomerIntent(BaseModel):
intent: str = Field(description="Customer intent category")
confidence: float = Field(description="Confidence score 0-1")
order_number: str | None = Field(description="Order number if mentioned")
# Create parser
parser = PydanticOutputParser(pydantic_object=CustomerIntent)
# Build chain with format instructions
llm = ChatOllama(model="qwen2.5", temperature=0.1, format="json")
prompt = ChatPromptTemplate.from_messages([
("system", "Classify the customer intent.\n{format_instructions}"),
("human", "{query}")
])
chain = prompt | llm | parser
# Invoke
result = chain.invoke({
"query": "I want to track my order #88765",
"format_instructions": parser.get_format_instructions()
})
print(f"Intent: {result.intent}")
print(f"Confidence: {result.confidence}")
print(f"Order: {result.order_number}")
出力:
TEXT
# Execution successful
6. ツール呼び出しエージェント
⚠️ 注: エージェントは不安定な出力を生成する可能性——ローカルモデルの推論ループが「無限ループ」(同じツールの反復呼び出し)や「幻覚ツール呼び出し」(存在しないツールの呼び出し)を引き起こす場合がある。必ず
max_iterations=3-5を設定してループ数を制限し、temperature=0で高い決定性を確保。3Bモデルはエージェント能力が限定的;8B以上を推奨。
💡 ヒント: OllamaローカルモデルのFunction Calling能力はGPT-4ほど安定ではない。ネイティブFunction Callingの代わりにReActエージェント+
@toolデコレータパターンを推奨——互換性が良く、temperature=0でツール呼び出しの決定性を向上できる。
(1) エージェントアーキテクチャ
flowchart TD
A[ユーザー入力] --> B[エージェント]
B --> C{ツール必要?}
C -->|はい| D[ツール呼び出し]
D --> E[ツール結果]
E --> B
C -->|いいえ| F[最終回答]
| エージェントタイプ | 説明 | ユースケース |
|---|---|---|
| ReActエージェント | 推論+アクションループ | ツール呼び出しを必要とする複雑タスク |
| Structured Chat | 構造化ツール呼び出し | マルチツール、マルチパラメータ |
| OpenAI Functions | Function Callingモード | Ollamaは部分対応 |
(2) ツール定義パターン
| 定義方法 | 説明 | 例 |
|---|---|---|
| @toolデコレータ | シンプルなツール | @tool def func(...) |
| StructuredTool | 説明付きツール | StructuredTool.from_function(...) |
| Pydantic + BaseTool | 強型付きツール | class MyTool(BaseTool) |
(3) ▶ サンプル:ReActエージェントの実践
PYTHON
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.prompts import PromptTemplate
# Define tools
@tool
def get_order_status(order_id: str) -> str:
"""Get the status of an order by its ID."""
# Simulated database lookup
orders = {
"12345": "Shipped, arriving in 2 days",
"88765": "Processing, shipping tomorrow",
"99999": "Delivered on 2024-01-15"
}
return orders.get(order_id, f"Order {order_id} not found")
@tool
def check_product_stock(product_name: str) -> str:
"""Check if a product is in stock."""
stock = {
"wireless headphones": "In stock: 45 units",
"laptop stand": "Out of stock, restock in 3 days",
"usb cable": "In stock: 200 units"
}
return stock.get(product_name.lower(), f"Product '{product_name}' not found")
# Build agent
llm = ChatOllama(model="qwen2.5", temperature=0)
tools = [get_order_status, check_product_stock]
prompt = PromptTemplate.from_template(
"""Answer the following question. You have access to these tools:
{tools}
Use this format:
Question: the input question
Thought: what to do
Action: tool name (one of [{tool_names}])
Action Input: tool input
Observation: tool output
... (repeat Thought/Action/Action Input/Observation)
Thought: I know the final answer
Final Answer: the final answer
Question: {input}
{agent_scratchpad}"""
)
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Run agent
result = agent_executor.invoke({
"input": "What is the status of order 88765?"
})
print(result["output"])
出力:
TEXT
# Function defined successfully
7. 総合サンプル:AliceのSaaSメトリクス分析エージェント
ℹ️ 情報: LangChainの
AgentExecutorはデフォルトでmax_iterations=15だが、ローカルモデルの推論はより遅い。3〜5に設定して無限ループを防ぐ。またverbose=Trueを設定してエージェントの推論過程を観察。
PYTHON
# ============================================
# Comprehensive: SaaS metrics analysis agent
# Uses LangChain + Ollama for automated analysis
# ============================================
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.prompts import PromptTemplate
import json
# Simulated metrics database
METRICS_DB = {
"mrr": {"current": 85000, "previous": 106250, "change": -20},
"churn_rate": {"current": 8.5, "previous": 5.2, "change": 63},
"arr": {"current": 1020000, "previous": 1275000, "change": -20},
"active_users": {"current": 12500, "previous": 11800, "change": 6},
"cac": {"current": 250, "previous": 180, "change": 39},
"ltv": {"current": 2400, "previous": 3200, "change": -25}
}
@tool
def get_metric(metric_name: str) -> str:
"""Get a SaaS metric value. Available: mrr, churn_rate, arr, active_users, cac, ltv."""
data = METRICS_DB.get(metric_name.lower())
if data:
return json.dumps(data)
return f"Metric '{metric_name}' not found. Available: {list(METRICS_DB.keys())}"
@tool
def compare_metrics(metric1: str, metric2: str) -> str:
"""Compare two metrics and identify correlations."""
m1 = METRICS_DB.get(metric1.lower())
m2 = METRICS_DB.get(metric2.lower())
if not m1 or not m2:
return "One or both metrics not found."
return json.dumps({
"metric1": {"name": metric1, **m1},
"metric2": {"name": metric2, **m2},
"correlation_note": "Both metrics show negative trends"
})
# Build analysis agent
llm = ChatOllama(model="qwen2.5", temperature=0.2)
tools = [get_metric, compare_metrics]
prompt = PromptTemplate.from_template(
"""You are a SaaS metrics analyst. Analyze data and provide insights.
Available tools: {tools}
Format:
Question: the input
Thought: what to do
Action: tool name from [{tool_names}]
Action Input: tool input
Observation: tool output
... (repeat as needed)
Thought: I have enough information
Final Answer: detailed analysis with actionable recommendations
Question: {input}
{agent_scratchpad}"""
)
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=5)
# Run analysis
if __name__ == "__main__":
result = executor.invoke({
"input": "MRR dropped 20% last month. Analyze the key metrics and identify likely causes with recommendations."
})
print("\n=== Analysis Report ===")
print(result["output"])
❓ よくある質問
Q langchain-ollamaとlangchain-communityの違いは?
A
langchain-ollamaは公式のOllama統合パッケージで、ChatOllamaとOllamaEmbeddingsを含む。langchain-communityにもレガシー統合が含まれるが、公式パッケージの使用を推奨。Q エージェントのツール呼び出しが不安定な場合は?
A temperatureを0に下げる、ツールの説明を明確にする、format=jsonで構造化出力を強制。小規模モデル(3B)のエージェント能力は限定的;8B以上を推奨。
Q LangChainのチェーンとエージェントはどう選ぶ?
A 固定ワークフロー(プロンプト→LLM→パーサー)にはチェーン、動的意志決定(自律的にツールとステップを選択)にはエージェントを使用。90%のシナリオではチェーンで十分。
Q PydanticOutputParserのパースが失敗する場合は?
A
format="json"を追加してJSON出力を強制。プロンプトに明示的なフォーマット指示を追加。失敗時はJsonOutputParserにフォールバック。Q OllamaモデルはFunction Callingに対応している?
A 部分的に。qwen2.5とllama3.1にFunction Calling能力があるが、GPT-4ほど安定ではない。ReActエージェント+@toolデコレータを代替として推奨。
Q エージェントの推論過程をデバッグするには?
A
verbose=Trueを設定してエージェントのThought/Action/Observationループを確認。LangSmith(tracelangchain.com)で実行チェーン全体を可視化可能。📖 まとめ
- ChatOllamaはLangChainとOllamaを接続するコアクラス、対話とストリーミング出力に対応
- プロンプトテンプレートでプロンプトをパラメータ化、チェーンでワークフローを合成
- 出力パーサーがLLMテキストを構造化データ(JSON/Pydantic/List)に変換
- ReActエージェントは推論+アクションループでカスタムツールを呼び出し可能
- AliceはLangChainエージェントでSaaSメトリクス分析を自動化、3日間の作業を3分に短縮
- エージェントには8B以上のモデルが必要;3Bモデルは十分な推論能力を持たない
📝 練習問題
- 基本(難易度 ⭐):ChatOllama + PromptTemplateでチェーンを構築し、カスタマーサービスのインテント分類(JSON形式出力)を実装する。
- 中級(難易度 ⭐⭐):2つのツール(注文照会+在庫確認)を持つReActエージェントを作成し、3つの異なる質問でツール呼び出しをテストする。
- 上級(難易度 ⭐⭐⭐):Alice向けのSaaSメトリクス分析エージェントを構築——少なくとも4つのツール(メトリクス取得、メトリクス比較、チャート記述生成、アラート送信)を含み、構造化分析レポートを出力する。