コンテンツにスキップ

Slack MCP Agent — システムアーキテクチャ

v2.1 / 2026-05-09

MVP のシステム構成を定義する。
先行プロジェクト caster-division-ai の思想(Custom Tool パターン、pause/resume、監査ログ)を継承しつつ、
本プロジェクト固有の要件(SQLite / シングルプロセス / 4 月 MVP)に最適化する。


1. 設計原則

# 原則 説明
A-1 シングルプロセスで十分 MVP はローカル実行 × 1 人運用。Backend・MCP Server・Web を個別プロセスで起動し、make dev で一括管理
A-2 Custom Tool ファースト セキュリティが重要な HR データ操作は Custom Tool で Backend 内完結。MCP Server は Managed Agent 経由のツール提供に使用
A-3 状態は SQLite に集約 Backend と Web が同一 DB ファイルを参照。Backend が書き込み、Web は読み取り主体
A-4 Slack が主 UI、Web が副 UI ユーザー体験は Slack で完結。Web は管理・監視・設定用
A-5 マルチテナント対応を構造で準備 全テーブルに tenant_id。MVP はシングルテナントだがスキーマレベルで将来に備える
A-6 caster-division-ai の継承 Custom Tool パターン、承認フロー、監査ログ、DashShell レイアウトを踏襲
A-7 二系統 Slack Bot 既存 MCP Agent Bot(業務パイプライン直結)と会話型 Bot(先に会話・計画提示・承認後に実行)を並存。同一 Backend プロセスで動作し、DB・Custom Tool Executor を共有する

2. システム構成図

flowchart TD
    subgraph user ["User / PM"]
        Slack["Slack\n(Channel / Agent Container)"]
        WebUI["Web 管理画面"]
    end

    subgraph backend ["Backend (Python)"]
        ExistingBolt["MCP Agent Bot\n(既存 Slack Bolt App)\n- app_mention → L1-L4直結\n- actions"]
        ConvBolt["会話型 Bot\n(別 Slack App)\n- app_mention → Conversation Layer\n- plan 承認 actions"]
        ConvLayer["Conversation Layer\n- LLM planner: intent/reply/plan/candidate_tools\n- plan承認 actions\n- approved_plan_context を L2 へ橋渡し"]
        Pipeline["Pipeline\nL1: Msg → Task\nL2: Task → Prompt\nL3: Prompt → Steps\nL4: Steps → Exec"]
        Routing["Routing Engine\nA/B/C rule decision\n- task_type\n- flag severity"]
        CTE["Custom Tool Executor\n(freee API 直接)"]
        HrSync["HR Employee Sync Job\nfreee employee_id × Slack UserID"]
        ExistingBolt --> Pipeline --> Routing
        ConvBolt --> ConvLayer
        ConvLayer -->|"approved_plan_context"| Pipeline
        Pipeline --> CTE
    end

    subgraph web ["Web (Next.js)"]
        SC["Server Components → SQLite 読取\nServer Actions → SQLite 書込\n(settings, audit)"]
    end

    subgraph anthropic ["Anthropic Cloud"]
        MAS["Managed Agent Session\n- L4 実行ランタイム\n- MCP Tool 呼出\n- tool_confirmation"]
        MCP["Combined MCP Server\n(scripts/combined_mcp_server.py, port 8001)\nhr-freee 系モック + Google Sheets + Slack DM"]
        MAS --> MCP
    end

    FreeeAPI["freee API\n(OAuth 2.0)"]
    SlackAPI["Slack Web API\nusers.lookupByEmail"]
    SQLite["SQLite DB\nagent.db"]

    Slack -->|"Socket Mode"| ExistingBolt
    Slack -->|"Socket Mode"| ConvBolt
    WebUI -->|"HTTP"| SC
    CTE --> FreeeAPI
    HrSync --> FreeeAPI
    HrSync --> SlackAPI
    HrSync --> SQLite
    SC --> SQLite
    backend --> SQLite
    MCP -->|"MCP (streamable-http)"| FreeeAPI

2.1 プロセス構成

MVP は開発容易性を優先し、MCP Server を 1 プロセスに統合scripts/combined_mcp_server.py)して運用する。mcp-servers/hr-freee/mcp-servers/common/ のパッケージは単体起動・テスト用に残っているが、make dev では使用しない。

プロセス 言語 ポート 起動コマンド 役割
Backend Python 8000 (設定用、通常は Socket Mode のため外部公開不要) python -m src.app (in backend/) MCP Agent Bot + 会話型 Bot + パイプライン + Custom Tool Executor + E2E control
Combined MCP Server Python 8001 python scripts/combined_mcp_server.py HR モック + Google Sheets + Slack DM を単一プロセスで提供
Web Node.js 3100 pnpm dev (in web/) 管理画面 (Next.js)
ngrok - - ngrok http --domain=$NGROK_DOMAIN 8001 MCP Server を Anthropic Cloud に公開

補助(必要時のみ個別起動):

プロセス ポート 用途
mcp-servers/hr-freee/server.py MCP_HR_FREEE_PORT (既定 8001) hr-freee 単体のユニットテスト・動作確認
mcp-servers/common/server.py MCP_COMMON_PORT (既定 8002) common 単体のユニットテスト・動作確認

2.1.1 会話型 Bot の追加設定

会話型 Bot は .env に以下の環境変数を追加することで有効になる。3 つすべて設定されている場合のみ起動する(ConversationSlackConfig.enabled)。設定がない場合は Backend 起動時にスキップされ、既存 MCP Agent Bot の動作に影響しない。

環境変数 説明
CONVERSATION_SLACK_BOT_TOKEN 会話型 Bot 専用の Bot Token (xoxb-...)
CONVERSATION_SLACK_APP_TOKEN 会話型 Bot 専用の App-Level Token (xapp-...)
CONVERSATION_SLACK_SIGNING_SECRET 会話型 Bot 専用の Signing Secret

Railway にデプロイする場合は、既存の SLACK_BOT_TOKEN / SLACK_APP_TOKEN / SLACK_SIGNING_SECRET はそのまま残し、上記 3 変数を追加する。

2.2 通信経路

From To プロトコル 説明
Slack → Backend WebSocket (Socket Mode) Slack Events / Actions をリアルタイム受信
Backend → Slack HTTPS chat.postMessage, chat.update, views.open
Backend → Slack Web API HTTPS 人物マスタ同期時の users.lookupByEmail
Backend → Anthropic (L1-L3) HTTPS messages.create (Anthropic SDK)
Backend → Anthropic (L4) HTTPS beta.sessions.create/retrieve/events
Anthropic → MCP Server HTTP (streamable-http) Managed Agent がツール実行 (ngrok 経由、combined MCP の単一ポート)
Backend → freee API HTTPS Custom Tool Executor 内で直接呼出。Lane B analysis tools もこの経路でfreee勤怠を取得する
Backend Sync / Lane B/C Jobs → freee API HTTPS sync_hr_employees が従業員・employee group membership を取得し、Lane B jobs が勤怠を取得して hr_snapshots を作成。Lane A jobs は人物マスタと設定を使って通知対象を解決する
Backend Routing Engine → SQLite ファイル I/O A/B/C の最新区分を tasks.routing_tier、履歴を routing_decisions、監査を audit_logs に保存
E2E Control → Backend HTTPS → nginx → localhost E2E_CONTROL_TOKEN で保護し、Railway E2E の Prompt/Process 承認を Slack Web クリックに依存せず実行
Backend → SQLite ファイル I/O aiosqlite (WAL モード)
Web → SQLite ファイル I/O Server Components から直接読取

3. レイヤー構成

3.1 概要

flowchart TD
    subgraph presentation ["Presentation Layer"]
        P1["Slack (Block Kit cards / Agent Container)"]
        P2["Web (Next.js Server Components)"]
    end

    subgraph application ["Application Layer"]
        A1["Slack Event/Action Handlers"]
        A2["Pipeline Orchestrator"]
        A3["Web Server Actions"]
    end

    subgraph domain ["Domain Layer"]
        D1["L1-L4 Pipeline (LLM 呼出 + 状態遷移)"]
        D2["Custom Tool Executor (freee API)"]
        D3["Approval Gate (承認/却下/再生成)"]
    end

    subgraph infrastructure ["Infrastructure Layer"]
        I1["SQLite (aiosqlite / WAL)"]
        I2["Anthropic SDK (Messages + Managed Agents beta)"]
        I3["Slack SDK (slack-bolt)"]
        I4["freee API Client (OAuth 2.0)"]
        I5["MCP Servers (FastMCP / streamable-http)"]
    end

    presentation --> application --> domain --> infrastructure

3.2 各レイヤーの責務

レイヤー 責務 依存方向
Presentation Slack Block Kit JSON 構築、Web ページレンダリング → Application
Application ユーザーイベントの受信、パイプライン起動、状態更新の調整 → Domain
Domain ビジネスロジック(L1-L4、承認フロー、A/B/C ルーティング、ツール実行) → Infrastructure
Infrastructure 外部サービス接続、永続化、SDK ラッパー (最下層)

4. 4 層パイプライン詳細

4.1 全体フロー

flowchart TD
    UserMsg["User Message"]

    subgraph L1 ["L1: Message → Task"]
        L1P["Claude messages.create (1-shot)\n→ Task JSON (title, desc, priority)\n→ DB: tasks INSERT\n→ Slack: Task Card 投稿"]
    end

    Routing["Rule Routing\n→ tasks.routing_tier\n→ routing_decisions\n→ audit_logs"]
    FAQ{"taskType faq"}
    FAQAnswer["FAQ Answer\n→ faq_system.md\n→ Slack FAQ Card"]

    subgraph L2 ["L2: Task → Prompt"]
        L2P["Claude messages.create (1-shot)\n→ 実行方針テキスト\n→ DB: prompts INSERT\n→ Slack: Prompt Card (承認待ち)"]
    end

    Gate1{"人間が承認 or 却下"}
    L2Reject["却下 → 理由入力\n→ L2 再実行 (新バージョン)"]

    subgraph L3 ["L3: Prompt → Process"]
        L3P["Claude messages.create (1-shot)\n→ steps JSON (ツール呼出計画)\n→ DB: processes INSERT\n→ Slack: Process Card (承認待ち)"]
    end

    Gate2{"人間が承認 or 却下"}
    L3Reject["却下 → 理由入力\n→ L3 再実行 (新バージョン)"]

    subgraph L4 ["L4: Process → Execution"]
        L4P["Managed Agent Session\n→ Custom Tool (freee) / MCP (common)\n→ DB: executions INSERT/UPDATE\n→ Slack: Execution Card (進捗更新)\n→ 完了/失敗/中止"]
    end

    UserMsg --> L1
    L1 --> Routing --> FAQ
    FAQ -->|"yes"| FAQAnswer
    FAQ -->|"no"| L2
    L2 --> Gate1
    Gate1 -->|"却下"| L2Reject --> L2
    Gate1 -->|"承認"| L3
    L3 --> Gate2
    Gate2 -->|"却下"| L3Reject --> L3
    Gate2 -->|"承認"| L4

4.2 L1-L3: Messages API 呼出

System Prompt 入力 出力 max_tokens
L1 prompts/l1_system.md <user_input> ラップされたユーザーメッセージ Task JSON (title, description, priority, taskType) 2048
L2 prompts/l2_system.md Task JSON + ツールカタログ 実行方針テキスト (自然言語) 8192
L3 prompts/l3_system.md 承認済み Prompt + ツールスキーマ (JSON) Process steps JSON (steps[]) 8192

共通パターン: - 全て anthropic.AsyncAnthropic.messages.create (1-shot) - モデル: claude-sonnet-4-6 (環境変数 CLAUDE_MODEL で上書き可) - System Prompt はファイルから読み込み

4.2.1 Rule-based A/B/C ルーティング

L1 の Task JSON は title, description, priority, taskType を維持し、orchestrator.handle_mention() が L1 完了直後に backend/src/routing/ の明示ルールを呼び出す。Phase 1 では taskType と、後続ジョブから渡される flag_kind / severity を入力に A/B/C を決定する。不明な入力は安全側の B(AI 補助 + 人レビュー)に倒す。

このルーティングは backend/src/tools/router.py の Custom Tool Executor ディスパッチとは別概念である。tools/router.py は L4 の tool_name → executorrouting/ は業務タスクの処理区分を担当する。

taskType: "faq" は社内ハウスルール FAQ の短絡フローである。Task と routing decision は通常通り保存するが、L2-L4 は生成せず、tenant settings の faq_knowledge_textfaq_system.md で一次回答を生成して FAQ カードを投稿する。ナレッジ未設定、不確実、法令・個別判断を含む場合は routing:C 人対応 に更新する。

4.3 L4: Managed Agent Session

Anthropic の Managed Agents API (beta) を使用。Agent のランタイムは Anthropic Cloud で実行され、ツール呼出は MCP Server 経由。

sequenceDiagram
    participant B as Backend
    participant A as Anthropic Cloud
    participant M as MCP Server

    B->>A: sessions.create(agent_id, env_id)
    B->>A: events.send(user.message, steps)
    A->>M: MCP tool call
    M-->>A: tool result
    B->>A: sessions.retrieve (polling)
    A-->>B: status: processing / idle

    Note over B,A: tool_confirmation が来た場合
    B->>A: events.send(allow_once)

    B->>A: sessions.events.list (完了確認)
    Note over B: summary 抽出
パラメータ 説明
ポーリング間隔 3 秒 POLL_INTERVAL_SECONDS
最大ポーリング回数 120 回 MAX_POLL_ATTEMPTS (= 360 秒タイムアウト)
tool_confirmation allow_once で自動承認 MVP では全ツール自動承認

4.4 Custom Tool Executor

freee API を扱う HR 3 ツール (list_employees, list_attendance, calculate_overtime) は Custom Tool Executor で Backend 内実行する。L4 ポーリングループが custom_tool_use イベントを検知し、Backend 内の Router → freee Adapter → freee API の順に処理する。

flowchart TD
    MAS["Managed Agent Session"] -->|"custom_tool_use event"| Backend["Backend (polling で検知)"]
    Backend --> CTE["Custom Tool Executor"]
    CTE --> T1["list_employees\n→ freee GET /api/1/employees"]
    CTE --> T2["list_attendance\n→ freee GET /api/1/employees/{id}/work_records"]
    CTE --> T3["calculate_overtime\n→ list_attendance 結果を集計 (Backend 内計算)"]
    T1 --> Result["events.send(tool_result)"]
    T2 --> Result
    T3 --> Result
    Result -->|"Managed Agent Session 続行"| MAS

セキュリティ上の利点: - OAuth トークンが Anthropic Cloud に渡らない - ツール実行ログが Backend 内で完結 - Policy Engine (Post-MVP) で制御可能

4.5 HR Employee Sync Job

sync_hr_employees は Phase 0 の人物マスタ構築用 job。freee employeesemployee_group_memberships を取得し、login_email をキーに Slack users.lookupByEmail で Slack UserID を解決して hr_employees に upsert する。

sequenceDiagram
    participant J as sync_hr_employees
    participant F as freee API
    participant S as Slack Web API
    participant DB as SQLite

    J->>F: GET /companies/{company_id}/employees
    F-->>J: employees
    J->>F: GET /employee_group_memberships
    F-->>J: employee_id + login_email
    loop membership ごと
        J->>S: users.lookupByEmail(login_email)
        S-->>J: Slack UserID or users_not_found
        J->>DB: hr_employees INSERT/UPDATE
    end

MVP では make sync-hr-employees による手動実行を提供する。sync_hr_employees はスケジューラから呼び出せる job 関数として実装済みだが、自動 cron 登録は後続 issue で扱う。Slack 未登録・メール不一致の場合は slack_user_id = NULL で保存し、warning log を出して手動補正対象にする。

4.6 In-Process Scheduler

MVP では Backend プロセス内に APScheduler ベースの scheduler を持つ。Railway 単一コンテナ構成で Slack Bolt と同じプロセス内に起動し、日次 HR パイプラインの取得・保存・差分検出・フラグ付けの基盤を提供する。

SCHEDULER_DISABLED=true で scheduler 全体を停止できる。MOCK_MODE=true は scheduler 基盤を停止しない。将来の業務 job は MOCK_MODE の場合に外部 API 呼び出しを避ける。

hr_snapshots は日次取得データのスナップショットを保存し、hr_flags は差分検出や異常検知のフラグを保存する。Lane A 通知では hr_flags を idempotency marker としても使い、同日の重複送信を防ぐ。AWS EventBridge + Lambda + RDS/PostgreSQL への移行は Post-MVP の課題とする。

Lane A scheduler jobs:

  • attendance_closing_notification(締め日前営業日の本人リマインド)
  • missing_clockin_detection(未打刻通知 + 連続閾値でエスカレーション)
  • paid_leave_expiry_detection(60/30/10/7日前の段階通知)

Monthly report scheduler job:

  • monthly_hr_report_generation(毎月1日 08:00、Lane A/B 指標を monthly_reports draft に保存し、monthly_report_channel_id に HR レビューカードを投稿)

monthly_reports は scheduler job または手動実行から再生成できる。同一 tenant_id + report_month は上書きし、Slack の approve_monthly_report action で delivered に更新する。


5. 承認フローアーキテクチャ

5.1 承認ゲート

stateDiagram-v2
    [*] --> 生成中
    生成中 --> 承認待ち
    承認待ち --> 承認済み : 承認
    承認待ち --> 却下 : 却下
    却下 --> 生成中 : 理由入力 → 再生成
    承認済み --> [*]

Prompt と Process の 2 箇所に同一パターンの承認ゲートを配置。

5.2 却下 → 再生成

  1. ユーザーが [却下] をクリック
  2. views.open で理由入力モーダルを表示
  3. 理由を DB に保存 (rejection_reason, rejected_by)
  4. 前バージョンの内容 + 却下理由を含めて L2/L3 を再実行
  5. 新バージョン (version + 1) を DB に INSERT
  6. Slack カードを新バージョンの承認待ちに更新

5.3 実行中止

  1. 実行中の Execution カードに [中止] ボタンを表示
  2. クリック → Managed Agent Session を停止 (sessions.cancel)
  3. Execution ステータスを cancelled に更新
  4. cancelled_by, cancelled_at を記録
  5. Task ステータスを cancelled に更新

5.4 再実行

  1. 失敗した Execution カードに [再実行] ボタンを表示
  2. クリック → 同じ Process steps で新しい L4 Session を開始
  3. 新しい Execution レコードを INSERT
  4. Task ステータスを running に戻す

6. 認証アーキテクチャ

6.1 Slack 認証

項目 方式
アプリ配信 Socket Mode (WebSocket)
トークン SLACK_BOT_TOKEN (xoxb-), SLACK_APP_TOKEN (xapp-)
署名検証 SLACK_SIGNING_SECRET (Bolt が自動検証)
ユーザー識別 Slack user ID (event.user / body.user.id)
メール突合 users.lookupByEmail (人物マスタ同期時のみ)

6.2 freee OAuth 2.0

sequenceDiagram
    participant W as Web 管理画面
    participant B as Backend/API
    participant F as freee

    W->>B: [接続] クリック
    B->>F: authorize URL 生成
    B-->>W: リダイレクト
    W->>F: freee で認可
    F-->>B: callback + auth code
    B->>F: token exchange
    F-->>B: access + refresh token
    Note over B: DB 保存 (暗号化)
    B-->>W: 接続完了リダイレクト
項目 方式
トークン保存 integrations テーブル (credentials JSON, 暗号化)
リフレッシュ 401 応答時に自動リフレッシュ → リトライ
スコープ employees:read, work_records:read
トークン管理 Backend プロセス内のみ。Anthropic Cloud には渡さない

6.3 Web 管理画面認証

フェーズ 方式 詳細
MVP HTTP Basic 認証 Next.js Middleware で全ルートに適用。ADMIN_USER / ADMIN_PASSWORD 環境変数
Post-MVP Firebase Auth + Google SSO @cast-er.com ドメイン制限。Caster 既存と同構成

6.4 Anthropic API

項目 方式
認証 API Key (ANTHROPIC_API_KEY)
SDK anthropic.AsyncAnthropic (Backend 内)
Managed Agent agent_id + environment_id で Session を作成

7. 永続化アーキテクチャ

7.1 SQLite 設計方針

項目 方式 理由
WAL モード 有効 読み書き並行性。Web の読取と Backend の書込が同時可能
ULID 全テーブルの PK 時系列ソート可能な分散 ID
JSON カラム steps, results, config, credentials, metadata, details 柔軟なスキーマ拡張
FK 制約 有効 (PRAGMA foreign_keys = ON) データ整合性
マイグレーション バージョン管理 (SCHEMA_VERSION + _MIGRATIONS dict) インクリメンタル DDL

7.2 テーブル一覧

テーブル 主要カラム 用途
tenants id, slug, name, agent_id, vault_id テナント管理
tasks id, tenant_id, title, status, channel, thread_ts タスク (L1 出力)
prompts id, task_id, version, content, status, rejection_reason 実行方針 (L2 出力)
processes id, task_id, version, steps (JSON), status, rejection_reason ステップ計画 (L3 出力)
executions id, task_id, process_id, session_id, status, results (JSON) 実行記録 (L4)
slack_messages id, task_id, card_type, channel, message_ts Slack カード ↔ DB マッピング
tool_permissions id, tenant_id, tool_name, permission, constraints (JSON) ツール権限 (Post-MVP)
audit_logs id, tenant_id, timestamp, actor_type, action, details (JSON) 監査ログ
settings id, tenant_id, config (JSON) テナント設定
integrations id, tenant_id, provider, status, credentials (JSON) 外部連携
hr_employees id, tenant_id, freee_employee_id, login_email, slack_user_id, status freee 従業員 ID と Slack UserID の人物マスタ
hr_snapshots id, tenant_id, target_date, snapshot_kind, payload_json 日次 HR データのスナップショット
hr_flags id, tenant_id, snapshot_id, target_date, flag_kind, severity, resolved_at 差分検出・異常検知フラグ

7.3 Backend ↔ Web の DB 共有

flowchart TD
    BackendPy["Backend (Python, aiosqlite)\nINSERT/UPDATE\n(tasks, prompts, processes,\nexecutions, audit_logs)"]
    WebNode["Web (Node.js, better-sqlite3)\nSELECT (全テーブル)\nUPDATE (settings)\nINSERT (audit_logs)"]
    DB["agent.db (WAL)"]

    BackendPy --> DB
    WebNode --> DB
  • Backend: 主要な書き込み担当 (パイプライン進行)
  • Web: 読み取り主体 + 設定変更・監査ログ書込
  • WAL モードにより同時アクセスが安全

8. MCP Server アーキテクチャ

8.1 MVP 構成

MCP Server は Managed Agent がツールを呼び出す際のインターフェース。Backend が直接呼び出すのではなく、Anthropic Cloud 経由で使用される。MVP では HR 系モックと出力系ツールを 1 プロセス(combined) に統合して運用する。

サーバー ポート ツール 備考
Combined MCP Server (scripts/combined_mcp_server.py) 8001 list_employees, list_attendance, calculate_overtime(モック), write_rows_to_google_sheet, send_slack_dm make dev で起動する本線。Google Sheets / Slack DM は GOOGLE_SERVICE_ACCOUNT_JSON / SLACK_BOT_TOKEN 設定時のみ実 API。HR 系は常にモックデータを返す(本番実行は Custom Tool Executor で行う)
mcp-servers/hr-freee/ MCP_HR_FREEE_PORT HR モックツール 単体テスト・動作確認用。make dev では未使用
mcp-servers/common/ MCP_COMMON_PORT write_rows_to_google_sheet, send_slack_dm 単体テスト・動作確認用。make dev では未使用

8.2 ツール実行パスの使い分け (MVP)

MVP では freee HR ツールを Custom Tool Executor、出力系ツールを MCP Server で実行する。

ツール 実行パス 理由
list_employees Custom Tool Executor (Backend 内) freee OAuth トークン保護・個人情報
list_attendance Custom Tool Executor (Backend 内) freee OAuth トークン保護・個人情報
calculate_overtime Custom Tool Executor (Backend 内) freee OAuth トークン保護・個人情報
write_rows_to_google_sheet MCP Server common (Agent 経由) 書込系・低リスク
send_slack_dm MCP Server common (Agent 経由) 書込系・低リスク
判断基準 Custom Tool Executor MCP Server
認証情報の扱い Backend プロセス内で保持。Anthropic Cloud に渡さない MCP Server プロセス内で保持
監査 Backend ログ + audit_logs テーブル Agent イベントログ
適用範囲 freee API (HR データ) Google Sheets, Slack DM

8.3 ネットワーク要件

  • MCP Server は localhost で起動
  • Anthropic Cloud からのアクセスには ngrok トンネルが必要
  • setup-managed-agent.py で Agent 定義に MCP Server URL を登録

9. 非同期処理アーキテクチャ

9.1 イベント駆動フロー

Slack Bolt は同期ハンドラ内で ack() を呼び、重い処理は asyncio.new_event_loop() で非同期実行。

def handle_approve_prompt(ack, body, client):
    ack()  # 3 秒以内に応答

    async def async_handler():
        # L3 実行 (10 秒程度)
        # DB 更新
        # Slack カード更新

    loop = asyncio.new_event_loop()
    loop.run_until_complete(async_handler())

9.2 L4 ポーリング

L4 は Managed Agent Session のステータスを 3 秒間隔でポーリング。

flowchart TD
    Start["Backend"] --> Create["sessions.create()"]
    Create --> Send["events.send(user.message)"]
    Send --> Loop{"loop\n(max 120 回 = 360 秒)"}
    Loop --> Retrieve["sessions.retrieve() → status"]
    Retrieve --> CheckStatus{"status?"}

    CheckStatus -->|"processing"| EventsList["sessions.events.list()"]
    EventsList --> ToolConf{"tool_confirmation?"}
    ToolConf -->|"Yes"| AllowOnce["events.send(allow_once)"] --> Loop
    ToolConf -->|"No"| CustomTool{"custom_tool_use?"}
    CustomTool -->|"Yes"| RunTool["handle_custom_tool()\n→ events.send(tool_result)"] --> Loop
    CustomTool -->|"No"| Loop

    CheckStatus -->|"idle"| Summary["break → summary 抽出"]
    CheckStatus -->|"error"| ErrorHandle["break → エラー処理"]

: on_progress コールバックは run_l4_session() のシグネチャに存在するが、MVP 実装ではポーリングループから呼び出されていない。Slack の Execution カードは L4 開始時と完了・失敗・中止の遷移タイミングでのみ更新される。ツール実行ごとのリアルタイム進捗更新は Post-MVP(docs/post-mvp-roadmap.md 参照)。

9.3 中止処理

  • ユーザーが [中止] → Backend が sessions.cancel() を呼出
  • 次回ポーリングで cancelled ステータスを検知
  • ポーリングループ終了 → DB + Slack 更新

10. エラー境界

境界 エラー種別 対処
Slack → Backend Socket Mode 切断 自動再接続 (Bolt 標準)
Backend → Anthropic (L1-L3) API エラー / タイムアウト リトライ (最大 3 回) → 失敗カード表示
Backend → Anthropic (L4) Session エラー Execution ステータス failed → [再実行] ボタン
Backend → freee API 401 トークンリフレッシュ → リトライ
Backend → freee API 429 Retry-After に従いリトライ
Backend → freee API 5xx 最大 3 回 (exponential backoff)
Backend → SQLite ロックエラー WAL + BUSY_TIMEOUT で軽減
Web → SQLite 接続エラー エラーページ表示
MCP Server ツール実行エラー エラーテキストを Agent に返却 → Agent が判断

11. セキュリティ境界

flowchart TD
    subgraph trust1 ["信頼境界 1: Backend プロセス"]
        FreeeToken["freee OAuth Token\n(暗号化保存)"]
        SlackToken["Slack Bot Token\nAnthropic API Key\n(環境変数)"]
        Note1["認証情報は Backend 外に出ない\nLLM API に送信されるのは業務データのみ"]
    end

    subgraph trust2 ["信頼境界 2: Anthropic Cloud"]
        ManagedAgent["Managed Agent Session"]
        MCPExec["MCP Server 経由のツール実行"]
        DataPolicy["データ保持なしポリシー (API)"]
    end

    subgraph trust3 ["信頼境界 3: MCP Server プロセス"]
        Localhost["localhost 起動、ngrok 経由でのみ外部公開"]
        ToolExec["ツール実行は MCP Server プロセス内で完結"]
    end

    trust1 --> trust2
    trust2 --> trust3

LLM 入力のセキュリティ

  • ユーザー入力は <user_input> タグで隔離 (Prompt Injection 軽減)
  • HR 個人データ (氏名、勤怠) の LLM API 送信は許容
  • マイナンバー等の重要個人情報は Post-MVP で対策

12. Post-MVP 拡張ポイント

領域 MVP Post-MVP
テナント シングル (tenant_id はスキーマに存在) Multi-tenant Router + Tenant Config
認証 Basic 認証 Firebase Auth + Google SSO
DB SQLite PostgreSQL (or Firestore)
Scheduler APScheduler in Backend process AWS EventBridge + Lambda / worker service
ツール Custom Tool + MCP (3+2 ツール) Shared Schema Layer + ベンダーアダプタ
デプロイ Railway 単一コンテナ (nginx + Backend + MCP + Web) Cloud Run / ECS (サービス分割)
監視 ログ出力のみ Datadog / Cloud Logging
Skills L4 システムプロンプト固定 Managed Agents Skills (動的ローディング)
Policy Engine 全ツール自動承認 tool_permissions テーブルで制御
Vault 環境変数 + DB 暗号化 Managed Agents Vault (テナント別)

変更履歴

日付 バージョン 変更内容
2026-04-18 v1.0 初版作成
2026-04-19 v1.1 freee ツール実行パスを Custom Tool Executor に一本化。MCP Server hr-freee を開発テスト用と明記 (issue #11)
2026-04-20 v1.2 デプロイを「ローカル」から「Railway 単一コンテナ」に更新 (issue #31)
2026-04-20 v1.3 実装実態に同期: MCP を combined 構成に修正、Web ポートを 3100 に訂正、L2/L3 max_tokens を 8192 に訂正、on_progress が未配線である旨を明記、Custom Tool Executor のポーリング分岐を追加
2026-04-29 v1.4 In-process scheduler と hr_snapshots / hr_flags、Post-MVP の EventBridge 移行方針を追加 (issue #48)
2026-04-29 v1.5 Rule-based A/B/C routing engine、tasks.routing_tierrouting_decisions の保存フローを追加 (issue #52)
2026-05-05 v1.6 Lane B の E2E control、勤怠分析ジョブ、AIルーティング昇格を追加
2026-05-05 v1.7 Lane B analysis tools を L4 Custom Tool 経路へ追加
2026-05-06 v1.8 Lane A 通知ジョブ(#49/#50/#51)と idempotent marker 運用を追加
2026-05-08 v1.9 FAQ 短絡フローと月次 HR レポート draft / review / delivered フローを追加