aider + Ollama 実践編 — CPU 推論でコード生成させてみた結果と性能評価
はじめに
前回・前々回で aider + Ollama のインストールとオプションを紹介しました。今回は最終回として、実際にコード生成させてみた結果と、CPU 推論環境での性能評価をまとめます。
本テーマは 3 回にわけて紹介しました。
- インストール編(第 1 回)
- aider のオプション編(第 2 回)
- 実践編(今回)
「CPU 推論で本当に使い物になるのか?」という疑問に対して、実際のタスクで試した正直な感想です。
対象者
- aider + Ollama を入れたけど、実際どの程度使えるのか知りたい人
- CPU 推論環境での性能感を把握したい人
- ローカル LLM のコード生成品質がどんなものか気になる人
環境
| 項目 | スペック |
|---|---|
| OS | WSL2 Ubuntu |
| CPU | Intel Core i7-4790 @ 3.60GHz |
| メモリ | 8GB(うちスワップ 2GB) |
| GPU | なし(CPU 推論のみ) |
| モデル | qwen2.5-coder:7b(4.7GB) |
| aider | v0.86.2 |
やったこと
お題
「CSV ファイルを読み込んで、縦と横に合計と平均を追加して出力する Python スクリプトを作成して」という指示を aider に出しました。
具体的な要件は以下の通りです。
- 引数で CSV ファイルのパスを受け取る
- 1 行目はヘッダー行
- 2 行目以降が数値データ
- 各行の右側に「合計」「平均」列を追加
- 最下部に各列の「合計」行と「平均」行を追加
- 出力は CSV 形式で標準出力
- 平均は小数点以下 2 桁
- サンプル CSV も一緒に作成
仕様書(Spec.md)を用意する
チャットに直接要件を書くのではなく、仕様書をファイルとして用意して読ませます。こうすることで要件が明確になり、何度でも同じ条件で再生成できます。
# 仕様: CSV集計スクリプト
## 概要
CSVファイルを読み込み、縦横の合計と平均を追加して出力するPythonスクリプト
## 入力
- 引数: CSVファイルのパス
- 1行目: ヘッダー行(文字列)
- 2行目以降: 数値データ(カンマ区切り)
## 出力
- CSV形式で標準出力
- 各行の右側に「合計」「平均」列を追加
- 最下部に各列の「合計」行と「平均」行を追加
## 出力例
A,B,C,合計,平均
1,2,3,6,2.00
4,5,6,15,5.00
合計,5,7,9,21,7.00
平均,2.50,3.50,4.50,10.50,3.50
## 制約
- 言語: Python
- 外部ライブラリは使わない(標準ライブラリのみ)
- 平均は小数点以下2桁
## コーディング規約
- インデント: スペース4つ
- 変数名: スネークケース(file_path)
- 関数名: スネークケース(calc_sum)
- コメントは日本語で記載
実行コマンド
--read オプションで Spec.md を読み取り専用で渡します。
aider --model ollama_chat/qwen2.5-coder:7b --edit-format whole --no-auto-commits --yes --read Spec.md --message "Spec.md の仕様に従って csv_summary.py と sample.csv を作成してください。"
生成結果
トークン数
aider の出力に表示された数値です。
| 項目 | 値 |
|---|---|
| 送信トークン | 1,700 |
| 受信トークン | 319 |
生成されたファイル
aider は以下の 2 ファイルを生成しました。
-
csv_summary.py— メインスクリプト(38 行) -
sample.csv— テスト用データ(3 列 x 2 行)
生成されたコード
import csv
def calc_sum_and_avg(data):
total = sum(data)
avg = round(total / len(data), 2) if data else 0
return total, avg
def add_sum_and_avg_columns(input_path, output_path):
with open(input_path, mode='r', newline='', encoding='utf-8') as infile, \
open(output_path, mode='w', newline='', encoding='utf-8') as outfile:
reader = csv.reader(infile)
writer = csv.writer(outfile)
header = next(reader)
header.extend(['合計', '平均'])
writer.writerow(header)
data_rows = []
for row in reader:
numeric_row = [float(item) for item in row]
total, avg = calc_sum_and_avg(numeric_row)
row.extend([total, avg])
writer.writerow(row)
data_rows.append(numeric_row)
sum_row = ['合計'] + [sum(col) for col in zip(*data_rows)]
avg_row = ['平均'] + [round(sum(col) / len(data_rows), 2) for col in zip(*data_rows)]
writer.writerow(sum_row)
writer.writerow(avg_row)
if __name__ == '__main__':
input_path = 'sample.csv'
output_path = 'output.csv'
add_sum_and_avg_columns(input_path, output_path)
実行結果
A,B,C,合計,平均
1,2,3,6.0,2.0
4,5,6,15.0,5.0
合計,5.0,7.0,9.0
平均,2.5,3.5,4.5
性能評価
そのまま動いたか? → 動いたが、バグあり
生成されたコードはエラーなく実行できましたが、仕様と異なる点が 4 つ ありました。
| # | バグ | 内容 |
|---|---|---|
| 1 | 合計・平均行が不完全 | 合計行・平均行の右側に行の合計・平均がない(仕様の出力例と不一致) |
| 2 | 小数点フォーマット不備 | 仕様は 2.00 なのに 2.0 と出力される(小数点以下 2 桁になっていない) |
| 3 | 標準出力でなくファイル出力 | 仕様は「標準出力」なのに output.csv にファイル出力している |
| 4 | 引数を受け取らない | 仕様は「引数: CSV ファイルのパス」なのにファイルパスがハードコード |
良かった点
| 項目 | 評価 |
|---|---|
| 全体構造 | ファイル読み込み → ヘッダー処理 → データ処理 → 出力という流れは正しい |
| Python の基本文法 | csv モジュール、リスト内包表記、zip の使い方は適切 |
| 要件の理解 | ヘッダー行の分離、縦横の合計/平均という要件は理解している |
| サンプルデータ | 要件通り 3 列の数値データを生成 |
| 関数設計 |
calc_sum_and_avg を分離する設計は良い |
悪かった点
| 項目 | 評価 |
|---|---|
| 仕様の読み落とし | 出力例まで仕様に書いたのに、合計・平均行の形式が一致しない |
| フォーマット |
round() で丸めているが f"{val:.2f}" のようなフォーマットをしていない |
| 入出力の仕様違反 | 「標準出力」「引数でパスを受け取る」という明確な仕様を無視している |
| csv モジュールの利用 | 仕様は「標準ライブラリのみ」なので csv は問題ないが、標準出力なら csv.writer は不要 |
総合評価
| 観点 | 評価 |
|---|---|
| コードの骨格 | ○ — 方針は正しく、修正のベースとして十分使える |
| そのままの動作 | △ — 実行はできるが仕様と一致しない |
| 修正の手間 | △ — バグは典型的なもので、分かる人なら数分で直せる |
| 仕様書の遵守 | × — 出力例や制約を明示しても読み落としがある |
CPU 推論の体感速度
| 項目 | 体感 |
|---|---|
| 初回ロード | モデルをメモリに展開するため数十秒かかる |
| コード生成(319 トークン) | 30 秒〜1 分程度 |
| 短い質問への回答 | 数十秒 |
i7-4790 + 8GB RAM という構成では「待てなくはないが、サクサクではない」という印象です。API 経由で Claude や GPT を使う場合の体感速度とは比べものになりません。
仕様書を使った指示のポイント
今回 Spec.md に出力例や制約まで書いたにもかかわらず、仕様通りのコードにはなりませんでした。7B モデルの限界として、仕様書の全項目を忠実に反映するのは難しいようです。
Spec.md に書くべき内容
| 項目 | 効果 |
|---|---|
| 出力例 | 最も効果が高い。形式を具体的に示せる |
| 制約 | 「外部ライブラリは使わない」等で余計な依存を防げる |
| コーディング規約 | 変数名やインデントの統一に効く |
使い方
--read オプションで仕様書を読み取り専用で渡します。
aider --model ollama_chat/qwen2.5-coder:7b --edit-format whole --no-auto-commits --read Spec.md
TUI 内からは /read-only コマンドでも追加できます。
> /read-only Spec.md
> Spec.md の仕様に従って csv_summary.py を実装して
こうすると aider は Spec.md の内容を参照しつつ、仕様書自体は書き換えません。
ローカル LLM コーディング支援の使いどころ
3 回にわたって試した結論として、CPU 推論のローカル LLM は 「ゼロから完璧なコードを生成してもらう」ツールではなく、「たたき台を高速に作ってもらう」ツール だと感じました。
向いている場面
- ボイラープレート(定型コード)の生成
- 「この言語でこういう処理ってどう書くんだっけ」という確認
- オフライン環境や API 利用料を気にしたくない場面
- プロトタイプの素早い作成
向いていない場面
- 複雑なロジックの一発生成
- 大きなコードベースの一括修正
- 即座にレスポンスが欲しい場面(CPU 推論の限界)
まとめ
| 項目 | 内容 |
|---|---|
| テスト内容 | Python で CSV 集計スクリプトを Spec.md の仕様に従って生成させた |
| 生成品質 | 骨格は正しいが仕様との不一致が 4 つ。手修正は必要 |
| 生成速度 | CPU 推論で 30 秒〜1 分(319 トークン) |
| 仕様書の効果 | 構造や関数設計には効くが、出力フォーマットや入出力仕様は読み落とされやすい |
| 使いどころ | たたき台の生成、ボイラープレート、オフライン環境 |
| 結論 | 無料・オフラインでここまで動くのは価値あり。過信せず「たたき台ツール」として使うのが正解 |
3 回にわたって aider + Ollama を試してきましたが、GPU なし・CPU 推論オンリーでも「使い方次第で十分役に立つ」というのが正直な感想です。完璧を求めると厳しいですが、コードの出発点を作ってくれるだけでもかなり楽になります。