0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ローカルLLM「Gemma 4」(12B) にApexを書かせてガチ採点してみた 〜VS Code + Ollama で7問検証〜

0
Last updated at Posted at 2026-07-27

先に結論

  • VS Code + Ollama でGoogleのオープンモデル Gemma 4(12Bモデル) を自分のPC上で動かし、Salesforce Apex のコード生成力を7問で採点しました
  • 総合スコアは 59点/70点(84%)
  • ふだんよく書くコード(メソッド・SOQL・トリガのバルク化・テスト)は、想像以上にしっかり書けました
  • 一方で あまり書く機会のない定型的なコード(Batch Apex の書き方など)では、実在しないメソッドや型を、さも本物のように作り出してしまう(=AIが事実でないことをそれらしく出力する「ハルシネーション」)
  • 一番危ないのは **「説明は正しいのに、コードは動かない」**パターン。アイデア出しの相棒としては優秀ですが、中身を確認せずにそのまま反映するのは危険です

はじめに

「クラウドのLLMに業務コードを貼りたくない」「APIの従量課金を気にせず試したい」——そんな動機でローカルLLMを触る人が増えています。

今回は、Googleのオープンモデル Gemma 4 をローカルで動かし、Salesforce Apex のコード生成にどこまで使えるのかを、実際に問題を解かせて採点する形で検証しました。単に「動いた」ではなく、Apex特有のベストプラクティス(バルク化・ガバナ制限・テストの作法)を押さえられるかにフォーカスしています。

使用モデルは gemma4:12b(4bit量子化、Ollama配布サイズ約7.6GB)です。


環境構築

全体構成

Ollamaがモデル本体を持つ「常駐エンジン」、VS Codeはそこに問い合わせる「窓口」という2層構成です。モデルはVS Codeの中には入りません。

┌──────────────┐
│  Ollama      │  モデル本体を保管し動かすエンジン(常駐)
│  (常駐)       │  localhost:11434 でAPIを待ち受ける
└──────┬───────┘
       │ HTTP API
┌──────┴───────┐
│  VS Code     │  Ollamaにリクエストを送るだけ
│ (Ollama拡張)  │  モデル本体は持っていない
└──────────────┘

手順

1. Ollama をインストール

ollama.com から Windows 版インストーラーを落として実行するだけ。インストール後は常駐サービスとして自動起動します。

2. モデルを取得

ollama pull gemma4:12b

C:\Users\<ユーザー名>\.ollama\models にダウンロードされます(約7.6GB)。

3. 動作確認

ollama run gemma4:12b

対話できればOK。/bye で抜けます。

4. VS Code に公式「Ollama」拡張を入れる

VS Codeの拡張機能タブで「Ollama」を検索し、発行元が ollama.com(認証済みバッジ付き) のものをインストールします。この拡張は「起動中のOllamaのモデルを、VS Codeのチャットのモデルピッカーに追加する」だけのシンプルなものです。

⚠️ 注意: この拡張は VS Code 1.120 以降 が必要です。古いバージョンだと「互換性がないためインストールできません」と出るので、その場合は code.visualstudio.com から最新版を上書きインストールしてください(設定・拡張はそのまま残ります)。

5. チャットで gemma4 を選ぶ

VS Codeのチャットを開き(Ctrl + Alt + I)、モデル選択のドロップダウンから gemma4:12b を選べば準備完了です。


検証方法

Apexの典型タスクを 難易度順に7問 用意し、各問題文をそのままチャットに投げて、返ってきたコードを 10点満点 で採点しました。採点は「動くか」だけでなく、Apex特有のベストプラクティスを押さえているかを重視しています。

# 難易度 テーマ 主な評価軸
1 メソッド基本 構文・null処理
2 易〜中 SOQL+Map バインド変数・SOQL1回
3 トリガ(ハンドラパターン) バルク化・薄いトリガ
4 ガバナ制限リファクタ ループ内SOQL/DMLを見抜けるか
5 中〜難 テストクラス @isTest・assert・例外検証
6 Batch Apex 非同期3メソッドの正確な実装
7 重複防止トリガ 既存重複+バッチ内重複の複合ロジック

検証結果

総合スコア:59/70(84%)

問題 難易度 得点 崩れ方
1 メソッド基本 9/10 引数nullの防御のみ欠落
2 SOQL/コレクション 易〜中 10/10 完璧(ガード句まで自発的に)
3 トリガ(ハンドラ) 10/10 完璧(バルク化・文脈ガード)
4 ガバナ制限リファクタ 8/10 分析◎/存在しない関数を混入
5 テストクラス 中〜難 9/10 作法◎/Test.startTest欠落のみ
6 Batch Apex 5/10 型名・シグネチャが総崩れ
7 重複防止トリガ 8/10 ロジック◎/トリガの受け渡しミス

前半(問1〜3)はほぼ満点。後半の難問(問4・6・7)で、はっきりとした「崩れ方の傾向」が見えました。以下、印象的だったケースを紹介します。


印象的だったケース

✅ 良かった例:問3 トリガのバルク化(10/10)

小型モデルが最も外しやすい「単一レコード前提のトリガを書いてしまう」罠にハマらず、Trigger.new をきちんと List として扱い、ロジックをハンドラに委譲できていました。将来のイベント追加を見越した Trigger.isBefore && Trigger.isInsert の文脈ガードまで自発的に付けており、定石を「理解して」書いていることが伺えます。

// トリガ本体は薄く
trigger AccountTrigger on Account (before insert) {
    if (Trigger.isBefore && Trigger.isInsert) {
        AccountTriggerHandler.handleBeforeInsert(Trigger.new);
    }
}

// ロジックはハンドラへ。for でバルク対応
public class AccountTriggerHandler {
    public static void handleBeforeInsert(List<Account> newList) {
        for (Account acc : newList) {
            if (String.isBlank(acc.Rating)) {
                acc.Rating = 'Warm';
            }
        }
    }
}

⚠️ 危険な例①:問4 存在しない関数の混入(8/10)

「ループ内SOQL/DML」の指摘とバルク化の考え方は完璧でした。しかし、生成コードの中にこんな行が紛れ込んでいました。

if (!contactsToUpdate.isEmpty()) {
    update_records(contactsToUpdate);   // ← こんなメソッドは存在しない
}

update_records()Apexには存在しない、AIが勝手に作り出した関数です。正しくは update contactsToUpdate; と書くだけ。考え方は合っているのに、いざコードにすると(存在しない関数のせいで)コンパイルが通らない——AIにありがちな「もっともらしい嘘」の典型例です。

❌ 最も崩れた例:問6 Batch Apex(5/10)

概念(QueryLocatorで分割、scopeを更新)は理解しているのに、定型APIの型名・シグネチャが総崩れしました。1つのクラスに複数のエラーが同居しています。

public class UpdateAccountDescriptionBatch implements Database.Batchable<SObject> {

    // ① Database.BatchContext という型は存在しない(正しくは BatchableContext)
    // ② 引数に変数名がない
    public Database.QueryLocator start(Database.BatchContext){
        return Database.getQueryLocator('SELECT Id, Description FROM Account');
    }

    // ③ execute の戻り値は void でなければならない
    // ④ Database.BatchUpdateResult も FULL_SUCCESS も存在しない(完全な捏造)
    public Database.BatchUpdateResult execute(Database.BatchContext bc, List<Account> scope){
        for (Account acc : scope) {
            acc.Description = 'Processed';
        }
        update scope;
        return Database.BatchUpdateResult.FULL_SUCCESS;
    }

    public void finish(Database.BatchContext bc){ }
}

正しくは以下です。

public class UpdateAccountDescriptionBatch implements Database.Batchable<SObject> {
    public Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator('SELECT Id, Description FROM Account');
    }
    public void execute(Database.BatchableContext bc, List<Account> scope) {
        for (Account acc : scope) {
            acc.Description = 'Processed';
        }
        update scope;
    }
    public void finish(Database.BatchableContext bc) { }
}

Batch Apexは実務でもトリガほど頻繁には書きません。学習データでの出現頻度がそのまま精度に出た格好です。

✅ 難問だが良かった例:問7 重複防止トリガ(8/10)

この問題の一番難しい「同じListの中に同一Emailが複数含まれるケース」を、出現回数をカウントする方式で正しく解いていました。

Map<String, Integer> emailCountInBatch = new Map<String, Integer>();
for (Contact c : newContacts) {
    if (String.isNotBlank(c.Email)) {
        Integer count = emailCountInBatch.containsKey(c.Email) ? emailCountInBatch.get(c.Email) : 0;
        emailCountInBatch.put(c.Email, count + 1);
    }
}
// ... 既存重複(SOQL 1回)とバッチ内重複(count > 1)の2段構えでチェック

「重複するEmailは全件エラーにする」という判断は、どちらが正しいレコードか決められない以上むしろ安全側で、設計として優れています。減点の理由は、登録時(before insert)のトリガなのに、ハンドラへ Trigger.oldMap(登録時は中身が空になる変数)を渡していた設計ミスです。動作自体はしますが、問3ではきちんとできていた正しい渡し方が、ここでは逆戻りしてしまっていました。


わかった3つの傾向

① 頻出コードは非常に強い

メソッド、SOQL/コレクション、トリガのバルク化、テストの作法、複合的なアルゴリズム——実務で日常的に書くパターンは、模範解答と同等か上回る品質でした。ガード句やメソッド分割など、指示していない配慮まで見せます。12Bのローカルモデルとしては期待以上です。

② あまり書かない「定型的なコード」で崩れる

Database.BatchableContext の正確な綴りや、execute メソッドの戻り値の型など、うろ覚えの定型的な書き方を、自信たっぷりに間違えます。今回の検証だけでも update_records()Database.BatchUpdateResult.FULL_SUCCESSDatabase.Deploy_Job_job といった、複数の「実在しない書き方」が出てきました。

③ 「解説は正しいのにコードは動かない」が最も危険

問4も問6も問7も、考え方の説明は正しいのです。だから出力を信用しやすい。しかしコードには幻のAPIや設計ミスが紛れています。「解説が正しいからコードも正しいはず」という思い込みが事故を招きます。


実務での使いどころ

用途 判定
日常的なApex(メソッド/SOQL/トリガ/テスト)の下書き ✅ 十分使える
ロジックの相談・アルゴリズム設計 ✅ 強い
Batch/Schedulable等の定型API記述 ⚠️ API名は必ず公式ドキュメントで照合
レビュー無しでのデプロイ ❌ 厳禁(幻のAPIでコンパイルが通らない)

ロジックの相棒としては優秀、ただしコンパイルが通る保証は自分で取る——これが正しい付き合い方だと感じました。特にローカル実行は入力コードが外部に送信されないという大きな利点があるので、機密性の高い業務コードの下書き用途とは相性が良いです。


【補足】他のローカルLLMは? 提供元・セキュリティをどう考えるか

「Gemma以外にもローカルLLMはあるし、コーディング用途ならもっと向いたモデルもあるのでは?」——その通りです。そして多くの人が気にするのが 「提供元の信頼性・セキュリティ」。Gemmaは天下のGoogle製で安心感がありますが、他社モデルはどうなのか。ここは誤解が多いポイントなので整理します。

コーディング特化モデルという選択肢

Gemma 4は文章も会話もこなす汎用モデルです。コード専用に鍛えられたモデルと比べると、コーディングでは一歩譲ります(今回 問6 のBatch Apexで崩れたのも、定型APIの正確さで差が出やすい部分)。主なコード特化モデルは以下です。

モデル 提供元 ライセンス 特徴
Gemma 4(今回) Google DeepMind 🇺🇸 Apache 2.0 汎用。素性・ライセンスが最もクリーン
Qwen3-Coder Alibaba 🇨🇳 Apache 2.0 オープンのコード特化で現状トップ級。HumanEval 88.4%
Codestral Mistral AI 🇫🇷 ⚠️非商用 コード補完が最強クラス・欧米系の有力候補。商用は別契約が必要
DeepSeek Coder DeepSeek 🇨🇳 概ね寛容 軽い割に高性能。338言語対応
Llama Meta 🇺🇸 独自(概ね商用可) 西側・実績豊富

大前提:ローカルで動かすモデルは、勝手に外部と通信しない

セキュリティを考えるうえで一番大事なのがこれです。**自分のPCで動かすモデルの正体は、ただの数値データ(重みファイル)**であって、プログラムではありません。Ollamaがそのデータを読み込んで計算しているだけなので、モデルが自分でインターネットにつないで、入力した内容をどこかに送信する——ということは、仕組み上まず起こりません。

  • クラウドのAI(ChatGPTなど)が心配されるのは、「入力した内容が、提供元のサーバーに送られて処理される」から
  • 一方ローカルでは、入力した内容がPCの外に一切出ません。 つまり Qwen(中国製)だろうと DeepSeek(中国製)だろうと、あなたの業務コードが海外に送られることはありません

「提供元がどこの国か」を過度に心配しなくていいのは、これが理由です。 Gemma(Google)と同じく、他社のモデルも、自分のPCで動かす限りは情報が漏れる経路そのものが存在しません。

気になる人は、PCのインターネットを切った状態(機内モードなど)でモデルを動かしてみると分かりやすいです。ネットを切っても普通に回答が返ってくるので、「外部と通信していない」ことを自分の目で確認できます。

では、残るリスクは何か

提供元の"素性"より、実務的に効くのはこの4点です。

① どこからダウンロードするか ← 最重要
モデルそのものより、「どこから入手するか」 のほうが危険です。第三者が細工を仕込んだファイルを配っている可能性があるためです。
Ollamaの公式ライブラリollama pull ...)か、Hugging Faceの公式アカウント(QwenならQwen、Mistralならmistralai)からのみ入手する。配布元がはっきりしない“非公式版”は避ける。

② ファイル形式
Ollamaが使う GGUF形式は"データ"であって"実行コード"ではないため、読み込んだだけで悪意あるコードが走ることはほぼありません(旧来のPyTorch pickle形式にはその余地がありました)。Ollama経由なら、この面は比較的安全。

③ ライセンス
"危険"というより"業務で使えるか"の問題。Gemma 4とQwenはApache 2.0で商用OKCodestral(Mistral)は非商用ライセンスで業務利用は別契約が必要です。業務コードに使うなら必ず確認を。

④ 回答の偏り・検閲
中国製のモデル(Qwen / DeepSeek)は、中国の規制に沿うように調整されており、政治的な話題などで回答を避けたり内容が偏ったりすることがあります。ただしこれは 情報漏洩の問題ではなく「回答内容が中立かどうか」の話で、Apexコードを書かせる用途ではまず問題になりません。

結論:どう選ぶか

  • 欧米製で揃えたいなら → Mistral(Codestral / Devstral)が有力候補。ただし商用利用にはライセンスの確認が必要
  • 業務で使い、ライセンス面もすっきりさせたいならGemma と Qwen(どちらもApache 2.0) が無難
  • 性能を最優先し、ローカル利用と割り切るなら → Qwen3-Coder(ローカルなら情報漏洩は起きないので、回答の偏りを許容できるなら実力トップ)

「ローカルなら情報漏洩は起きない。気にすべきは ①どこから入手するか ②ライセンス ③回答の偏り」——この3点が、ローカルLLMを選ぶときの実践的な判断軸になります。


まとめ

  • VS Code + Ollama で Gemma 4(12B)をローカル実行し、Apex生成を7問で採点 → 59/70
  • 頻出コードは優秀、低頻度の定型APIは捏造しやすいという明確な傾向
  • 一番の学びは「解説の正しさとコードの正しさは別物」。ローカル小型モデルは、アイデア出しとレビュー前提の下書きに割り切ると強い

クラウドの巨大モデルには推論力で及びませんが、**「手元で無料で動き、コードが外に出ない」**という価値は、日常のApex開発の下書きツールとして十分に実用的でした。


※本記事の検証は gemma4:12b(4bit量子化)で実施。モデルの出力は実行ごとに変動するため、同じ問題でも結果が変わる可能性があります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?