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 を維持) | 0002 → 0007 |
| ストレージ | Turso → D1(ベクトルのみ Vectorize)、S3 → R2、Upstash → KV | 0004, 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設計の不変条件(既存規約に準拠)
- クリーンアーキテクチャ:
domain→application→infrastructureの依存方向を守り、domain/applicationは外部 SDK をインポートしない。 - Slack への ack は 3 秒以内。重い処理はハンドラ内で行わず、Agent (DO) への委譲または
ctx.waitUntilに逃がす。 - 公開エンドポイントは署名検証必須(
SLACK_SIGNING_SECRET)。noisy-crow-appのapp/routes/slack-events.tsの実装を踏襲する。 - 認証はアプリ層に置かない:管理画面を載せる場合も Cloudflare Access(Zero Trust)に委譲。
- 相対 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 の
generateText(stopWhen: 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 専用) | unpdf | Workers 対応の serverless PDF.js ビルド。Cloudflare 公式チュートリアルでも採用。代替として OpenAI へ PDF を input_file で直接渡す案もある(トークン消費増に注意) |
jsdom(URL 本文抽出) | HTMLRewriter | Workers 組み込み。依存ゼロ |
parquets(Node 専用) | NDJSON で R2 に書く | Parquet/Iceberg が必要になったら Cloudflare Pipelines(ベータ)で R2 Data Catalog へ書き出す案を再検討 |
@libsql/client | D1 バインディング | |
@upstash/redis | KV バインディング |
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 + tools | Agents 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_clear | POST /slack/commands(ack → response_url) |
| Block Kit interactions | POST /slack/interactions |
Turso reactions | D1 reactions(Drizzle スキーマ + マイグレーション) |
Turso messages_vectors | Vectorize インデックス(メタデータは vector metadata / KV) |
| S3 parquet / files / master-data | R2(キー構成は踏襲、parquet → NDJSON) |
| Upstash 重複排除 / ロック / キャッシュ | KV(TTL 付き)+ BotAgent (DO) 内の強整合チェック |
| Vercel Cron(master-data 日次 / weekly-report 週次) | 同一 wrangler の [triggers] crons + scheduled ハンドラ |
| 週報の画像生成(nanobanana / Gemini) | packages/cloudflare の AiGatewayClient(Gemini 画像生成は実装済み) |
next.config / vercel.json | wrangler.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. データ層の詳細
- KV:
tied-ai-kv1 namespace。キー設計:dedupe:{event_id}(TTL 60s)/memory:{userId}/cache:workspace-guide(TTL 1h、Notion ガイド)。 - D1:
wrangler d1 create tied-ai。Drizzle でreactionsを定義。drizzle-kitでdb/migrations/*.sqlを生成し、wrangler d1 migrations applyで適用。D1 と Drizzle はリポジトリに初導入。 - Vectorize:
tied-ai-messagesインデックス。埋め込みは AI Gateway 経由(text-embedding-3-small相当)。apps/noisy-crow-appのvectorize-emoji-store.tsが手本。 - R2:
tied-ai-data(NDJSON 分析)/tied-ai-files(Slack ファイル)。キー構成(slack-events/.../slack-files/.../slack-data/...)は現行を踏襲。 - Durable Objects:
BotAgent(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-message | BotAgent (DO)(AI 応答、AI Gateway) |
process-slash-command(/memory_*) | fetch ハンドラ(ack → response_url) |
slack-data-fanout | fetch ハンドラで分類し ctx.waitUntil で各保存処理を直接実行(Queue 廃止) |
slack-message-store / slack-attachment-store | ctx.waitUntil → R2(NDJSON / files) |
slack-message-vectorize | ctx.waitUntil → AI Gateway 埋め込み → Vectorize |
slack-reaction-store / slack-reaction-turso-store | ctx.waitUntil → D1 reactions |
paparazzi-analysis / paparazzi-weekly-report | scheduled ハンドラ(cron は最大 15 分実行可。AI Gateway + Gemini 画像生成) |
slack-master-data-sync | scheduled ハンドラ → R2 JSON |
8. Cron / CI
- Cron:
wrangler.tomlの[triggers] cronsに master-data(日次0 13 * * *)・weekly-report(週次0 22 * * 0)。keepalive cron は不要(常駐プロセスがないため)。 - CI:
deploy-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_KEY | AI Gateway 設定(CLOUDFLARE_ACCOUNT_ID / AI_GATEWAY_ID / プロバイダキー) |
INNGEST_EVENT_KEY / INNGEST_SIGNING_KEY | 廃止 |
TURSO_DATABASE_URL / TURSO_AUTH_TOKEN | D1 バインディング |
AWS_* / S3_BUCKET_NAME / S3_FILES_BUCKET_NAME | R2 バインディング |
KV_REST_API_URL / KV_REST_API_TOKEN(Upstash) | KV バインディング |
NOTION_API_KEY | 同左(wrangler secret) |
CRON_SECRET | 廃止(scheduled ハンドラで完結) |
10. 段階的ロードマップ
- フェーズ 0 – 基盤:
apps/tied-ai-bot雛形(fetch ルーティング + 署名検証 + BotAgent 雛形 +wrangler.toml)、packages/cloudflareに AI Gateway chat completions / 埋め込みを追加、db/に Drizzle + D1 マイグレーション、CI 1 本。 - フェーズ 1 – コア会話:app_mention / DM → BotAgent → AI Gateway 経由
gpt-5-mini応答(fetchUrl/fetchWorkspaceGuide、ファイル添付はunpdf)。重複排除は KV + DO。「考え中…」ステータス更新を維持。 - フェーズ 2 – メモリ:KV
memory:{userId}+/memory_*コマンド。 - フェーズ 3 – 分析:reactions → D1 / 埋め込み → Vectorize / NDJSON・files → R2(すべて
ctx.waitUntil)。 - フェーズ 4 – Cron:master-data 同期 / weekly-report(レポート生成 → Gemini 画像生成 → Slack アップロード)。
- フェーズ 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)には別途バックフィルスクリプトが必要。