OpenHands CLIでローカルLLM（Ollama・LiteLLM）を使ったAIエージェント開発入門

## はじめに

みなさんこんにちは！！！！！

今朝、突然OpenHandsのCLI版が降ってきて飛び起きたんですが、アツいですねー！

https://x.com/neubig/status/1935094481537384589

最近はタイムラインを眺めていてもClaude Code一色なわけですが、Claude Codeは基本的にClaudeのモデルしか利用できないので比較的高価です。
（Maxプランで定額$200は、むしろ安いくらいだと思っていますが…）

Claude Code自体は最高ですが、この間のGCPの大規模障害の件などもあり、一応代替手段は持っておきたいなーと思っていたところでこのポストだったので、使わざるを得ませんでした！

最近のGeminiは激安だったり、オープンLLMの性能も上がってきたりするので、ハードルはさらに下がっていると思います！

なので今回はOpenHands CLIとオープン（ローカル）LLMでコーディングエージェントを構築してみることにします！

本記事では、OllamaとLiteLLM Proxyを使用して、完全にローカル環境でOpenHands CLIを動作させる方法を詳しく解説します！

## 環境構築

今回はmacOSでのコマンドを紹介していきますが、インストール方法以外はさほど変わらないと思うので、適宜読み替えてください！

### Python環境の確認

OpenHands CLIはPython 3.12以上が必要ですので、確認しておきましょう。
Pythonのバージョンは`uv`を利用するのが楽です。
こちらの記事なんかが参考になると思います！！

https://zenn.dev/tabayashi/articles/52389e0d6c353a


```bash
❯ python -V
Python 3.12.5
```

### OpenHands CLIのインストール

pipを使って簡単にインストールできます。

https://docs.all-hands.dev/usage/how-to/cli-mode#cli

```bash
❯ pip install openhands-ai
```

### Ollamaのセットアップ

https://ollama.com/

ローカルLLMを実行するためにOllamaを使用します。
僕はこの記事をいろいろ参考にしました！

https://zenn.dev/kei1232/articles/c8cf32da6c9beb

```bash
# macOSの場合
❯ brew install ollama

# インストールの確認
❯ ollama -v
ollama version is 0.9.0
```

### 4. モデルのダウンロード

今回は高性能なQwen3の32Bモデルを使用します。
モデル性能（パラメーター数）と利用可能PCスペックはだいたいこんなイメージらしいです。
32Bのモデルを動かすにはそれなりのスペックのPCが必要ですので、もっと小さいモデルでも良いと思います！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203944/536deda7-4e33-45f8-82d7-fdd79087a266.png)
[ローカルllmの基礎から最新モデル比較・実践導入まで徹底解説【2025年版】](https://assist-all.co.jp/column/ai/20250617-5350/)

```bash
# モデルのダウンロード
❯ ollama pull qwen3:32b

# ダウンロードしたモデルの確認
❯ ollama ls
NAME          ID              SIZE     MODIFIED
qwen3:32b     030ee887880f    20 GB    9 hours ago

# モデルの動作確認
❯ ollama run qwen3:32b
>>> Send a message (/? for help)
```

## 基本設定

OpenHands CLIをローカルLLMで使う方法は2つあります。

### 方法1: Ollamaに直接接続

最もシンプルな方法は、OpenHands CLIから直接Ollamaに接続することです。

まずOllamaのサーバーが起動している確認します。

```bash
# サーバーの立ち上げ
❯ ollama serve
Error: listen tcp 127.0.0.1:11434: bind: address already in use

# 立ち上がっているプロセスの確認
❯ lsof -i:11434
COMMAND   PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ollama                                        TCP localhost:11434 (LISTEN)
```

`address already in use`というエラーが出て、すでにポートが占有されてしまっていますが、利用プロセスを調べるとollamaが立ち上がっているのが確認できますね！

次に`~/.openhands/settings.json`を作成し、以下の内容を記述します。
先ほどダウンロードしたきたモデルの名称や、ローカルマシンのOllamaのURLを書き加えてあげます。
`llm_api_key`は空文字でも良いはずですが、慣例としてollamaと記述しておくとかなんとか。

```json
{
    "llm_model": "ollama/qwen3:32b",
    "llm_api_key": "ollama",
    "llm_base_url": "http://localhost:11434",
    "mcp_config": null
}
```

### 方法2: LiteLLM Proxy経由での接続

https://www.litellm.ai/

LiteLLMは様々なツールから配信されるモデルを統合して呼び出し可能にするツールです。
Ollamaのみを利用するよりも、より柔軟な設定が可能で、LiteLLM Proxyを利用することでhttp経由で複数のモデルを呼び出すことができます

#### LiteLLM Proxyのインストール

https://docs.litellm.ai/docs/

こちらもpip経由でインストールすることができます。

```bash
❯ pip install 'litellm[proxy]'
```

#### 設定ファイルの作成

設定ファイルはどこに作ってもいいんですが、今回は`~/.config/litellm/config.yml`を作成します。
以下の内容を記述します。

```yaml
model_list:
  - model_name: qwen3
    litellm_params:
      model: ollama/qwen3:32b
      api_base: "http://localhost:11434"
    model_info:
      supports_function_calling: true

litellm_settings:
  master_key: "my-master-key"
```

上記設定でOllamaを利用してインストールし、起動しているモデルのプロキシとして立ち上がることができます。
master_keyは空文字でもいいらしいですが慣例的にうんぬんかんぬん…

`model_name`は任意のものを設定して良いですが、`itellm_params.model`については`<プロバイダー>/<モデル名>`で、かつモデル名は`32b`まで含めた正確なものを記載する必要があるようです。

#### LiteLLM Proxyの起動

以下のようなコマンドでサーバーを起動できます！
`--config`を指定することで、`model_list`に設定されたモデルを利用することができます。
いちいちコマンドに設定ファイルのパスを入力するのがめんどくさい場合は、環境変数が設定できたはずです。
詳しくは公式を…

```bash
❯ litellm --config ~/.config/litellm/config.yml --port 8888
```

#### OpenHands CLIの設定

LiteLLMを利用する場合は以下のような設定になります。

`~/.openhands/settings.json`を以下のように設定します：

```json
{
    "llm_model": "litellm_proxy/qwen3",
    "llm_api_key": "my-master-key",
    "llm_base_url": "http://localhost:8888",
    "mcp_config": null
}
```

## 動作確認

LiteLLM Proxyが正しく動作しているか確認してみましょう。

```bash
curl -X POST 'http://0.0.0.0:8888/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer my-master-key' \
-d '{
    "model": "qwen3",
    "messages": [
        {
            "role": "user",
            "content": "こんにちは。今日は何をしますか？"
        }
    ]
}'

{
	"id": "chatcmpl-08f6c7c2-f95e-4602-ba12-e4cdbf98887d",
	"created": 1750253624,
	"model": "ollama/qwen3:32b",
	"object": "chat.completion",
	"system_fingerprint": null,
	"choices": [
		{
			"finish_reason": "stop",
			"index": 0,
			"message": {
				"content": "<think>\nThe message is in Japanese. It says \"Hello. What are you doing today?\" or more naturally \"Hi, what are you up to today?\"\n\nI should respond in Japanese since the question was asked in Japanese. I'll greet them back and offer to help with whatever they might be planning for the day.\n\nMy response should be friendly and open-ended to allow them to share what they're interested in doing.\n</think>\n\nこんにちは！今日は特に予定がありませんが、何か面白いことを計画しています。あなたはどんな一日にしたいですか？何かお手伝いできることがあれば、いつでも教えてください！",
				"role": "assistant",
				"tool_calls": null,
				"function_call": null
			}
		}
	],
	"usage": {
		"completion_tokens": 127,
		"prompt_tokens": 19,
		"total_tokens": 146,
		"completion_tokens_details": null,
		"prompt_tokens_details": null
	}
}

```

正常にレスポンスが返ってくれば、設定は成功です。

## OpenHands CLIの実行

設定が完了したら、OpenHands CLIを実行してみましょう！

```bash
❯ openhands

> あなたはどのようなことができますか？返事をしてください。

<think>
Okay, the user is asking, "あなたはどのようなことができますか？返事をしてください。" which translates to "What can you do? Please reply." I need to explain the capabilities based on the tools provided.

First, looking at the available functions: execute_bash, str_replace_editor, browser, and finish. These allow running commands, editing files, controlling the browser, and ending the session.

The example showed creating a Flask app, handling errors, and modifying code. So, I should mention web development help, file manipulation, running servers, and browser interactions. Also, note the restrictions like working directory and file access.

I should structure the response to list key abilities, mention the tools used, and clarify limitations. Make sure to use the function call format if needed, but since the user is asking about capabilities, a regular response is appropriate here.
</think>

I can assist with tasks involving file manipulation, running servers, browser automation, and basic development workflows within the allowed directory (~/sample-project). Key capabilities include:

1. Creating/editing files (via `str_replace_editor`)
2. Running command-line tools (via `execute_bash`)
3. Controlling browser interactions (via `browser` function)
4. Setting up simple web servers
5. Debugging and error resolution

I can only access files in the specified working directory and its subdirectories. Let me know what specific task you'd like to accomplish!
```

ちゃんとエージェントとして動作してそうですね！！！
これで、完全にローカル環境でAIエージェントを実行できるようになりました！！！

## まとめ

OpenHands CLIを使うことで、完全にローカル環境でAIエージェントを実行できるようになりました！
特にLiteLLM Proxyを経由することで、柔軟な設定と拡張性を持たせることができます。

あまりオープンLLMを利用したことがなかったのですが、以下のようなメリットとデメリットがあるなーと思いました！

### メリット

- プライバシーが完全に保護される
- APIコストがかからない
- オフライン環境でも動作する
- カスタマイズが容易

### デメリット

- 初期設定がやや複雑
- 高性能なハードウェアが必要（特に大規模モデルの場合）
- モデルのダウンロードに時間がかかる

ただ、初期設定もそこまで大変ではないですし、今後、ローカルLLMの性能は向上していくことが目に見えているので、どんどん面白くなっていくと思います！

皆さんも使ってみましょうー！