PythonでのOllama Web Search APIの利用

PythonとOllamaでAI検索エージェントを構築する

目次

OllamaのPythonライブラリには、今やネイティブのOllama ウェブ検索機能が搭載されました。わずか数行のコードで、ウェブからのリアルタイム情報をローカルのLLMに追加することで、ハルシネーション(幻覚)を減らし、精度を向上させることができます。

デジタル空間

はじめに

OllamaのPythonライブラリをウェブ検索用にインストールするには? pip install 'ollama>=0.6.0' を使用して、バージョン0.6.0以降をインストールしてください。このバージョンには、web_search と web_fetch 関数が含まれています。

pip install 'ollama>=0.6.0'

Python環境とパッケージの管理には、高速なPythonパッケージマネージャーである uv を使用したり、venv を使用して仮想環境を設定したりすることで、依存関係を分離保つかどうかを検討してください。

Ollama アカウント からAPIキーを作成し、環境変数として設定します:

export OLLAMA_API_KEY="your_api_key"

Windows PowerShellの場合:

$env:OLLAMA_API_KEY = "your_api_key"

基本的なウェブ検索

Ollamaでウェブを検索する最も簡単な方法は以下の通りです:

import ollama

# 簡単なウェブ検索
response = ollama.web_search("What is Ollama?")
print(response)

出力例:

results = [
    {
        "title": "Ollama",
        "url": "https://ollama.com/",
        "content": "Cloud models are now available in Ollama..."
    },
    {
        "title": "What is Ollama? Features, Pricing, and Use Cases",
        "url": "https://www.walturn.com/insights/what-is-ollama",
        "content": "Our services..."
    },
    {
        "title": "Complete Ollama Guide: Installation, Usage & Code Examples",
        "url": "https://collabnix.com/complete-ollama-guide",
        "content": "Join our Discord Server..."
    }
]

結果数の制御

import ollama

# より多くの結果を取得
response = ollama.web_search("latest AI news", max_results=10)

for result in response.results:
    print(f"📌 {result.title}")
    print(f"   {result.url}")
    print(f"   {result.content[:100]}...")
    print()

フルページコンテンツの取得

Ollama Pythonにおける web_search と web_fetch の違いは何ですか? web_search はインターネットをクエリし、タイトル、URL、スニペット付きの複数の検索結果を返します。一方、web_fetch は特定のURLのフルコンテンツを取得し、ページタイトル、マークダウンコンテンツ、リンクを返します。web_fetch が返すマークダウンコンテンツは、さらなる処理に最適です。他のコンテキストでHTMLをマークダウンに変換する必要がある場合は、PythonでのHTMLからMarkdownへのコンバージョン に関する私たちのガイドをご覧ください。

from ollama import web_fetch

result = web_fetch('https://ollama.com')
print(result)

出力例:

WebFetchResponse(
    title='Ollama',
    content='[Cloud models](https://ollama.com/blog/cloud-models) are now available in Ollama\n\n**Chat & build with open models**\n\n[Download](https://ollama.com/download) [Explore models](https://ollama.com/models)\n\nAvailable for macOS, Windows, and Linux',
    links=['https://ollama.com/', 'https://ollama.com/models', 'https://github.com/ollama/ollama']
)

検索とフェッチの組み合わせ

一般的なパターンは、まず検索を行い、関連する結果からフルコンテンツを取得することです:

from ollama import web_search, web_fetch

# 情報の検索
search_results = web_search("Ollama new features 2025")

# 最初の結果からフルコンテンツを取得
if search_results.results:
    first_url = search_results.results[0].url
    full_content = web_fetch(first_url)
    
    print(f"Title: {full_content.title}")
    print(f"Content: {full_content.content[:500]}...")
    print(f"Links found: {len(full_content.links)}")

検索エージェントの構築

Ollama検索エージェントにはどのPythonモデルが最適ですか? ツール使用能力が優れたモデルが最も適しており、qwen3、gpt-oss、および qwen3:480b-cloud や deepseek-v3.1-cloud のようなクラウドモデルが含まれます。これらのモデルからの構造化出力を必要とするより高度なユースケースについては、OllamaとQwen3を使用した構造化出力を備えたLLM に関する私たちのガイドをご覧ください。一度ツールやJSONモードから信頼できるペイロードが返された場合、アプリケーション側でのパース、Pydanticチェック、リトライ、テストを含む、PythonでのLLM構造化出力バリデーション(堅牢な方法) をご覧ください。

まず、有能なモデルをプルしてください:

ollama pull qwen3:4b

シンプルな検索エージェント

以下は、検索を行うタイミングを自律的に判断できる基本的な検索エージェントです:

from ollama import chat, web_fetch, web_search

available_tools = {'web_search': web_search, 'web_fetch': web_fetch}

messages = [{'role': 'user', 'content': "what is ollama's new engine"}]

while True:
    response = chat(
        model='qwen3:4b',
        messages=messages,
        tools=[web_search, web_fetch],
        think=True
    )
    
    if response.message.thinking:
        print('🧠 Thinking:', response.message.thinking[:200], '...')
    
    if response.message.content:
        print('💬 Response:', response.message.content)
    
    messages.append(response.message)
    
    if response.message.tool_calls:
        print('🔧 Tool calls:', response.message.tool_calls)
        for tool_call in response.message.tool_calls:
            function_to_call = available_tools.get(tool_call.function.name)
            if function_to_call:
                args = tool_call.function.arguments
                result = function_to_call(**args)
                print('📥 Result:', str(result)[:200], '...')
                # 限られたコンテキスト長に合わせて結果を切り詰める
                messages.append({
                    'role': 'tool', 
                    'content': str(result)[:2000 * 4], 
                    'tool_name': tool_call.function.name
                })
            else:
                messages.append({
                    'role': 'tool', 
                    'content': f'Tool {tool_call.function.name} not found', 
                    'tool_name': tool_call.function.name
                })
    else:
        break

Pythonで大きなウェブ検索結果を扱うには? コンテキストの制限に収まるよう結果を切り詰めてください。推奨されるアプローチは、モデルに渡す前に結果文字列を約8000文字(2000トークン × 4文字)にスライスすることです。

エラー処理付きの高度な検索エージェント

以下は、エラー処理を強化した拡張版です:

from ollama import chat, web_fetch, web_search
import json

class SearchAgent:
    def __init__(self, model: str = 'qwen3:4b'):
        self.model = model
        self.tools = {'web_search': web_search, 'web_fetch': web_fetch}
        self.messages = []
        self.max_iterations = 10
        
    def query(self, question: str) -> str:
        self.messages = [{'role': 'user', 'content': question}]
        
        for iteration in range(self.max_iterations):
            try:
                response = chat(
                    model=self.model,
                    messages=self.messages,
                    tools=[web_search, web_fetch],
                    think=True
                )
            except Exception as e:
                return f"Error during chat: {e}"
            
            self.messages.append(response.message)
            
            # ツール呼び出しがない場合、最終回答がある
            if not response.message.tool_calls:
                return response.message.content or "No response generated"
            
            # ツール呼び出しの実行
            for tool_call in response.message.tool_calls:
                result = self._execute_tool(tool_call)
                self.messages.append({
                    'role': 'tool',
                    'content': result,
                    'tool_name': tool_call.function.name
                })
        
        return "Max iterations reached without final answer"
    
    def _execute_tool(self, tool_call) -> str:
        func_name = tool_call.function.name
        args = tool_call.function.arguments
        
        if func_name not in self.tools:
            return f"Unknown tool: {func_name}"
        
        try:
            result = self.tools[func_name](**args)
            # コンテキスト制限のため切り詰め
            result_str = str(result)
            if len(result_str) > 8000:
                result_str = result_str[:8000] + "... [truncated]"
            return result_str
        except Exception as e:
            return f"Tool error: {e}"

# 使用例
agent = SearchAgent(model='qwen3:4b')
answer = agent.query("What are the latest features in Ollama?")
print(answer)

非同期ウェブ検索

OllamaのPythonウェブ検索を非同期コードで使うことができますか? はい、Ollama Pythonライブラリは非同期操作をサポートしています。非同期アプリケーションでのブロッキングしないウェブ検索およびフェッチ操作には AsyncClient を使用してください。

import asyncio
from ollama import AsyncClient

async def async_search():
    client = AsyncClient()
    
    # 複数の検索を並行して実行
    tasks = [
        client.web_search("Ollama features"),
        client.web_search("local LLM tools"),
        client.web_search("AI search agents"),
    ]
    
    results = await asyncio.gather(*tasks)
    
    for i, result in enumerate(results):
        print(f"Search {i + 1}:")
        for r in result.results[:2]:
            print(f"  - {r.title}")
        print()

# 非同期検索の実行
asyncio.run(async_search())

非同期検索エージェント

import asyncio
from ollama import AsyncClient

async def async_research_agent(question: str):
    client = AsyncClient()
    messages = [{'role': 'user', 'content': question}]
    
    while True:
        response = await client.chat(
            model='qwen3:4b',
            messages=messages,
            tools=[client.web_search, client.web_fetch],
        )
        
        messages.append(response.message)
        
        if not response.message.tool_calls:
            return response.message.content
        
        # ツール呼び出しを並行して実行
        tool_tasks = []
        for tool_call in response.message.tool_calls:
            if tool_call.function.name == 'web_search':
                task = client.web_search(**tool_call.function.arguments)
            elif tool_call.function.name == 'web_fetch':
                task = client.web_fetch(**tool_call.function.arguments)
            else:
                continue
            tool_tasks.append((tool_call.function.name, task))
        
        # 結果の収集
        for tool_name, task in tool_tasks:
            result = await task
            messages.append({
                'role': 'tool',
                'content': str(result)[:8000],
                'tool_name': tool_name
            })

# 実行
answer = asyncio.run(async_research_agent("What's new in Python 3.13?"))
print(answer)

コンテキスト長とパフォーマンス

Python検索エージェントに設定すべきコンテキスト長は何ですか? 適切なパフォーマンスを得るために、コンテキスト長を約32000トークンに設定してください。web_search と web_fetch が数千トークンを返す可能性があるため、検索エージェントはフルコンテキスト長で最もよく動作します。

from ollama import chat, web_search

# 検索中心のタスクにはより高いコンテキストを設定
response = chat(
    model='qwen3:4b',
    messages=[{'role': 'user', 'content': 'Research the latest AI developments'}],
    tools=[web_search],
    options={
        'num_ctx': 32768,  # 32K コンテキスト
    }
)

MCPサーバー統合

Ollamaは、任意のMCPクライアントでウェブ検索を可能にするPython製MCPサーバーを提供しています。ウェブ検索およびスクレイピング機能を備えたPythonでのMCPサーバー構築に関する包括的なガイドについては、PythonでのMCPサーバー構築 に関する詳細なチュートリアルをご覧ください。

Cline 統合

Clineの設定でMCPサーバーを構成します:

MCPサーバーの管理 → MCPサーバーの構成 → 追加:

{
  "mcpServers": {
    "web_search_and_fetch": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "path/to/web-search-mcp.py"],
      "env": { "OLLAMA_API_KEY": "your_api_key_here" }
    }
  }
}

Codex 統合

~/.codex/config.toml に追加します:

[mcp_servers.web_search]
command = "uv"
args = ["run", "path/to/web-search-mcp.py"]
env = { "OLLAMA_API_KEY" = "your_api_key_here" }

独自のMCPサーバーの作成

#!/usr/bin/env python3
"""Ollamaウェブ検索のためのシンプルなMCPサーバー。"""

import os
from mcp.server import Server
from mcp.types import Tool, TextContent
from ollama import web_search, web_fetch

app = Server("ollama-web-search")

@app.tool()
async def search_web(query: str, max_results: int = 5) -> str:
    """ウェブで情報を検索します。"""
    results = web_search(query, max_results=max_results)
    
    output = []
    for r in results.results:
        output.append(f"**{r.title}**\n{r.url}\n{r.content}\n")
    
    return "\n---\n".join(output)

@app.tool()
async def fetch_page(url: str) -> str:
    """ウェブページのフルコンテンツを取得します。"""
    result = web_fetch(url)
    return f"# {result.title}\n\n{result.content}"

if __name__ == "__main__":
    app.run()

実用的な例

これらの例は、Ollamaのウェブ検索APIの実際の応用を示しています。これらのパターンを拡張して、より複雑なシステムを構築できます。例えば、検索結果を組み合わせることで、調査レポートを作成するために PythonでのPDF生成 と連携することが可能です。

ニュースサマライザー

from ollama import chat, web_search

def summarize_news(topic: str) -> str:
    # 最近のニュースを検索
    results = web_search(f"{topic} latest news", max_results=5)
    
    # モデル向けに検索結果を整形
    news_content = "\n\n".join([
        f"**{r.title}**\n{r.content}"
        for r in results.results
    ])
    
    # モデルに要約を依頼
    response = chat(
        model='qwen3:4b',
        messages=[{
            'role': 'user',
            'content': f"Summarize these news items about {topic}:\n\n{news_content}"
        }]
    )
    
    return response.message.content

summary = summarize_news("artificial intelligence")
print(summary)

研究アシスタント

from ollama import chat, web_search, web_fetch
from dataclasses import dataclass

@dataclass
class ResearchResult:
    question: str
    sources: list
    answer: str

def research(question: str) -> ResearchResult:
    # 関連情報の検索
    search_results = web_search(question, max_results=3)
    
    # 上位ソースからフルコンテンツを取得
    sources = []
    full_content = []
    
    for result in search_results.results[:3]:
        try:
            page = web_fetch(result.url)
            sources.append(result.url)
            full_content.append(f"Source: {result.url}\n{page.content[:2000]}")
        except:
            continue
    
    # 包括的な回答を生成
    context = "\n\n---\n\n".join(full_content)
    
    response = chat(
        model='qwen3:4b',
        messages=[{
            'role': 'user',
            'content': f"""Based on the following sources, answer this question: {question}

Sources:
{context}

Provide a comprehensive answer with citations to the sources."""
        }]
    )
    
    return ResearchResult(
        question=question,
        sources=sources,
        answer=response.message.content
    )

# 使用例
result = research("How does Ollama's new model scheduling work?")
print(f"Question: {result.question}")
print(f"Sources: {result.sources}")
print(f"Answer: {result.answer}")

推奨モデル

モデル パラメータ数 最適な用途
qwen3:4b 4B クイックなローカル検索
qwen3 8B 汎用エージェント
gpt-oss 各種 研究タスク
qwen3:480b-cloud 480B 複雑な推論(クラウド)
gpt-oss:120b-cloud 120B 長文の研究(クラウド)
deepseek-v3.1-cloud - 高度な分析(クラウド)

ベストプラクティス

  1. 結果の切り詰め: コンテキスト制限に収まるよう、常にウェブ結果を切り詰めましょう(約8000文字)
  2. エラー処理: ネットワーク障害に備えて、ツール呼び出しを try/except でラップします
  3. レート制限: Ollamaのウェブ検索APIレート制限を遵守してください
  4. コンテキスト長: 検索エージェントには約32000トークンを使用してください
  5. スケーリングには非同期: 並行操作には AsyncClient を使用してください
  6. テスト: 信頼性を確保するために、検索エージェント用のユニットテスト を作成してください
  7. Python基礎: 構文や共通のパターンに関するクイックリファレンスとして、Pythonチートシート を手元に置いておくと便利です

有用的リンク

購読する

システム、インフラ、AIエンジニアリングの新記事をお届けします。