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(Ollama)でAIコード支援マクロを作る

0
Last updated at Posted at 2026-07-11

秀丸エディタ × ローカルLLM(Ollama)でAIコード支援マクロを作る

秀丸エディタの操作に慣れている方であれば、マクロの設定や外部プログラムとの連携はすぐにコツをつかめます。
この記事では、秀丸エディタからローカルLLM環境である Ollama を呼び出し、選択したコードに対して次のような処理を行う AIコード支援マクロ を作ります。

  • リファクタリング・簡易バグ修正
  • 日本語コメントの追加
  • コードの解説
  • テストコードの生成

生成AIの専門知識がなくても、

  1. AIをローカルで動かす準備:Ollama
  2. 秀丸とOllamaをつなぐ中継役:Python
  3. 秀丸側のUI:秀丸マクロ

という3ステップを踏むだけで、秀丸エディタをローカルAI対応の高機能エディタへ進化させることができます。

また、実運用を意識して以下の工夫も盛り込んでいます。

  • メニューで機能を切り替える
  • 元ファイルの拡張子から言語を自動判定する
  • 置換モードでは選択範囲を直接置換する
  • 解説・テスト生成モードでは結果を新規タブに表示する
  • AI通信エラー時に元コードを消さない
  • 一時ファイル名の衝突を避け、使い終わったら後片付けする
  • 失敗時に詳細エラーを確認できる

前提と注意点

この構成では、選択したコードをクラウドAIには送信せず、ローカルPC上で動作しているOllamaに送ります。そのため、クラウド型AIよりも機密コードを扱いやすい構成です。

ただし、業務コードで使う場合は次の点に注意してください。

  • 社内規程に反していないか確認してください
  • Ollamaの待ち受けポートを外部ネットワークに公開しないでください
  • AIの生成結果には誤りや仕様変更が含まれる場合があります
  • 置換モードを使った後は、必ず内容を確認してください。
  • 重要なコードでは、まず新規タブ出力で結果を確認してから手動で反映する運用をおすすめします

文字コードについて

このマクロは、編集中のファイルが UTF-8 であることを前提にしています。

秀丸エディタで、次の設定をしておくと安定します。

その他 → 動作環境 → ファイル → エンコード1

image.png

ここで、新規作成時の文字コードを UTF-8 にしておくのがおすすめです。Shift-JIS のファイルに対して使う場合は、末尾の「補足:Shift-JIS ファイルで使う場合」を参照してください。

上級者向け設定にチェックを入れていないと上記のメニューは表示されません。

また、選択範囲の保存に、

saveas ... selection

を使用します。

saveas ... selection 機能を使うには

① 秀丸エディタのバージョンが Ver 8.58 以降であること

このバージョンで追加された機能のため、古いバージョンではエラーになります。

② 通常の範囲選択であること

選択範囲を保存する機能ですが、「BOX選択(矩形選択)」や「複数選択」を行っている状態では機能しません。通常の文字選択をしている必要があります。


ローカルAI「Ollama」の構築

Ollama(オラマ)は、自分のPC上で安全にLLMを動かすためのソフトウェアです。コードが外部に送信されないため、業務コードでも比較的安心して使えます。

Ollamaのインストール

  1. Ollama公式サイトにアクセスします

    https://ollama.com/download
    
  2. 「Download」から Windows 版をダウンロードしてインストールします
    image.png

  3. インストールが完了すると、Ollamaがバックグラウンドで常駐します

※ Windows環境では、画面右下のタスクトレイにOllamaのアイコン(ラマの顔)が表示される場合があります。

AIモデルのダウンロード

Windowsの「コマンドプロンプト」または「PowerShell」を開き、以下を実行します。今回はプログラミングに特化した軽量で優秀なモデル qwen2.5-coder:7b を使用します。

PS C:\Users\toshi> ollama run qwen2.5-coder:7b
pulling manifest
pulling 60e05f210007: 100% ▕██████████████████████████████████████████████████████████▏ 4.7 GB
pulling 66b9ea09bd5b: 100% ▕██████████████████████████████████████████████████████████▏   68 B
pulling 1e65450c3067: 100% ▕██████████████████████████████████████████████████████████▏ 1.6 KB
pulling 832dd9e00a68: 100% ▕██████████████████████████████████████████████████████████▏  11 KB
pulling d9bb33f27869: 100% ▕██████████████████████████████████████████████████████████▏  487 B
verifying sha256 digest
writing manifest
success
>>> Send a message (/? for help)

初回は数GB程度のモデルファイルがダウンロードされます。完了すると、次のようなプロンプトが表示され、チャットできる状態になります。

確認できたら、次のように入力して終了します。(Ollama自体はバックグラウンドで動き続けます)

>>> /bye

1-3. モデルについて

この記事では、プログラミング向けモデルとしてqwen2.5-coder:7bを使います。

7Bモデルは、環境にもよりますが、おおむね5〜6GB以上の空きメモリを使用します(できればGPU)。

初回実行時、またはモデルがメモリから解放された後の実行時は、モデルの読み込みに時間がかかります。最初の1回だけは応答まで数十秒〜1分近くかかることがあり、2回目以降は数秒で返ってくるのが一般的です。

今回のPythonスクリプトでは、Ollama APIに keep_alive を指定し、一定時間モデルをメモリ上に保持しやすくしています。ただし、PCのメモリ状況やOllama側の設定により、常に保持されるとは限りません。


中継用Pythonスクリプトの作成

秀丸マクロから直接Ollama APIを扱うのは面倒なので、間に短いPythonスクリプトを挟みます。役割は次のとおりです。

  • 秀丸が保存した選択範囲の一時ファイルを読む
  • モードに応じたプロンプトを作る
  • OllamaのHTTP APIを呼び出す
  • 結果をUTF-8の一時ファイルに書き出す
  • エラー時は詳細メッセージを書き出して異常終了する

外部ライブラリのインストール(pip など)は不要で、Python標準ライブラリだけで動くようにしています。

Pythonのインストール

Pythonが入っていない場合は、Python公式サイトからWindows版をインストールしてください。

https://www.python.org/

インストール時は、最初の画面で次のチェックを 必ず 入れてください。(秀丸からpythonコマンドを呼び出すために必要です)

Add Python.exe to PATH

もしpythonコマンドがうまく動かない場合は、後述の秀丸マクロ内でpython.exeのフルパスを指定してください

スクリプトの配置

任意の場所にフォルダを作ります。

C:\hidemaru_ai

その中に、次の名前でファイルを作成します。文字コードはUTF-8で保存してください。

agent.py

以下を C:\hidemaru_ai\agent.py として保存します。

agent.py
# -*- coding: utf-8 -*-
# agent.py : 秀丸マクロ CodingAgent.mac から呼ばれる中継スクリプト
#
# 引数1: 入力ファイル(選択範囲のコード, UTF-8)
# 引数2: 出力ファイル(AIの回答を書き出す, UTF-8)
# 引数3: モード番号("1"〜"4")
# 引数4: 元ファイルのフルパス(拡張子から言語を判定。無題のときは空文字)

import sys
import os
import re
import json
import urllib.request
import urllib.error

# 使用するOllamaモデル名
MODEL = "qwen2.5-coder:7b"

# Ollamaのgenerate API
OLLAMA_URL = "http://localhost:11434/api/generate"

# Ollama応答待ちの上限秒数
# 大きなコードや初回ロードでは時間がかかるため、少し長めにしている。
TIMEOUT_SEC = 300

def fail(out_file, msg):
    """
    エラー内容を出力ファイルに書き、異常終了する。

    exit code を 1 にしておくことで、マクロ側が失敗を検知できる。
    これにより、置換モードで元コードを誤って消す事故を防ぐ。
    """
    try:
        with open(out_file, "w", encoding="utf-8") as f:
            f.write(msg)
    except Exception:
        pass

    sys.stderr.write(msg + "\n")
    sys.exit(1)

def detect_language_from_path(filepath):
    """
    ファイルパスから拡張子を取り出し、プロンプト用の言語名として使う。
    厳密な言語判定ではなく、LLMへのヒントとして使う。
    """
    ext = os.path.splitext(filepath)[1].lower()

    if not ext:
        return "不明な言語"

    mapping = {
        ".py": "Python",
        ".js": "JavaScript",
        ".ts": "TypeScript",
        ".jsx": "React JSX",
        ".tsx": "React TSX",
        ".html": "HTML",
        ".css": "CSS",
        ".scss": "SCSS",
        ".java": "Java",
        ".cs": "C#",
        ".cpp": "C++",
        ".c": "C",
        ".h": "C/C++ header",
        ".hpp": "C++ header",
        ".go": "Go",
        ".rs": "Rust",
        ".php": "PHP",
        ".rb": "Ruby",
        ".swift": "Swift",
        ".kt": "Kotlin",
        ".sql": "SQL",
        ".sh": "Shell script",
        ".bat": "Windows batch",
        ".ps1": "PowerShell",
        ".json": "JSON",
        ".xml": "XML",
        ".yaml": "YAML",
        ".yml": "YAML",
        ".md": "Markdown",
    }

    return mapping.get(ext, ext)

def extract_code_from_markdown(text):
    """
    LLMがMarkdownコードフェンス付きで返した場合に、最初のコードブロックだけを取り出す。

    置換モードでは、前置き説明やコードフェンスが混ざると元コードに余計な文字が入る。
    そのため、コードフェンスがあれば中身だけを取り出す。
    """
    m = re.search(r"```[^\n]*\n(.*?)\n?```", text, re.DOTALL)
    if m:
        return m.group(1)
    return text

def main():
    if len(sys.argv) < 4:
        sys.stderr.write("引数が不足しています。\n")
        sys.exit(1)

    in_file = sys.argv[1]
    out_file = sys.argv[2]
    mode = sys.argv[3]
    filepath = sys.argv[4] if len(sys.argv) > 4 else ""

    language = detect_language_from_path(filepath)

    # 秀丸が保存した選択範囲を読み込む。
    # utf-8-sig にしておくと、BOM付きUTF-8でも文字化けせずに読み込める。
    try:
        with open(in_file, "r", encoding="utf-8-sig") as f:
            code = f.read()
    except Exception as e:
        fail(out_file, f"入力ファイルの読み込みに失敗しました: {e}")

    if not code.strip():
        fail(out_file, "選択範囲が空です。コードを選択してから実行してください。")

    # 置換モード(1,2)では、Markdownや前置きを出力しないよう強く指示する。
    prompts = {
        "1": (
            f"以下の{language}コードをリファクタリングし、明らかなバグがあれば修正してください。\n"
            f"ただし、仕様を勝手に変更しないでください。\n"
            f"解説、前置き、Markdown、コードフェンスは出力しないでください。\n"
            f"修正後のコード本文だけを出力してください。\n\n"
        ),
        "2": (
            f"以下の{language}コードに、分かりやすい日本語コメントを追加してください。\n"
            f"コードの動作は変更しないでください。\n"
            f"解説、前置き、Markdown、コードフェンスは出力しないでください。\n"
            f"コメント追加後のコード本文だけを出力してください。\n\n"
        ),
        "3": (
            f"以下の{language}コードが何をしているか、日本語で詳しく解説してください。\n"
            f"処理の流れ、主要な変数・関数の役割、注意点、改善案があれば説明してください。\n\n"
        ),
        "4": (
            f"以下の{language}コードに対するテストコードを作成してください。\n"
            f"可能であれば、想定するテストフレームワーク、テスト観点、境界条件も説明してください。\n\n"
        ),
    }

    prompt_text = prompts.get(mode, "以下のコードを処理してください:\n\n") + code

    # Ollamaへのリクエスト。
    # temperature を低めにして、コード修正時の出力ブレを抑える。
    # keep_alive で、しばらくモデルをメモリに保持しやすくする。
    payload = {
        "model": MODEL,
        "prompt": prompt_text,
        "stream": False,
        "keep_alive": "30m",
        "options": {
            "temperature": 0.1,
            "num_ctx": 8192,
        },
    }

    req = urllib.request.Request(
        OLLAMA_URL,
        data=json.dumps(payload).encode("utf-8"),
        headers={"Content-Type": "application/json"},
    )

    try:
        with urllib.request.urlopen(req, timeout=TIMEOUT_SEC) as res:
            raw = res.read().decode("utf-8")
            response = json.loads(raw)

        if "error" in response:
            fail(out_file, f"Ollama API エラー: {response['error']}")

        result_text = response.get("response", "")

    except urllib.error.HTTPError as e:
        # HTTPErrorはURLErrorのサブクラスだが、こちらを先に捕まえることで
        # レスポンスボディ(Ollamaが返すJSONエラーメッセージ)を読み出せる。
        # モデル名の誤りなど、原因がここに書かれていることが多い。
        try:
            body = e.read().decode("utf-8", errors="replace")
        except Exception:
            body = "(エラー本文を読み取れませんでした)"

        detail = body
        try:
            body_json = json.loads(body)
            if isinstance(body_json, dict) and "error" in body_json:
                detail = body_json["error"]
        except Exception:
            pass

        fail(
            out_file,
            "AI通信エラーが発生しました(HTTPエラー)。\n"
            f"ステータスコード: {e.code}\n"
            f"詳細: {detail}\n\n"
            "確認してください:\n"
            f"・モデル {MODEL} が `ollama list` に表示されるか\n"
            f"・agent.py の MODEL 名と Ollama側のモデル名が一致しているか\n"
        )
    except urllib.error.URLError as e:
        fail(
            out_file,
            "AI通信エラーが発生しました。\n"
            f"詳細: {e}\n\n"
            "確認してください:\n"
            f"・Ollama が起動しているか\n"
            f"・Ollama API が {OLLAMA_URL} で待ち受けているか\n"
            f"・モデル {MODEL} がインストール済みか\n"
        )
    except json.JSONDecodeError as e:
        fail(
            out_file,
            "Ollamaからの応答をJSONとして解析できませんでした。\n"
            f"詳細: {e}\n"
        )
    except Exception as e:
        fail(
            out_file,
            "AI処理中に予期しないエラーが発生しました。\n"
            f"詳細: {e}\n"
        )

    # 置換モードでは、万一Markdownコードフェンスが付いた場合に除去する。
    if mode in ("1", "2"):
        result_text = extract_code_from_markdown(result_text)

    result_text = result_text.strip("\n")

    # 空回答は置換モードでは危険(元コードを消すだけ)なのでエラー扱いにする。
    if not result_text.strip():
        fail(out_file, "AIから有効な回答が得られませんでした。")

    try:
        with open(out_file, "w", encoding="utf-8") as f:
            f.write(result_text)
    except Exception as e:
        fail(out_file, f"出力ファイルの書き込みに失敗しました: {e}")

if __name__ == "__main__":
    main()

このPythonスクリプトのポイント

  • urllib.request(標準ライブラリ)を使うため、追加ライブラリは不要です
  • Ollamaの /api/generate を呼び出します
  • timeout を設定し、Ollamaがハングしても秀丸が長時間固まらないようにしています
  • temperature を低くして、コード修正結果を安定させます
  • keep_alive により、モデルの再ロードを減らします
  • 拡張子からの言語判定を辞書化し、.py → Python のように分かりやすい言語名をLLMへのヒントとして渡します。拡張子なし・無題ファイルでも崩れません
  • エラー時は出力ファイルに詳細を書いて exit 1 します。マクロ側が終了コードを見て、失敗時には置換しません
  • 入力読み込みは utf-8-sig にして、万一BOMが付いても文字化けしないようにしています
  • 置換モードではMarkdownコードフェンスを除去し、空回答はエラー扱いにします

秀丸マクロの作成

次に、秀丸エディタ側のマクロを作ります。このマクロは次の処理を行います。

  1. 選択範囲があるか確認
  2. メニューを表示
  3. 選択範囲をUTF-8の一時ファイルに保存
  4. Pythonスクリプトを同期実行
  5. 成功・失敗を確認
  6. 置換モードなら選択範囲をAI結果で置換
  7. 解説・テスト生成モードなら新規タブに結果を表示
  8. 使い終わった一時ファイルを削除

マクロファイルの作成

秀丸エディタのマクロフォルダに、次の名前で保存します。マクロファイル自体は、秀丸が正しく読める文字コード(一般的には Shift-JIS または BOM付きUTF-8)で保存してください。

CodingAgent.mac

以下を CodingAgent.mac として保存し、$python_script を手順2で保存した場所に合わせて書き換えてください。

CodingAgent.mac
// ============================================================
//  CodingAgent.mac
//  秀丸エディタ × ローカルLLM(Ollama) 連携 AIコード支援マクロ
//
//  対応: 秀丸エディタ Ver 8.58 以降(saveas ... selection を使用)
//
//  機能:
//    1. リファクタリング・バグ修正   → 選択範囲を置換
//    2. コメント追加                 → 選択範囲を置換
//    3. コード解説                   → 新規タブに表示
//    4. テストコード生成             → 新規タブに表示
// ============================================================


// ------------------------------------------------------------
// 選択範囲チェック
// ------------------------------------------------------------
if (! selecting) {
    message "対象のコードを選択してから実行してください。";
    endmacro;
}


// ------------------------------------------------------------
// メニュー表示(拡張アイデア: メニューによる機能切り替え)
// ------------------------------------------------------------
menu "1. リファクタリング・バグ修正 (選択範囲を置換)",
     "2. コメントを追加             (選択範囲を置換)",
     "3. コードを詳しく解説         (新規タブ)",
     "4. テストコードを生成         (新規タブ)";

if (result == 0) {
    endmacro;   // キャンセル時
}

// menu の result は、他の文を実行する前に退避しておく
#mode = result;


// ------------------------------------------------------------
// パス設定
// ------------------------------------------------------------

// Python実行コマンド。通常は "python" のままで動作します。
// うまく動かない場合は、python.exe のフルパスに変更してください。
//   例:
//   $python_exe = "C:\\Users\\YourName\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";
$python_exe = "python";

// ▼ 手順2で保存した agent.py のフルパスに合わせて変更してください。
$python_script = "C:\\hidemaru_ai\\agent.py";

// 一時ファイル名。
// 固定名にすると、複数ウィンドウからの同時実行や前回結果との衝突が起きやすい。
// tickcount を使って実行ごとに異なる名前にする。
$temp_base = getenv("TEMP") + "\\hm_llm_" + str(tickcount);
$temp_in   = $temp_base + "_in.txt";
$temp_out  = $temp_base + "_out.txt";


// ------------------------------------------------------------
// 選択範囲をUTF-8で一時ファイルに保存
// ------------------------------------------------------------
// saveas に selection を付けると、選択範囲だけを別ファイルに書き出せる。
// クリップボードもウィンドウも汚さず、現在のファイル名も変わらない。
saveas $temp_in, utf8, selection;

if (! result) {
    message "選択範囲の保存に失敗しました。\n"
          + "BOX選択や複数選択ではなく、通常の範囲選択で実行してください。";
    endmacro;
}


// ------------------------------------------------------------
// Python経由でOllamaを同期実行
// ------------------------------------------------------------
// title の第2パラメータ 1 = ステータスバーに表示。マクロ終了時に自動で元へ戻る。
// (同期実行中は、秀丸が一時的に反応しなくなります)
title "AIが処理中です… しばらくお待ちください", 1;

// 現在のファイルパス filename2 を渡す(拡張子から言語を自動認識させる)。
// 無題ファイルの場合は空文字相当になる。
$cmd = "\"" + $python_exe + "\" "
     + "\"" + $python_script + "\" "
     + "\"" + $temp_in + "\" "
     + "\"" + $temp_out + "\" "
     + str(#mode) + " "
     + "\"" + filename2 + "\"";

runsync2 $cmd;


// ------------------------------------------------------------
// 実行結果の検査
// ------------------------------------------------------------
// result が false         → python の起動に失敗(PATH未設定など)
// getresultex(9) が 0以外 → agent.py がエラー終了(Ollama停止・空回答など)
//
// 失敗した場合は置換に進まない。これにより、AI通信失敗時に選択範囲を
// 消してしまう事故を防ぐ。
if (result == 0 || getresultex(9) != 0) {
    message "AI処理に失敗しました。\n"
          + "詳細が出力されている場合は、別タブで開きます。\n\n"
          + "確認してください:\n"
          + "・Ollama が起動しているか\n"
          + "・モデルがインストール済みか\n"
          + "・Python が実行できるか(PATHまたはフルパス)\n"
          + "・agent.py のパスが正しいか";

    // Python側が詳細エラーを書き込めている場合は、それを新規タブに表示する。
    if (existfile($temp_out)) {
        newfile;
        insertfile $temp_out;
    }

    // 一時ファイルの後片付け
    if (existfile($temp_in))  { deletefile $temp_in; }
    if (existfile($temp_out)) { deletefile $temp_out; }
    endmacro;
}


// 入力用の一時ファイルはもう不要なので削除
if (existfile($temp_in)) { deletefile $temp_in; }


// ------------------------------------------------------------
// 実行結果の反映(拡張アイデア: アウトプット制御)
// ------------------------------------------------------------
if (#mode == 1 || #mode == 2) {

    // --------------------------------------------------------
    // 置換モード
    //   選択範囲を削除し、AIの回答をその場に挿入する。
    //   ウィンドウを切り替えないので、選択範囲が確実に保持される。
    //
    //   注意: AIの生成結果には誤りが含まれる可能性があります。
    //         実行後は必ず内容を確認してください(Undoで元に戻せます)。
    // --------------------------------------------------------
    disabledraw;
    delete;
    insertfile $temp_out;
    enabledraw;

} else {

    // --------------------------------------------------------
    // 新規タブ表示モード
    //   一時ファイルそのものを openfile すると、次回実行時の上書きと
    //   干渉することがある。そのため、無題の新規タブを作り、
    //   そこへ結果だけを挿入する。
    // --------------------------------------------------------
    newfile;
    insertfile $temp_out;
}


// 出力用の一時ファイルも読み終わったので削除
if (existfile($temp_out)) { deletefile $temp_out; }

endmacro;

このマクロのポイント

以前よくあった、

copy → newfile → paste → saveas → close

という一連の流れは使っていません。代わりに、

saveas $temp_in, utf8, selection;

の1行で、選択範囲だけを一時ファイルに保存しています。これにより次の利点があります。

  • クリップボードを汚さない
  • 余計なウィンドウを開かない
  • 選択範囲を確実に保持できる
  • 処理が単純になる

その他の要点は次のとおりです。

  • 実行前の進捗表示に messageを使うと OK を押すまで閉じないため、代わりにtitle(ステータスバー)でAI処理中を表示しています
  • runsync2の後にresultとgetresultex(9)で成否を判定し、失敗時は置換に進みません。Ollama未起動・モデル無し・Python起動失敗などでも、元コードを誤って削除する事故を防げます
  • 失敗時に、Python側が書き出した詳細エラーを新規タブで確認できます
  • 一時ファイル名は tickcountで毎回変え、使い終わったらdeletefileで後片付けするため、%TEMP%に古いファイルが溜まりません。

実行してみる

テスト用ファイルを開く

秀丸エディタで、適当なソースコードを開きます(例:sample.pytest.jsmain.cpp)。UTF-8のファイルで試すのがおすすめです。

コードを選択する

処理したいコードをマウスまたはキーボードで選択します。最初は、ファイル全体ではなく、関数単位やクラス単位など、比較的小さい範囲で試してください。

マクロを実行する

CodingAgent.mac を実行します。よく使う場合は、秀丸エディタ側でマクロ登録し、ショートカットキーを割り当てておくと便利です。

image.png

メニューから処理を選ぶ

次のメニューが表示されます。

image.png

  • 1. リファクタリング・バグ修正:選択範囲をAIが修正したコードで置換します。実行後は必ず内容を確認してください(Undoで元に戻せます)。
  • 2. コメントを追加:選択範囲のコードに日本語コメントを追加し、元の選択範囲を置換します。
  • 3. コードを詳しく解説:元コードは変更せず、新規タブに解説を表示します。
  • 4. テストコードを生成:元コードは変更せず、新規タブにテストコード案を表示します。

ステータスバーに「AIが処理中です…」と出たあと、コードが自動で書き換わる、あるいは新しいタブに結果が表示されます。

使い方のコツ

最初は小さな範囲で試す

LLMには一度に扱える文脈量の上限があります。大きすぎるコードを渡すと、次のような問題が起きることがあります。

  • 出力が途中で切れる
  • 一部のコードだけが修正される
  • 前半の内容を忘れる
  • 余計な説明が混ざる
  • 仕様が変わってしまう

最初は、1つの関数・1つのメソッド・1つのクラス、あるいは100〜300行程度までのコードを目安に実行するのがおすすめです。

重要なコードでは新規タブ出力を使う

リファクタリングやコメント追加は便利ですが、AIの出力をそのまま信用しすぎないでください。業務コードや重要な処理では、次の運用がおすすめです。

  1. まず「コード解説」や「テストコード生成」で内容を確認する
  2. 必要ならプロンプトを調整する
  3. 小さい範囲で置換モードを使う
  4. 置換後に必ず差分や動作を確認する

より安全にしたい場合は、リファクタリング結果も直接置換せず新規タブに出すよう改造してもよいです(末尾「補足」の方法2を参照)。

生成結果が遅い場合

初回実行時はモデルの読み込みに時間がかかります。また、選択範囲が大きい、PCのメモリが不足している、GPUを使えていない、モデルが大きい、他のアプリがメモリを多く使っている、といった場合も遅くなります。動作が重い場合は、小さいモデルに変えることも検討してください。

モードを増やす

機能を増やしたい場合は、Python側の prompts と、秀丸マクロ側の menu を同じ番号で増やします。例えば、セキュリティチェックを追加する場合です。

Python側にプロンプトを追加

agent.pyprompts"5" を追加します。

"5": (
    f"以下の{language}コードをセキュリティ観点でレビューしてください。\n"
    f"脆弱性、入力検証不足、例外処理不足、認可・認証の問題があれば指摘してください。\n\n"
),

秀丸マクロ側のメニューを追加

CodingAgent.macmenu に5番を追加します。

menu "1. リファクタリング・バグ修正 (選択範囲を置換)",
     "2. コメントを追加             (選択範囲を置換)",
     "3. コードを詳しく解説         (新規タブ)",
     "4. テストコードを生成         (新規タブ)",
     "5. セキュリティレビュー       (新規タブ)";

置換モードの条件はそのままにします。

if (#mode == 1 || #mode == 2) {

これにより、5番は自動的に新規タブ表示モードになります。このように、prompts の辞書を書き換えるだけで機能(プロンプト)を無限に増やせます。

モデルを変更する

使用するモデルは、agent.py の次の部分で指定しています。

MODEL = "qwen2.5-coder:7b"

別のモデルを使いたい場合は、ここを書き換えます。

MODEL = "qwen2.5-coder:14b"

大きいモデルほど高精度になりやすい一方で、メモリ消費と応答時間が増えます。モデルを変更した場合は、事前にOllamaで取得しておきます。

ollama run qwen2.5-coder:14b

トラブルシュート

Pythonが起動しない

マクロ実行時にPythonが見つからない場合は、コマンドプロンプトで次を確認します。

python --version

バージョンが表示されない場合は、PATH設定ができていません。対処は2つあります。

方法1: Pythonを再インストールしてPATHを通す

Pythonインストーラで、次にチェックを入れてインストールします。

Add Python.exe to PATH

方法2: マクロ内でpython.exeのフルパスを指定する

CodingAgent.mac の次の部分を変更します。

$python_exe = "python";

例えば次のようにします(パスは環境に合わせてください)。

$python_exe = "C:\\Users\\YourName\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";

Ollamaに接続できない

次を確認してください。

ollama list

モデル一覧が表示されれば、Ollama自体は動作しています。対象モデルがない場合は、次を実行します。

ollama run qwen2.5-coder:7b

モデル名が違う

agent.py のモデル名と、Ollamaにインストールされているモデル名が一致している必要があります。ollama list の結果と、agent.py 側の MODEL = "qwen2.5-coder:7b" が一致しているか確認してください。

結果がMarkdown付きで返ってくる

置換モードでは、Python側で最初のコードフェンスを取り出す処理(extract_code_from_markdown())を入れており、プロンプトでも「解説、前置き、Markdown、コードフェンスは出力しないでください。」と指示しています。それでも余計な文章が混ざる場合は、プロンプトをさらに強くしてください。

あなたの出力は、エディタにそのまま挿入されます。
コード以外の文字を1文字も出力しないでください。

処理が遅い

選択範囲を小さくする、7Bより小さいモデルを使う、PCのメモリ使用量を減らす、他の重いアプリを閉じる、GPUを使える環境にする、初回ロード後に再実行してみる、などを試してください。

エラー詳細を確認したい

今回のマクロでは、Python側でエラー内容を一時ファイルに書き出します。マクロ実行に失敗した場合、詳細が書き込まれていれば新規タブに表示されます。例えば次のようなエラーが表示されます。

AI通信エラーが発生しました。
詳細: <urlopen error ...>

確認してください:
・Ollama が起動しているか
・Ollama API が http://localhost:11434/api/generate で待ち受けているか
・モデル qwen2.5-coder:7b がインストール済みか

補足:Shift-JIS ファイルで使う場合

このマクロは、編集中のファイルがUTF-8であることを前提にしています。Shift-JISのファイルで置換モードを使うと、insertfile でUTF-8の回答を読み込む際に、環境や設定によって文字化けすることがあります。その場合は、次のいずれかで回避してください。

方法1: 対象ファイルをUTF-8に変換してから使う

秀丸エディタで対象ファイルを開き、文字コードをUTF-8に変更して保存します。

ファイル → エンコードの種類 → UTF-8

その後、マクロを実行します。秀丸内部の文字列はUnicodeで扱われるため、いったんタブに正しく開いてしまえば、貼り付け先のファイルの文字コードに関係なく文字化けしません。

方法2: 置換モードを使わず、新規タブ出力にする

安全を優先する場合は、1番・2番の結果も直接置換せず、新規タブに表示するように変更します。CodingAgent.mac の次の部分を、

if (#mode == 1 || #mode == 2) {
    disabledraw;
    delete;
    insertfile $temp_out;
    enabledraw;
} else {
    newfile;
    insertfile $temp_out;
}

すべて新規タブ表示にします。

newfile;
insertfile $temp_out;

元コードを直接変更しないため、文字化けや誤置換のリスクを下げられます。まずはUTF-8のファイルで動作を確認し、必要になったら上記を検討するのがおすすめです。

まとめ

この構成により、秀丸エディタからローカルLLMを呼び出し、選択したコードに対してAI支援を行えるようになります。

秀丸エディタ
  ↓
秀丸マクロ (CodingAgent.mac)
  ↓
Pythonスクリプト (agent.py)
  ↓
Ollama
  ↓
ローカルLLM (qwen2.5-coder:7b)

できることは、選択範囲のリファクタリング、コメント追加、コード解説、テストコード生成、そしてプロンプト追加による機能拡張です。実運用を考慮して、次の安全策も入れています。

  • AI通信失敗時に置換しない
  • 空回答時に置換しない
  • 一時ファイル名の衝突を避け、使用後に削除する
  • 失敗時に詳細エラーを表示する
  • 置換モードではMarkdown除去を行う
  • プロンプトでコード以外を出さないよう指示する

まずはUTF-8の小さなコード範囲で試し、動作を確認してから、プロンプトやメニューを自分の用途に合わせて拡張していくのがおすすめです。

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?