「LangChainは覚えることが多すぎる。もっとシンプルにローカルLLMをPythonで使いたい」
そんな悩みを抱えるLinuxエンジニアは多いはずです。Ollamaには公式のPythonライブラリ(`ollama`パッケージ)が用意されており、`pip install ollama` 一行でインストールできます。この記事では、基本的なチャット呼び出しからストリーミング応答・非同期クライアント・ツール使用(Function Calling)・マルチターン対話まで、公式ライブラリを使った実装手順を解説します。
curlやrequestsを使った低レベルなHTTP操作や、LangChainなどのフレームワーク不要で、数行のコードからローカルLLMを呼び出せるようになることが目標です。
この記事のポイント
・pip install ollama でフレームワーク不要のPythonクライアントを導入できる
・ollama.chat(stream=True) でストリーミング応答をジェネレータとして逐次処理できる
・AsyncClient と asyncio.gather() を組み合わせて複数リクエストを並列処理できる
・tools パラメータで Function Calling を OpenAI 互換フォーマットで実装できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜOllama公式Pythonライブラリを選ぶのか
OllamaにはREST APIが備わっているため、curlやPythonの `requests` ライブラリで直接叩くことも可能です。しかし、ストリーミング応答の逐次処理・エラーハンドリング・レスポンスのJSON解析を自前で実装すると、コード量が増えてメンテナンスコストが上がります。公式Pythonライブラリ(`ollama`パッケージ)は、これらの煩雑な処理をすべて内部で吸収します。同期・非同期・ストリーミングの切り替えもAPIパラメータ一つで対応できます。LangChainのような大規模フレームワークと比べてインストールが軽量で、依存パッケージが `httpx` のみのため、スクリプトや社内マイクロサービスへの組み込みに向いています。
Ollamaサーバー自体の構築方法については「Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド」を参照してください。本記事はOllamaサービスが `http://localhost:11434` で稼働している前提で進めます。
前提環境とインストール手順
1. 前提条件を確認する
本記事の動作確認環境は Ubuntu 24.04 LTS・Python 3.11・Ollama 0.3系です。Ollamaサービスが起動していることを `ollama list` と `curl` で確認してください。$ ollama list NAME ID SIZE MODIFIED llama3.3:70b-instruct-q4_0 abc1234def56 43 GB 2 hours ago $ curl -s http://localhost:11434/api/tags | python3 -m json.tool | grep '"name"' "name": "llama3.3:70b-instruct-q4_0",
2. 仮想環境を作成してインストールする
システムのPython環境を汚染しないよう、プロジェクト専用のvenvを作成してからインストールします。$ python3 -m venv ~/ollama-py-env $ source ~/ollama-py-env/bin/activate (ollama-py-env) $ pip install ollama Collecting ollama Downloading ollama-0.3.0-py3-none-any.whl (11 kB) Installing collected packages: ollama Successfully installed ollama-0.3.0 (ollama-py-env) $ python3 -c "import ollama; print(ollama.__version__)" 0.3.0
基本的なチャット呼び出しを実装する
1. 同期チャットの最小構成
`ollama.chat()` にモデル名とメッセージリストを渡すだけで応答が返ります。最小構成は以下のとおりです。import ollama # basic synchronous chat call response = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=[ {'role': 'user', 'content': 'Linuxのinodeとは何ですか?50字以内で答えてください'} ] ) print(response['message']['content'])
2. レスポンスオブジェクトの構造を確認する
`response` は辞書型で、主なキーは `message`・`model`・`created_at`・`done`・`total_duration` です。トークン消費量は `prompt_eval_count`・`eval_count` で取得できます。import ollama response = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=[{'role': 'user', 'content': 'Hello'}] ) print(f"回答: {response['message']['content']}") print(f"入力トークン: {response['prompt_eval_count']}") print(f"出力トークン: {response['eval_count']}") print(f"処理時間: {response['total_duration'] / 1_000_000:.0f}ms")
3. システムプロンプトを設定する
ロールに `system` を指定することで、モデルの振る舞いを制御できます。業務用途では専門領域に特化した指示を与えると回答精度が向上します。response = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=[ { 'role': 'system', 'content': 'あなたはLinuxサーバー管理の専門家です。技術的な質問に簡潔に答えてください。' }, { 'role': 'user', 'content': 'systemctlとserviceコマンドの違いを教えてください' } ] ) print(response['message']['content'])
ストリーミング応答で出力をリアルタイムに受け取る
1. stream=Trueでジェネレータを取得する
長文を生成する場合、全文が生成されるまで待つのではなく逐次的に受け取る方が体験が良くなります。`stream=True` を指定するとジェネレータが返り、各チャンクをforループで処理できます。import ollama stream = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=[ {'role': 'user', 'content': 'Linuxのファイルパーミッションについて詳しく説明してください'} ], stream=True ) for chunk in stream: print(chunk['message']['content'], end='', flush=True) print() # 最後に改行
2. ストリーミングの完了と処理時間を計測する
各チャンクに含まれる `done` フラグが `True` になったとき、生成が完了しています。完了時のトークン数と処理時間を出力する例を示します。import ollama import time start = time.time() stream = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=[{'role': 'user', 'content': 'bashのfor文の書き方をコード例付きで説明してください'}], stream=True ) for chunk in stream: print(chunk['message']['content'], end='', flush=True) if chunk['done']: elapsed = time.time() - start print(f"\n\n--- 処理時間: {elapsed:.1f}秒 / 出力トークン: {chunk.get('eval_count', 'N/A')} ---")
非同期クライアントで並列リクエストを処理する
1. AsyncClientの基本構成
複数のリクエストを同時に処理したい場合は `AsyncClient` を使います。`asyncio.gather()` と組み合わせることで、複数の質問を並列実行できます。import asyncio from ollama import AsyncClient async def ask(client: AsyncClient, question: str) -> str: response = await client.chat( model='llama3.3:70b-instruct-q4_0', messages=[{'role': 'user', 'content': question}] ) return response['message']['content'] async def main(): client = AsyncClient() questions = [ 'cronの書き方を教えてください', 'rsyncの主なオプションを教えてください', 'iptablesでポートを開ける方法を教えてください', ] results = await asyncio.gather(*[ask(client, q) for q in questions]) for q, a in zip(questions, results): print(f"Q: {q}\nA: {a[:100]}...\n") asyncio.run(main())
2. 非同期ストリーミングを実装する
非同期クライアントでもストリーミングが使えます。`async for` でチャンクを受け取ります。import asyncio from ollama import AsyncClient async def stream_response(prompt: str): client = AsyncClient() async_stream = await client.chat( model='llama3.3:70b-instruct-q4_0', messages=[{'role': 'user', 'content': prompt}], stream=True ) async for chunk in async_stream: print(chunk['message']['content'], end='', flush=True) print() asyncio.run(stream_response('DockerとPodmanの違いを教えてください'))
ツール使用(Function Calling)を実装する
1. ツール定義を用意する
Function Callingを使うと、モデルがPython関数を呼び出すタイミングを自律的に判断できます。ツール定義はOpenAI互換フォーマットで記述します。以下はLinuxコマンドを実行するツールの例です。import ollama import subprocess tools = [ { 'type': 'function', 'function': { 'name': 'run_readonly_command', 'description': '読み取り専用のLinuxコマンドを実行して結果を返す', 'parameters': { 'type': 'object', 'properties': { 'command': { 'type': 'string', 'description': '実行するコマンド(例: df -h, free -m, uptime)' } }, 'required': ['command'] } } } ] SAFE_COMMANDS = ('df ', 'free ', 'uptime', 'uname ', 'date', 'hostname') def run_readonly_command(command: str) -> str: if not any(command.startswith(p) for p in SAFE_COMMANDS): return "エラー: 許可されていないコマンドです" result = subprocess.run( command.split(), capture_output=True, text=True, timeout=5 ) return result.stdout or result.stderr
2. ツール呼び出しループを実装する
モデルがツール使用を要求した場合、実際に関数を実行して結果をメッセージリストに追記し、再度チャットを呼び出します。messages = [ {'role': 'user', 'content': 'このサーバーのディスク使用状況とメモリ使用状況を教えてください'} ] response = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=messages, tools=tools ) while response['message'].get('tool_calls'): messages.append(response['message']) for tool_call in response['message']['tool_calls']: func_name = tool_call['function']['name'] func_args = tool_call['function']['arguments'] if func_name == 'run_readonly_command': result = run_readonly_command(func_args['command']) messages.append({'role': 'tool', 'content': result}) response = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=messages, tools=tools ) print(response['message']['content'])
会話履歴を保持するマルチターン対話を実装する
1. メッセージリストを蓄積する
マルチターン対話は、メッセージリストにアシスタントの応答を追記しながら `ollama.chat()` を繰り返し呼び出すことで実現できます。import ollama conversation = [ {'role': 'system', 'content': 'あなたはLinuxの技術サポートAIです。'} ] def chat(user_input: str) -> str: conversation.append({'role': 'user', 'content': user_input}) response = ollama.chat( model='llama3.3:70b-instruct-q4_0', messages=conversation ) answer = response['message']['content'] conversation.append({'role': 'assistant', 'content': answer}) return answer print(chat('sshdの設定ファイルはどこにありますか?')) print(chat('そのファイルでポート番号を変えるにはどうすればいいですか?')) print(chat('変更後にどんなコマンドで反映させますか?'))
2. 会話履歴のトークン数を管理する
長い会話はコンテキストウィンドウ(`num_ctx`)の上限に達する可能性があります。古いメッセージを切り捨てる簡易的なトリミング処理を用意しておくと安全です。def trim_history(messages: list, max_pairs: int = 10) -> list: """systemプロンプトを保持しつつ最新N往復のみ残す""" system = [m for m in messages if m['role'] == 'system'] non_system = [m for m in messages if m['role'] != 'system'] return system + non_system[-(max_pairs * 2):] # 50往復を超えたらトリミング if len(conversation) > 105: conversation[:] = trim_history(conversation, max_pairs=10) print("会話履歴を最新10往復に圧縮しました")
よくあるエラーと対処法
公式ライブラリを使ってもいくつかのエラーに遭遇することがあります。代表的なケースと対処法をまとめます。ConnectionError: Ollamaサーバーに接続できない
`httpx.ConnectError` が発生する場合、Ollamaサービスが停止しているか、ポートが異なる可能性があります。`systemctl status ollama` でサービス状態を確認してください。リモートサーバーに接続する場合は `Client(host='http://192.168.1.10:11434')` のように `host` を指定します。
ResponseError: model not found
モデル名のタイポや量子化タグの誤りが原因です。`ollama list` で正確なモデル名を確認し、`llama3.3:70b-instruct-q4_0` のようにサフィックス形式で指定してください。`:q4_0` 単独表記(`llama3.3:q4_0` 形式)は実在しません。
TimeoutError: レスポンスが返らない
大規模モデルのロード時間が原因のことがあります。`Client(timeout=300)` のようにタイムアウトを延ばしてください。デフォルトは `httpx` のデフォルト値(約5秒)です。
ストリーミングが途中で止まる
コンテキストウィンドウが満杯になると生成が停止します。`options` パラメータで `num_ctx` を明示的に指定して対処します。
from ollama import Client # タイムアウトとコンテキストウィンドウを明示指定する例 client = Client(host='http://localhost:11434', timeout=300) response = client.chat( model='llama3.3:70b-instruct-q4_0', messages=[{'role': 'user', 'content': '長い文書の要約をお願いします...'}], options={'num_ctx': 8192, 'temperature': 0.1} ) print(response['message']['content'])
まとめ
Ollama公式Pythonライブラリを使えば、フレームワーク不要でローカルLLMをPythonから簡潔に呼び出せます。同期・非同期・ストリーミング・Function Callingのすべてが `import ollama` 一行でアクセスできる状態になり、スクリプト・社内ツール・APIサーバーへの統合がシンプルに実現できます。| 機能 | 主なメソッド・クラス | ポイント |
|---|---|---|
| 同期チャット | ollama.chat(model=..., messages=[...]) | 最小構成・辞書型レスポンス |
| ストリーミング | ollama.chat(..., stream=True) | forループでチャンク逐次処理 |
| 非同期チャット | AsyncClient().chat(...) | asyncio.gather()で並列化 |
| 非同期ストリーミング | await client.chat(..., stream=True) | async forで逐次処理 |
| ツール使用 | ollama.chat(..., tools=[...]) | OpenAI互換フォーマット |
| マルチターン対話 | messagesリスト蓄積 | trim_history()でトークン管理 |
| 接続カスタマイズ | Client(host=..., timeout=...) | リモート接続・タイムアウト設定 |
ローカルLLMのPython連携を2日間体験する
公式ライブラリの使い方を手を動かして習得したい方、ストリーミングや非同期処理をチームのプロジェクトに組み込みたい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。
・Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
・社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
・ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:OllamaのAPIをnginxで保護する方法|Basic認証・HTTPS・レートリミットでチームサーバーのセキュリティを強化する手順
- この記事の属するカテゴリ:ローカルLLMへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます