【第3回】PicoClawに乗り換えて安定運用を目指す、そして残るバグとの付き合い方
前回、UGREEN DXP2800上のAIボットをローカルLLMからClaude APIへ切り替えたことで、精度と使い勝手は一気に良くなりました。
ただし、その代わりに今度は OpenClawの400エラー に悩まされることになります。
しかも、パッチを当ててかなり改善しても、まだ完全には安心できない。さらに、UGREEN DXP2800(Intel N100 / 8GB RAM)という限られた環境では、Node.jsベースのOpenClaw本体がそれなりに重い。
ここで次のテーマになったのが、「もっと軽いAIエージェント基盤はないのか」 でした。
そこで見つけたのが、Go製の PicoClaw です。
今回は、OpenClawからPicoClawへ移行して、NAS上での常駐運用をどこまで現実的にできたのか。そして、軽量化してもなお残った問題と、最終的にどう付き合うことにしたのかを書きます。
この記事でわかること
なぜOpenClawからPicoClawへ乗り換えたのか
UGREEN NASのような省メモリ環境でPicoClawが有利な理由
実際に移行してハマった設定上の罠
Claude APIのtool_use / tool_result問題がPicoClawでも起きた理由
完璧な解決ではなく「壊れても戻せる運用」にした経緯
前回のあらすじ
前回は、UGREEN DXP2800に載せたOpenClawのLLMを、OllamaのローカルLLMからClaude APIへ切り替えた話でした。
その結果、
日本語の自然さ
文脈保持
応答速度
実用性
は一気に改善しました。
一方で新たに見えてきたのが、
tool_use と tool_result のペア崩れによる400エラー
長い会話で壊れやすいセッション履歴
OpenClaw本体の重さ
という問題です。
つまり、「モデルは解決した。でもフレームワークはまだ悩みが残る」 状態でした。
そこで私は、より軽量な選択肢を探し始めました。
前回のOpenClaw+Claude API編から読みたい人はこちらです。
【第2回】ローカルLLMに限界を感じてClaude APIに切り替えたら、エラーとの戦いが始まった
https://note.com/jibun_updating/n/n64ef95ddd360
PicoClawを選んだ理由
PicoClawを見つけたとき、最初に惹かれたのはとにかく軽いことでした。
ざっくり魅力を並べると、こんな感じです。
メモリ使用量がかなり小さい
起動が速い
Goのシングルバイナリで動く
Discord連携やClaude API対応、ツール実行もある
OpenClawはNode.jsベースなので、どうしても node_modules やランタイムごと抱えることになります。
一方でPicoClawは、コンパイル済みバイナリ1本で動く。
これは、UGREEN DXP2800のような8GBメモリ環境ではかなり大きい差 です。
ローカルLLMをやめてClaude API一本にしたとはいえ、NAS側に余計な負荷を残したくない。写真やファイル共有の本来業務と、AIボットの常駐をできるだけ共存させたい。
その意味で、PicoClawはかなり理想的に見えました。
「これならようやく、NAS常駐AIボットの実用ラインに近づけるかもしれない」
そんな期待がありました。
DockerでPicoClawを動かす構成にした
PicoClawはビルド済みイメージをそのまま使うというより、ソースからビルドして使う形にしました。
マルチステージビルドにして、最終的なイメージを軽く保つ構成です。
FROM golang:1.26-alpine AS builder
RUN apk add --no-cache git make
WORKDIR /src
RUN git clone --depth 1 https://github.com/sipeed/picoclaw.git .
RUN go mod download && make build
FROM alpine:3.21
RUN apk add --no-cache ca-certificates tzdata curl
COPY --from=builder /src/build/picoclaw /usr/local/bin/picoclaw
RUN /usr/local/bin/picoclaw onboard
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
ポイントは、
builderステージでGoをコンパイル
runtime側はAlpine最小構成
起動前に entrypoint.sh でセッション掃除などを実施
という流れです。
NAS上のDockerビルドには少し時間がかかりましたが、一度できてしまえば起動はかなり速い。ここまでは「いい感じ」でした。
問題は、そのあとです。
罠①:PicoClawは環境変数を展開してくれなかった
OpenClawでは、設定ファイルに ${DISCORD_BOT_TOKEN} のように書いておけば、環境変数を拾ってくれていました。
だから私はPicoClawでも同じ感覚で、こんな設定を書いていました。
{
"token": "${DISCORD_BOT_TOKEN}",
"api_key": "${ANTHROPIC_API_KEY}"
}
でも、これが通らない。
docker compose up -d のあとログを見ると、接続エラーや認証エラーが続きます。
最初はcompose側の設定を疑いました。でも原因はもっと単純でした。
PicoClawは設定ファイルのJSONをそのまま読むだけで、環境変数展開の仕組みを持っていなかった んです。
つまり必要だったのは、こういう形でした。
{
"token": "MTIzNDU2Nzg5MDEyMzQ1Njc4OQ.XXXXXX.XXXXX...",
"api_key": "sk-ant-api03-xxxxxxxxxxxxxxxx..."
}
DiscordトークンもAPIキーもベタ書き。気持ちとしてはあまりうれしくないですが、少なくとも「OpenClawの常識をそのまま持ち込むとハマる」ということが、ここでよくわかりました。
フレームワークを変えたら、前提も全部見直す。
この教訓はかなり大きかったです。
罠②:Claude APIの401エラーでまた止まる
環境変数問題を超えたあとも、まだ終わりませんでした。
今度はClaude API側で401エラーです。
APIキーは入れているのに、なぜか認証が通らない。原因を追うと、PicoClawの config.json の構造がOpenClawと微妙に違っていました。
特に重要だったのが model_list の扱いです。
{
"model_list": [
{
"model_name": "claude-sonnet-4-6",
"model": "anthropic/claude-sonnet-4-6",
"api_key": "sk-ant-api03-実際のキー",
"api_base": "https://api.anthropic.com/v1"
}
]
}
この形に直してようやく認証が通りました。
さらにややこしかったのが、PicoClawは auth.json でも認証情報を管理していたことです。
つまり、設定ファイルを書くだけでは不十分で、認証情報を永続化するボリューム設計まで含めて考えないといけない。
volumes:
- ./config.json:/root/.picoclaw/config.json:rw
- ./auth.json:/root/.picoclaw/auth.json:rw
- picoclaw-data:/root/.picoclaw/workspace
このへんは、OpenClawからの移行を「ほぼ同じ感覚でいけるでしょ」と思っていたぶん、余計に時間を食いました。
軽量化は成功した。でも、3日後にまた同じ壁が来た
ここまで来ると、体感はかなり良くなりました。
起動は速い
メモリ使用量は明らかに軽い
Claude APIの精度もそのまま使える
NAS本体への圧迫感も減った
正直、この時点ではかなり手応えがありました。
「これでやっと落ち着くかもしれない」
そう思っていたんですが、3日後にまた止まりました。
ログを見ると、見覚えのあるエラーが出ています。
API request failed: Status: 400
"An assistant message with 'tool_calls' must be followed by
tool messages responding to each 'tool_call_id'."
……お前、またか。
PicoClawでもtool_use問題は消えなかった
ここでようやく見えてきたのは、これはOpenClaw固有の不具合というより、もっと構造的な問題だということでした。
エージェント系フレームワークでは、モデルがツールを使うときに
tool_use
tool_result
の対応関係が崩れないことが前提になります。
でも、
ツール実行中の中断
タイムアウト
セッション履歴の不整合
のどれかが起きると、そのペアが壊れることがある。
Anthropic APIはここを厳密に見ているので、整合性が崩れた履歴は400エラーで弾かれます。
つまり、問題の本質は
「Anthropic APIの厳格な履歴要件」 × 「エージェントフレームワークのセッション管理」
の組み合わせでした。
この視点が見えたことで、第1回の「ローカルLLMは厳しい」、第2回の「Claude APIなら精度は上がる」、第3回の「でも運用は別問題」という流れが、ようやく一本の線でつながりました。
OpenClawからPicoClawに変えたことで、
重さ
起動速度
構成のシンプルさ
は改善しました。
でも、ツール実行を伴う会話履歴の破損リスクそのものはゼロにならなかった んです。
OpenClawと違って、PicoClawは簡単にモンキーパッチできない
ここが、PicoClawのつらいところでもありました。
OpenClawはNode.jsなので、最悪 node_modules を直接書き換えるモンキーパッチができます。実際、前回はそれでかなり改善できました。
でもPicoClawはGoのバイナリです。
つまり、同じことをやるには
ソースコードを修正する
再ビルドする
イメージを作り直す
という流れになります。
もちろん不可能ではないですが、OpenClawのときみたいにその場でサッと当てる感じではありません。
ここで私は、考え方を少し変えることにしました。
完全に壊れないことを目指すより、壊れてもすぐ戻せるようにする。
最終的に選んだのは「壊れたら直す」運用だった
PicoClawで完全予防を目指すのは、コスパが悪いと判断しました。
そこで最終的には、復旧の速さを優先する運用 に寄せました。
発生時の対処はかなりシンプルです。
sudo docker exec picoclaw sh -c \
'rm -rf /root/.picoclaw/workspace/sessions/*'
sudo docker compose restart picoclaw
セッションファイルを削除して、コンテナを再起動する。
会話の文脈は消えます。でも、壊れたまま延々と400エラーを吐き続けるより、30秒で復旧したほうがずっといい。
この割り切りは、かなり大事でした。
被害を小さくするための予防策も入れた
ただ毎回手動復旧だけだと面倒なので、セッションが大きくなりすぎる前に掃除する設定も入れました。
environment:
- SESSION_MAX_AGE_HOURS=24
- SESSION_MAX_SIZE_KB=512
- CLEANUP_INTERVAL_MIN=30
たとえば、
セッションの最大寿命を24時間にする
サイズ上限を512KBにする
30分ごとにクリーンアップする
という形です。
さらに、ツール呼び出し回数も控えめにして暴走しにくくしました。
{
"max_tool_iterations": 10
}
この設定で、
壊れたセッションがいつまでも残る
複雑なタスクでツール呼び出しが伸びすぎる
といった被害をだいぶ抑えられました。
ここまでやって、ようやくUGREEN NASとの付き合い方が見えてきた
今の構成をざっくりまとめると、こんな感じです。
現在の構成まとめ
ハードウェア:UGREEN DXP2800(Intel N100 / 8GB RAM)
AIフレームワーク:PicoClaw(Go製、軽量)
LLM:Claude API
デプロイ:Docker Compose
セッション管理:24時間自動整理 + 必要時手動クリア
ツール:ファイル操作、シェル実行、Web検索系
ここに至って、ようやくはっきりしたことがあります。
UGREEN NASでAIボット常駐はできる。けれど、安定運用は「完璧な構成」を探すより、「壊れ方を理解して戻しやすくする」ほうが現実的。
これは、最初にローカルLLMのロマンを追っていた頃には見えていなかった視点でした。
この3回で見えてきたこと
第1回では、UGREEN NASにAIボットを住まわせる発想自体は成立することが見えました。
第2回では、ローカルLLMを捨ててClaude APIへ寄せることで、精度と使い勝手が大きく改善する一方、エージェント特有のセッション問題にぶつかりました。
そして第3回では、PicoClawで軽量化してもなお、完全な無風運用にはならないことがわかりました。
でも、この流れは失敗ばかりだったわけではありません。
むしろ、
どこにリソース制約があるのか
どこで履歴が壊れやすいのか
何を捨てて、何を残すと実用になるのか
がかなりクリアになりました。
まとめ
PicoClawに乗り換えたことで、UGREEN DXP2800のような省リソースNASでも、AIボット常駐の現実味はかなり上がりました。
Node.js系より軽い
起動が速い
Claude APIと組み合わせれば精度も高い
NAS本来の役割を圧迫しにくい
この意味では、乗り換えは正解でした。
ただし、軽量化しても tool_use / tool_result の整合性問題 までは消えません。
だから最終的には、
完全予防より復旧の速さ
1回で完璧より被害の最小化
壊れない幻想より壊れても回る運用
を優先する考え方に落ち着きました。
もし今、UGREEN NASでAIエージェントやAIボットを常駐させたいと思っているなら、この順番で考えるのがおすすめです。
まず「動くか」を試す
次に「実用になるか」を見極める
最後に「壊れたときどう戻すか」を決める
この3つが揃って、やっと日常運用に近づきます。
シリーズを順番に読むなら
【第1回】UGREEN NASでAIエージェントは動く? DXP2800にDockerとOllamaを入れて試した話
https://note.com/jibun_updating/n/nab7d19228fc7
【第2回】ローカルLLMに限界を感じてClaude APIに切り替えたら、エラーとの戦いが始まった
https://note.com/jibun_updating/n/n64ef95ddd360
この次につながる話
このNAS連載で見えてきたのは、「自宅で24時間動く相棒」をどう実現するかというテーマでした。
そこから私は、NAS常駐の試行錯誤を経て、最終的に Hermes Agentを常駐させる運用 にたどり着いています。
24時間動く相棒としてHermes Agentを導入した話
https://note.com/jibun_updating/n/n323caa8ab327
NASで遊んだからこそ見えた「実用寄りの答え」が、そこにあります。
※本記事はAIエージェント「Hermes Agent」との共同執筆です。リサーチと構成整理をエージェントが担当し、筆者が内容の確認・編集を行いました。
