前回はAI市場の動向(p934)を調べ、その中で「クラウド型AIは便利だが有料・トークン消費が課題」「ローカルLLMが実用域に入ってきた」という話をしました。今回はその続きとして、“自分のPCを操作してくれるAIエージェント”を、完全ローカル(ネット上のAIに頼らない)で作ってみます。使うのは2025年末に出た新しめのローカルモデル Ministral 3 14B です。

🤖 この記事でわかること
・ローカルLLM(Ministral 3 14B・Q4)に“道具”を持たせてエージェント化する方法
Web検索・ファイル読み書き・PC操作(マウス/キーボード)・RAG記憶・画像理解を1つのアプリに統合
・さらにCLI/MCP操作(Unity・Blender向け)とClaude Code連携古い知識の自動更新
・暴走させないための緊急停止重要操作の確認の作り方
・Mac(M4)での実機動作・つまずき・対処。コードと設定値つき

作るもの:完全ローカルのAIエージェント

スポンサーリンク

完成したのがこの「ローカルAIエージェント」。見た目はシンプルなチャットアプリですが、中身はネットに繋がっていなくても(モデルは手元・検索だけ任意でネット利用)動く、道具を使えるAIです。

ローカルAIエージェントの起動画面
起動画面。Ministral 3 14Bを読み込むと「準備OK」に。下部に緊急停止・PC操作の許可トグル・記憶カウンタ・学習リセット

このアプリ1つで、次のことがすべて手元のPCの中でできます。

  • テキストで質問・相談(雑談や計算はそのまま回答)
  • 知らないことはWeb検索して答える(検索結果は学習データに蓄積)
  • ファイルを読む・書く(txt/csv/xlsx/pdf/画像)
  • PCを操作(マウス・キーボード・画面)=ここがエージェントの肝
  • コマンド(CLI)やMCPで操作:Unity・Blenderなどを画面クリックより正確・高速に
  • Claude Codeに委譲:難しいコーディングはクラウドの強力なAIへ任せる
  • 記憶(RAG):覚えた知識や指示を次回に活かす/古くなったら自動で再検索・更新
  • 画像を理解(写真やグラフを読む)

そして強い機能だからこそ、いつでも止められる・勝手にやらせないという安全装置も入れています(後述)。

なぜ作る?クラウド型エージェントの“有料・消費”問題

最近は「PC操作までやってくれるAIエージェント」のサービスが増えました。とても便利ですが、本格的に使おうとすると――

  • 有料プランが前提:常用するなら月額や従量課金がかかる
  • トークン消費が膨大:PCの作業を任せるほど、AIに送る文章量が増えて消費が跳ね上がる
  • プライバシー:画面やファイルの内容を外部に送ることになる

前回(p934)で見たとおりローカルLLMは急速に進化しています。「日常のPC作業の補助くらいなら、手元のAIで十分では?」――これを確かめるのが今回の目的です。完全ローカルなら無料・無制限・プライバシーも安心です。

全体構成と用意するもの

構成はこの通り。p933と同じ llama-cpp-python を土台に、道具とGUIを足してエージェントにします。

部品役割
Ministral 3 14B(gguf・Q4)頭脳。Mistral AIが2025年末に公開した、画像も読める高性能ローカルモデル
llama-cpp-python(MTMD)モデルを動かす。道具(function calling)と画像を1インスタンスで両対応
PySide6デスクトップGUI(Windows・Mac共通)
pyautoguiマウス/キーボード/画面の操作(+緊急停止のFAILSAFE)
sentence-transformersRAG用の文章ベクトル化(多言語e5・日本語対応)
ddgs / openpyxl / pypdfWeb検索/Excel・PDFの読み取り
subprocess / 自前MCPクライアントCLIコマンド実行/MCPサーバ連携(Unity・Blender等)
claude CLI難しい実装をClaude Codeに委譲する連携(任意)

モデルはQ4量子化で約8.2GB。画像用の補助ファイル(mmproj)が約0.9GB。RTX3060のVRAM 12GBに収まり、M4/32GBでも余裕です。

# 必要ライブラリ
pip install llama-cpp-python PySide6 pyautogui sentence-transformers ddgs openpyxl pypdf

# モデル本体(Q4_K_M 約8.2GB)と画像用mmproj(約0.9GB)をダウンロード
#   from: huggingface.co/unsloth/Ministral-3-14B-Instruct-2512-GGUF
#   Ministral-3-14B-Instruct-2512-Q4_K_M.gguf
#   mmproj-F16.gguf

※ Windowsで RTX3060のGPU を使うときは、CUDA対応版の llama-cpp-python を入れます(環境変数 CMAKE_ARGS=-DGGML_CUDA=on でビルド、または対応wheel)。Macは追加設定なしでGPU(Metal)が使われます。

実装①:Ministralに“道具”を持たせる(function calling)

エージェントの核心は「AIに道具を持たせ、必要なときだけ自分で使わせる」こと。Ministral 3はfunction calling(道具呼び出し)に対応しているので、道具の一覧を渡すだけで「検索が要るか/自分で答えられるか」をAIが判断してくれます。

ポイントは、モデルに付属のテンプレートを使うこと。llama-cpp-pythonのMTMDハンドラでモデルとmmprojを読み込むと、道具呼び出しと画像理解の両方が1つのインスタンスで動きます(VRAM12GBに収めるうえで重要)。

from llama_cpp import Llama
from llama_cpp.llama_chat_format import MTMDChatHandler

handler = MTMDChatHandler(clip_model_path="models/mmproj-F16.gguf")  # 画像対応
llm = Llama(model_path="models/Ministral-3-14B-Instruct-2512-Q4_K_M.gguf",
            n_ctx=8192, n_gpu_layers=-1, chat_handler=handler)  # -1=全部GPUへ

# 道具の定義(JSONスキーマ)。これを渡すだけでAIが使い分ける
tools = [{"type":"function","function":{
    "name":"web_search","description":"最新情報や知らない情報をネット検索する",
    "parameters":{"type":"object","properties":{"query":{"type":"string"}},"required":["query"]}}}]

out = llm.create_chat_completion(
    messages=[{"role":"system","content":"必要なら道具を使う日本語アシスタント"},
              {"role":"user","content":"今日の東京の天気を調べて"}],
    tools=tools, tool_choice="auto")
💡 ハマりどころ:道具の出力をどう拾うか
このモデルは道具を使うとき、標準のtool_callsではなく web_search{"query":"…"} のような形でも返してきました。そこで「tool_calls / name{json} / {"tool":…} のどれでも拾える」パーサを書いて吸収しています。これで「天気→検索する」「計算→そのまま答える」を安定して判断するようになりました。

実装②:エージェントのループと安全機構

エージェントは「考える→道具を使う→結果を見てまた考える」をくり返します(ReActと呼ばれる方式)。無限ループや暴走を防ぐため、道具の使用回数に上限を設けます。

for step in range(MAX_STEPS):          # 道具の使用回数に上限(暴走防止)
    self.stop.checkpoint()             # ← 緊急停止が押されていたら即中断
    msg = llm.chat_message(messages, tools=TOOLS)
    name, args = parse_tool_call(msg)
    if not name:                       # 道具を使わない=最終回答
        return msg["content"]
    result = self.run_tool(name, args) # 道具を実行(危険操作は確認を挟む)
    messages.append({"role":"user","content": f"[ツール結果]\n{result}"})

PCを操作できるAIは強力なので、2本の安全装置を必ず入れました。

(1) 緊急停止:いつでも止められるようにします。①画面のSTOPボタンEscキー、さらに②pyautoguiのFAILSAFE(マウスを画面の四隅に勢いよく当てると例外で即停止)。ループの要所でcheckpoint()を呼び、停止要求が出ていたら処理を打ち切ります。

import pyautogui
pyautogui.FAILSAFE = True   # マウスを画面四隅へ=物理的な非常ブレーキ
pyautogui.PAUSE   = 0.3     # 各操作の間に必ず間を空ける(人が割り込める)

(2) 重要操作の確認:ファイル書き込み・クリック・入力・「削除/送信」などの取り返しのつかない操作の前に、必ず確認ダイアログを出します。さらに、マウス/キーボード操作は画面下の「PC操作を許可」トグルがONのときだけ動くようにし、普段は誤操作しないようにしています。

重要操作の前に出る確認ダイアログ
ファイル書き込みやクリックなど“取り返しのつかない操作”の前に、必ず人間の承認を取る

実装③:Web検索・RAG記憶・ファイル・画像理解

残りの道具も、それぞれ小さな部品にしてエージェントに持たせます。

  • Web検索ddgs(DuckDuckGo)でAPIキー不要・無料。検索結果はそのまま記憶(RAG)に蓄積して、次回以降に再利用します(=“学習”)。
  • RAG記憶:文章を多言語e5でベクトル化し、SQLiteに保存。質問が来たら意味の近い記憶を思い出して回答に添えます。学習データのリセットもボタン1つ。
  • 恒久メモリ:「常体で答えて」等のずっと守る指示を保存し、毎回プロンプト先頭に差し込みます。
  • ファイル:txt/csv/xlsx(openpyxl)/pdf(pypdf)/画像を読み、結果をファイルに書き出し。
  • 画像理解:mmprojのおかげで写真やグラフを読めます(後述のデモ参照)。
# RAG記憶(要点):文章をベクトル化してSQLiteへ。質問時は意味の近い順に思い出す
def remember(self, text):
    vec = self.embed(text)                       # multilingual-e5 で数値ベクトル化
    db.execute("INSERT INTO knowledge(text, vec) VALUES(?,?)", (text, vec.tobytes()))

def recall(self, query, k=3):
    qv = self.embed(query)
    scored = [(float(np.dot(qv, v)), t) for t, v in all_rows]  # コサイン類似度
    return sorted(scored, reverse=True)[:k]      # 近いものを上位k件

動かす:検索・記憶・画像・PC操作

実際に使ってみます。まずは「知らないことはWeb検索して答える」。下の例では、最新モデルの情報を自分で検索し、結果を要約しています(道具の使用が黄色で見えます)。

エージェントがWeb検索して回答する様子
「17×23は?」は道具を使わず即答、「Ministral 3とは?」は自分でweb_searchして要約。判断はAIに任せている

検索した内容は記憶(RAG)に貯まるので、あとで「さっき調べたこと、思い出して」と言うと、ちゃんと引っ張り出してまとめてくれます(右下の「記憶: 知識3」がそれ)。

記憶(RAG)から思い出してまとめる様子
recall(思い出す)で、さきほど検索した内容を記憶から取り出して回答。学習データはボタンでリセットも可能

画像も読めます。手元の売上グラフ画像を渡して「読んで傾向を教えて」と頼むと――

グラフ画像を読み取って傾向を答える様子
棒グラフの画像を渡すと「最多はB店200万円、最少はC店90万円」と数値・傾向まで読み取った(完全ローカルの画像理解)

そして本命のPC操作。「入力欄をクリックして、この文を入力して」と頼むと、AIが自分でclick→type_textの道具を選び、確認を経て実行します。

エージェントがクリックと入力の操作を行う様子
「PC操作を許可」をONにすると、AIが自分でclick/type_textを選択。実行前に確認ダイアログを挟む

使い勝手も少し整えました。14Bは応答に少し時間がかかるので、「今は処理中か/次を入力してよいか」がひと目で分かるインジケータを付けています(処理中はオレンジ・入力できるときは緑)。回答は改行や箇条書きで読みやすく整形し、チャットをクリアするボタンも用意しました。

処理完了で緑インジケータ・整形された回答・チャットクリアボタン
緑の「● 入力できます」で次の入力OK(処理中はオレンジの「● 処理中…」)。回答は改行・箇条書きで整形、右に「チャットをクリア」

発展:知識の更新・CLI/MCP・Claude Code連携

ここまでで“使える”エージェントになりましたが、実際に使い込むと欲しくなる機能を3つ足しました。

(1) 古い知識を自動で更新する。検索で溜めた知識は時間が経つと古くなります。そこで各記憶に保存日時を持たせ、7日を過ぎたweb由来の知識は「古い情報」と印を付けます。AIは古い記憶を見つけると、refresh_knowledge古い記憶を消して再検索→最新で上書きします。

def refresh_knowledge(query):
    removed = memory.forget(query)          # queryに近い古い記憶を削除(重複防止)
    results = web_search(query)             # 最新を取り直す
    for r in results: memory.remember(r)    # 新しい知識として保存し直す

(2) 画面操作の代わりにCLI/MCPで操作するUnityやBlenderのような重いアプリは、マウスでカチカチやるより“コマンド”で動かす方が速くて正確です。そこで2つの道具を追加しました。

  • run_command(CLI):シェルコマンドを実行。例:blender --background --python script.pyUnity -batchmode -executeMethod Build.Run
  • mcp_call(MCP)MCP(Model Context Protocol)は、AIに外部ツールを提供する共通規格。BlenderやUnity用のMCPサーバを繋げば、その道具をそのままAIが使えます。今回は依存を増やさず自前の軽量MCPクライアントを書き、繋いだサーバの道具を“エージェントの道具”として自動登録しました。
エージェントがMCPとCLIコマンドを使う様子
上:MCPサーバの道具(system_info)を呼び出し/下:run_commandでCLI実行。どちらも実行前に確認を挟む(上部に「MCP 1台」)

※ MCP設定は mcp_config.json(Claude Desktopと同じ形式)に書くだけ。記事では動作確認用の小さなデモMCPサーバを用意しましたが、ここを blender-mcp / unity-mcp などの本物のサーバに差し替えれば、そのまま3D制作に使えます。

(3) 難しい実装はClaude Codeに任せる。ローカルLLM(14B)は賢くなったとはいえ、本格的なコーディングはまだクラウドの上位AIに分があります。そこで「司令塔はローカル、重い実装はClaude Code」というハイブリッドにしました。claude_code 道具が claude -p "指示" を呼び出し、結果を受け取ります。

def claude_code(prompt):                    # Claude Code(CLI)に委譲
    out = subprocess.run(["claude", "-p", prompt, "--output-format", "json"], ...)
    return json.loads(out.stdout)["result"]  # 実装やコードレビューを任せて結果を受け取る

普段の調べもの・ファイル整理・PC操作は手元のMinistralで無料・無制限、ここぞの実装だけClaude Codeに依頼――と使い分けられます。

つまずきポイントと対処

  • 道具を使ってくれない:最初はシステムプロンプトで「JSONを出して」と頼む方式にしたら、知っている質問では検索せず勝手に答えてしまいました。→ モデル付属のテンプレート+tools=(function calling)に切り替えたら、検索/直答の判断が安定しました。
  • tool_callsが空で返る:道具呼び出しが web_search{...} という独自形で返ることがあり、標準フィールドが空でした。→ 複数形式に対応するパーサで解決。
  • macOSで操作が無視される:Macではpyautoguiのクリック/入力が静かに無視されることがあります(エラーも出ない)。原因は権限で、システム設定 > プライバシーとセキュリティ > アクセシビリティ(と画面収録)でアプリ(実行元のターミナル等)を許可すると動きます。最初の関門なので要注意。
  • 14Bは速くはない:M4(32GB)で約11トークン/秒、読み込み約5秒。3Bクラス(p933は約38 tok/s)より遅いですが、賢さは上。用途で選ぶのがよいです。
  • 終了時のクラッシュ報告:Metalの後始末で終了時に警告が出ることがあり、os._exit(0)でクリーンに閉じるようにしました(p933と同じ対処)。
  • VRAMに収める:14BはQ4でも約8.2GB。画像用mmprojと合わせ、RTX3060の12GBに収まる量子化(Q4_K_M)を選ぶのがコツです。
  • MCPの道具を呼べない:最初はMCPの道具をシステムプロンプトで“紹介”しただけだと、AIが system_info を直接呼ぼうとして「未知の道具」になりました。→ MCPの道具を“第一級の道具”として動的登録(名前→サーバの対応表で実行)したら、自然に使えるように。
  • Claude Code連携の認証claude CLIは普通のターミナルからは正常に動きますが、Claude Codeのセッション内から呼ぶと認証エラー(401)になることがあります。アプリ単体起動/通常のターミナルから使うのが安全です。

Windowsでも動かす(RTX3060で検証)

ここまではMac(M4)の話でした。まったく同じコードを Windows 11(RTX3060・VRAM12GB)でも動かして検証しました。結論、ほぼそのままで全機能が動作。GPU(CUDA)にもモデルが全41層フルに載りました(VRAM約9.3GBで12GBに収まる)。

項目Mac(M4 / 32GB)Windows(RTX3060 / 12GB)
モデル読込約5秒約3.8〜5.7秒
生成速度約11 tok/s約6.3〜7.3 tok/s
GPUMetalCUDA・全41層・VRAM約9.3GB
PC操作(マウス/キーボード)権限の許可が必要権限不要で動作
全機能OKOK

※ 生成速度はMacの方が速め。WindowsはRTX3060でも、今回使ったCUDA版ビルドの都合で伸び切らず(それでも会話には十分)。読込・GPU動作・機能面はMacと遜色なし。

個人的にうれしかったのがPC操作です。Macは権限の都合で“入力しようとする”ところまででしたが、Windowsは特別な権限なしで、実際にメモ帳へ文字が入力されました。「AIが自分のPCを操作する」が目に見える形で動いた瞬間です。

Windowsでエージェントがメモ帳に文字を入力した画面
Windows:エージェントが確認ダイアログの承認後、メモ帳へ「WindowsでもローカルAIエージェントが動いています」を実入力(28文字)

CLIやMCPもWindowsネイティブのまま動作。下は ver コマンドとMCPの system_info を実行したところです。

WindowsでCLIとMCPを実行した画面
Windows:run_command(ver)とMCP(system_info)が動作。verが10系を返すのはWindows 11がWindows 10と基盤共通のためで、AIもその点を補足した

そしてスタートメニュー/デスクトップのショートカットからも、アイコンをクリックするだけで起動できました。

Windowsのスタートメニューにアプリのショートカットが登録された様子
Windows:スタートメニュー(とデスクトップ)に「ローカルAIエージェント」が登録され、検索でもベストマッチ表示

Windows特有のつまずきもいくつかありました。代表的なのは――

  • CUDA版llama-cpp-pythonのDLL不足:プリビルド版だけだと起動時に FileNotFoundError。→ pip install nvidia-cuda-runtime-cu12 nvidia-cublas-cu12 でCUDAランタイムを補い、DLLを配置して解決(CUDA Toolkit本体は不要)。
  • アイコン起動(pythonw)でコンソール窓がちらつく+MCPがエラー:コンソールなしで起動すると、子プロセス(MCP・コマンド実行・日本語貼り付け)が黒い窓を一瞬開き、MCPの呼び出しが [Errno 22] で失敗。→ 全てのサブプロセスにWindows用フラグ CREATE_NO_WINDOW を付与して、窓ゼロで全機能が動くようにしました(このコード修正はMac/Linuxでは無影響)。
  • PowerShellの文字化け:Mac作成のスクリプトがBOM無しで日本語がパースエラー → UTF-8 BOM付き保存で解決。
  • 確認ダイアログにキー入力を奪われる:メモ帳へ入力する直前に承認した確認ダイアログ(=アプリ側の窓)が前面に残り、文字がアプリに入ってしまうことが。→ 「最後に使っていた外部ウィンドウ」を常に記憶しておき、入力の直前にそのウィンドウを前面へ戻してから打つことで解決(Windowsのみの処理)。

いずれも対処できて、「Mac/Windowsどちらでも、同じローカルAIエージェントが同じように使える」ことが確認できました。

まとめ

ローカルLLM(Ministral 3 14B)に道具を持たせるだけで、検索・ファイル・PC操作・記憶・画像理解までこなすAIエージェントが、完全ローカルで作れました。クラウド型のような“最高の賢さ”はありませんが、無料・無制限・プライバシー安心で、日常のPC作業の相棒としては十分に実用的です。

  • function callingで「道具を使うか/自分で答えるか」をAIが判断
  • 緊急停止+確認ダイアログ+許可トグルで、強い機能を安全に使う
  • RAG記憶で検索結果や指示を学習し、次回に活かす(古くなったら再検索で更新・リセットも可能)
  • CLI/MCPでUnity・Blender等を正確に操作、難所はClaude Codeに委譲するハイブリッド
  • Q4量子化でRTX3060(12GB)にも収まる=普段使いのPCで動く

今回はMac(M4)で構築し、Windows(RTX3060)でも実機検証しました(前章)。GPUはMac=Metal/Windows=CUDAで自動的に使われ、同じコードが両OSで動作。普段使いのPCで完結するローカルAIエージェントになりました。

最後に、毎回ターミナルから起動するのは面倒なので、普通のアプリのように起動できるようにしました。Macは .app 化してLaunchpad(アプリケーション)からWindowsはスタートメニュー/デスクトップのショートカットから、アイコンをクリックするだけで立ち上がります。自作のローカルAIが“自分のPCの常駐アプリ”になると、ぐっと身近になりました。

それでは、今回はここまで。最後までありがとうございました😊