連載: AIに仕事を奪われる不安から始めるハーネス作成入門
第16回: Slack承認を想定したハーネス処理フローを描く
第1〜15回: ✅ 公開済み | 当該記事: 📖 | 次回: 第17回
はじめに――承認ポイントを「動くフロー」にする
前回(第15回)では、Human-in-the-loop(HITL)の承認ポイント一覧を作成しました。「どこに承認を入れるか」の設計はできたものの、具体的にどう実装するかはまだ見えていません。
今回は、Slackを承認チャネルとして使うフローをMermaidシーケンス図で可視化します。「絵で見える」ことで、実装のイメージがつかみやすくなります。
なぜSlackなのか
承認チャネルの選択肢は色々ありますが、個人開発や小規模チームでSlackを選ぶ理由は明確です。
- 既に使っている: 多くのエンジニアが日常的に利用
- APIが充実: Bot、Interactive Message、Webhookが揃っている
- インタラクティブボタン: 承認・却下をワンクリックで実現できる
- 通知が届く: モバイルでも反応できる
もちろん、Slack以外(Teams、メール、Web UI等)でも同じパターンは適用可能です。今回はSlackを例に取りますが、考え方は汎用的です。
全体フローの概要
ハーネスのSlack承認フローは、大きく5つのフェーズに分かれます。
- タスク受付: ユーザーからタスクを受け取る
- リスク判定: タスクのリスクレベルを自動判定
- Slack通知: 高リスクの場合、Slackで承認リクエストを送信
- 承認待ち・実行: 承認後にツールを実行
- 結果通知: 実行結果をSlackにフィードバック
Mermaidシーケンス図――承認フローの全体像
以下が、Slack承認を組み込んだハーネスのシーケンス図です。
各フェーズの設計ポイント
フェーズ1: タスク受付
タスクを受け取ったら、まず「このタスクは処理してよいか」を判定します。
def receive_task(task: dict) -> dict:
"""タスク受付と初期バリデーション"""
# ホワイトリストチェック
if task["action"] not in ALLOWED_ACTIONS:
return {"status": "rejected", "reason": "許可されていないアクション"}
# 機密情報スキャン
if contains_sensitive_data(task["payload"]):
return {"status": "rejected", "reason": "機密情報が含まれています"}
return {"status": "accepted", "task_id": generate_task_id()}
フェーズ2: リスク判定
タスクの内容に応じてリスクレベルを判定します。判定基準は前回の4軸(影響範囲・コスト・セキュリティ・可逆性)を使います。
def assess_risk(task: dict) -> str:
"""タスクのリスクレベルを判定"""
score = 0
if task["action"] in WRITE_ACTIONS:
score += 3 # 外部書き込みは高リスク
if task.get("estimated_cost", 0) > COST_THRESHOLD:
score += 2 # コスト閾値超過
if task.get("handles_pii", False):
score += 3 # 個人情報あり
if not task.get("reversible", True):
score += 2 # 不可逆操作
if score >= 5:
return "high" # 承認必須
elif score >= 2:
return "medium" # 通知のみ
else:
return "low" # 自動実行
フェーズ3: Slack承認リクエスト
高リスクの場合、Slackにインタラクティブメッセージを送信します。
def send_approval_request(task_id: str, task_summary: str, risk_level: str):
"""承認リクエストをSlackに送信"""
message = {
"channel": APPROVAL_CHANNEL,
"text": f"承認リクエスト: {task_summary}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*タスク*: {task_summary}\n"
f"*リスク*: {risk_level}\n"
f"*ID*: {task_id}"
}
},
{
"type": "actions",
"elements": [
{"type": "button", "text": {"type": "plain_text", "text": "✅ 承認"}, "action_id": "approve", "value": task_id},
{"type": "button", "text": {"type": "plain_text", "text": "❌ 却下"}, "action_id": "reject", "value": task_id}
]
}
]
}
slack_client.chat_postMessage(**message)
フェーズ4: 承認待ちとタイムアウト
承認待ちにはタイムアウトを設けることが重要です。応答がない場合は「安全側に倒す(デフォルト拒否)」が基本です。
import asyncio
async def wait_for_approval(task_id: str, timeout: int = 300) -> str:
"""承認を待つ(タイムアウト付き)"""
try:
result = await asyncio.wait_for(
poll_approval_status(task_id),
timeout=timeout
)
return result # "approved" or "rejected"
except asyncio.TimeoutError:
log_approval_event(task_id, "timeout", "rejected", "system")
return "rejected" # デフォルト拒否
フェーズ5: 結果通知
処理完了後、Slackに結果をフィードバックします。これにより承認者は「自分の承認がどうなったか」を確認できます。
拒否・タイムアウト時のフロー
承認だけでなく、拒否された場合やタイムアウトの場合のフローも設計しておきましょう。
実装時の考慮事項
実際に実装する際に考慮すべきポイントを整理します。
| 観点 | 内容 | 判断基準 |
|---|---|---|
| タイムアウト値 | 何分待つか | 5分~30分が現実的。業務の緊急度で調整 |
| 承認チャネル | DMかチャンネルか | チーム規模で判断。少人数ならDM |
| 承認者 | 誰が承認するか | ロールベースで指定。個人開発なら自分自身 |
| リトライ | タイムアウト後の再送 | 原則は再送しない。必要なら手動で再実行 |
| ログ | 何を記録するか | 承認・却下・タイムアウトすべて記録 |
SE経験との接点
この承認フローの設計は、業務システムでの経験がそのまま活きます。
- ワークフローエンジンの設計経験 → シーケンス図の作成に活きる
- 申請・承認システムの開発経験 → 承認ステータス管理に活きる
- 障害通知の設計経験 → Slack通知メッセージの設計に活きる
まとめ
- Slack承認フローは5フェーズ(受付→リスク判定→Slack通知→承認待ち→結果通知)で構成
- リスクレベルに応じて自動実行・通知のみ・承認必須を切り替える
- タイムアウトは「デフォルト拒否」で安全側に倒す
- Mermaidでフローを可視化することで、実装前に設計を確認できる
- SEのワークフロー設計経験がそのまま活かせる
次回予告
第17回「AI時代でもSEの品質保証経験が価値を持つ理由」では、品質保証・テスト設計の経験がAI時代にどう活きるかを整理します。AI生成物の品質をどう担保するか、SEのレビュー観点をQA観点表としてまとめます。「AIに任せた結果、どうチェックする?」という問いへの答えを探ります。
📖 連載一覧: 第1〜15回 ✅ 公開済み | 第16回(本記事) | 第17回〜 続刊予定