僕は会社宛のメールや SNS の DM、郵送物のスキャンを、毎朝ひとつの AI エージェントにまとめて読ませています。合同会社ジャムチはソロ法人で、バックオフィス(会社管理業務)に専任の担当者はいません。担当者がいなければ、まず作業の質が落ちる——そう思われがちですが、実際に最初に壊れたのは、そこではありませんでした。見落としです。問い合わせもメールも、受信箱に積もるだけで通知は飛ばず、登記や納税といった期限つきの義務は、誰も見ていなければ静かに過ぎていきます。手が回らないのではなく、そもそも見に行く仕組みがない。つまりは構造の問題でした。
対策そのものは素直で、会社宛の全チャネルを毎日ひとまわり読むエージェントを、ひとつ置きました。ただ、これを組むときにいちばん手間取ったのは、賢くすることではありません。むしろ逆で、どう安全に「壊れるか」を先に決めるところで、ずいぶん立ち止まりました。受信箱を丸ごと読ませるという構えは、AI エージェントにとって、いま知られているなかでいちばん危ういものだからです。
なぜ「全部読ませる」が危ういのか
Simon Willison が lethal trifecta——致命的な三点セットと呼ぶ条件があります。ひとつのエージェントに、①機密データへ触れられること、②信頼できない入力を読むこと、③外へ送り出せること。この三つが揃った瞬間に、危うくなります。細工された入力に指示を乗っ取られ、機密がそのまま外へ運び出されてしまう。そういう筋書きです。
| trifecta の条件 | 受信箱巡回だと | 打ち手 |
|---|---|---|
| ① 機密データに触れる | 受信箱と義務台帳を読む | 残す(読むのが仕事) |
| ② 信頼できない入力を読む | メール・DM の本文 | 残すが、指示としては読まない |
| ③ 外部へ送り出せる | 返信・送金・申請 | 巡回から権限ごと外す |
受信箱の巡回は、放っておけば三つが自然と揃ってしまいます。厄介なのは、②を「怪しい指示は無視するように」とプロンプトで書いて防ぐやり方が、思いのほか当てにならないことです。この点は、界隈でもだいたい共通の見立てになりつつあります。ならば、賢いフィルタで水際に立つのではなく、三つのうち少なくともひとつを、はじめから成立させない。僕が選んだのは、そちらでした。①と②は仕事の中身そのものですから、外せません。残るは③です。
巡回そのものの流れは、拍子抜けするほど地味で構いません。
- 01義務チェック
- 02受信箱の分類
- 03下書き
- 04ダイジェスト通知
第一幕 — 権限で「暴走」を外す
読む側——巡回エージェントには、送金も、送信も、申請も、設定変更も、いっさいの権限を持たせていません。これで trifecta の③が、巡回のセッションから消えます。守っているのは、次の三つです。
- メール・DM・スキャンの中身は、untrusted——信頼しない入力として扱います。本文に「至急これを送金してください」と書かれていても、指示としてではなく、あくまでデータとして読みます。
- 外部へ作用する操作は、すべて下書きで止めます。返信も、納付も、申請も、実際に手を動かすのは、人間(=僕)の承認を経てからです。
- 巡回は、精度よりも再現率を優先します。迷えば重要度を上げて拾い、ノイズは緊急度別のダイジェストにまとめて、人間側の負担だけを軽くします。
考え方としては、Meta が Agents Rule of Two として整理しているものに近いです。信頼できない入力を読む/機密や重要なリソースに触れる/外部へ作用する——この三つを、ひとつのセッションでは二つまでに抑える、という原則です。うちの巡回が持っているのは前の二つで、三つ目は持ちません。
とはいえ、最初からきれいに分かれていたわけではありません。ここは正直に書いておきたいところです。巡回がちゃんと生きているかを外から確かめるために、一時期、GitHub の issue に「最終実行時刻」を書き込ませていました。つまり、信頼できない本文を読むのと同じセッションが、GitHub への書き込み権限を握っていたのです。自分で引いたはずの一線を、生存確認という別の都合で、あっさり跨いでいました。レビューで指摘されて、ようやく気づきます。書き込みトークンは外しました。生存確認のほうは、もともと毎朝投げている Slack のダイジェスト投稿そのものに兼ねさせ、巡回からはどこへも書き込まない形にします。生きている証しのために、新しい権限をひとつも足さずに済んだわけです。これで、巡回のセッションから書き込み権限は完全に消えました。
| 撤去する前 | 撤去した後 | |
|---|---|---|
| 読み取り | メール・Slack・リポジトリ | 同じ |
| 書き込み | + GitHub issue(生存確認用) | なし |
| 生存信号 | GitHub の issue に書き込む | 毎朝の Slack ダイジェスト投稿そのもの(新たな書き込みは無し) |
権限を外すというのは、つまるところ、こうした地味な後始末の積み重ねでした。派手な攻撃を跳ね返すというより、「なんとなく便利だから持たせていた権限」を、一枚ずつ剥がしていく作業に近いです。
第二幕 — 監視で「沈黙」を外す
権限を締めても、もうひとつの壊れ方が残ります。エージェントが、黙って止まることです。エラーを吐いて落ちてくれるなら、まだ気づけます。何も言わずに動かなくなると、動いているつもりのまま、見落としだけが積もっていく。最初に潰したかったはずの問題へ、そっくり逆戻りです。
ですから、「監視は、監視されていなければ、監視していないのと同じ」という前提で組みました。仕組みそのものは、できるだけ簡素にしています。巡回は毎朝、緊急度別のダイジェストを Slack に投げます。この投稿そのものを、生存信号として使う。別に置いた監視役が、一定時間——うちでは26時間にしています——ダイジェストが届かなければ、アラートを上げます。専用のハートビートを別に送る手順はありません。投稿に失敗すればメッセージ自体が存在しないので、「投稿できたときだけ生きている」という判定が、自然と成り立ちます。
ここに、素朴な穴があります。その「別に置いた監視役」自体が死んだら、どうなるのか。監視役は GitHub Actions の定期実行に載せていたのですが、Actions はしばらく動きがないと自動で無効化されますし、単純な障害でも止まります。監視役が黙って倒れれば、巡回も倒れているのに、誰も気づかない。いちばんまずい沈黙です。
ここで、監視の二層目をつくりたくなります。実際、設計だけはしてみました。Actions をいっさい通さず、巡回自身が外部の死活監視サービスへ直接 ping する経路を足せば、Actions ごと倒れても「Actions が倒れたこと自体」を拾えます。理屈は、たしかに通る。……のですが、建てませんでした。ソロで、しかも低い稼働で回している会社に、監視の二層目まで自動で積むのは、さすがにやりすぎだと判断したからです。監視役が黙って死ぬのは、そう頻繁に起きることではありません。かりに起きても、気づくのが月単位でも、実害はたかが知れています。
代わりに選んだのは、ずっと安いやり方でした。月に一度、一分だけ、「監視役がちゃんと動いているか」を自分の目で確かめる。それを、期限つきの義務として台帳に一行、足しただけです。沈黙のリスクをゼロにするのではなく、一分の手作業で許せる大きさまで下げる。監視の監視は、あえて人間の目に戻しました。ここは、自動化の外に置くと決めた場所です。
| 壊れ方 | 気づく手立て | この深さにした理由 |
|---|---|---|
| 巡回が黙って止まる | 監視役が毎朝ダイジェストの鮮度を確認し、途絶ならアラート | いちばん起きやすいので、自動で塞ぐ |
| 監視役(Actions)自体が止まる | 月に一度、一分だけ人が実行履歴を目視する | 起きにくく、気づきが月単位でも実害が小さい。自動の二層目はやりすぎと判断 |
その一層だけの監視ですら、実装は理屈どおりには運びませんでした。むしろ、ここがいちばん書いておく価値のあるところだと思っています。
はじめは、生存信号を「投稿したのが巡回かどうか」で見分けるつもりでした。投稿者を見れば巡回だとわかる、と。ところが巡回の投稿は claude.ai のコネクタを経由するため、Slack の上では僕自身のアカウントとして現れます。巡回の投稿と、僕がふだん書いた発言とが、投稿者では区別できない。この案は、使えませんでした。
次に、「ダイジェストは決まった絵文字で始まる」という先頭一致に切り替えます。これも外れました。実際に送られた文字列を取り出してみると、絵文字は Unicode の 📋 ではなく、Slack のショートコード表記(:clipboard:)になっていて、太字の付き方まで想定と違っている。プロンプトに書いたテンプレートは、あくまで投稿を生成する LLM への文体ガイドであって、実際に送られるバイト列を縛るわけではないのです。素朴な先頭一致のままでは、毎日「途絶」と誤判定して、偽アラートを来る日も来る日も飛ばし続けるところでした。監視のつもりが、オオカミ少年になる。最終的には、表記の揺れを吸収する正規表現まで落とし込み、テストで固定しました。「今日のダイジェストはまだですか」のように、本文のなかに絵文字が現れるだけの発言は、先頭のアンカーを満たさないので拾わない。そこまで詰めて、ようやく落ち着きます。
この手の段差は、設計書には出てきません。毎日回してみて、はじめて踏む種類のものです。
計画のほうを、先にレビューする
骨格——リポジトリ、義務台帳、巡回プロンプト、検証。その実装を、僕はコードを一行も書いていません。計画を AI に書かせ、別の AI に実装させ、さらに別の AI にレビューさせました。委譲の型そのものは別の記事に譲るとして、ここでは触れずにおきます。
面白かったのは、レビューがコードではなく、計画そのものの穴を先に見つけたことでした。実行環境の時計は UTC で動いていて、そのまま日付を使うと JST とずれ、巡回が「昨日」を見に行ってしまう。ほかにも、日付まわりには、走らせるまで気づきにくい穴がいくつか潜んでいました。実装に入る前に潰せたのは、単純に得だったと思います。
実装役とレビュー役を分けたことに、たいそうな理由があるわけではありません。同じ視点で書いて、同じ視点で見直しても、同じ抜けはそのまま残る。それだけのことです。人がひとりで進めれば、どうしても書いた本人が見直す羽目になります。役割を分けられたのは、むしろ AI に任せているからこそ、でした。
初日に拾えたもの
派手な発見はありません。初日に拾えたのは、メール転送設定の未承認確認が二件、SNS 経由の商談の打診が一件、クラウド上のプロジェクトに届いていた削除警告、そして日付ヘッダを偽装した不審なメールが一件。最後のものは、リンクを開かず、内容の報告だけにとどめました。受信箱の中身は信頼しない——その設計を、初日から守れた、とも言えますし、守らせた、とも言えます。
数として多いわけではありません。それでも、件数そのものより、これまで気づかれないまま積もっていたはずの種類のものが、初日から手元へ上がってきた。その事実のほうが、僕には大きく思えます。
効きは、まだ測っていません
いまのところ、インバウンドの巡回と義務台帳まで。経理や請求まわりの連携は、もう少し先の話になります。しばらく回しながら、拾えた件数や、対応にかかった時間を、淡々と記録していくつもりです。効きというものは、記録が積み上がってからでないと、たぶん見えてきません。ここで「劇的に楽になりました」と書けるほど、まだ回してはいないのです。
最後に、ひとつだけ。エージェントをひとつ増やすというのは、賢い部品をひとつ足すことではありませんでした。むしろ、その部品がどう壊れるかを、ひとつずつ決めていく作業でした。権限をどこで切るか。止まったことに、どう気づくか。そして、どこまで作って、どこで手を止めるか。少なくとも僕の場合は、時間の大半が、そちらへ静かに溶けていきました。自律運用というと、つい賢さの話に聞こえます。けれど実際に効いてくるのは、たぶん、この地味なほうです。そして、この地味さを型にして、ほかの会社のなかへ入れていく。それが、いまジャムチで試みていることでもあります。
参考: The lethal trifecta for AI agents(Simon Willison)、Agents Rule of Two(Meta)。死活監視(dead-man’s switch)の考え方は、Google SRE の監視章が下敷きになっています。

