生成AI開発ツールをローカルLLMで回したら、ファイルを書けたのは1本だけ

目次
生成AI開発ツールの比較記事には「Ollama対応」「ローカルモデル可」という行がよく並ぶ。しかしそれは「接続設定が存在する」という意味であって、「実際にファイルを書くところまで回る」とは別の話だ。確かめずに無料・ローカル前提の運用を選ぶと、入れてから「応答は返るのに作業が完走しない」ことに気づいて積む。
なので同じワンショットタスクを、ローカルLLMに繋いだ3ツールに投げて記録した。対象は Ollama 接続をうたう CLI エージェントの aider、Codex CLI、opencode。
測り方: ローカルLLMに同じ指示を投げる
環境は macOS 26.6.1 (arm64)、Ollama 0.31.1。モデルは qwen2.5-coder:7b(4.7GB)を全ツール共通で使う。選んだのは ollama show で Capabilities に tools が付いていたからで、ツール呼出しを使うタイプのエージェントでも条件を揃えられる。
$ sh -c 'sw_vers; uname -m; python3 -V; ollama --version; aider --version'
ProductName: macOS
ProductVersion: 26.6.1
BuildVersion: 25G76
arm64
Python 3.14.7
ollama version is 0.31.1
aider 0.86.2
# exit=0 (518 ms)
$ sh -c 'ollama list; echo ===; ollama show qwen2.5-coder:7b | sed -n "1,15p"'
...
===
Model
architecture qwen2
parameters 7.6B
context length 32768
embedding length 3584
quantization Q4_K_M
Capabilities
completion
tools
insert
...
# exit=0 (43 ms)
投げる指示は固定で「fizzbuzz.py を作れ」にした。ファイル新規作成はエージェントの最小ループ——応答を編集に変換してディスクに書き、必要なら自分で検証する——を一通り踏むので、「動く」と「動いたふり」を分けやすい。判定は「ファイルが生成され、python3 で正しい FizzBuzz が出るか」。
各ツールは HOME・XDG_CONFIG_HOME・XDG_DATA_HOME を隔離ディレクトリに向けて非対話で実行した。私の検証環境はワークスペース外への書き込みを拒否するサンドボックスで走っているので、ツールが設定や履歴をどこへ書こうとするかもそのまま観測材料になった。
aider: 新規作成も追従編集も回った
aider の Ollama 接続は --model ollama_chat/<モデル> を渡すだけだった(公式ドキュメント)。OLLAMA_API_BASE 未設定の警告が出るが、localhost:11434 が既定値なのでそのまま通る。
$ sh -c 'env HOME=../../home XDG_CONFIG_HOME=../../xdg-config XDG_DATA_HOME=../../xdg-data aider --model ollama_chat/qwen2.5-coder:7b --no-git --yes-always --message "Create a file fizzbuzz.py in the current directory that prints FizzBuzz for numbers 1 to 20, one per line." < /dev/null'
...
Aider v0.86.2
Model: ollama_chat/qwen2.5-coder:7b with whole edit format
Git repo: none
Repo-map: disabled
...
Tokens: 617 sent, 79 received.
fizzbuzz.py
Applied edit to fizzbuzz.py
...
# exit=0 (16976 ms)
617トークン送って79トークン返ってきて、16976ms で Applied edit まで到達した。生成物は正しく動いた。
$ sh -c 'cat fizzbuzz.py; echo ===; python3 fizzbuzz.py; echo ===; ls -la; find ../../home -type f 2>/dev/null | head -20'
...
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
...
# exit=0 (36 ms)
新規作成だけでなく追従編集も試した。既存の fizzbuzz.py をコンテキストに載せて「argparse で --limit を受け取るように変えろ」と投げると、diff 形式の編集をそのまま適用した。
$ sh -c 'env HOME=../../home XDG_CONFIG_HOME=../../xdg-config XDG_DATA_HOME=../../xdg-data aider --model ollama_chat/qwen2.5-coder:7b --no-git --yes-always --message "Edit fizzbuzz.py: parse an optional --limit argument with argparse (default 20) and print FizzBuzz up to that limit." fizzbuzz.py < /dev/null'
...
Tokens: 744 sent, 273 received.
Applied edit to fizzbuzz.py
...
# exit=0 (24250 ms)
$ sh -c 'cat fizzbuzz.py; echo ===; python3 fizzbuzz.py --limit 15'
...
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
# exit=0 (35 ms)
aider が回った理由は構造にあると思う。aider はモデルの応答テキストから diff を自前でパースして適用する「whole edit format」で動くので、モデル側にネイティブのツール呼出しが無くても編集ループが成立する。
ただしノイズはあった。--no-git を付けていても、チャット履歴は git リポジトリのルートに書きに行く。私の環境ではそこが書き込み禁止で EPERM を吐いた——実行自体は継続したが、隔離環境で aider を動かす人は同じ警告に出るはずだ。ついでに osascript の通知がコケて、syntax error の日本語メッセージが stderr に散らばった。
$ sh -c 'env HOME=../../home XDG_CONFIG_HOME=../../xdg-config XDG_DATA_HOME=../../xdg-data aider --model ollama_chat/qwen2.5-coder:7b --no-git --yes-always --message "Create a file fizzbuzz.py in the current directory that prints FizzBuzz for numbers 1 to 20, one per line." < /dev/null'
Warning: Unable to write to chat history file <repo>/.aider.chat.history.md.
[Errno 1] Operation not permitted: '<repo>/.aider.chat.history.md'
...
# exit=0 (16976 ms)
Codex CLI: Ollama には繋がるが、ツール呼出しが本文に化ける
Codex CLI(github.com/openai/codex)は --oss --local-provider ollama で Ollama に繋がる。npm でワークスペース内に @openai/codex を入れて node_modules/.bin/codex から実行した(codex-cli 0.161.0)。
$ sh -c 'env HOME=../../home CODEX_HOME=../../codex-home ./node_modules/.bin/codex exec --oss --local-provider ollama -m qwen2.5-coder:7b -s workspace-write "Create a file fizzbuzz.py in the current directory that prints FizzBuzz for numbers 1 to 20, one per line." < /dev/null'
...
model: qwen2.5-coder:7b
provider: ollama
approval: never
sandbox: workspace-write [workdir, /tmp, $TMPDIR]
...
warning: Model metadata for `qwen2.5-coder:7b` not found. Defaulting to fallback metadata; this can degrade performance and cause issues.
...
tokens used
2,127
# exit=0 (19462 ms)
provider: ollama で接続自体は成功し、2127トークン使って応答が返った。ところが返ってきたのは ```python フェンスのコード表示だけで、fizzbuzz.py は生成されなかった。「Model metadata for qwen2.5-coder:7b not found」の警告も出ていて、メタデータのフォールバックが性能劣化を起こし得ると Codex 自身が言っている。
ツール使用を明示して再試行すると、もっと妙なものが返ってきた。
$ sh -c 'env HOME=../../home CODEX_HOME=../../codex-home ./node_modules/.bin/codex exec --oss --local-provider ollama -m qwen2.5-coder:7b -s workspace-write "Use your tools to write a file named fizzbuzz.py in the current directory printing FizzBuzz for 1 to 20. Do not just print the code in your reply; actually create the file, then run python3 fizzbuzz.py to verify." < /dev/null'
...
codex
{"name": "exec_command", "arguments": {"cmd": "echo 'for i in range(1, 21):\\n if i % 3 == 0 and i % 5 == 0:\\n print(\"FizzBuzz\")\\n elif i % 3 == 0:\\n print(\"Fizz\")\\n elif i % 5 == 0:\\n print(\"Buzz\")\\n else:\\n print(i)' > fizzbuzz.py", "justification": "", "login": false, "max_output_tokens": 10000, "prefix_rule": [], "sandbox_permissions": "use_default", "shell": "/bin/bash", "tty": false, "workdir": ".", "yield_time_ms": 10000}}
tokens used
2,220
# exit=0 (23624 ms)
モデルは exec_command を呼ぼうとしていた——ただし構造化されたツール呼出しではなく、JSON がそのまま応答本文として表示された。プロンプトで「ツールを使え」と押したら「ツール呼出しっぽい文字列」を吐くのは、ツール呼出しの定型が弱いモデルでよくある壊れ方だ。Ollama 側では qwen2.5-coder:7b に tools 能力があると出ているが、Codex の harness との間で関数呼出しとして成立しなかった。結果としてファイルは2回とも生まれていない。
opencode: エージェント処理に辿り着く前に止まった
opencode(公式ドキュメント)は 2.0.20。Ollama はプロバイダ設定を opencode.json に書けば繋がるはずだが、opencode run はエージェントにすら到達しなかった。
$ sh -c 'env HOME=../../home XDG_CONFIG_HOME=../../xdg-config XDG_DATA_HOME=../../xdg-data opencode run "Create a file fizzbuzz.py in the current directory that prints FizzBuzz for numbers 1 to 20, one per line." < /dev/null'
Error: Timed out waiting for the background service to start
at <anonymous> (/$bunfs/root/opencode:1206:36658)
at service.ensure (/$bunfs/root/opencode:4042:571)
at service.ensure (definition) (/$bunfs/root/opencode:1206:34885)
at cli.server-connection.resolve (/$bunfs/root/opencode:4294:476)
at cli.server-connection.resolve (definition) (/$bunfs/root/opencode:4041:4356)
# exit=1 (120401 ms)
内部の serve --service プロセスは起動自体はしている——ログ上は DB のスキーマ初期化まで進んでいる——のに、親側が待ち続けてタイムアウトで exit=1 になった(ログ末尾の通り)。serve を単体で起動するとクラッシュはせず、こちらはタイムアウト打ち切りの25秒まで生き続けたので、待ち受け自体は立つ。--standalone なら待ち合わせを迂回できるかと思ったが、今度は UnexpectedStatus: 500 で即死した。
$ sh -c 'env HOME=../../home XDG_CONFIG_HOME=../../xdg-config XDG_DATA_HOME=../../xdg-data opencode run --standalone "noop" 2>&1 | sed -E "s/\x1b\[[0-9;]*m//g"; echo ===; grep -E "realPath|UnexpectedStatus|http.status=500" ../../xdg-data/opencode/log/opencode.log | tail -4'
Error: UnexpectedStatus: 500
===
timestamp=2026-10-08T13:10:28.630Z level=INFO run=96e31b36 cause="PlatformError: Unknown: FileSystem.realPath (~/.claude)\n at nb (/$bunfs/root/opencode:25:49106)\n at <anonymous> (/$bunfs/root/opencode:25:47760)\n at guarded (internal:shared:112:26)\n at processTicksAndRejections (native:7:39)\n at FileSystem.resolve (/$bunfs/root/opencode:4919:35807)\n at FileSystem.resolve (definition) (/$bunfs/root/opencode:389:92551)\n at ConfigDiscovery.discover (/$bunfs/root/opencode:4919:42986)\n at ConfigDiscovery.discover (definition) (/$bunfs/root/opencode:4919:35331)\n at ServerProcess.start (/$bunfs/root/opencode:5661:16623)\n at ServerProcess.start (definition) (/$bunfs/root/opencode:5661:13059) {\n [cause]: Error: EPERM: operation not permitted, lstat '~/.claude'\n}" http.span=49 role=server http.method=GET http.url=/api/location http.status=500
...
# exit=0 (290 ms)
500 の中身を内部ログから辿ると、ConfigDiscovery が FileSystem.realPath (~/.claude) を呼んで EPERM になっていた。HOME を隔離先に向けても実ホームの .claude を見に行く設計らしく、私のサンドボックスはその lstat を許可しない。つまりこの失敗は私の環境依存であり、通常の Mac で同じように止まるとは限らない。ただし「設定探索が外部要因で落ちるとサーバ全体が 500 で起き上がれない」という脆さ自体は、観測された事実として残っている。
「無料で動く」をどこまで信じていいか
3ツールに同じ指示を投げて、ファイルが実際に生成されたのは aider の1本だけだった。ファイルを書けたのは3本中1本。
$ sh -c 'find tasks -name "fizzbuzz.py" -exec ls -la {} \; ; echo ===; for f in $(find tasks -name fizzbuzz.py); do echo "-- $f"; python3 "$f" --limit 5 2>&1 | tr "\n" " "; echo; done'
-rw-------@ 1 user staff 574 Oct 8 22:15 tasks/aider/fizzbuzz.py
===
-- tasks/aider/fizzbuzz.py
1 2 Fizz 4 Buzz
# exit=0 (42 ms)
終了までの時間を集計すると次の通り。codex の2回とも「応答は返って完走したがファイルは無い」なので、所要時間が問題を分ける指標にはならない——戻り値も exit=0 で、外からは成功に見える。
$ sh -c 'for spec in aider-create:003 aider-edit:011 codex-1st:010 codex-2nd:013; do name=${spec%%:*}; n=${spec##*:}; f=$(ls ../logs/${n}-*.txt); ms=$(sed -nE "s/^# exit=[0-9]+ \(([0-9]+) ms\)/\1/p" "$f"); echo "$name $ms ms"; done'
aider-create 16976 ms
aider-edit 24250 ms
codex-1st 19462 ms
codex-2nd 23624 ms
# exit=0 (21 ms)
見えたのは、壊れ方がツールごとに全然違うことだ。aider はモデルの出力テキストを自分で編集に変換する方式なので回った。Codex CLI はツール呼出しが成立しないと「応答は返るが何もしない」で終わる。opencode は今回の環境では起動前に潰れた。
だから「Ollama対応」の表記だけで選ぶのは危ない。課金前・導入前に「うちのモデルで fizzbuzz レベルのファイル生成が一周回るか」を自分で投げてみるのが、この実測が出した最短の答えだ。10分あれば確かめられる。
限界ははっきり書いておく。今回測ったのは1モデル・1タスク・1環境だけだ。Codex が別モデルなら普通にツールを呼ぶかもしれないし、opencode が非サンドボックス環境なら何事もなく動くかもしれない。クラウドモデルでの挙動は今回測っていない。この記事が言えるのは「謳い文句と動作は別」であって、「このツールはダメ」ではない。
関連書籍
