AIコーディングのおすすめ構成を無料で組んで実測した

目次
  1. 無料でそろえるローカルAIコーディング環境
  2. 題材はバグを3つ仕込んだCSV集計スクリプト
  3. 素の ollama run にバグ修正させる — 1.5Bは2つ直して1つ見逃す
  4. aider に任せる — ファイル編集と静的チェックまで自動になる
  5. 結局どれをおすすめするか

「AIコーディング おすすめ」で検索すると、ツール名と料金表を並べた「10選」「11選」の比較記事が上位を埋めている。だがどの記事にも、実際にツールへタスクを与えて動かした結果は載っていない。私が知りたいのは機能一覧ではなく、「課金する前に手元で試したら、どの程度動くのか」だ。

そこで、無料でそろうローカル構成(Ollama + Qwen2.5-Coder + aider)に、バグを仕込んだ同一のPythonコードを渡し、「失敗しているテストを通るまで直せるか」をモデルサイズ別に実測した。課金が必要なツール(Claude Code・GitHub Copilot・Cursor など)は今回実測していない。以下はすべて手元で実行したログに基づく。

無料でそろえるローカルAIコーディング環境

構成は3つ。Ollama(ローカルLLM実行環境)、Qwen2.5-Coder(コーディング特化のオープンウェイトモデル)、aider(ターミナルで動くOSSのコーディングエージェント)。すべて無料で、APIキーも外部アカウントも要らない。コードが手元から出ないので、社内コードで試すときの心理的な障壁も低い。

実行環境は次のとおり。

$ sw_vers
ProductName:		macOS
ProductVersion:		26.6.1
BuildVersion:		25G76
# exit=0 (10 ms)

Python 3.14.6、Ollama 0.31.1、aider 0.86.2 を使った。モデルの取得は ollama pull 一発で、1.5Bは986MB。回線は50 MB/s出ていて、1分もかからず終わった。

$ ollama pull qwen2.5-coder:1.5b
...
verifying sha256 digest
writing manifest
...
# exit=0 (29915 ms)

7B(4.7GB)も同じ手順で、2分ほどで取得できた。aider のインストールは uv tool install で数秒だった。

$ uv tool install aider-chat
Resolved 108 packages in 287ms
...
Installed 1 executable: aider
# exit=0 (3180 ms)

導入で詰まる箇所はなかった。ここまでで比較記事を1本読むより短い。

題材はバグを3つ仕込んだCSV集計スクリプト

「おすすめかどうか」をテストの成否で機械的に判定したいので、題材は自分で用意した。売上CSVを集計する小さなスクリプトに、わざとバグを3つ仕込む。

CSVは date,product,unit_price,quantity の4列で、読み込みは csv.DictReader に任せた(値はすべて文字列で返ってくる。これが2つ目のバグの伏線になる)。バグを仕込んだのは集計側の2関数だ。

def monthly_totals(rows):
    """月(YYYY-MM)ごとの売上合計を返す。"""
    totals = defaultdict(int)
    for row in rows:
        month = row["date"][:8]
        amount = row["unit_price"] * row["quantity"]
        totals[month] += amount
    return dict(totals)


def top_product(rows):
    """売上合計が最大の商品名を返す。"""
    totals = defaultdict(int)
    for row in rows:
        amount = int(row["unit_price"]) * int(row["quantity"])
        totals[row["product"]] = amount
    return max(totals, key=totals.get)

仕込んだバグは次の3つ。

  1. monthly_totals[:8] — 月キーが 2026-07- になる(正しくは [:7]2026-07
  2. monthly_totals の掛け算 — CSVの値は文字列なので int() なしでは TypeError
  3. top_product= — 商品ごとの売上を合計せず最後の行で上書きする(正しくは +=

2は実行した瞬間に落ちる明示的なバグ、3は例外を出さずに間違った答えを返す嫌なバグだ。修正対象を渡すだけでなく「何が正しいか」を機械判定させたいので、月別集計の期待値と売上トップ商品名を検査する pytest のテストを2本書いた。ベースラインを取ると、当然2件とも失敗する。

$ python3 -m pytest test_sales_report.py -v
...
>           amount = row["unit_price"] * row["quantity"]
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: can't multiply sequence by non-int of type 'str'
...
FAILED test_sales_report.py::test_top_product - AssertionError: assert 'mouse...
FAILED test_sales_report.py::test_monthly_totals - TypeError: can't multiply ...
============================== 2 failed in 0.81s ===============================
# exit=1 (1065 ms)

この「失敗する状態」から、各構成にコードを渡して直させ、同じテストで判定する。

素の ollama run にバグ修正させる — 1.5Bは2つ直して1つ見逃す

まずエージェントを使わず、ollama run にプロンプトで直接依頼した。渡したのはバグ入りコードの全文と、pytest の失敗内容の要約、期待する仕様の3点。1.5Bの応答は7秒弱で返ってきた。

$ sh -c 'ollama run qwen2.5-coder:1.5b "$(cat prompt.txt)"'
...
def monthly_totals(rows):
    """月(YYYY-MM)ごとの売上合計を返す。"""
    totals = defaultdict(int)
    for row in rows:
        month = row["date"][:7]  # パディングゼロを追加
        amount = int(row["unit_price"]) * int(row["quantity"])
        totals[month] += amount
    return dict(totals)
...
# exit=0 (6417 ms)

[:7]int() の2つは直っている([:7] に添えられた「パディングゼロを追加」というコメントは的外れだが、修正自体は正しい)。一方 top_product は元の = のまま出力され、上書きバグを見逃した。プロンプトには「上書きされている」という失敗の要約まで書いたのに、である。

ところで、この修正案を適用したとき、私は先にひとつ恥をかいた。適用後のテストがこう落ちたのだ。

$ python3 -m pytest test_sales_report.py -v
...
    def test_monthly_totals():
        rows = load_rows(CSV_PATH)
        totals = monthly_totals(rows)
        # 2026-07: 1200*2 + 800*1 = 3200 / 2026-08: 1200*1 + 500*4 + 800*2 = 4900
>       assert totals == {"2026-07": 3200, "2026-08": 4900}
E       AssertionError: assert {'2026-07': 3...026-08': 4800} == {'2026-07': 3...026-08': 4900}
...
# exit=1 (1166 ms)

1200 + 2000 + 1600 は 4800 だ。テストに期待値 4900 と書いた私の暗算が間違っていた。モデルは間違った期待仕様を渡されても 4800 を返す正しいコードを出しており、この件に関しては人間の負けである。テストを直してベースラインを取り直し(失敗2件は変わらず)、以降はこの修正済みテストで判定した。

修正済みテストでの1.5Bの結果は、集計側が通って top_product が落ちる「1勝1敗」だった。

$ python3 -m pytest test_sales_report.py -v
...
[gw0] [ 50%] PASSED test_sales_report.py::test_monthly_totals
...
E       AssertionError: assert 'mouse' == 'keyboard'
...
========================= 1 failed, 1 passed in 0.67s ==========================
# exit=1 (841 ms)

同じプロンプトを7Bに渡すと、3つとも直した。

$ sh -c 'ollama run qwen2.5-coder:7b "$(cat prompt.txt)"'
...
def top_product(rows):
    """売上合計が最大の商品名を返す。"""
    totals = defaultdict(int)
    for row in rows:
        amount = int(row["unit_price"]) * int(row["quantity"])
        totals[row["product"]] += amount
    return max(totals, key=totals.get)
...
# exit=0 (18221 ms)

適用してテストを回すと全件通過。応答は1.5Bより時間がかかるが、それでも20秒前後だ。

$ python3 -m pytest test_sales_report.py -v
...
============================== 2 passed in 0.74s ===============================
# exit=0 (971 ms)

aider に任せる — ファイル編集と静的チェックまで自動になる

ollama run 方式は、出てきたコードを人間がコピペで適用する必要がある。aider はそこを自動化するエージェントで、対象ファイルを渡して依頼文を書くと、ファイルを直接編集して差分を見せてくれる。接続先は OLLAMA_API_BASE で手元の Ollama を指定した。オプションが長いのは非対話実行のためで、--yes-always で確認プロンプトを通し、git 管理外の作業ディレクトリなので --no-git を付け、テレメトリ系を無効にしている。バグ入り状態に戻して、まず1.5Bで試した。

$ sh -c 'OLLAMA_API_BASE=http://127.0.0.1:11434 aider --model ollama_chat/qwen2.5-coder:1.5b --no-git --yes-always --no-check-update --no-analytics --no-show-model-warnings --message "test_sales_report.py が通るように sales_report.py のバグをすべて直してください" sales_report.py test_sales_report.py'
...
Applied edit to test_sales_report.py
Applied edit to sales_report.py
...
sales_report.py:42:16: F821 undefined name 'Path'
...
Applied edit to sales_report.py
...
# exit=0 (47023 ms)

このログには1.5Bの悪い癖と、aider の良い仕事が両方出ている。1.5Bは頼んでいない if __name__ == '__main__': ブロックをスクリプトとテストの両方に追加し、そこで未importの Path を使って F821(未定義名)を作った。aider は編集後に flake8 を回してこれを検知し、モデルに追い修正させて import を足させている。エージェント側の安全網が小さいモデルの粗を拾った形だ。

ただし肝心のバグは int() しか直っておらず、pytest は2件とも失敗のままだった。そこで2回目の依頼では、こちらで原因を特定して「月キーは7文字に」「+= で合計する」と直し方まで指示した。これは通り、全件パスした。つまり1.5Bは「バグを見つける」役はこなせないが、「指示された修正を適用する」役ならこなせる。

同じタスクを7Bに渡すと、「テストが通るように直して」という依頼文だけで一発で全部直した。

$ sh -c 'OLLAMA_API_BASE=http://127.0.0.1:11434 aider --model ollama_chat/qwen2.5-coder:7b --no-git --yes-always --no-check-update --no-analytics --no-show-model-warnings --message "test_sales_report.py が通るように sales_report.py のバグをすべて直してください" sales_report.py test_sales_report.py'
...
+        totals[row["product"]] += amount  # Accumulate total for each product
...
Applied edit to test_sales_report.py
Applied edit to sales_report.py
...
# exit=0 (47013 ms)
$ python3 -m pytest test_sales_report.py -v
...
[gw1] [ 50%] PASSED test_sales_report.py::test_top_product
...
# exit=0 (1036 ms)

依頼からテスト通過まで1分弱。人間がやったのは依頼文を1行書くことだけだ。

結局どれをおすすめするか

今回の実測の範囲での結論はこうなる。

まず試すべきは Ollama + qwen2.5-coder:7b + aider の組み合わせ。 7Bは3つのバグ(即死する TypeError、境界ずれ、静かに間違う上書き)を、素のプロンプトでも aider 経由でも全部直した。実行中の ollama ps で確認したメモリ消費は5.0GBで、処理は全部GPU側に載っていた。この程度なら開発機を圧迫しない。

1.5Bを「おすすめ」とは言えない。 3つのうち1つを見逃し、頼んでいないコードを足し、そのコードで未定義名エラーまで作った。ただし直し方を具体的に指示すれば適用はできるので、非力なマシンで「修正の実行役」に限定して使う余地はある。

限界も書いておく。今回の題材は1ファイル・数十行のスクリプトで、バグ修正という正解の明確なタスクだ。複数ファイルにまたがる設計変更や大きなリポジトリでの振る舞いは測っていないし、GPUの無いマシンやメモリの少ないマシンでの速度も測っていない。Claude Code や GitHub Copilot などの課金ツールとの優劣も、実測していない以上ここでは述べない。この記事が言えるのは「無料のローカル構成は、正解の明確な小さいタスクなら7Bで実用になった」というところまでだ。

それでも、比較記事を何本読んでも分からなかった「実際どの程度動くのか」は、モデルの取得から判定まで含めて30分足らずで自分の手元で確かめられた。おすすめを探して検索を続けるより、986MBを落として自分のコードで試すほうが早い。

関連書籍