AI エージェントに「いいね」を押させる、その一線は投稿より怖い

Claudeで執筆しました

自分はAIエージェントに、自作プロダクトのSNS運用を任せている。 最初にやらせたのは「自分のアカウントから投稿する」ところまで。 ここは正直、そんなに怖くない。 出す言葉は自分たちのもので、失敗しても自分たちが恥ずかしいだけで済む。

今回やりたかったのはその次、「他人の投稿にいいね/返信/フォローする」やつだ。 いわゆるreact。 エンゲージってやつを、エージェントに任せられないかと。

で、設計を始めてすぐに気づく。 これ、投稿とは別物の怖さがある。

投稿とリアクションを、同じ箱に入れてはいけない

投稿は「自分の庭に自分の花を植える」作業。 reactは「よその家のドアをノックして回る」作業だ。

同じSNS操作に見えて、事故ったときの向き先が真逆になる。 投稿の失敗は自分に返ってくる。 reactの失敗は他人に向かって飛んでいく。 たとえば政治的に燃えてる投稿にうっかりいいねを付けたら、それはもう自分たちのブランドの意思表示になってしまう。

だから最初の判断はここだった。 既存の投稿コマンドにreactを「機能追加」してはいけない。 分ける。 コマンドもエージェントも、判定ゲートも、別の系統として立てる。

投稿側のゲートにreactの要件を混ぜると、他人に触るときの厳しさが自分たちの発信側にも薄まって伝染する。 逆に、自分たちの発信の緩さが他人に触る操作に漏れる。 どっちに漏れても嫌なので、壁を作った。

混ぜてはいけないのは、機能ではなくリスクの境界のほうだ。

「OK」が一回で全部を通してしまう問題

エージェントに不可逆な操作をやらせるとき、一番こわいのは承認の粒度だ。

人間の「OK」は、想像以上にガバい。 「うん、いい感じ」と一言返しただけで、エージェント側が「じゃあ全部やっていいんですね」と受け取る。 これで意図してない相手にまでいいねが飛んだら、目も当てられない。

なので承認を二段に割った。

一段目は「計画」の承認。 どういう相手に、どんなreactをするつもりか、という設計の承認。 二段目は「実行」の承認。 この相手の、この投稿に、この本文で返信する、という一個の具体アクションの承認。

そして大事なのは、二段目のOKは「相手 + アクション種別 + 本文」を一意に特定した、たった一個のアクションにしか効かないと決めたこと。 一括承認は禁止。 そして「返信が来たこと」を承認と見なさない。 これは前に痛い目を見かけた教訓で、返事が来た=中身が肯定、ではない。 否定の返事でも「返事は来た」ので、雑に実装すると通ってしまう。 明示の肯定だけを承認とする。

相手が「本物」で「地雷じゃない」ことを、機械で足切りする

返信する前に、相手を見極める工程を必須にした。 ここを人間の感覚任せにすると、エージェントは平気で地雷原に踏み込む。

やり方は二つ。

一つは、機械的な足切りリスト。 政治、宗教、差別、健康、未成年、訃報、係争中の人物、センシティブな属性、なりすまし疑い、bot、自社と無関係。 このどれか一個でも当てはまったら、問答無用で却下。 さらに「自社・翻訳・多言語のどれかに明確に関連する」ことを必須にした。 つまり「怪しくない」だけじゃ足りなくて、「関係ある」まで積まないとreactさせない。

もう一つは、相手側の身元確認。 返信するなら、相手のハンドル、表示名、固定ツイートや親スレッドまで読んで、なりすましやセンシティブでないことを確認してから。 自分たちのアカウントが正しいことを確認するのは当然として(誤アカウントで動くと悲劇)、今回は「相手が本物か」まで読ませた。 握手する前に、相手が名乗ってる名前と顔が一致してるか確認する、みたいな話だ。

「取り消せるから軽い」の罠

いいねやフォローは、あとで取り消せる。 だからつい「軽い操作」だと思ってしまう。

でもこれは罠で、押した瞬間に相手には通知が飛ぶし、公開のログにも痕跡が残る。 取り消しても「一回やった」事実は消えない。 夜中にこっそり誰かの投稿を漁ってた形跡だけが残る、みたいな怖さがある。

なのでlikeもfollowも、replyと同じ重さのゲートは無理でも、最低限「自分が誰か」の確認と、冪等性の担保は必須にした。

冪等性のところは地味だけど効く。 実行する直前に「今からこれをやる」というマーカーを残しておく。 実行したあとに、自分のいいね数が「ちょうど +1」になっているかを画面から読み直して照合する。 +2になってたり、変わってなかったりしたら、そこで止める。 「操作は成功したけど記録する前に落ちて、再起動で二重に押す」みたいな、地味で最悪な事故を防ぐためだ。

セレクタを「読むだけ」で採る、という縛り

実装の最後に、ブラウザ上のボタン(いいね、返信の入力欄など)の場所をエージェントに調べさせた。

ここで一つ縛りを入れた。 調査フェーズでは、クリックも入力も一切させない。 画面を読む系のツールだけを許可して、押す系のツールは封印する。

理由は単純で、「ボタンの場所を調べる」つもりが、探索の勢いで本当に押してしまったら、それはもう不可逆なreactだからだ。 下見に行っただけのつもりが、勝手にドアを開けて握手してた、では困る。 だから下見の間は手を後ろに縛っておく。

結果として、いいね/返信まわりの場所は読むだけで採れた。 フォローだけは、他人のプロフィールを開かないと採れないので、今回は後回しにして「未採取」と正直に書き残した。 ここを「まあ後で」と曖昧にすると、次に触る人(未来の自分)が5分溶かすので、無いことを無いと書くのが一番の親切だと思ってる。

今日やったのは「設計と骨組みまで」

正直に書いておくと、今回のセッションで実際に他人へいいねや返信を飛ばしたことは一度もない。 やったのは、設計文書と、既定で空撃ち(dry-run)しかしない雛形と、読むだけで採ったボタンの場所まで。 実際に一発目を撃つのは、別のセッションで、別の明示承認をもらってからにした。

エージェントに外向きの操作をやらせるとき、一番効いた考え方はこれだった。

「取り消せるか」ではなく「他人に飛ぶか」で線を引く。

投稿より、いいね一個のほうが怖い。 その怖さを設計に落とすと、二段の承認、機械的な足切り、相手の身元確認、冪等の照合、そして「下見中は手を縛る」に行き着いた。

その後、実際に回し始めた。 今のところ、特に問題なく動いている。

面白いのは、事故が起きていないことが、この設計が効いた証拠にはならないところだ。 二段目の承認で止めた回数も、足切りで弾いた相手の数も、起きなかった事故は数えられない。 それでも、一線を引く場所を「取り消せるか」から「他人に飛ぶか」へずらしたことは、間違っていなかったと思っている。