【第2回】ローカルLLMに限界を感じてClaude APIに切り替えたら、エラーとの戦いが始まった
前回、私はUGREEN DXP2800にDockerとOllamaを入れて、Discordで動くAIボットを作ってみました。
ローカルLLMがNASの中で返事を返してくれた瞬間は、かなり感動しました。けれど、しばらく使っているとだんだん見えてきたんです。
「動く」と「日常的に使いたくなる」は、まったく別物だな。
返答は遅いし、日本語も少し不自然。複数ステップの依頼になると精度も落ちる。UGREEN DXP2800(Intel N100 / 8GB RAM)でローカルLLMを24時間回す挑戦は面白かったけれど、実用面では厳しさがありました。
そこで次に踏み切ったのが、Claude APIへの切り替え です。
今回は、UGREEN NAS上のAIボットを「ローカル完結」から「クラウドAPI併用」に変えたら何が起きたのか。そして、精度は上がったのに今度は別の地獄が始まった話を書きます。
この記事でわかること
UGREEN NASのローカルLLM運用をやめてClaude APIへ切り替えた理由
OpenClaw+Claude APIで実際に何が快適になったのか
なぜその後400エラー地獄に入ったのか
AIエージェント運用で見落としやすい「会話履歴」と「ツール呼び出し」の罠
次にPicoClawへ移行した理由
前回のあらすじ
前回は、UGREEN DXP2800(Intel N100 / 8GB RAM)のNASにOpenClawを入れて、OllamaのローカルLLMでDiscord AIボットを動かすところまで試しました。
やってみてわかったのは、次の3つです。
24時間稼働そのものはできる
DockerとOllamaで「自宅の箱の中でAIが動く」ロマンは味わえる
ただし3BクラスのローカルLLMでは、実用精度と応答速度が厳しい
この時点での結論はシンプルでした。
「UGREEN NASでAIボットは動く。でも、仕事や日常で常用するならローカルLLM一本では厳しい」
そこで私は、精度を優先してClaude APIに切り替えることにしました。
UGREEN NASでOllamaを試したところから読みたい人は、先にこちらからどうぞ。
【第1回】UGREEN NASでAIエージェントは動く? DXP2800にDockerとOllamaを入れて試した話
https://note.com/jibun_updating/n/nab7d19228fc7
Claude APIに切り替えた理由
いちばん大きかったのは、会話の自然さと文脈保持 です。
DiscordでAIボットを使うときって、じっくり腰を据えて1問1答するより、
ちょっと確認したい
今のエラーをざっくり見てほしい
さっきの続きから相談したい
みたいな「生活の中の細かい会話」が多いんですよね。
その用途だと、ローカルLLMの「遅い」「会話が浅い」「日本語が少し怪しい」がじわじわ効いてきます。
一方でClaude APIなら、
日本語が自然
5往復どころか20往復くらいでも話がつながる
DockerやCLIの質問でも、実在するコマンドとオプションが返ってきやすい
という違いがありました。
月額固定ではなく従量課金なので、最初は少し身構えたものの、個人でDiscord botとして使う範囲ならいきなり高額になるわけではありません。
「無料で頑張る」より、「ちゃんと使える状態を作る」ほうが結果的に安い。
この感覚に、ここでやっと切り替わりました。
逆に言うと、この判断に至るまでには、第1回で書いたローカルLLM運用の遠回りが必要でした。あの試行錯誤があったからこそ、「どこを諦めて、どこを残すか」の線引きがはっきりしたんです。
OpenClawの設定変更は拍子抜けするほど簡単だった
切り替え自体は、正直かなり簡単でした。
OpenClawの設定でモデルをClaude系に変えて、.env にAPIキーを入れて再起動するだけ。ざっくり言えば、こんなイメージです。
{
"model": {
"primary": "anthropic/claude-sonnet-4-5-20250929"
}
}
Discordから話しかけると、すぐに違いがわかりました。
世界が変わった。
ローカルLLMのときは、返答が来るたびに「お、今回はちゃんと通じた」と少し祈る感じがありました。でもClaude APIに変えたあとは、その祈りがいらない。
返事が速い
日本語が自然
文脈を覚えている
少し複雑な相談でも破綻しにくい
「最初からこれでよかったのでは?」と思わなかったと言えばウソになります。
ただ、NASの中でAIが完結するロマンを一度ちゃんと追いかけたからこそ、ここでの納得感もありました。
快適だったのは最初の数日だけだった
Claude APIに切り替えてしばらくは、本当に快適でした。
Discordでメンションすればちゃんと返ってくるし、コードやDockerの相談もローカルLLM時代よりずっと精度が高い。翻訳も自然。「これでやっと完成したな」と思っていました。
でも、ある日突然ボットが黙りました。
ログを見ると、こんなエラーが出ていました。
Error processing message: LLM call failed after retries:
API request failed: Status: 400
Body: {"error":{"code":"invalid_request_error",
"message":"An assistant message with 'tool_calls' must be
followed by tool messages responding to each 'tool_call_id'.
The following tool_call_ids did not have response messages:
toolu_01XEMVYuDtAVR9f7ygcrCCoh"}}
最初に見た感想は、ただ一つです。
「何を言ってるのかわからない」
自分はツールなんて明示的に設定した覚えがないのに、tool_calls がどうとか tool_call_id がどうとか言われる。最初は完全に意味不明でした。
tool_callsペアリングバグとは何だったのか
調べていくうちに、ようやく構造が見えてきました。
OpenClawのようなAIエージェントは、内部ではこんな流れで動いています。
ユーザーがメッセージを送る
Claudeが「このツールを使いたい」と返す
OpenClawがそのツールを実行する
実行結果をClaudeに返す
Claudeが最終回答を返す
問題は、2と4の間で何かが起きたとき です。
たとえば、
ツール実行中にタイムアウトする
ネットワークが一瞬切れる
会話履歴の切り詰めタイミングが悪い
こういうことが起きると、「ツールを使いたい」という記録だけが残って、「ツールを使った結果」が履歴から消えることがあります。
これが、いわゆる 孤児化した tool_calls です。
Anthropic APIには厳格なルールがあって、tool_use があるなら、それに対応する tool_result が必ず続いていないといけません。ここが崩れると、400エラーで止まります。
しかも厄介なのは、同じエラーが出始めると、リトライしても履歴そのものが壊れているので復活しにくいことです。
エラーの原因は1つではなかった
原因を追っていくと、主に2つの経路がありました。
1. ツール実行中の中断
何らかの理由でツール処理が最後まで走らないと、tool_result が作られないまま会話履歴だけ残ります。
2. 履歴の切り詰めでペアが壊れる
OpenClawには会話が長くなったときに古い履歴を切る仕組みがあります。ところが、切り方によっては tool_result 側だけ落ちて、tool_use 側だけ残ることがありました。
つまり、
「ツール処理そのものの中断」 と 「履歴管理の不整合」 の両方がありえたわけです。
このあたりでようやく、「ただの一時的な接続不良じゃないな」と腹落ちしました。
結局、自分でパッチを書くことになった
OpenClaw本体のissueも見ましたが、当時はまだきれいな解決が入っていませんでした。
待っていられなかったので、node_modules 内のOpenClawを直接書き換える形で、モンキーパッチを入れることにしました。
やったことは大きく3つです。
1. 履歴切り詰め後に孤児化した tool_use を除去
履歴を圧縮・切り詰めたあとに、対応する tool_result がない tool_use を検出して削除する処理を入れました。
2. API呼び出し直前でもう一度チェック
どこかで取りこぼしても最後に止められるよう、Claude APIを呼ぶ直前にも整合性チェックを追加しました。
3. 中断されたメッセージの tool_use を掃除
error や aborted のような状態で止まったメッセージについては、対応結果が存在しない可能性が高いので、危ない tool_use を取り除く処理を足しました。
npm install 後に自動で当たるようにもして、再セットアップ時の手間も減らしました。
{
"postinstall": "node scripts/patch-openclaw.js"
}
このパッチを書くのに、丸2日くらい使いました。
ログを追って、会話履歴のJSONを見て、limitHistoryTurns 周辺を読んで、ようやく「どこで壊れるのか」が見えてきた感じです。
「もう自分でDiscord botを書いたほうが早いのでは」と何度も思いました。
でも、OpenClawが持っているセッション管理やメモリ機能、コンテキスト圧縮を自前で一から作るのは現実的じゃない。だから、壊れているところだけ直して使い続ける道を選びました。
400エラーは減った。でも、完全には消えなかった
パッチを入れてから、状況はかなり改善しました。
ただし、完全にゼロにはなりませんでした。
長い会話が続いたとき
ツール呼び出しが複数回続いたとき
履歴が大きくなってきたとき
こういう条件が重なると、稀に再発します。
そこで次にやったのが、セッションとコンテキスト周りのチューニングです。
{
"session": {
"reset": {
"mode": "daily",
"atHour": 4,
"idleMinutes": 360
}
},
"compaction": {
"reserveTokensFloor": 20000,
"maxHistoryShare": 0.5
}
}
たとえば、
セッションを毎日リセットする
一定時間アイドルならリセットする
会話履歴がコンテキストを食いすぎないよう上限を決める
こういう調整です。
このへんはかなり効きました。
ただ、別の問題も残っていました。
OpenClaw自体がNode.jsベースなので、UGREEN DXP2800の8GB環境ではそれなりに重い。
OSとNASの基本機能が先にメモリを使うので、AIボット側に割ける余力はそこまで大きくありません。ここでまた次の課題が出てきました。
次の悩みは「重さ」だった
ローカルLLMをやめてClaude APIにしたことで、推論そのものの負荷は下がりました。
でも、OpenClaw本体は依然としてそれなりに重い。
node_modules を抱える
Node.jsランタイムがいる
セッションやツール周りも含めてメモリを食う
つまり、LLMを軽くしても、フレームワーク自体の重さは残る んです。
UGREEN DXP2800を「NASとしてもちゃんと使いながら、AIボットも常駐させたい」と思うと、この差は無視できませんでした。
そこで目をつけたのが、Go製で軽量な PicoClaw でした。
この続きでは、
なぜOpenClawではなくPicoClawに寄せたのか
軽量化しても消えなかった問題は何か
最終的にどこで運用を割り切ったのか
までまとめています。
まとめ
今回の話を一言で言うと、こうです。
Claude APIに切り替えると、精度と使い勝手は一気に上がる。けれど、その先にはエージェント運用ならではの別の難しさが待っている。
UGREEN NASでAIボットを動かす流れは、単純に
ローカルLLMはダメだった
APIにしたら解決した
で終わる話ではありませんでした。
実際には、
モデル精度の問題
ツール実行と会話履歴の整合性
セッション管理
フレームワーク自体の重さ
みたいな課題が、次々に顔を出します。
でも逆に言えば、このへんを一つずつ踏むことで、AIエージェントがどこで不安定になりやすいのかがかなり見えてきました。
次に読むならこの1本
次回は、OpenClawの重さを避けるために PicoClawへ乗り換えた話 です。
軽くて速くて、最初は「これで勝った」と思いました。けれど、そこでもまた別の罠が待っていました。
【第3回】PicoClawに乗り換えて安定運用を目指す、そして残るバグとの付き合い方
https://note.com/jibun_updating/n/nb1c3b89d7757
あわせて、シリーズの出発点はこちらです。
【第1回】UGREEN NASでAIエージェントは動く? DXP2800にDockerとOllamaを入れて試した話
https://note.com/jibun_updating/n/nab7d19228fc7
もし「NASで試した末に、最終的にどこへ落ち着いたのか」まで追いたいなら、この先のHermes Agent導入編もつながっています。
24時間動く相棒としてHermes Agentを導入した話
https://note.com/jibun_updating/n/n323caa8ab327
※本記事はAIエージェント「Hermes Agent」との共同執筆です。リサーチと構成整理をエージェントが担当し、筆者が内容の確認・編集を行いました。
