コーディング Agent に画像圧縮を持たせる
Claude Code や Codex に「このフォルダの画像を圧縮して」と頼んで、何が起きるか見てほしい。
sharp のインストールを提案し、使い捨ての Node スクリプトを書き、品質設定を当てずっぽう
で決め、最後に渡してくるのは——いくつかは小さくなり、一つは大きくなり、どれがどれだか
誰にも言えないフォルダだ。
問題は Agent の側にはない。画像圧縮は「簡単そうに見えて実はそうではない」能力の典型だ。 フォーマットごとに異なるエンコーダ、品質のヒューリスティック、そして測ってみないと良し 悪しを判断できない結果。これはツールの仕事であって、Agent が毎回その場で即興するスクリプ トの仕事ではない。
これを成立させる分担
Agent には本当にできない工程が一つある。サインインだ。pipic login はブラウザを開いて
あなたの承認を待つ。サンドボックスで動く Agent は HOME が違い、ブラウザもなく、その
ボタンを押す手段もない。
だから頼まない。「実際に誰ができるか」で仕事を分ける:
あなたが、一度だけ。 自分のターミナルで:
npm i -g @pipic/cli
pipic login
認証情報は ~/.config/pipic/config.json に置かれる。そのマシンであなたとして動くツールは
すべて、これ以降それを使える——あなたの Agent も含めて。
あなたの Agent が、その後は毎回。 これを会話に貼る:
Use the `pipic` CLI to compress the images in ./assets.
It's already installed and signed in — do not install it or run
`pipic login` yourself.
pipic ./assets --replace --json
It prints one JSON object per file:
{"file":"a.png","status":"ok","before":102400,"after":41000,"saved":61400}
status is "ok", "skipped" or "error". "skipped" is not a failure — it
carries an `error` string explaining why (e.g. already small enough).
Exit 0 = all good, 1 = some files failed, 3 = not signed in,
4 = monthly quota exhausted.
Don't retry on exit 2 (usage error), 3 or 4 — on 3, stop and tell the
user to sign in. On exit 1, re-run only the paths whose rows had status
"error": a full re-run re-uploads every file and spends quota again.
この中の一行一行が特定の失敗を防いでいる。うち二行が防ぐのは、高くつくほうの失敗だ。
"do not install it or run pipic login yourself" がないと、Agent はとにかく試そうとする
——そして pipic login は、Agent には与えられないブラウザ承認を最大 10 分待つ。Codex や
Claude Code の中では、フレームワークがいずれ強制終了するツール呼び出しとして現れ、
「ツールがハングした」ように見える。
終了コード 1 についての一行がないと、100 ファイルの実行で 2 件の失敗を見た Agent は、 いかにもありそうな判断をする——ディレクトリ全体を再実行するのだ。ローカルには「圧縮済み」 のチェックは存在しない。全ファイルが再アップロードされ、月間の割り当てはサーバーが受け 付けたリクエストをすべて数える。無料枠は月 100 なので、盲目的な再試行 1 回で使い切りうる。
残りはもっと安い間違いだ。出力先フラグがなければ使い方エラーになるし、skipped は失敗
ではないと伝えておかないと、何も問題がないところで問題を報告してくる——スキップされた行も
error 文字列を持っていて、なぜ何も変わらなかったかを説明しているからだ。
Agent が見ているもの
説明用のセッション——形は本物、数字は作りもので、あなたの環境では違う値になる:
> compress the images in ./assets
I'll use the pipic CLI.
$ pipic ./assets --replace --json
{"file":"assets/hero.png","status":"ok","before":2411233,"after":486201,"saved":1925032}
{"file":"assets/team.jpg","status":"ok","before":880640,"after":712704,"saved":167936}
{"file":"assets/icon.svg","status":"skipped","before":0,"after":0,"saved":0,"error":"unsupported file type"}
{"file":"assets/logo.webp","status":"skipped","before":18944,"after":18944,"saved":0,"error":"not smaller — original kept"}
Done. Two files compressed, saving about 2.0 MB — hero.png did most of
the work, dropping from 2.4 MB to 486 KB. I left logo.webp alone because
compressing it came back larger, and skipped icon.svg since SVG isn't a
raster format.
注目すべきは最後の段落だ。Agent は推測していない——契約を読んでいる。saved は比率では
なくバイト数。skipped は error ではない。not smaller — original kept は、なぜ何も
変わらなかったのかを正確に述べている。
即興のスクリプトはこれを生まない。Agent がその場で書きたいと思った console.log を出す
だけで、返ってくる要約は、Agent が自分の出力に対して抱いた印象にすぎない。
「小さくなったときだけ」がここで効く理由
最適化済みの画像は、圧縮するとかえって大きくなることがある。素朴なスクリプトはそれでも 上書きし、リポジトリは実行のたびに静かに太っていく。
pipic は結果が実際に小さいときだけ書き戻す。--replace では元ファイルは触られないまま、
-o ではコピーされるので出力ディレクトリは完全な一式のままになる。どちらの場合もその
ファイルは error ではなく skipped として報告される——だから出力を読む Agent が、何も
失敗していないのに「失敗しました」と言い出すことはない。
省略できないフラグ
--replace と -o <dir> は排他で、どちらか一方が必須だ。pipic ./assets をどちらもなし
で実行すると、リクエストを一つも送る前に使い方エラーで終了する。
これは意図的で、Agent が運転しているときほど効いてくる。デフォルトで上書きするツールは、 いずれあなたが十分に読まなかった実行のなかで、残したかった何かを上書きする。出力先を明示 させるということは、Agent に意図を宣言させるということであり、その意図は Agent が見せて くるコマンドの中で確認できる。
Agent があなたの認証情報を見られないとき
サンドボックス化された Agent、コンテナ、CI——~/.config があなたのものではない場所すべて。
アカウントページでトークンを作り、その環境で PIPIC_TOKEN
として渡す。認証情報ファイルより優先されるので、pipic login なしで同じコマンドが動く。
pipic token create があえてローカルで発行せずウェブページへ誘導するのは意図的だ。盗まれ
た CLI 認証情報が、新しい認証情報を発行できてはならない。
終了コードがインターフェースだ
自動化されたものにとって、ここがいちばん重要だ:
| コード | 意味 |
|---|---|
| 0 | すべて成功 |
| 1 | 一部のファイルが失敗 |
| 2 | 使い方エラー |
| 3 | サインインが必要 |
| 4 | 月間クォータ超過 |
3 は pipic login を実行せよという意味——あなた自身がやるしかない唯一のことだ。4 は
CLI と API の月間割り当てを使い切ったという意味で、自動的な課金は一切なく、pipic.cc 上での
圧縮はいずれにせよ無料・無制限のままだ。
これらのコードで分岐する Agent は、あなたに長文を解釈させることなく正しく振る舞う。メッ
セージ文字列にパターンマッチする Agent は、こちらが言い回しを変えた瞬間に壊れる——だから
こそ、コードと status の値とフィールド名が安定した契約であり、人間向けの error テキスト
はそうではない。