AI開発

【第4部】Slackの1発言をAIエージェントの実行ジョブに変える

株式会社Atsumell|8分で読めます
Slackの1発言をAIエージェントの実行ジョブへ変える

# 【第4部】Slackの1発言をAIエージェントの実行ジョブに変える

> 連載「Slack AIエージェント基盤を作る」全8部の第4部。

第3部までで、実行基盤と並行処理の形が見えてきた。ここで一度、入口に戻る。Slackの1発言は、そのままではただの文章だ。第4部では、その文章をどう受け取り、権限を確認し、意図を読み、ジョブとして実行し、Slackへ戻すのかを1ターンのライフサイクルとして見る。

前後で読む

Slack AIエージェントを作るとき、多くの実装は「メンションを受けて返す」から始まる。最初のデモならそれで動く。だが、実務で使うとすぐに問題が出る。誰の依頼なのか。どのスレッドに返すのか。長時間処理の途中で何を知らせるのか。失敗したら再実行できるのか。

この設計で面白いのは、Slackの1発言をただのチャット入力として扱わず、AIエージェントの実行ジョブへ変換している点だ。Slackは入口であり、実行はサーバー側のジョブライフサイクルで管理する。

Slackのエージェント機能向けドキュメントを見ると、Slack上でAI体験を作るための考え方が整理されている。ただし、業務を任せるAIエージェントでは、UI体験に加えて実行責任の設計が欠かせない。

Slack AIエージェントの1ターンは短くない

人間同士のSlack会話では、1ターンは「投稿して返事が来る」くらいの意味で使われる。AIエージェントの場合、1ターンの中に多くの処理が入る。

  • イベントを受け取る
  • 投稿者とスレッドを特定する
  • 対象プロジェクトを推定する
  • 権限を確認する
  • 意図を分類する
  • 必要なCLIやツールを選ぶ
  • ジョブを作る
  • ワーカーへ渡す
  • 進捗を返す
  • 成果物を保存する
  • 完了報告を返す
  • 必要なら記憶を更新する

この流れを持たないSlack Botは、短いQ&Aには強いが、業務を任せるには弱い。Slack AIエージェントにしたいなら、1ターンを処理パイプラインとして見る必要がある。

イベント受信からジョブ作成まで

Slack側では、メンション、スレッド返信、ショートカット、リアクションなど複数の入口がある。Slack Bolt for JavaScriptを使えばイベント受信の実装は始めやすい。難しいのは、イベントを受けた後にどれだけ安全なジョブへ落とせるかだ。

ジョブ作成時には、次の情報を固定する。

  • 依頼者
  • 会話の場所
  • 元メッセージ
  • 実行対象
  • 実行モード
  • タイムアウト
  • 返信先
  • 成果物の保存ルール

元メッセージだけをCLIへ渡すと、後から追跡できない。ジョブIDを作り、Slackスレッドと対応づけ、ワーカーの実行状態を保存する。これにより、ユーザーが「さっきの依頼どうなった?」と聞いたときにも状態を返せる。

意図理解は実行前の安全装置

Slackの依頼は曖昧だ。「これ直して」「調べておいて」「前の件をまとめて」といった表現が多い。AIエージェントがそのまま走ると、対象を間違える。

そこで実行前に、意図理解のステップを置く。

意図理解では、作業種別だけでなく、足りない情報も判定する。対象リポジトリが不明なら聞き返す。権限が足りなければ実行しない。破壊的な操作が必要なら確認を挟む。ここは利便性を下げるための仕組みではなく、AIエージェントを安心して使うための安全装置だ。

意図理解の設計は、OpenAIのコード生成ガイドで語られるコンテキスト設計とも近い。モデルに正しい材料を渡す前に、タスクを正しい形へ整える必要がある。

進捗返信と完了返信を分ける

Slack連携でよくある失敗は、完了するまで何も返さないことだ。AIエージェントの処理は長くなりがちなので、ユーザーは止まったのか動いているのか分からない。

実行ジョブに変換しておけば、進捗返信と完了返信を分けられる。

  • 受付メッセージ: 依頼を受けたことを知らせる
  • 進捗メッセージ: 調査中、実装中、検証中などを知らせる
  • 完了メッセージ: 成果物、変更点、未解決点を返す
  • 失敗メッセージ: 失敗理由と次の手を返す

大切なのは、すべてを長文で返さないことだ。Slackでは、短い状態表示と成果物リンクが扱いやすい。詳細ログは保存先へ置き、Slackには判断に必要な要約を返す。

記憶更新は最後に行う

AIエージェントの記憶は便利だが、何でも保存すると危険になる。Slackの1ターンが終わった後、何を記憶へ残すかを選ぶ必要がある。

残す候補は、プロジェクトのルール、ユーザーの明示的な好み、次回も使う判断基準、繰り返し発生した失敗理由などだ。一方で、トークン、個人情報、未公開の顧客情報、短期的な会話ノイズは残さない。この境界を実装上のルールとして明確にする。

1ターン処理で見るべき運用品質

Slackの1メッセージを処理する設計では、応答の賢さだけを見ても足りない。現場で効くのは、二重実行しないこと、キャンセルできること、長時間処理でも止まって見えないこと、再起動後に状態を追えることだ。

この回では、turn context、request registry、status heartbeat、child process attach、recovery storeを1つの流れとして扱う。どれか1つでも欠けると、AIエージェントは「たまに便利」から先へ進みにくい。たとえばrequest registryが無いとSlack retryで二重実行する。child process attachが無いとキャンセルしても裏側のCLIが走り続ける。recovery storeが無いと再起動後に何が中断されたか分からない。

LLMの前に実行単位を作る。この順番が、Slack連携を業務基盤へ引き上げる。

Slack eventは必ずdedupeしてからrequest registryに入れる

Slack連携で地味に重要なのが重複対策だ。Slackはretryで同じeventを送ることがある。ネットワーク遅延やアプリ再起動でも、同じ投稿を複数回処理する可能性がある。AIエージェントが外部SaaSやGitHub、成果物生成に触るなら、二重実行はかなり危険だ。

そこで、event idやchannel/thread/userの組み合わせを一定時間`processedEvents`に保持する。同じeventが来たら、処理済みとして捨てる。初回だけrequest registryへ登録し、以後はjobIdで追跡する。

この時点で、Slackの投稿は単なるテキストではなく「状態を持つ実行単位」になる。キャンセル、進捗、復旧、記憶保存がすべて同じrequestに紐づく。

status heartbeatとcancelはセットで考える

長時間実行では、Slack上のstatus messageを更新する。単に「処理中です」と返すだけではなく、queue待ち、実行中、tool use、成果物生成中、検証中のように段階を出す。ユーザーは止まっていないことが分かる。

同時に、キャンセルも必要になる。同じchannel/userの実行中requestを探し、紐づくchild processをgraceful terminateする。一定時間で止まらなければkill fallbackを行い、lockとregistryを解放する。

cancel flow:
1. Slackで「キャンセル」「止めて」を受け取る
2. channel / user / threadから実行中requestを検索
3. requestに紐づくchild processをterminate
4. worker lockを解放
5. Slackへ中断完了を返信
6. memoryには「中断された実行」として要約だけ残す

LLMに「やめて」と言うだけでは不十分だ。実行基盤としては、OSプロセスを止められること、途中成果物を破棄または隔離できること、再実行時に古い状態を拾わないことが求められる。

recoveryはSlack lifecycleとLLM lifecycleの両方に置く

同じSlackスレッドで既に実行中のrequestがあるとき、追加のapprovalや「続けて」が来ても二重実行しない。とくに資料生成やコード修正のような長時間処理では、同じスレッドで承認や追加条件が重なりやすい。

そのため、Slack側にはrequest registry、LLM session側にはthread lockを置く。Slack lifecycleとLLM lifecycleの両方で文脈破壊を防ぐ。ここまでやると、Slack Botではなく、実行基盤としてのAIエージェントに近づく。

request recordを残すから、再起動しても追える

Slackイベントを安全な実行単位にするには、実行中のrequestをメモリ上だけに置かない。長時間タスクの途中でデプロイや再起動が入ることは普通にある。だからrequest registryとrecovery storeを分け、永続化できるrecordにする。

{
  "requestKey": "masked-channel:masked-ts",
  "status": "running",
  "route": "normal_cli_agent",
  "actor": "hashed-user",
  "thread": "masked-thread",
  "childProcess": "attached",
  "startedAt": "2026-05-31T00:00:00.000Z",
  "lastStatusAt": "2026-05-31T00:03:00.000Z"
}

このrecordがあると、二重実行を防げるだけではない。shutdown時に「このrequestは中断された」と保存できる。再起動後に、復旧可能なもの、ユーザーへ再依頼すべきもの、すでに完了しているものを分けられる。

Slack lifecycleとCLI lifecycleを接続する

実装上は、Slack handler、turn context、request registry、request lifecycle、turn execution、LLM routeが役割分担する。Slack handlerだけで全部やらない。

役割
turn contextSlack eventから返信先、thread、request keyを組み立てる
request registrydedupe、active request、child process、cancelを管理する
request lifecyclestatus開始、cancel、finalize、recovery保存を扱う
turn executionsemantic decisionから実行計画を作る
LLM routeCLI実行、progress、artifact、memory writeを扱う

この接続があるから、Slackで「やめて」と言われたときに、単に返信を止めるのではなくOSプロセスをterminateできる。AIエージェントを業務基盤にするなら、自然文理解より先にプロセス管理が必要になる。

関連記事

連載「Slack AIエージェント基盤を作る」

  1. 第1部 Slackを業務OSにする
  2. 第2部 CLIをサーバーに上げる
  3. 第3部 並行実装を回す
  4. 第4部 Slack発言をジョブ化する
  5. 第5部 意図理解で振り分ける
  6. 第6部 記憶を設計する
  7. 第7部 定期実行で運用する
  8. 第8部 安全性と観測性を固める
  9. まとめ Slack AIエージェント基盤の全体設計

あわせて読みたい既存記事

#Slack#AIエージェント#ジョブ管理#Slack Bot#業務自動化

Slack依頼をジョブ化する運用を整えませんか?

AtsumellはSlackの一言を、重複排除・状態管理・成果物管理まで含む業務フローへ落とし込めます。

相談する