はじめに: Slackでエージェントがメンションなしに働く時代
AIエージェントをSlackに統合して使うことは、もはや一般的になりました。しかし従来は毎回メンション(@)を付けないとエージェントが反応せず、会話が途切れてしまうことがよくありました。VercelのEveエージェントが新たに発表した機能を活用すれば、この問題を解決できます。
今回のアップデートの核心は2つです:
- スレッド内自動応答: 一度アクティブになったスレッドでは、メンションなしでもエージェントが継続して応答します。
- イベントフック拡張:
reaction_added、team_joinなど、Slackアプリが購読するすべてのイベントに反応できます。
この記事では、実際のコードを通じて実装方法と実務上の注意点を解説します。
なぜこの機能が重要なのか
Slackでエージェントを運用する際、最も大きな不便は「メンション依存」でした。ユーザーが毎回@Eveを付けなければならず、会話が断絶し、チームメンバーがエージェントの存在を忘れてしまうことも多々ありました。今回のアップデートでエージェントがスレッド内で能動的に会話を続けられるようになり、まるで同僚と話しているかのような体験を提供します。
また、イベントフックが追加されたことで、Slackで発生するすべてのイベント(例: 新メンバー参加、リアクション追加)をエージェントのトリガーとして使用できるようになりました。これはオンボーディング自動化や通知処理など、様々なシナリオに拡張可能です。
この記事で扱う内容
onMessageフックとヘルパー関数(isBotMentioned、isSubscribed)- スレッド参加者の確認とチャンネル履歴権限
ctx.cancel()とctx.reset()によるセッション制御onEventフックによるイベントファンアウト- 実務適用時の注意点と次の学習ステップ

本論1: 核心概念とコード例
1. onMessageフックでメンションなしに会話を続ける
Eveエージェントがスレッドでメンションなしに応答するには、onMessageフックでctx.isSubscribed()を使用します。以下のコードは、DM、明示的メンション、アクティブスレッドのフォローアップメッセージをすべて処理する例です。
// EveエージェントのSlackチャンネル設定
import { slackChannel } from "@vercel/eve";
export default slackChannel({
credentials: connectSlackCredentials("slack/my-agent"),
async onMessage(ctx, message) {
// ボットのメッセージには応答しない
if (message.author?.isBot) return null;
// DMかどうかを確認
const isDirectMessage = message.raw.channel_type === "im";
// DM、明示的メンション、またはアクティブスレッドのメッセージの場合のみ処理
const shouldHandle = isDirectMessage || ctx.isBotMentioned() || (await ctx.isSubscribed());
return shouldHandle ? { auth: null } : null;
},
});
主要ポイント:
ctx.isBotMentioned(): メッセージにボットメンションが含まれるか確認ctx.isSubscribed(): メッセージがアクティブセッションのあるスレッドに属するか確認message.raw.channel_type === "im": DMかどうかを確認
2. スレッド参加者リストの取得
スレッドに誰が参加しているかによってルーティングを変えたい場合は、ctx.thread.listParticipants()を使用します。この関数は、一意な人間ユーザーIDを最初の出現順で返します。
// スレッド参加者リストの取得
const participants = await ctx.thread.listParticipants();
if (participants.includes("U123456")) {
// 特定ユーザーが参加している場合のロジック
}
権限の注意:
- 公開チャンネル:
message.channelsトリガーイベントとchannels:historyボットスコープが必要 - プライベートチャンネル:
message.groupsとgroups:historyが追加で必要
3. セッションのキャンセルとリセット
ユーザーが誤ってメッセージを送信した場合や会話を初期化したい場合、ctx.cancel()とctx.reset()を使用できます。
// 途中で誤ったメッセージが送信された場合 - 現在のターンをキャンセル
export default slackChannel({
credentials: connectSlackCredentials("slack/my-agent"),
async onMessage(ctx, message) {
// "キャンセル"コマンドが来たら現在のターンをキャンセルし、メッセージを新しい入力としてキューイング
if (message.text.trim() === "!cancel") {
await ctx.cancel();
return { auth: null };
}
// それ以外は既存のロジックを実行
},
});
// 会話を完全に初期化したい場合 - セッションリセット
export default slackChannel({
credentials: connectSlackCredentials("slack/my-agent"),
async onMessage(ctx, message) {
if (message.text.trim() !== "!new") return null;
await ctx.reset({ reason: "Slack user requested !new" });
await ctx.thread.post("新しい会話を開始します。");
return null;
},
});
違い:
ctx.cancel(): 現在のターンのみ中断し、セッションは維持、新しいメッセージが代替入力として処理されるctx.reset(): セッションを完全に終了し、次のメッセージは新しいセッションとして開始
4. onEventフックで全てのイベントに反応する
これでreaction_added、team_join、channel_createdなど、Slackアプリが購読するすべてのイベントを処理できます。ctx.receive()を呼び出すとエージェントターンが開始され、複数回呼び出すと1つのイベントを複数のターゲットにファンアウトできます。
// 新メンバーが参加したらオンボーディングメッセージを複数チャンネルに送信する例
const onboardingChannels = ["C0123ABC", "C0456DEF"];
export default slackChannel({
credentials: connectSlackCredentials("slack/my-agent"),
async onEvent({ receive }, event) {
// チーム参加イベントのみ処理
if (event.type !== "team_join") return;
// 各オンボーディングチャンネルにイベント情報を渡してエージェントターンを開始
await Promise.all(
onboardingChannels.map((channelId) =>
receive({
message: `新しいメンバーがSlackワークスペースに参加しました。このイベントに基づいてオンボーディングを進めてください:\n${JSON.stringify(event)}`,
target: { channelId },
auth: null,
})
)
);
},
});
活用シナリオ:
reaction_added: 特定のリアクションが追加されたら通知処理channel_created: 新しいチャンネルが作成されたらルール案内team_join: 新メンバーのオンボーディング自動化
5. 始め方
- Eveエージェントの作成: Eve公式ドキュメントを参照してください。
- Slackチャンネルの追加: ターミナルで
eve channels add slackコマンドを実行 - テンプレート活用: Vercelダッシュボードでテンプレートを選べば数分でデプロイ可能

本論2: 注意点と応用テクニック
注意点
- 権限設定: 公開チャンネルとプライベートチャンネルで必要なスコープが異なります。イベントが動作しない場合は、まずスコープを確認してください。
- 無限ループ防止: ボットメッセージに反応しないよう、
message.author.isBotチェックは必須です。 - セッション管理:
ctx.reset()はセッションを終了するため、ユーザーに明確なフィードバックを与えることをお勧めします。 - イベントファンアウト: 複数チャンネルに同時にメッセージを送る際は
Promise.allを使用しますが、API呼び出しの制限に注意してください。
応用テクニック
- 状態管理: セッションがアクティブなスレッドでのみ動作するため、長い会話が必要な作業はスレッドを維持するのが良いでしょう。
- エラーハンドリング:
onMessageで例外が発生するとデフォルトでエージェントが停止します。try-catchで囲んでログを残しましょう。 - テスト: Vercelダッシュボードでセッションごとのデバッグが可能なので、デプロイ前に様々なシナリオをテストしましょう。
日本市場での適用コンテキスト
日本ではSlackの導入企業が増えており、特にリモートワークの普及に伴い、Slackベースの自動化への需要が高まっています。今回のアップデートは、従来のメンション依存のエージェント運用を改善し、実際の業務でより自然に活用できる契機となるでしょう。

結論: 実務適用のアドバイスとまとめ
今回のEveエージェントアップデートは、SlackでAIエージェントを運用する方法を一段階引き上げました。メンションなしで会話を継続し、イベントフックで様々な通知に反応する機能は、実務で非常に有用です。
実務適用のヒント
- オンボーディング自動化:
team_joinイベントを活用すれば、新入社員に自動で歓迎メッセージを送り、必要なドキュメントを案内できます。 - カスタマーサポート: スレッド内で顧客問い合わせを処理する際、
ctx.cancel()を活用すれば誤った回答を迅速に修正できます。 - モニタリング:
reaction_addedイベントで特定のリアクションが付いたら管理者に通知するなど、モニタリング自動化が可能です。
次の学習ステップ
- Eve公式ドキュメント: Slackチャンネル設定とより多くの例を確認してください。
- ADK Go 1.0: GoベースのAIエージェント開発に興味があれば、ADK Go 1.0公開の記事を参照してください。
- AIモデル最適化: AIでコンクリート配合を最適化した事例に興味があれば、BOxCreteオープンソースモデル解読を読んでみてください。