Ollama: LangChain統合

LangChainはAIアプリケーションの指揮者——モデルは役者、チェーンは台本、エージェントは監督。

💡 ヒント: LangChainの中核はエージェント/チェーン/ツールの3層アーキテクチャ——チェーンは固定パイプライン(プロンプト→LLM→パーサー)、ツールは呼び出し可能な外部機能(データベース検索、文書検索)、エージェントはどのツールを呼び出すかを自律的に決定する意思決定者。90%のシナリオはチェーンで十分;動的な意思決定が必要な場合のみエージェントを使用。

📋 前提条件: まず以下を習得していること

1. 学べること


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接続

💡 ヒント: ChatOllamabase_urlパラメータはデフォルトでhttp://localhost:11434。Ollamaが別のホストやDockerコンテナにデプロイされている場合、base_url="http://ollama:11434"または該当アドレスを明示的に設定する必要がある。

(1) インストールと初期化

BASH
pip install langchain langchain-ollama langchain-community

(2) 2つのコアコンポーネント

コンポーネント クラス 目的
チャットモデル ChatOllama 対話生成
埋め込み OllamaEmbeddings テキストベクトル化
100%
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) チェーンプロンプトエンジニアリング

プロンプトテンプレートにより、プロンプトをパラメータ化、再利用、合成可能に:

100%
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) エージェントアーキテクチャ

100%
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)で実行チェーン全体を可視化可能。

📖 まとめ


📝 練習問題

  1. 基本(難易度 ⭐):ChatOllama + PromptTemplateでチェーンを構築し、カスタマーサービスのインテント分類(JSON形式出力)を実装する。
  2. 中級(難易度 ⭐⭐):2つのツール(注文照会+在庫確認)を持つReActエージェントを作成し、3つの異なる質問でツール呼び出しをテストする。
  3. 上級(難易度 ⭐⭐⭐):Alice向けのSaaSメトリクス分析エージェントを構築——少なくとも4つのツール(メトリクス取得、メトリクス比較、チャート記述生成、アラート送信)を含み、構造化分析レポートを出力する。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%