Ollama+LangChainを触ってみた

# 背景

今更ながらローカルLLMに興味が出てきたので触ってみようと思い、始めてみました。
モチベーションについてもう少し具体的に話しておくと、LLMを使ったアプリケーションを自分で開発しようというときにトークン数とコストが頭によぎるのが嫌だなという思いがあったので、使いたい放題できる環境が欲しかったということと、色んなモデルをお手軽に試せるのならお得じゃないか、という低俗な理由です。
ということで、今回はOllamaでローカルLLMを動かし、これを[LangChain](https://www.langchain.com/)から使ってみようと思います。
LangChainを選んだのは、LangChainがモデルのプロバイダーを気にせず、簡単に切り替えて使えるということで、便利かもしれないと思い、個人的に使い始めたからです。

では、実際に試した手順を説明していきます。今回、私が試した環境はwsl2になっています。

# 手順

## Python環境準備

LangChainをPythonで使うので、今回は[uv](https://github.com/astral-sh/uv)を使ってパッケージなどを管理しようと思います。
uvをまだインストールしていない方は、以下のコマンドでインストールしてください。（もちろんpipなど他の方法でも問題ありません。）

```shell
curl -LsSf https://astral.sh/uv/install.sh | sh
```

今回作業するディレクトリを作成します。

```shell
uv init ollama-ws
cd ollama-ws
```

## Ollama準備

[こちら](https://ollama.com/download/)のサイトを参考にインストールします。
今回は以下。

```shell
curl -fsSL https://ollama.com/install.sh | sh
```

インストールが完了した後は、[こちら](https://ollama.com/search)から試してみたいモデルを選択してください。
今回は[gemma3](https://ollama.com/library/gemma3)からgemma3:1b-it-qatを選びました。私が試す環境ではメモリも少なく、GPUも使えなかったので、軽量なgemma3:1bの量子化されたモデル（？）を使うことにしました。

以下のコマンドを実行して入力を受け付け、レスポンスを返してくれるようになっていればOKです。
```shell
$ ollama run gemma3:1b-it-qat
pulling manifest 
pulling 57c9b4a897df: 100% ▕████████████████████████████████████████████████████████████████████▏ 1.0 GB                         
pulling 5be6630b666f: 100% ▕████████████████████████████████████████████████████████████████████▏  359 B                         
pulling 3116c5225075: 100% ▕████████████████████████████████████████████████████████████████████▏   77 B                         
pulling 59d7262b304a: 100% ▕████████████████████████████████████████████████████████████████████▏  416 B                         
verifying sha256 digest 
writing manifest 
success 
>>> hi
Hi there! How can I help you today? 😊
```

## LangChainでの動作確認

### パッケージのインストール

```shell
uv add langchain langchain-ollama
```

### ソースファイルの作成

自動で作られているはずのmain.pyを以下に置き換えてください。

```python:main.py
from langchain.chat_models import init_chat_model

model = init_chat_model("gemma3:1b-it-qat", model_provider="ollama")
res = model.invoke("Hello, world!")
print(res)
```

### 実行

```shell
$ uv run main.py 
content="Hello there! It's great to hear from you. 😊 How can I help you today?" additional_kwargs={} response_metadata={'model': 'gemma3:1b-it-qat', 'created_at': '2025-05-25T08:41:51.748357385Z', 'done': True, 'done_reason': 'stop', 'total_duration': 2180198150, 'load_duration': 218324600, 'prompt_eval_count': 13, 'prompt_eval_duration': 94606200, 'eval_count': 21, 'eval_duration': 1863794075, 'model_name': 'gemma3:1b-it-qat'} id='run--216431f3-ab8d-441b-bcc9-86aae03e510b-0' usage_metadata={'input_tokens': 13, 'output_tokens': 21, 'total_tokens': 34}
```

## APIからの利用

今回使うことはありませんでしたが、ollamaとしてはAPIのエンドポイントも自動で用意してくれるようで、以下のような形でアクセスすることができます。

```shell
$ curl http://localhost:11434/api/generate -d '
{
    "model": "gemma3:1b-it-qat",
    "prompt": "こんにちは！",           
    "stream": false
}'
```

```log
{"model":"gemma3:1b-it-qat","created_at":"2025-05-25T09:21:46.353172647Z","response":"こんにちは！何かお手伝いできることはありますか？ 😊\n","done":true,"done_reason":"stop","context":[105,2364,107,85141,237354,106,107,105,4368,108,85141,237354,98662,203956,239542,236985,17125,41277,17442,237116,237536,103453,107],"total_duration":12141152725,"load_duration":7424890959,"prompt_eval_count":11,"prompt_eval_duration":3173200659,"eval_count":14,"eval_duration":1487826925}
```

# おわり

ollamaとlangchainを使ってミニマムで動かしてみました。
これだけ簡単に動かせることにちょっとびっくりしました。