Claude Code for VS CodeでローカルLLM

ローカルLLMが賢くなてきた

2025年8月 gpt-oss-20bが発表になったころから、家庭のPCで動作できる比較的小さなモデルが賢くなってきたと思います。

gpt-oss-20b以前は、16GBのGPU環境で動作するローカルモデルにコーディングエージェントから何か仕事をさせようとしたとき幻滅感しかありませんでした。

gpt-oss-20bは16GBのGPU環境でも動作しツールコールも何とかこなします。16G環境で動作するモデルは、とりあえずダウンロードしてブラウザで動作するPONGゲーム作りをお願いしてできを確かめてきました。

最近のモデル

最近立て続けに、Gemma4 26B A4B, Qwen3.6 35B A3B, Qwen3.6 27Bこれらのモデルが発表され、お気軽に使えるLMStudioからダウンロードができるようになりました。

早速試してみました。

  • Gemma4 26B A4B
    PONGゲームは楽勝でしたので、テトリスをお願いすると良い感じのコードを出力します。ツールコールもそこそここなしますが、仕事を投げて目を離しているとループになって返ってこないことが多くありました。
  • Qwen3.6 35B A3B
    デコーダの速度がそこそこ早いです。ツールは普通に使えますし、コーディング性能も高いですが、コンテキストが長くなったり、本気で難しいことを考えると中国語が出てくるのが面白いです。
  • Qwen3.6 27B
    16GBでは少し厳しくjackrong/qwen3.6-27b Q3_K_S 12.1GBを選びました。unsloth/qwen3.6-27b Q3_K_S 14.2GBに比べて約2GBも小さいです。どうやら視覚部分のレイヤを取り去った物らしく自分の環境にはぴったりでした。コーディングで使おうとすると速度的に待ちきれないです。MoEモデルのサクサク感を味わった後だと辛いです。

Claude Code for VS Codeにつなげたい

ローカルLLMの場合はコンテキスト長とそこに収めるツールの量などの問題もあり、コーディングエージェントの種類によっても動作が変わってきます。

GitHub Copilot, Roo Code, OpenCode, Claude Codeを使っているなかで、世の中の評価の通り、コーディングにおけるユーザ体験はClaude Codeが頭一つ抜けていると感じています。

Claude Codeはモデルもさることながらエージェントも優れているのではと言った仮説が生まれてきます。これをローカルLLMにつなげてみたくなってきます。

自分はどのエージェントもVSCodeの拡張から使っています。CLIも入れてあるのですが、ついなんとなく拡張を呼び出して使う癖がついています。

一般的にClaude Codeからローカルモデルへの接続は、以下のような感じで環境変数を設定して、

$Env:ANTHROPIC_BASE_URL = "http://localhost:1234"
$Env:ANTHROPIC_AUTH_TOKEN = "lmstudio"

モデル名を引数に起動するらしいのですが、、

claude --model openai/gpt-oss-20b

VSCode拡張の場合はこんな感じで設定すれば良さそうでした。

.vscode/settings.json

{
    "claudeCode.environmentVariables": [
        {
            "name": "ANTHROPIC_BASE_URL",
            "value": "http://localhost:1234"
        },
        {
            "name": "ANTHROPIC_AUTH_TOKEN",
            "value": "lmstudio"
        }
    ]
}

ではモデルはどうやって設定するのか?

悩んだ結果、LMStudioのモデルの歯車マークを選んで、以下の「<>Load Model」ボタンでロードしたら、Claude Codeからそのモデルがお話できるようになりました。

ClaudeCodeのエージェント機能は良い

しばらくClaudeCodeからローカルLLMを使ってみて分かったことは、たぶんClaudeCodeのエージェント機能は優秀、しかしながらギリギリの環境で動いているローカルLLMには荷が重いです。

毎回、多くのコンテキストを消費する、しかもKVキャッシュが効きにくいプロンプトを投げることが多い。それはプロンプトプロセッシングに多くの時間を割く動作から推測できます。この動きはプロンプトの情報量が多いとも言い換えられると思いました。

ユーザ体験的にはClaudeCode+ローカルLLMは待ちきれない、と言うのが正直なところです。RooCode+ローカルLLMやGithubCopilot+ローカルLLMの方がレスポンス良く感じます。

最近の賢いモデル+ClaudeCodeでバラ色のオフラインコーディングライフを夢見ましたが現実は厳しいです。

昨今はメモリやGPUの相場も上がっており気軽にローカルPC環境を更新できません。折を見てもう少し快適な環境を揃えたいです。

 

Windows11 HomeからSMB1.0のNASに接続できなくなった

ある日突然やってくる

Windows11がアプデされ、これまで使えていたNASにアクセスできない。古いNASは要注意です。

Windows11 ProからはアクセスできているのにHomeだけアクセスできなくなりました。

古いNASに接続するには設定が多い

しばらく前からWindowsはデフォルトでSMB1.0を許さなくなりました。使いたい場合は「Windowsの機能の有効化または無効化」から「SMB 1.0クライアント」のチェックを入れる必要があります。ここまでは認識していました。

今回の災難はNASの共有フォルダまでは見えているけど、なぜか設定したユーザIDとパスワードでログインできない現象です。

NTLMv2を試す

使っているNASの認証は特に設定していないのでNTLMv1でした。最初のトライとしてNAS側でNTLMv2の有効化を行ってみましたが効果ありません。

レジストリの書き換え

Windows11 Homeは「ローカルグループポリシーエディタ」(gpedit.msc)がインストールされておらず、ネットワーク認証関連の変更はレジストリを直接変更しなくてはならないらしいです。

SMB1.0はそろそろ引退させるべきかもですが、とりあえず使えないと不便なのでレジストリを書き換えます。

 

変更すべきキーはAIに教えて頂きました。以下の3つです。

1. SMB1.0の未署名通信を許可する設定

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters

RequireSecuritySignature を1 → 0 に修正

EnableSecuritySignature = 0 を追加(DWORD 32bit)

2. ゲスト/古い認証を許可する設定

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa

LmCompatibilityLevel = 1 を追加(DWORD 32bit)

 

レジストリを変更後再起動で認証が通るようになりました。

 

GitHub CopilotのGPT-5-miniが答えなくなった

VSCodeのGitHub Copilotを3台のPCで使っています。そのうち1台で以下のエラーが出るようになりました。

Reason: Request Failed: 400 {"error":{"message":"Unsupported value: 'xhigh' is not supported with the 'gpt-5-mini-2025-08-07' model. Supported values are: 'minimal', 'low', 'medium', and 'high'.","code":"invalid_request_body"}}

以下はとりあえず回避するまでのメモです。

上記のエラーの内容は、gpt-5-mini-2025-06-07に対して'xhigh'でReasoningするようなリクエストが投げられて、それは対応してません的な意味に見受けられます。

3台ともにReasoningの設定をした覚えが無く、1台だけこの症状は不可解です。ためしにしVSCodeに関する全てをアンインストールして再インストールしましたが、変わりませんでした。

こまったなぁと思いつつ検索すると逆にxhighを設定している記事を見つけました。

zenn.dev毎度どこにあるか分からない settings.jsonを書けば良さそうと言うヒントを頂けました。

メニューよりFile>Preferences>Settingsを選ぶか、C:\Users\{ユーザ名}\AppData\Roaming\Code\Userにありますね。

こんなふうに書いて

helloを送ってみると

エラーが無くなりお返事が返ってくるようになりました。

 

Dockerで公開したポートがufwをすり抜ける話

■dockerでサーバを作ろうと思ったらセキュリティ的な問題が指摘されていた

10年以上動いているメールサーバの調子悪くなってきたのでRasPiに乗り換えようと思い docker-mailserverのサーバ構築事例を読んでいたところ、dockerでマップしたポートは ufwをすり抜ける的な話が書かれているのを見かけました。

nosubject.io

www.ryotosaito.com

記事によると、dockerサーバは iptablesを使うので、それを禁止するのは誤った対策なので、compose.yamlportsでバインドするアドレスを書きましょう的な結論でした。

    ports:

        - "127.0.0.2:8000:80"

確かにこれならデータベースのポートをお手軽な設定で外からのアクセスができないようにできます。

自分のメールサーバは、25番を外向け、143、587番を内向けに公開したいので、各ポートに外のアドレスと、内のアドレスを書けばOKかなと頭をよぎったのですが、ufwが効かないってのは気持ち悪い状態です。docker + ufwの組み合わせがダメはなかなか大きな問題だと思いました。

■この問題をもう少し掘り下げたい

ufwiptablesも詳しくないのでとりあえず AIに教えてもらうと、dockerが設定する iptablesには、DOCKER-USERと呼ばれる空のチェーンがあり、そこにルールを定義する。設定ファイルは /etc/nftables.confに書けばいいよという事です。

iptablesの知識が乏しいので最初は???でした。チェーンって何?とか、dockerは iptabelesを書き換えているのに何で /etc/nftables.confなの?みたいな感じです。

■そもそも iptablesを良く知らない

この問題を調べながら、iptabeles は近年 nftablesにとって代わられつつあることを知りました。カーネルにはパケットの処理段階にフックがあって、そこに複数のチェーンと呼ばれる設定グループみたいなものを紐付けて処理できるとか、フックに紐つくものをベースチェーンと読び、そのほかにフックに直接紐つかない通常のチェーンもあることを知りました。

dockerはコンテナ用のプライベートネットワークを持っています。ports:の設定を行うと外部から入ってきたパケットを、コンテナまで運ぶために iptablesでNATを使ったルーティングを定義をするみたいです。(初心者が全部を正確に読むのは大変なのでざっくりした雰囲気の話です。細部は間違っているかもしれませんので悪しからず。)

dockerの作る定義はパケットがルーティングされると FORWARDと呼ばれるフックに紐ついたチェーンのアクセスルールに到達します。その段階でまず DOCKER-USERと呼ばれる空のチェーンが呼ばれるようになっていました。

フックしたチェーンがどのような構成になっているのか以下の説明が分かりやすかったです。

christina04.hatenablog.com

つまり、dockerのコンテナにパケットが配信されようとしたときに、いつも空っぽのDOCKER-USERが呼ばれるわけです。ですのでここに何かを定義すれば、コンテナへのアクセスの可否を自分で決めることができると言った理屈です。

■理屈が分かってきたので設定してみる

以下が今回作った nftables.confです。

/etc/nftables.conf

table ip filter {
    # dockerへのフォワード
    chain DOCKER-USER {
        # 既存のコネクション
        ct state established,related accept
        # 外からのアクセスは25, 80以外は拒否
        iifname "eth0" oifname "docker0" tcp dport != { 25, 80 } drop
        # 外からのUDPを拒否
        iifname "eth0" oifname "docker0" ip protocol udp drop
    }
}

最初に既存のコネクションを許可します。これは dockerコンテナから任意のポートで外部にアクセスしたときのリザルトを切らないためにあります。内⇒外の通信を途中で切らないために、おまじない的に入れる定義と理解しました。

これまでの文脈はメールサーバでしたが横にWEBサーバもあるので、そのルールを書きました。eth0は RasPiの有線ネットワークで外につながっています。docker0は dockerネットワークのブリッジです。eth0 ⇒ docker0のパケットは 25, 80以外は拒否します。

また、eth0 ⇒ docker0の UDPを全て拒否します。

RasPiにはもう一個、内側のネットワークにつながったeth1(USBネットワークアダプタ)があります。こちらから143、587番経由でメールを取りに行けばパケットは拒否されません。

このファイルを書いたら、nftablesのサービスを有効にする必要がありますがその前に、/usr/lib/systemd/system/nftables.serviceの[Unit]に以下の設定を追加します。

[Unit]
After=docker.service

これはサービスの起動順で、dockerサーバが起動する前にDOCKER-USERのチェーンを定義しようとすると、docker0がありません的なエラーが出て止まってしまいます。なので、nftablesのサービスはdockerサーバの起動後に実行される必要があります。

■応用編

ご紹介した例は dockerコンテナへのアクセス制御ですが、これと同じノリで nftables.confへ適当なフックのチェーンとルールを書き足せば母艦へのアクセス制御を細かく行うことができます。

最初 ufwはお手軽でいいなと思っていましたが、nftablesを使えば ufwと同等以上の定義を書くことができますので、最初に良いなと思った ufwは使いませんでした。

 

Windows11で pure-ftpdを起動する

■はじめに

出先のローカル環境でファイルを交換したいとき、USBメモリの貸し借りはイマイチなので、一時的にファイルを受け渡しできる手軽な方法としてFTPを使ってみようと思いました。

かと言ってノートPCに ftpdをあげっぱにるのも気持ち悪いです。必要な時だけサクッと立ち上げる方法として Dockerを使うことにしました。若干ハマり所があったので備忘のため記録を残すことにしました。

 

■DockerDesktopを使います

compose.yamlはこんな感じ


version: "3.8"
services:
  ftpd:
    image: stilliard/pure-ftpd
    container_name: ftpd
    restart: unless-stopped
    ports:
      - "21:21"
      - "30000-30009:30000-30009"
    environment:
      PUBLICHOST: "${PUBLICHOST:-localhost}"
      FTP_USER_NAME: "${FTP_USER_NAME:-guest}"
      FTP_USER_PASS: "${FTP_USER_PASS:-guest}"
      FTP_USER_HOME: "${FTP_USER_HOME:-/home/}${FTP_USER_NAME:-guest}"
    volumes:
      - "./data:${FTP_USER_HOME:-/home/}${FTP_USER_NAME:-guest}"

ファイヤウォールの受信の規則に必要なポートを追加します。

ここまで設定したら

docker-compose up -d

で終わるはずでしたが、、ローカルマシンは繋がるけど、肝心のリモートマシンからは繋がりません。

 

■アクティブモードとパッシブモード

FTPを多用していたときは常に意識していたことですが、FTPには2つの転送モードがあることをすっかり忘れていました。

データ転送ポートをサーバ、クライアントどちらから繋ぎに行くのか問題で、クライアント側がNATの内側だったり、ローカル環境でもクライアントの口にファイヤウォールがあったりするとアクティブモードでは繋がらないです。

大概のクライアントはこれらのモードを自動でよろしく判定してくれますが、パッシブモードを使う時のつなぎ先を正しく得られないとデータポートを接続することができずログインはできるけどデータ転送できない状態になります。

 

■パッシブモードのつなぎ先

pure-ftpdの PUBLICHOSTをサーバマシンのローカルアドレスに設定するとパッシブモードのアクセスが正しく行われるようになります。しかしながら、DHCP環境下でいちいち設定するのがめんどくさいです。AIに聞いてみたら好さげなスクリプトを作ってくれました。

 

start-ftpd.bat


@echo off
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0start-ftpd.ps1"    

 

start-ftpd.ps1


# Determine IPv4 matching 192.168.* and export PUBLICHOST, then run docker compose up
$ip = Get-NetIPAddress -AddressFamily IPv4 |
    Where-Object { $_.IPAddress -like '192.168.*' } |
    Select-Object -First 1 -ExpandProperty IPAddress

if (-not $ip) {
    Write-Error 'No matching IPv4 address found'
    exit 1
}

$env:PUBLICHOST = $ip
Write-Output "PUBLICHOST set to: $ip"

# Run docker compose up (remove -d if you want foreground logs)
docker compose up -d

自分のローカルアドレスを見つけて PUBLICHOSTに設定してくれます。

 

■クライアントは?

Windows11標準のファイルエクスプローラに以下を指定して接続できました。

ftp://guest@<IPアドレス>/

それ以外に WinSCPでも正常につながりました。

振り子時計また壊れる

 2020年に振り子が動かなくなって修理した時計、日に日に時間の遅れがひどくなってきた。一日後、5分遅れ、10分遅れ、1時間遅れるように。。。

 時計のユニットをばらしてみると下部にコイルがあります。一秒ごとに励磁され秒針のタイミングを作っているのでしょう。その駆動基板がコイルの左側のやつです。

 基板の水晶振動子を交換します。下の絵はダメになった水晶振動子を摘出したところ。一般的な32.768kHz(15回2で割ると1秒になる)の丸いやつを交換しました。

 以前に起きた電池の液漏れの影響でパターンに腐食が見られます。気休めですがアルコールで洗浄しました。これで元気になってくれたらいいな。

 

VSCodeでSSH接続した先でGithub Copilotが使えない

ソースコードを書くときにLLMに教えて頂くことが多くなりました。最初はLLMがコードを教えてくれることが目新しくそれだけで満足でしたが、多用するにつれWebUIに質問をしてエディタにコードを貼り付ける作業が煩わしくなってきます。

そこで、Github Copilotを使ってみることに。エディタと一体になった使い勝手は素晴らしく、AIにコメントを書いて頂く、逆に人間のコメントからコードを生成して頂く、両者ともに良く機能してくれますのでコーディングのパートナーとして欠かすことのできない存在になりつつあります。

そんな中、VSCodeSSH接続したとき何故かGithub Copilotが機能していないことに気が付きました。困ったなと思っていたら解決策を書いて頂いているページを発見。

qiita.com偶にしか行わない設定、毎回settings.jsonは何処にあるの?となって調べるのでメモ代わりにここに記録しておくことにします。

メニューよりFile>Preferences>Settingsを選ぶ。

右上に矢印付の用紙アイコンが表示されるのでそれをクリック、よくわからん場所にあるsettings.jsonが開かれる。

これを追加し再起動でSSH接続した先でもGithub Copilotが使えるようになりました。