Skip to content

tied-bot → tied-workspace 統合 移行計画

tied-inc/tied-bot(Next.js 14 / Mastra / Inngest / Turso / S3 / Upstash)の Slack bot 機能を、単一の Cloudflare Worker として tied-workspace に再構成し、Cloudflare へデプロイ可能にするための移行計画です。

これは計画ドキュメントです

本ページは実装に先立つ移行計画であり、ここに記載のアプリ(apps/tied-ai-bot)はまだ存在しません。実装はフェーズに分割して行います(§10 段階的ロードマップ参照)。アプリ名・リソース名は実装着手時に確定します。

2026-06 改訂

初版(Socket Mode コンテナ + Queue + Consumer + 管理画面の 3 アプリ構成)を、noisy-crow-app の単一 Worker 化(Container 常駐廃止・HTTP Events API 移行)の実績を踏まえて全面改訂した。旧構成の ADR 0001 / 0003 は 0006 に、0002 は 0007 に置換。

0. 確定した方針

各判断の背景・代替案は ADR 一覧 に記録しています。

論点決定ADR
Slack 連携 / 処理トポロジーHTTP Events API + 単一 Worker。Inngest / Queue / Container を廃止0006
エージェント実装Cloudflare Agents SDK(Durable Object ベース)。Mastra 不採用、専門エージェント網(delegateToTeam + 11 体)は廃止0007
LLM 呼び出しCloudflare AI Gateway 経由(0002 から継承。モデルは現行 gpt-5-mini を維持)00020007
ストレージTurso → D1(ベクトルのみ Vectorize)、S3 → R2、Upstash → KV0004, 0008
ユーザーメモリKV(D1 から変更)0008
管理画面後続フェーズ。構築する場合は同一 Worker に React Router v7 を載せる(noisy-crow-web と同型)0005

1. ゴールと全体像

Slack 連携は tied-bot と同じ HTTP Events API とし、受け口から AI 応答・分析・cron まですべてを 1 つの Cloudflare Worker に収める。デプロイ単位は 1 つ、Dockerfile・Queue・Consumer・keepalive cron は持たない。

 Slack (HTTP Events API)
   │ POST /slack/events | /slack/commands | /slack/interactions

┌────────────────────────────────────────────────────────────┐
│ apps/tied-ai-bot — 単一 Cloudflare Worker                    │
│                                                            │
│  fetch ハンドラ(署名検証 → 3 秒以内に 200)                    │
│   ├─ 重複排除 (KV) → 「考え中…」を即時投稿                      │
│   ├─ app_mention / DM → BotAgent (DO) へ委譲                 │
│   ├─ /memory_* → ack → response_url で応答                   │
│   └─ message / reaction → ctx.waitUntil で保存系を実行         │
│        (reactions → D1 / embedding → Vectorize / R2)         │
│                                                            │
│  BotAgent(Cloudflare Agents SDK / Durable Object)          │
│   スレッド単位 idFromName(`${channel}:${thread_ts}`)          │
│   └─ schedule(0) で自走(呼び出し元の寿命に依存しない)           │
│       AI Gateway 経由で gpt-5-mini + tools                   │
│       (fetchUrl / fetchWorkspaceGuide) → Slack へ投稿         │
│                                                            │
│  scheduled ハンドラ([triggers] crons)                        │
│   ├─ master-data 同期 (日次) → R2                            │
│   └─ weekly-report (週次) → AI Gateway (レポート + 画像生成)    │
└────────────────────────────────────────────────────────────┘
  bindings: KV / D1 / R2 / Vectorize / DO(BotAgent) / AI Gateway

設計の不変条件(既存規約に準拠)

  1. クリーンアーキテクチャdomainapplicationinfrastructure の依存方向を守り、domainapplication は外部 SDK をインポートしない。
  2. Slack への ack は 3 秒以内。重い処理はハンドラ内で行わず、Agent (DO) への委譲または ctx.waitUntil に逃がす。
  3. 公開エンドポイントは署名検証必須SLACK_SIGNING_SECRET)。noisy-crow-appapp/routes/slack-events.ts の実装を踏襲する。
  4. 認証はアプリ層に置かない:管理画面を載せる場合も Cloudflare Access(Zero Trust)に委譲。
  5. 相対 import に拡張子を付けないmoduleResolution: "bundler")。

2. 重要な設計判断

2-1. AI 応答は ctx.waitUntil ではなく Agent (DO) で自走させる

ctx.waitUntil はレスポンス返却後 30 秒で打ち切られる。gpt-5-mini の複数ステップ + ツール呼び出しは 30 秒を超え得るため、AI 応答は BotAgent (Durable Object) に委譲し、schedule(0) で DO 自身の実行コンテキストに移してから処理する。これで呼び出し元 Worker の寿命と切り離され、完了まで実行できる。軽い保存系(D1 insert・embedding 1 件・KV 書き込み)のみ ctx.waitUntil で十分。

2-2. Cloudflare Agents SDK の使い方

  • BotAgent は スレッド単位のインスタンス(channel:thread_ts を名前に getAgentByName で取得)。同一スレッドへの連続メンションが DO により直列化され、競合しない。
  • 会話コンテキスト(スレッド履歴のキャッシュ等)は DO の組み込みストレージに保持できる。イベント ID の処理済み記録を DO 側に持てば、KV の結果整合に依らない強整合な重複排除も併用できる。
  • Agent 内部のモデル呼び出しは AI SDK の generateTextstopWhen: stepCountIs(3)、現行 maxSteps 3 相当)を AI Gateway の baseURL に向けて使う。Agents SDK の標準パターンであり、ADR 0002 の Gateway 統一と両立する。

2-3. 専門エージェント網(delegateToTeam)は廃止する

delegateToTeam ツールと 11 体の専門エージェントは移植しない(明示的な機能削減、ADR 0007)。botAgent の instructions に必要な要点のみ統合する。週次レポートで使う paparazziAgent はネットワークとは独立した「プロンプト + 単発の generateText」として cron 側に残す。

2-4. ユーザーメモリは KV の JSON ドキュメント

メモリシステム(10 種別・TTL・重要度スコア・ユーザー毎上限 100 件)は独自実装でありストレージ非依存。KV の memory:{userId} 単一 JSON ドキュメントに格納し、TTL・スコアリング・剪定は現行ロジックをアプリ層で移植する(ADR 0008)。D1 は reactions のみとなり、スキーマ・マイグレーション運用が最小化される。

2-5. Node 依存ライブラリの置き換え

tied-bot置き換え備考
pdf-parse(Node 専用)unpdfWorkers 対応の serverless PDF.js ビルド。Cloudflare 公式チュートリアルでも採用。代替として OpenAI へ PDF を input_file で直接渡す案もある(トークン消費増に注意)
jsdom(URL 本文抽出)HTMLRewriterWorkers 組み込み。依存ゼロ
parquets(Node 専用)NDJSON で R2 に書くParquet/Iceberg が必要になったら Cloudflare Pipelines(ベータ)で R2 Data Catalog へ書き出す案を再検討
@libsql/clientD1 バインディング
@upstash/redisKV バインディング

3. コンポーネント移行マッピング

tied-bot(現行)移行後(tied-workspace)
Next.js app/api/slack/events(HTTP webhook)同一 Worker の POST /slack/events(署名検証 + 即 200)
Inngest 12 関数fetch ハンドラ + ctx.waitUntil(保存系)/BotAgent (DO)(AI 応答系)/scheduled(cron 系)
Mastra botAgent + toolsAgents SDK の BotAgent。モデルは AI Gateway 経由 gpt-5-mini、tools は fetchUrl(HTMLRewriter)/ fetchWorkspaceGuide(Notion + KV キャッシュ)
Mastra team-network(delegateToTeam + 11 体)廃止(機能削減)
メモリシステム(Upstash)KV memory:{userId} JSON ドキュメント
/memory_show /memory_stats /memory_clearPOST /slack/commands(ack → response_url
Block Kit interactionsPOST /slack/interactions
Turso reactionsD1 reactions(Drizzle スキーマ + マイグレーション)
Turso messages_vectorsVectorize インデックス(メタデータは vector metadata / KV)
S3 parquet / files / master-dataR2(キー構成は踏襲、parquet → NDJSON)
Upstash 重複排除 / ロック / キャッシュKV(TTL 付き)+ BotAgent (DO) 内の強整合チェック
Vercel Cron(master-data 日次 / weekly-report 週次)同一 wrangler の [triggers] crons + scheduled ハンドラ
週報の画像生成(nanobanana / Gemini)packages/cloudflareAiGatewayClient(Gemini 画像生成は実装済み)
next.config / vercel.jsonwrangler.toml 1 つ

4. 新規ワークスペース構成(予定)

apps/tied-ai-bot/                 # 単一 Cloudflare Worker
  src/
    domain/                       # メモリ種別・スコアリング等の純粋ロジック
    application/                  # usecases + ports
    infrastructure/               # slack / kv / d1 / r2 / vectorize / ai-gateway
    agent/                        # BotAgent (Agents SDK / DO)
    worker/                       # fetch ルーティング + scheduled + 署名検証
  wrangler.toml                  # KV / D1 / R2 / Vectorize / DO / crons を 1 ファイルに集約
  package.json / tsconfig.json
db/                               # Drizzle スキーマ + D1 マイグレーション(reactions のみ)
packages/cloudflare/              # AiGatewayClient に chat completions / 埋め込みを追加

Dockerfile・DO shim・keepalive cron・REST クライアント(KV REST / D1 REST)は不要。Worker からは全リソースにバインディングで直結する。

5. データ層の詳細

  • KVtied-ai-kv 1 namespace。キー設計:dedupe:{event_id}(TTL 60s)/memory:{userId}cache:workspace-guide(TTL 1h、Notion ガイド)。
  • D1wrangler d1 create tied-ai。Drizzle で reactions を定義。drizzle-kitdb/migrations/*.sql を生成し、wrangler d1 migrations apply で適用。D1 と Drizzle はリポジトリに初導入
  • Vectorizetied-ai-messages インデックス。埋め込みは AI Gateway 経由(text-embedding-3-small 相当)。apps/noisy-crow-appvectorize-emoji-store.ts が手本。
  • R2tied-ai-data(NDJSON 分析)/ tied-ai-files(Slack ファイル)。キー構成(slack-events/... / slack-files/... / slack-data/...)は現行を踏襲。
  • Durable ObjectsBotAgent(Agents SDK)。[[migrations]] new_sqlite_classes で宣言。

6. 管理画面(後続フェーズ)

tied-bot に管理 UI は存在しないため、「現状機能の維持」には不要。構築する場合は 別アプリにせず、同一 Worker に React Router v7 framework モードを載せるnoisy-crow-web と同型。loader/action からバインディング直アクセス、認証は Cloudflare Access)。ADR 0005 のルート案(/, /reactions, /memories, /search, /master-data, /jobs)は据え置き。

7. Inngest 12 関数の振り分け

Inngest 関数移行先
start-process-slack-message / process-assistant-thread-messageBotAgent (DO)(AI 応答、AI Gateway)
process-slash-command/memory_*fetch ハンドラ(ack → response_url
slack-data-fanoutfetch ハンドラで分類し ctx.waitUntil で各保存処理を直接実行(Queue 廃止)
slack-message-store / slack-attachment-storectx.waitUntil → R2(NDJSON / files)
slack-message-vectorizectx.waitUntil → AI Gateway 埋め込み → Vectorize
slack-reaction-store / slack-reaction-turso-storectx.waitUntil → D1 reactions
paparazzi-analysis / paparazzi-weekly-reportscheduled ハンドラ(cron は最大 15 分実行可。AI Gateway + Gemini 画像生成)
slack-master-data-syncscheduled ハンドラ → R2 JSON

8. Cron / CI

  • Cronwrangler.toml[triggers] crons に master-data(日次 0 13 * * *)・weekly-report(週次 0 22 * * 0)。keepalive cron は不要(常駐プロセスがないため)。
  • CIdeploy-tied-ai-bot.yml を 1 本追加(PR で wrangler versions upload → version_id を PR コメント、main で wrangler deploy)。旧計画の 3 本構成から削減。

9. 環境変数の対応

現行移行後
SLACK_BOT_TOKEN / SLACK_SIGNING_SECRET同左(wrangler secret)。SLACK_APP_TOKEN は不要(Socket Mode を使わないため)
OPENAI_API_KEYAI Gateway 設定(CLOUDFLARE_ACCOUNT_ID / AI_GATEWAY_ID / プロバイダキー)
INNGEST_EVENT_KEY / INNGEST_SIGNING_KEY廃止
TURSO_DATABASE_URL / TURSO_AUTH_TOKEND1 バインディング
AWS_* / S3_BUCKET_NAME / S3_FILES_BUCKET_NAMER2 バインディング
KV_REST_API_URL / KV_REST_API_TOKEN(Upstash)KV バインディング
NOTION_API_KEY同左(wrangler secret)
CRON_SECRET廃止(scheduled ハンドラで完結)

10. 段階的ロードマップ

  1. フェーズ 0 – 基盤apps/tied-ai-bot 雛形(fetch ルーティング + 署名検証 + BotAgent 雛形 + wrangler.toml)、packages/cloudflare に AI Gateway chat completions / 埋め込みを追加、db/ に Drizzle + D1 マイグレーション、CI 1 本。
  2. フェーズ 1 – コア会話:app_mention / DM → BotAgent → AI Gateway 経由 gpt-5-mini 応答(fetchUrl / fetchWorkspaceGuide、ファイル添付は unpdf)。重複排除は KV + DO。「考え中…」ステータス更新を維持。
  3. フェーズ 2 – メモリ:KV memory:{userId} + /memory_* コマンド。
  4. フェーズ 3 – 分析:reactions → D1 / 埋め込み → Vectorize / NDJSON・files → R2(すべて ctx.waitUntil)。
  5. フェーズ 4 – Cron:master-data 同期 / weekly-report(レポート生成 → Gemini 画像生成 → Slack アップロード)。
  6. フェーズ 5 –(任意)管理画面:同一 Worker に RR7 framework を載せる。

11. リスク・要確認

  • ctx.waitUntil の 30 秒制限:保存系が 30 秒を超えないことを確認する(埋め込み 1 件 + D1 insert + R2 put なら十分収まる想定)。超える処理は BotAgent と同様に DO の schedule へ逃がす。
  • Agents SDK はリポジトリ初導入:DO + new_sqlite_classes マイグレーションの運用が新規。noisy-crow-app に DO はないため、wrangler 設定のレビューを丁寧に。
  • Vectorize 切り替えvector_distance_cos の挙動差(コサイン類似のスコア閾値)を再チューニング要。
  • KV の結果整合:Slack のイベント再送が別コロニーに着弾した場合、KV のみの重複排除はすり抜けの可能性がある。AI 応答系は BotAgent (DO) 内の処理済みチェックで強整合に防ぐ。
  • 機能削減の周知:delegateToTeam / 専門エージェント網の廃止は利用者に見える変更。アナウンスする。
  • 既存データの移行(Turso / S3 / Upstash → D1 / R2 / KV)には別途バックフィルスクリプトが必要。