docs(multi-tenant): 加入 Headless API 改造轨道(Pattern A/B)+ Stage 1 双轨修订
新增 headless-api-track 文档,把 DeerFlow 作为后端服务的两种集成 pattern 全部纳入设计: - Pattern A(业务 backend 代理):业务系统 backend 用 API key 调, 浏览器走他们的 backend。覆盖 IM channels + server-to-server 集成。 - Pattern B(自研 web 浏览器直连):业务 backend 调 /api/v1/auth/exchange-token 换 5-15 min 短期 JWT → 浏览器拿 JWT 直连 含 SSE。核心 4 件事:exchange-token endpoint、ServiceTokenAuthBackend (AuthMiddleware 第三条路径)、workspaces.allowed_origins + CORS 中间件、SSE 跨域验证。 - 不做 widget / iframe(纯 API)。 数据模型:service_accounts / api_keys / external_users 表(schema 在 Stage 0 末加上不阻塞);身份模式三态(collapsed / external_passthrough / both)按 endpoint 分支。 Stage 1 改为三轨并行:付费 SaaS / Pattern A / Pattern B;时间盒从 6-10 周延到 10-15 周。Pattern B 依赖 Pattern A 完成,建议 Stage 1 末 1-2 周做。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,565 @@
|
||||
# Headless API · 业务系统集成轨道
|
||||
|
||||
> 写于 2026-05-09。承接 [phased-rollout-by-scale.zh-CN.md](./phased-rollout-by-scale.zh-CN.md)。
|
||||
>
|
||||
> **触发**:现已有 1-2 个明确的业务系统集成需求,1-3 个月内要 demo / 调通。集成形态包括 IM channels(已支持)+ 业务系统自研 web 页面。
|
||||
> **商业形态**:SaaS + on-prem 双主线。
|
||||
> **身份模式**:service account 折叠 + external_user_id 透传,两种都支持,按 endpoint 选。
|
||||
> **集成 pattern**:**Pattern A(业务系统 backend 代理)+ Pattern B(浏览器直连 + 短期 JWT)**。不做嵌入式 widget。
|
||||
|
||||
---
|
||||
|
||||
## 0. 现状评估
|
||||
|
||||
DeerFlow 架构已经接近"可被业务系统调用的后端"——证据:`backend/app/channels/` 下的 IM 集成(Slack / 飞书 / 钉钉 / Telegram)就是这种用法的活体范例。它们通过 `langgraph-sdk` 调 Gateway HTTP API + 把响应转给 IM 平台,**根本不经过 `frontend/` 工程**。
|
||||
|
||||
### 已经具备(约 80%)
|
||||
|
||||
| 能力 | 实现位置 |
|
||||
|---|---|
|
||||
| 完整 REST API | `backend/app/gateway/routers/`(threads / runs / messages / events / feedback / models / skills / mcp / memory / uploads / artifacts)|
|
||||
| LangGraph SDK 兼容路径 | `/api/langgraph/*` —— 任何 `langgraph-sdk` 客户端可直接接 |
|
||||
| Streaming(SSE) | `runs/stream` + `messages-tuple` delta + `values` + `custom` |
|
||||
| 嵌入式 Python 客户端 | `packages/harness/deerflow/client.py` `DeerFlowClient` |
|
||||
| 现成的"无 web 前端"调用证明 | `app/channels/manager.py` 完全不依赖 frontend |
|
||||
|
||||
### Stage 0 完成后还差什么
|
||||
|
||||
Stage 0 把 workspace 概念立起来了,但 **auth 模式仍是 cookie + JWT + CSRF**——这是为 web 前端设计的,不适合 server-to-server,更不适合"业务系统自研 web 页面浏览器直连"的场景。要让业务系统调,必须补两条平行 auth 路径 + 一组管理能力,详见 §1。
|
||||
|
||||
### 集成 pattern 速览
|
||||
|
||||
| Pattern | 链路 | 适用 | 优先级 |
|
||||
|---|---|---|---|
|
||||
| **A. Backend 代理**(默认) | browser → 业务系统 backend → DeerFlow API key → DeerFlow | 业务系统已有 backend;不在乎多一跳 | Stage 1 必做 |
|
||||
| **B. Browser 直连**(streaming 友好) | 业务系统 backend 颁短期 JWT → browser 直连 DeerFlow `/api/v1/*`(含 SSE) | chat / agent 类、对 token 流延迟敏感、自研 web 页面 | Stage 1 末必做 |
|
||||
| ~~C. 嵌入式 widget / iframe~~ | DeerFlow 托管 chat widget URL,业务系统 embed | 集成方零前端开发 | **不做**(产品决定) |
|
||||
|
||||
Pattern A 是"server-to-server",Pattern B 是"browser-to-server"。两者共用同一组 service account / API key 数据模型,**只是 auth 路径不同**:
|
||||
- Pattern A:`Authorization: Bearer dfk_live_<long-lived API key>`
|
||||
- Pattern B:`Authorization: Bearer eyJ<short-lived JWT>`
|
||||
|
||||
---
|
||||
|
||||
## 1. 改造清单(按 MVP 优先级)
|
||||
|
||||
| # | 能力 | 是否 MVP | 缺它会怎么样 | 工作量 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **API Key 认证(Pattern A)** | **必须** | 业务系统 backend 没法调,跨域 + CSRF 灾难 | M |
|
||||
| 2 | **Service Account 概念** | **必须** | API key 必须挂在某个"账户"上做归属、计费、quota | M |
|
||||
| 3 | **CSRF bypass on bearer** | **必须** | bearer 路径走 CSRF middleware 直接 403 | XS |
|
||||
| 4 | **External User ID 透传** | **必须** | 业务系统的"小明"在 DeerFlow 内不能体现,memory / 个性化失效 | M |
|
||||
| 5 | **API 版本化 `/api/v1/`** | **必须**(早做便宜)| 演进时业务系统要全量改 | S(早做)/ L(晚做)|
|
||||
| 6 | **Rate limit per API key** | **必须**(基础版) | 业务系统 bug 把 DeerFlow 打爆 | S(基础)/ M(分层)|
|
||||
| 7 | **Token Exchange(Pattern B)** | **必须** | 浏览器只能走 backend 代理,自研 web 页面延迟差 | S |
|
||||
| 8 | **CORS 中间件 + per-workspace allowed_origins** | **必须**(与 Pattern B 配套)| 浏览器请求被同源策略挡 | M |
|
||||
| 9 | **短期 JWT 验证路径**(AuthMiddleware 第三条) | **必须**(与 Pattern B 配套)| 短期 JWT 没法验 | S |
|
||||
| 10 | **Idempotency keys** | 推荐 | 业务系统重试时重复建 thread/run | S |
|
||||
| 11 | **Webhook outbound** | 推迟 | 业务系统轮询事件,多调几次 SSE 而已 | M(推到 Stage 2)|
|
||||
|
||||
**MVP 包**(前 9 项)= **4-5 周**;放进 Stage 1 并行做。Pattern B(7-9)依赖 1-3 完成,建议 Stage 1 末(最后 1-2 周)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心设计:API Key + Service Account
|
||||
|
||||
### 数据模型
|
||||
|
||||
```sql
|
||||
service_accounts (
|
||||
id UUID PK,
|
||||
workspace_id UUID FK NOT NULL, -- 必属于一个 workspace
|
||||
name VARCHAR(64), -- "X 业务系统集成"
|
||||
role VARCHAR(16), -- 在 workspace 内的 role:member / admin
|
||||
identity_mode VARCHAR(16), -- collapsed | external_passthrough | both
|
||||
status VARCHAR(16), -- active / suspended / revoked
|
||||
created_by UUID, -- 哪个 user 创建的(必须是 workspace owner/admin)
|
||||
created_at, updated_at
|
||||
)
|
||||
|
||||
api_keys (
|
||||
id UUID PK,
|
||||
service_account_id UUID FK NOT NULL,
|
||||
key_prefix VARCHAR(16) UNIQUE, -- 前 16 字符明文(dfk_live_abc123...)UI 可显示
|
||||
key_hash BYTEA NOT NULL, -- 完整 key 的 sha256,比对用
|
||||
name VARCHAR(64), -- "生产环境 key" / "灰度 key"
|
||||
scopes TEXT[], -- ["threads:read", "runs:create", "uploads:write"...]
|
||||
rate_limit_rpm INT NULL, -- 每分钟请求数;NULL=用 workspace plan 默认
|
||||
expires_at TIMESTAMP NULL, -- 可选过期时间
|
||||
last_used_at TIMESTAMP NULL,
|
||||
revoked_at TIMESTAMP NULL,
|
||||
created_at
|
||||
)
|
||||
|
||||
external_users ( -- ghost user,按需建(identity_mode=external_passthrough 时)
|
||||
id UUID PK,
|
||||
workspace_id UUID FK NOT NULL,
|
||||
service_account_id UUID FK NOT NULL,
|
||||
external_id VARCHAR(128) NOT NULL, -- 业务系统传过来的 ID,原样存
|
||||
display_name VARCHAR(128) NULL,
|
||||
metadata JSONB, -- 可选业务字段
|
||||
created_at, last_active_at,
|
||||
UNIQUE (workspace_id, service_account_id, external_id)
|
||||
)
|
||||
```
|
||||
|
||||
### Key 格式约定
|
||||
|
||||
```
|
||||
dfk_live_<24 字符随机> # 生产 key
|
||||
dfk_test_<24 字符随机> # 测试 key
|
||||
```
|
||||
|
||||
- 前缀 `dfk_live_` / `dfk_test_` 让一眼区分环境(防止把测试 key 投进生产)
|
||||
- 写入 DB 时只存 `sha256(key)`,明文创建后只能在 UI 显示一次
|
||||
- `key_prefix` 列存前 16 字符(`dfk_live_abc12345`)—— UI 列表 + 审计日志可识别但不能用
|
||||
|
||||
### 认证流程
|
||||
|
||||
```
|
||||
请求 → AuthMiddleware → 检测 Authorization header
|
||||
│
|
||||
├── "Bearer dfk_..."
|
||||
│ ↓
|
||||
│ APIKeyAuthBackend.authenticate
|
||||
│ ↓
|
||||
│ SELECT api_keys WHERE key_hash = sha256(token)
|
||||
│ ↓
|
||||
│ load service_account + workspace
|
||||
│ ↓
|
||||
│ set_current_workspace(workspace_id)
|
||||
│ set_current_service_account(account)
|
||||
│ set_current_user(None) # 没有真人 user
|
||||
│ ↓
|
||||
│ CSRFMiddleware skip(bearer 路径不要 CSRF)
|
||||
│ ↓
|
||||
│ 如有 X-External-User-Id header 且 identity_mode 允许:
|
||||
│ → upsert external_users → set_current_external_user
|
||||
│
|
||||
└── "Cookie: access_token=..." → 走现有 web 前端流程
|
||||
```
|
||||
|
||||
### `@require_permission` 装饰器升级
|
||||
|
||||
```python
|
||||
# 现有签名(cookie 模式)
|
||||
@require_permission("threads", "read", owner_check=True)
|
||||
|
||||
# 改为同时支持 service account 路径
|
||||
@require_permission(
|
||||
resource="threads",
|
||||
action="read",
|
||||
scopes=["threads:read"], # API key 必须有此 scope
|
||||
owner_check="workspace_or_user", # SA: 校验 workspace 归属;user: 校验 user_id
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心设计:两种身份模式
|
||||
|
||||
### 模式 A:service account 折叠(identity_mode=`collapsed`)
|
||||
|
||||
业务系统调一切操作都归到该 service account 名下。**适合工具类集成**(CRM 自动总结、安全面板分析)。
|
||||
|
||||
```
|
||||
POST /api/v1/threads
|
||||
Authorization: Bearer dfk_live_xxx
|
||||
|
||||
→ 创建 thread,所有归属都是 service_account_id
|
||||
threads_meta.user_id = NULL
|
||||
threads_meta.service_account_id = <SA.id>
|
||||
threads_meta.workspace_id = <SA.workspace_id>
|
||||
```
|
||||
|
||||
memory 也是 service account 共享的(`workspaces/{wid}/service_accounts/{sa_id}/memory.json`)。
|
||||
|
||||
### 模式 B:external_user_id 透传(identity_mode=`external_passthrough`)
|
||||
|
||||
业务系统的"小明"在 DeerFlow 内独立。**适合 chatbot / 助手类集成**。
|
||||
|
||||
```
|
||||
POST /api/v1/threads
|
||||
Authorization: Bearer dfk_live_xxx
|
||||
X-External-User-Id: bizsys_user_42 # 业务系统的用户 ID
|
||||
|
||||
→ AuthMiddleware:
|
||||
1. 解 API key → load SA
|
||||
2. 看 SA.identity_mode 允许 external_passthrough
|
||||
3. SELECT external_users WHERE (workspace_id, service_account_id, external_id="bizsys_user_42")
|
||||
4. 没找到 → 自动建 ghost external_user
|
||||
5. set_current_external_user(...)
|
||||
|
||||
→ 创建 thread:
|
||||
threads_meta.workspace_id = <SA.workspace_id>
|
||||
threads_meta.service_account_id = <SA.id>
|
||||
threads_meta.external_user_id = <external_users.id>
|
||||
```
|
||||
|
||||
memory 是 per external_user 的(`workspaces/{wid}/service_accounts/{sa_id}/external_users/{eu_id}/memory.json`)。
|
||||
|
||||
### 模式选择策略
|
||||
|
||||
`SA.identity_mode` 三态:
|
||||
- `collapsed`:忽略所有 `X-External-User-Id` header
|
||||
- `external_passthrough`:必须传 `X-External-User-Id`,缺失时 400
|
||||
- `both`:传了走透传、不传走折叠(最灵活但最复杂;建议默认不开放)
|
||||
|
||||
业务系统接入时由 workspace owner 创建 SA 时选定。
|
||||
|
||||
### 这影响哪些 endpoints
|
||||
|
||||
| endpoint | service account 折叠 | external_user_id 透传 |
|
||||
|---|---|---|
|
||||
| `POST /threads` | thread 归 SA | thread 归 external_user |
|
||||
| `GET /threads` | 列出 SA 的所有 thread | 仅列出该 external_user 的 thread |
|
||||
| memory 注入 | SA 共享 memory | 该 external_user 的 memory |
|
||||
| `usage_daily` 写入 | `(workspace_id, SA_id, "main", ...)` | 同上 + `external_user_id` 维度 |
|
||||
| feedback | feedback.user_id 留空,标 SA | feedback.external_user_id 标人 |
|
||||
|
||||
---
|
||||
|
||||
## 3.5 核心设计:Pattern B(Browser 直连 + 短期 JWT 交换)
|
||||
|
||||
### 为什么需要
|
||||
|
||||
业务系统自研的 web 页面如果走 Pattern A(Backend 代理),他们 backend 必须实现 SSE 流式转发——这是个不小的工程量,且每个 token 多一跳延迟。**Pattern B 把 streaming 直接交给浏览器**,业务系统 backend 只做一次性 token 颁发。
|
||||
|
||||
### 数据模型扩展
|
||||
|
||||
```sql
|
||||
-- workspaces 表加列
|
||||
workspaces.allowed_origins TEXT[] -- ["https://app.partner.com", "https://staging.partner.com"]
|
||||
|
||||
-- 不需要新表;短期 JWT 不持久化(足够短就不需要 revoke list)
|
||||
```
|
||||
|
||||
### Token Exchange 流程
|
||||
|
||||
```
|
||||
[业务系统 backend] [DeerFlow] [浏览器]
|
||||
│ │ │
|
||||
│ 1. 用户在业务系统登录 │ │
|
||||
│ ◄────────────────────────────────│────────────────────────────────│
|
||||
│ │ │
|
||||
│ 2. 业务 backend 鉴权后调 │ │
|
||||
│ POST /api/v1/auth/exchange-token │
|
||||
│ Authorization: Bearer dfk_live_xxx │
|
||||
│ { external_user_id: "ming_42", expires_in: 600 } │
|
||||
│ ────────────────────────────────►│ │
|
||||
│ │ │
|
||||
│ │ 3. 校验 API key + SA 状态 │
|
||||
│ │ upsert external_users 行 │
|
||||
│ │ 签短期 JWT │
|
||||
│ │ │
|
||||
│ { access_token: "eyJ...", │ │
|
||||
│ expires_at: "..." } │ │
|
||||
│ ◄────────────────────────────────│ │
|
||||
│ │ │
|
||||
│ 4. 把 access_token 发给浏览器 │ │
|
||||
│ ─────────────────────────────────────────────────────────────────►│
|
||||
│ │ │
|
||||
│ │ 5. browser 直连 DeerFlow │
|
||||
│ │ GET /api/v1/threads/.../events│
|
||||
│ │ Authorization: Bearer eyJ...│
|
||||
│ │ Origin: https://app.partner.com│
|
||||
│ │ ◄──────────────────────────────│
|
||||
│ │ │
|
||||
│ │ 6. CORS preflight 通过 │
|
||||
│ │ SSE stream 200 │
|
||||
│ │ ──────────────────────────────►│
|
||||
```
|
||||
|
||||
### 短期 JWT 设计
|
||||
|
||||
```python
|
||||
# 不复用现有 cookie JWT 的 TokenPayload,新增 ServiceTokenPayload
|
||||
class ServiceTokenPayload(BaseModel):
|
||||
sub: str # external_user_id(DeerFlow 内部 id,不是业务方原始 id)
|
||||
sa: str # service_account_id
|
||||
wid: str # workspace_id
|
||||
eid: str # external_id(业务方原始 id,传给 audit log)
|
||||
scopes: list[str] # 从 api_key.scopes 继承(不能放大)
|
||||
exp: int # 5-15 min(默认 10 min)
|
||||
iat: int
|
||||
iss: "deerflow" # 区分自签 vs 业务方签
|
||||
typ: "service" # 区分 cookie JWT(typ=user)
|
||||
```
|
||||
|
||||
### Endpoint 规格
|
||||
|
||||
```
|
||||
POST /api/v1/auth/exchange-token
|
||||
Authorization: Bearer dfk_live_xxx (API key)
|
||||
Content-Type: application/json
|
||||
|
||||
Request:
|
||||
{
|
||||
"external_user_id": "ming_42", // 必填(identity_mode=external_passthrough)
|
||||
"expires_in": 600, // 可选,默认 600s,最大 3600s
|
||||
"scopes": ["threads:write", "runs:read"] // 可选;省略则继承 API key 全部 scopes
|
||||
}
|
||||
|
||||
Response 200:
|
||||
{
|
||||
"access_token": "eyJ...",
|
||||
"token_type": "Bearer",
|
||||
"expires_at": "2026-05-09T15:20:00Z"
|
||||
}
|
||||
|
||||
Errors:
|
||||
- 401 invalid API key
|
||||
- 403 SA suspended / API key revoked
|
||||
- 400 identity_mode 不允许 external_user_id(collapsed 模式)
|
||||
- 400 scopes 超出 API key 授权
|
||||
```
|
||||
|
||||
### AuthMiddleware 第三条路径
|
||||
|
||||
```
|
||||
请求 → AuthMiddleware → 检测 Authorization
|
||||
│
|
||||
├── "Bearer dfk_..." → APIKeyAuthBackend (Pattern A)
|
||||
├── "Bearer eyJ...typ=service" → ServiceTokenAuthBackend (Pattern B) ← 新增
|
||||
└── "Cookie: access_token=..." → CookieAuthBackend (web 前端)
|
||||
```
|
||||
|
||||
`ServiceTokenAuthBackend.authenticate`:
|
||||
1. 验 JWT 签名(DeerFlow 自签私钥;HS256 即可,不需要 RSA)
|
||||
2. 检 `iss=deerflow, typ=service`(防止把 cookie JWT 误用)
|
||||
3. 校 `sa` service account 状态(可能在签发后被 suspend)
|
||||
4. 校 scope 子集合法(不能超过 SA 当前 scopes)
|
||||
5. `set_current_workspace(wid)` + `set_current_service_account(sa)` + `set_current_external_user(sub)`
|
||||
|
||||
### CORS 设计
|
||||
|
||||
```python
|
||||
# CORSMiddleware 在 AuthMiddleware 之前加载(FastAPI 中间件顺序)
|
||||
class WorkspaceAwareCORSMiddleware:
|
||||
async def dispatch(self, request, call_next):
|
||||
origin = request.headers.get("origin")
|
||||
if not origin:
|
||||
return await call_next(request) # 非浏览器请求
|
||||
|
||||
# 解析当前 workspace(从 token 或 query string)
|
||||
# CORS preflight (OPTIONS) 没有 token,要从其他维度推
|
||||
# 简化方案:所有 /api/v1/* 路径在 OPTIONS 时回 wildcard,但不带 credentials;
|
||||
# 真请求时按 token 上的 wid 查 workspaces.allowed_origins 做精确匹配
|
||||
...
|
||||
```
|
||||
|
||||
**关键安全点**:
|
||||
- `Access-Control-Allow-Credentials: false`(短期 JWT 不依赖 cookie,不需要 credentials;防止意外打开 cookie 跨域)
|
||||
- `Access-Control-Allow-Origin` 精确匹配,不用通配
|
||||
- `Access-Control-Max-Age` 短一点(5 分钟),方便切换 origin 时不被缓存卡
|
||||
|
||||
### 与现有 CSRF 的关系
|
||||
|
||||
CSRFMiddleware 检测到 `Authorization: Bearer ...` 直接 skip——Pattern A 和 Pattern B 都走同一条 skip 逻辑。CSRF 仅对 cookie 路径生效。
|
||||
|
||||
### Token revoke 策略
|
||||
|
||||
短期 JWT 默认**不显式 revoke**(5-15 min 过期,自然失效)。但有三个例外:
|
||||
1. SA `status=suspended` → ServiceTokenAuthBackend 在 step 3 检测时直接拒
|
||||
2. API key 被 `revoked_at` → 同上(短期 JWT 验证时关联回 `sa.api_key`)
|
||||
3. 如果客户提需要立即 revoke 用户访问 → 业务系统调 DeerFlow `POST /api/v1/external-users/{id}/revoke-tokens` 把 `external_users.token_version` bump(`exchange-token` 签发时把它写进 JWT,验证时比对)
|
||||
|
||||
> 第 3 条是 nice-to-have;MVP 不做,等业务方明确提需求再加。
|
||||
|
||||
### Pattern B 不做的事(重要)
|
||||
|
||||
- **不**让浏览器直接持有 API key(`dfk_live_*`)——再短的 TTL 也不行;API key 必须留在业务方 backend
|
||||
- **不**支持 OAuth 2.0 完整 flow(authorize / consent / refresh)——太重,业务方 backend 自己做完用户认证再换 token 即可
|
||||
- **不**做客户端 SDK——给一份 OpenAPI + 浏览器原生 fetch / EventSource 例子,业务方自己接
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心设计:API 版本化
|
||||
|
||||
**早做的成本**:仅是 `app/gateway/routers/__init__.py` 里 mount prefix 改 `/api/v1`。
|
||||
**晚做的成本**:所有业务系统 client 全量改地址。
|
||||
|
||||
### 设计
|
||||
|
||||
```python
|
||||
# 现状
|
||||
app.include_router(threads_router, prefix="/api/threads")
|
||||
|
||||
# Stage 1 改造
|
||||
app.include_router(threads_router, prefix="/api/v1/threads")
|
||||
# 同时保留 /api/threads → 转发到 v1(前端用),sunset 在 Stage 3
|
||||
```
|
||||
|
||||
**deprecation 策略**:
|
||||
- `/api/*`(无版本)路径在 response 加 `X-API-Deprecated: 2027-01-01` header
|
||||
- frontend 同步迁到 `/api/v1`
|
||||
- LangGraph SDK 兼容路径 `/api/langgraph/` 不带版本(跟随上游 SDK 约定)
|
||||
|
||||
**版本演进规则**:
|
||||
- 加字段:v1 内做,不升 v2
|
||||
- 改语义、删字段、改默认值:v2
|
||||
- 整体 endpoints 重组:v2
|
||||
|
||||
---
|
||||
|
||||
## 5. 核心设计:Rate Limit + Idempotency
|
||||
|
||||
### Rate Limit(基础版)
|
||||
|
||||
```
|
||||
api_keys.rate_limit_rpm 设了值:用 key 自己的
|
||||
没设:用 workspace plan 默认(free=60, pro=600, team=3000,可配)
|
||||
|
||||
实现:进程内 sliding window(依赖 Postgres 即可,不需要 Redis):
|
||||
rate_limit_log(api_key_id, minute_bucket, count) 单独小表
|
||||
```
|
||||
|
||||
进阶(Stage 2):分维度(per endpoint、per LLM、per sandbox quota)的复合 rate limit;引入 Redis。
|
||||
|
||||
### Idempotency Keys
|
||||
|
||||
```
|
||||
请求带 Idempotency-Key: <client-generated-uuid>
|
||||
↓
|
||||
SELECT idempotency_records WHERE (api_key_id, key=...) AND created_at > NOW() - 24h
|
||||
↓
|
||||
命中 → 直接返回上次 response(200 + body 原样)
|
||||
未命中 → 处理请求 → 写 idempotency_records + 返回
|
||||
```
|
||||
|
||||
只在写操作(POST / PUT / PATCH / DELETE)支持;GET 不需要。
|
||||
|
||||
---
|
||||
|
||||
## 6. 与 Stage 1 的整合
|
||||
|
||||
把 headless API MVP 包并入 Stage 1,**时间盒从 6-10 周延到 8-13 周**。
|
||||
|
||||
### 修订后 Stage 1 必做项
|
||||
|
||||
| 改动 | 类型 | 估工 |
|
||||
|---|---|---|
|
||||
| Postgres 切换 | 原 Stage 1 | M |
|
||||
| Quota 系统 + TokenUsage 持久化 | 原 Stage 1 | M+ |
|
||||
| Stripe 基础订阅 | 原 Stage 1 | M |
|
||||
| AioSandbox 出网/资源收紧 | 原 Stage 1 | M |
|
||||
| **API Key + Service Account 数据模型 + 仓储** | **新增(Pattern A)** | M |
|
||||
| **APIKeyAuthBackend + AuthMiddleware 双路径** | **新增(Pattern A)** | M |
|
||||
| **CSRF skip on bearer** | **新增** | XS |
|
||||
| **External User ID 透传 + ghost user** | **新增** | M |
|
||||
| **`/api/v1/` 版本化** | **新增**(早做便宜)| S |
|
||||
| **Per-API-key rate limit(基础版)** | **新增** | S |
|
||||
| **`@require_permission` 装饰器升级支持 SA 路径** | **新增** | S |
|
||||
| **API key 管理 UI(workspace settings 内)** | **新增**(最低限:CLI 也行)| S(CLI)/ M(UI)|
|
||||
| **`POST /api/v1/auth/exchange-token` endpoint** | **新增(Pattern B)** | S |
|
||||
| **`ServiceTokenAuthBackend`(短期 JWT 验证)** | **新增(Pattern B)** | S |
|
||||
| **`workspaces.allowed_origins` + CORS 中间件** | **新增(Pattern B)** | M |
|
||||
| **Idempotency keys** | **可选**(推荐) | S |
|
||||
|
||||
合计原 Stage 1(M+M++M+M=4M)+ 新增 Pattern A(M+M+XS+M+S+S+S+S=4M)+ 新增 Pattern B(S+S+M=2M)= 约 10M-15 周。Pattern B 依赖 Pattern A 完成,建议放 Stage 1 末。
|
||||
|
||||
### 修订后 Stage 1 PR 顺序
|
||||
|
||||
**轨道一:付费 SaaS 基础**(与下面并行)
|
||||
1. Postgres 切换(dev → 灰度 → 全切)
|
||||
2. `workspace_quotas` / `workspace_usage_daily` 表 + 仓储
|
||||
3. `TokenUsageMiddleware` 升级为持久化
|
||||
4. `QuotaMiddleware` 加入中间件链
|
||||
5. Stripe webhook + 订阅状态同步
|
||||
6. AioSandbox 收紧
|
||||
7. 基础监控
|
||||
|
||||
**轨道二:Headless API Pattern A**(与轨道一并行;步骤 1 必须先完成轨道一的 1)
|
||||
1. `service_accounts` + `api_keys` + `external_users` 仓储(**先于业务路径**)
|
||||
2. `APIKeyAuthBackend` + `AuthMiddleware` 双路径(cookie + bearer)
|
||||
3. CSRF middleware skip on bearer
|
||||
4. `/api/v1/` mount prefix 切换 + 旧路径兼容转发
|
||||
5. `external_user_id` 透传机制(依赖 1-4)
|
||||
6. `service_accounts.identity_mode` 三态行为分支
|
||||
7. `@require_permission` 升级 + scope 校验
|
||||
8. 基础 rate limit
|
||||
9. API key 管理 CLI + UI
|
||||
10. Idempotency keys(可选)
|
||||
|
||||
**轨道三:Headless API Pattern B**(依赖轨道二的 1-7 完成;建议 Stage 1 最后 1-2 周)
|
||||
1. `workspaces.allowed_origins` 列 + workspace settings UI 的 origin 管理
|
||||
2. `WorkspaceAwareCORSMiddleware`(在 AuthMiddleware 之前)
|
||||
3. `POST /api/v1/auth/exchange-token` endpoint + `ServiceTokenPayload` 设计
|
||||
4. `ServiceTokenAuthBackend`(AuthMiddleware 第三条路径)
|
||||
5. SSE 在 CORS 跨域下的 streaming 验证(写一个 fixture web 页面测)
|
||||
6. 业务方文档:从换 token 到浏览器直连的端到端示例(HTML + JS)
|
||||
|
||||
---
|
||||
|
||||
## 7. SaaS vs on-prem 差异
|
||||
|
||||
| 能力 | SaaS 形态 | on-prem 形态 |
|
||||
|---|---|---|
|
||||
| API key 管理 | workspace settings UI + CLI | CLI 必须;UI 可选;env var 注入预置 key 也合理 |
|
||||
| 配额 / 计费 | 按 plan,Stripe 同步 | 配额作为容量管理(防内部失控),不接 Stripe |
|
||||
| KMS | AWS / 阿里云 KMS(Stage 2 后落) | 客户自带 KMS(HashiCorp Vault / 客户自有);env var 兜底(明文)|
|
||||
| 监控 | 你们运维的 Grafana | 客户自有;DeerFlow 暴露 `/metrics` Prometheus 端点(Stage 2 加)|
|
||||
| Webhook | 平台默认 | 客户内网回调;要求 url 可配置 |
|
||||
| Rate limit | 平台分档强制 | 客户自定义;默认放宽 |
|
||||
| 升级路径 | 你们灰度发布 | docker image tag + 升级文档;schema migration 跑通 |
|
||||
|
||||
**重要**:Stage 0 的 schema 设计已经兼容两者(workspace 是 self-hosted 时也 1 对 1 对应一个安装)。**不需要为 on-prem 单独建分支**。
|
||||
|
||||
### on-prem 专属工作(Stage 1+ 一次性)
|
||||
|
||||
- docker compose 模板(已部分有,加 service_accounts seed 流程)
|
||||
- "首次安装预置 1 个 workspace + 1 个 admin + 1 个 platform key" 的 init script
|
||||
- 升级脚本(schema migrations 自动跑)
|
||||
- 部署文档(一份就够)
|
||||
|
||||
工作量约 1 人周,可以在 Stage 1 末或 Stage 2 头做。
|
||||
|
||||
---
|
||||
|
||||
## 8. 不可逆决策(动手前想清楚)
|
||||
|
||||
| 决策 | 难回头的原因 |
|
||||
|---|---|
|
||||
| API Key 格式(前缀 `dfk_` / 长度 / 加密路径)| 业务系统接入后改格式所有 key 全失效 |
|
||||
| `service_accounts` 表是否能跨 workspace(暂定不允许) | 改了所有 quota / 计费归属逻辑 |
|
||||
| `X-External-User-Id` header 名 | 业务系统集成后改 header 名要全部联调 |
|
||||
| `identity_mode` 三态语义(collapsed / external / both) | 改语义要联调所有业务系统 |
|
||||
| `/api/v1/` 是不是从 Stage 1 第 5 步开始;具体 deprecation 时间 | 业务系统接了之后再要求换 prefix 很不友好 |
|
||||
| ghost user 自动创建策略(X-External-User-Id 缺失时拒绝还是 fallback collapsed) | 改了业务系统接入测试要重跑 |
|
||||
| memory 隔离粒度(SA 共享 vs per external_user) | 一旦客户用上,迁移 memory 数据极麻烦 |
|
||||
| **短期 JWT TTL 默认值**(5 / 10 / 15 min)和 max | 业务系统集成后调短会断当前会话;调长会留更大被偷窃的窗口 |
|
||||
| **`ServiceTokenPayload` 字段集**(claim 名 / 是否带 `eid`) | 改 claim 名所有签名校验失败;早一点把审计需要的字段都设计进去 |
|
||||
| **`workspaces.allowed_origins` 是 workspace 级还是 SA 级** | 暂定 workspace 级(更简单);改成 SA 级要拆数据 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 推荐推进路径
|
||||
|
||||
**第 0-1 周**:
|
||||
- 找现有需要集成的业务系统聊一下,把"两种身份模式哪个适合 / 是否有 webhook 需求 / 是否有 rate limit 偏好"问清楚
|
||||
- 写一份 API key 创建 + 第一次 hello-world 调用的快速指南草稿(不写代码,纯设计验证)
|
||||
|
||||
**第 2-3 周(Stage 0 末)**:
|
||||
- Stage 0 收尾时把 `service_accounts` / `api_keys` / `external_users` schema 也加上(schema 加上,路径不接)
|
||||
|
||||
**第 4-13 周(Stage 1)**:
|
||||
- 按上面的 13 步 PR 顺序推进
|
||||
- 第 6-8 周可以拉一个业务系统做内测集成(用真实 API key 调真实 endpoint)
|
||||
- 第 12-13 周收尾,给业务系统正式 go-live
|
||||
|
||||
**与 Stage 1 业务客户上线的关系**:
|
||||
- Stage 1 一头是付费 SaaS 客户、一头是业务系统集成客户。两条产品线**共用同一套** workspace + auth + quota,**只是认证模式不同**(cookie vs bearer)
|
||||
- 因此架构上没有冲突,团队可以并行推
|
||||
|
||||
---
|
||||
|
||||
## 10. 与现有 ADR / rollout 的关系
|
||||
|
||||
| 引用 | 关系 |
|
||||
|---|---|
|
||||
| [adr-007 §6 路由](../01-redesign/adr-007-routing-frontend.zh-CN.md) | 路由层假设 cookie + JWT;本文档加了 bearer + API key 平行路径 |
|
||||
| [adr-007 §8 Auth](../01-redesign/adr-007-routing-frontend.zh-CN.md) | TokenPayload 扩 `tid/role` 已对齐;本文档新增 API key 路径不复用 JWT |
|
||||
| [adr-004 RBAC](../01-redesign/adr-004-tenant-rbac.zh-CN.md) | service_accounts.role 复用 owner/admin/member;不另建 role 体系 |
|
||||
| [adr-003 §4.4 quota](../01-redesign/adr-003-llm-key-billing.zh-CN.md) | quota 写入按 SA 归属时只看 workspace_id;按 external_user 归属时多一维 |
|
||||
| [phased-rollout-by-scale Stage 1](./phased-rollout-by-scale.zh-CN.md) | 本文档 §6 给出 Stage 1 修订后必做项与 PR 顺序 |
|
||||
| [stage-0-code-map](./stage-0-code-map.zh-CN.md) | Stage 0 schema 时把 `service_accounts` 也加上不阻塞 |
|
||||
@@ -17,11 +17,13 @@
|
||||
| Stage | 触发条件(业务事实) | 主旋律 | 时间盒 |
|
||||
|---|---|---|---|
|
||||
| **0** | 现在 → 第一个付费客户准备 | workspace 模型立起来;现有 auth 收紧;不做真隔离 | 3–4 周 |
|
||||
| **1** | 第一批付费客户(10–50 付费 / 500–2000 free) | **Postgres + Quota 必落**;workspace 全链路 + 入口强校验;AioSandbox 收紧 | 6–10 周 |
|
||||
| **2** | 增长期(100–500 付费 / 5k–20k 用户) | DeerFlow 表 RLS、KMS、ObjectStorage S3、内部 LLM 计费分类、付费分层 | 10–16 周 |
|
||||
| **1** | 第一批付费客户(10–50 付费 / 500–2000 free) + **1-2 业务系统集成(含自研 web 页面)** | **Postgres + Quota + Headless API(Pattern A backend 代理 + Pattern B browser 直连)必落**;workspace 全链路 + 入口强校验;AioSandbox 收紧 | 10–15 周 |
|
||||
| **2** | 增长期(100–500 付费 / 5k–20k 用户) | DeerFlow 表 RLS、KMS、ObjectStorage S3、内部 LLM 计费分类、付费分层、Webhook outbound | 10–16 周 |
|
||||
| **3** | 成熟期(1k+ 付费 / 50k+ 用户)**或** 出现安全/成本事故 | K8s sandbox + namespace、BYO key(付费档福利)、audit DB 拆分、prewarm 池 | 16–26 周 |
|
||||
| **4** | 单客户合同驱动(合规 / 企业销售) | SSO、custom domain、per-tenant DB(仅强合规) | 按需,单客户 4–8 周 |
|
||||
|
||||
> **并行轨道**:Stage 1 同时承载"第一批付费 SaaS 客户"和"业务系统集成"两条产品线,共用 workspace + auth 基座;详见 [headless-api-track.zh-CN.md](./headless-api-track.zh-CN.md)。On-prem 部署形态在 Stage 0 schema 设计层面就已兼容,Stage 1 末加部署文档即可。
|
||||
|
||||
**核心原则**:每期只做下一档规模真正逼出来的事;做了就不回头的"不可逆决策"集中在 Stage 0/1,避免后期重写。
|
||||
|
||||
---
|
||||
@@ -39,6 +41,7 @@
|
||||
| **`workspaces` 表 + 自动建 1 人 workspace** | 每个新注册用户自动获得 1 个 workspace;用户 = workspace owner。这是后面所有租户改造的底座。 |
|
||||
| **`workspace_id` 列加到现有 SQLite 表** | `threads_meta` / `runs` / `feedback` / `users` 加 `workspace_id`。**不可逆决策**——SQLite 上加列后再迁 Postgres 比直接在 Postgres 上加痛苦得多。 |
|
||||
| **`workspace_memberships` 表** | 即使个人用户也是"1 个 owner 成员",团队功能未启用但模型先就位。`role` 字段先只有 `owner`。 |
|
||||
| **`service_accounts` / `api_keys` / `external_users` schema** | Stage 1 才接路径,但 schema 在 Stage 0 末加上不阻塞——避免 Stage 1 临时改表。详见 [headless-api-track §2](./headless-api-track.zh-CN.md)。 |
|
||||
| **JWT 扩 `wid` 字段** | 沿用现有 `app/gateway/auth/jwt.py` `TokenPayload`(参 ADR-007 §8 修订版),加 `wid`(workspace_id),不引入 Better Auth。 |
|
||||
| **入口路由 `(workspace_id, thread_id)` 校验** | `threads.py` + `thread_runs.py` 入口处必校验(参 ADR-001 §4.1.2)。SQLite 阶段就上,避免 Stage 1 临时补。 |
|
||||
| **CLI / admin UI 的"workspace 管理"基础** | platform admin 能看 workspace 列表、暂停/删除某个 workspace(防止滥用第一时间反应)。 |
|
||||
@@ -70,37 +73,64 @@
|
||||
|
||||
---
|
||||
|
||||
## Stage 1 — 第一批付费客户(freemium 真上线)
|
||||
## Stage 1 — 第一批付费客户 + 业务系统集成
|
||||
|
||||
**触发**:Stage 0 跑稳 + 拿到第一批付费用户(10–50 付费 / 500–2000 free)。
|
||||
**退出**:能放心让媒体/产品社区曝光,不会被白嫖跑偏。
|
||||
**时间盒**:6–10 周
|
||||
**触发**:Stage 0 跑稳 + 拿到第一批付费用户(10–50 付费 / 500–2000 free)+ 1-2 个业务系统集成需求。
|
||||
**退出**:① 能放心让媒体/产品社区曝光,不会被白嫖跑偏;② 业务系统能用 API key 调通核心 endpoint,go-live。
|
||||
**时间盒**:10–15 周(原稿 6–10 周;headless API Pattern A 加 2-3 周 + Pattern B 加 2 周)
|
||||
|
||||
### 必做
|
||||
> Stage 1 是**双轨并行**:付费 SaaS(cookie auth + Stripe + quota)和 Headless API(bearer auth + service account + `/api/v1/`)。两者共用 workspace + auth + quota 基座。详细 headless API 设计见 [headless-api-track.zh-CN.md](./headless-api-track.zh-CN.md)。
|
||||
|
||||
### 必做(付费 SaaS 轨道)
|
||||
|
||||
| 改动 | 说明 | 关联 ADR |
|
||||
|---|---|---|
|
||||
| **Postgres 切换** | 老数据 `pg_loader` 导入;`workspace_id` 已就位(Stage 0 加过)。**不可逆**。 | ADR-001 §4.4 |
|
||||
| **Quota 系统 v1**(强制) | `workspace_quotas` + `workspace_usage_daily` 表;`QuotaMiddleware` 在 lead_agent 链最前;硬限到达拒调用。**Freemium 不上 quota = 信用卡递给攻击者**。 | ADR-003 §4.4 |
|
||||
| **`TokenUsageMiddleware` 持久化** | 当前只 log(参 audit ADR-003);要写入 `workspace_usage_daily(workspace_id, date, model, tokens_in, tokens_out)`。 | ADR-003 §4.3 |
|
||||
| **`TokenUsageMiddleware` 持久化** | 当前只 log(参 audit ADR-003);要写入 `workspace_usage_daily(workspace_id, date, model, tokens_in, tokens_out)`,按 SA / external_user 维度同时支持。 | ADR-003 §4.3 |
|
||||
| **悲观预扣**(轻量版) | 按 `model_max_input_tokens` 估上限;幽灵 token 防御。 | ADR-003 §4.4.1 |
|
||||
| **Stripe 对接(基础订阅)** | 单档付费先;webhook 同步到 `workspace_quotas.plan` 字段。 | — |
|
||||
| **AioSandbox 出网收紧** | egress 白名单(默认禁出网,按需放行)+ cgroup CPU/memory 限额。**不上 K8s**——AioSandbox 加这两个补丁就能撑到 Stage 3。 | ADR-002(轻量版) |
|
||||
| **Sandbox 资源 quota** | 每 workspace 的"沙箱 CPU 秒/月"也进 quota(防止白嫖跑挖矿)。 | ADR-003 §4.3 |
|
||||
| **基础监控** | per-workspace token 用量曲线、quota 命中率、异常用量告警。 | — |
|
||||
|
||||
### 必做(Headless API 轨道 - Pattern A:业务 backend 代理)
|
||||
|
||||
| 改动 | 说明 | 关联文档 |
|
||||
|---|---|---|
|
||||
| **API Key + Service Account 仓储** | `service_accounts` / `api_keys` 表(schema Stage 0 已加)+ 仓储 + 哈希存储。 | headless-api §2 |
|
||||
| **APIKeyAuthBackend + AuthMiddleware 双路径** | bearer 走 SA 路径、cookie 走 user 路径;CSRF middleware 在 bearer 路径 skip。 | headless-api §2 |
|
||||
| **External User ID 透传 + ghost user** | `external_users` 表(schema Stage 0 已加)+ `X-External-User-Id` header 解析;`identity_mode` 三态语义。 | headless-api §3 |
|
||||
| **`/api/v1/` 版本化** | mount prefix 切换;旧 `/api/*` 兼容转发并加 deprecation header。**早做便宜**。 | headless-api §4 |
|
||||
| **`@require_permission` 装饰器升级** | 同时支持 cookie user 路径和 SA + scope 校验;`owner_check` 扩为 enum(`workspace_or_user`)。 | headless-api §2 |
|
||||
| **Per-API-key rate limit(基础版)** | sliding window,存 Postgres;分档默认配 free/pro/team。 | headless-api §5 |
|
||||
| **API key 管理(CLI 优先 + UI 跟进)** | workspace owner / admin 创建 SA + key + 选 identity_mode;CLI 必有,UI 在前端 workspace settings 跟。 | headless-api §2 |
|
||||
| **Idempotency keys**(推荐) | 业务系统重试不重复建 thread/run。 | headless-api §5 |
|
||||
|
||||
### 必做(Headless API 轨道 - Pattern B:自研 web 浏览器直连)
|
||||
|
||||
| 改动 | 说明 | 关联文档 |
|
||||
|---|---|---|
|
||||
| **`POST /api/v1/auth/exchange-token` endpoint** | 业务系统 backend 用 API key + `external_user_id` 换 5-15 min 短期 JWT。 | headless-api §3.5 |
|
||||
| **`ServiceTokenAuthBackend`(AuthMiddleware 第三条路径)** | 验短期 JWT 签名 + `iss=deerflow,typ=service` + SA 当前状态 + scope 子集合法。 | headless-api §3.5 |
|
||||
| **`workspaces.allowed_origins` 列 + WorkspaceAwareCORSMiddleware** | per-workspace 配置允许的 origin;浏览器请求过 CORS preflight。 | headless-api §3.5 |
|
||||
| **SSE 在 CORS 跨域下的 streaming 验证** | 写一份业务方对接示例(HTML + 原生 EventSource) | headless-api §3.5 |
|
||||
|
||||
### 不做(推迟到 Stage 2+)
|
||||
|
||||
- ❌ RLS — Stage 2(应用层 + 入口校验先撑着)
|
||||
- ❌ KMS — Stage 2(先用环境变量管理 key)
|
||||
- ❌ KMS — Stage 2(先用环境变量管理 key + 平台 key 散列入 DB)
|
||||
- ❌ ObjectStorage S3 — Stage 2(先继续本地文件 + 备份脚本)
|
||||
- ❌ K8s sandbox — Stage 3
|
||||
- ❌ BYO key — Stage 3(先全部用平台 key + quota)
|
||||
- ❌ 团队 invitation 流程 — Stage 2(除非有团队客户先到)
|
||||
- ❌ 多档付费 — Stage 2
|
||||
- ❌ Webhook outbound — Stage 2(先轮询)
|
||||
- ❌ 分维度 / 分档 rate limit — Stage 2
|
||||
|
||||
### 关键 PR 顺序
|
||||
### 关键 PR 顺序(双轨)
|
||||
|
||||
**轨道 A:付费 SaaS**
|
||||
1. Postgres 切换(dev 双驱动 → 生产灰度 → 全切)
|
||||
2. `workspace_quotas` / `workspace_usage_daily` 表 + 仓储
|
||||
3. `TokenUsageMiddleware` 升级为持久化(参 audit + ADR-003 §4.3)
|
||||
@@ -109,6 +139,33 @@
|
||||
6. AioSandbox egress 白名单 + 资源限额
|
||||
7. 监控/告警接入
|
||||
|
||||
**轨道 B:Headless API Pattern A**(与 A 并行;步骤 1-2 必须先完成轨道 A 的 1)
|
||||
1. `service_accounts` / `api_keys` / `external_users` 仓储(schema 已在 Stage 0 加上)
|
||||
2. `APIKeyAuthBackend` + `AuthMiddleware` 双路径(cookie + bearer)
|
||||
3. CSRF middleware skip on bearer
|
||||
4. `/api/v1/` mount prefix 切换 + 旧路径兼容转发
|
||||
5. `external_user_id` 透传机制
|
||||
6. `service_accounts.identity_mode` 三态行为分支
|
||||
7. `@require_permission` 升级 + scope 校验(依赖轨道 A 的 quota 完成)
|
||||
8. 基础 rate limit
|
||||
9. API key 管理 CLI + UI
|
||||
10. Idempotency keys(可选)
|
||||
|
||||
**轨道 C:Headless API Pattern B**(依赖轨道 B 的 1-7 完成;建议 Stage 1 末 1-2 周)
|
||||
1. `workspaces.allowed_origins` 列 + workspace settings UI 的 origin 管理
|
||||
2. `WorkspaceAwareCORSMiddleware`(在 AuthMiddleware 之前)
|
||||
3. `POST /api/v1/auth/exchange-token` endpoint + `ServiceTokenPayload` 设计
|
||||
4. `ServiceTokenAuthBackend`(AuthMiddleware 第三条路径)
|
||||
5. SSE 跨域 streaming 验证 + 业务方对接示例(HTML + JS)
|
||||
|
||||
### Go/No-Go 进入 Stage 2
|
||||
|
||||
- 月活付费用户 ≥ 50 **或** 月活免费用户 ≥ 1000
|
||||
- 1-2 个业务系统集成完成 go-live 并稳定运行 ≥ 1 个月
|
||||
- 出现一次"差点超额"事件(quota 在悲观预扣下还是漏了一次)
|
||||
- 文件存储或 secret 管理出现一次手忙脚乱(备份遗漏 / key 误提交等)
|
||||
- 业务系统开始要求 webhook 推送(不再满足于轮询)
|
||||
|
||||
### Go/No-Go 进入 Stage 2
|
||||
|
||||
- 月活付费用户 ≥ 50 **或** 月活免费用户 ≥ 1000
|
||||
@@ -139,7 +196,9 @@
|
||||
| **skill 上传权限收口** | workspace owner / admin 才能上传 skill 包;其他成员只能 enable/disable + 填自己的 config。 | ADR-004 §5.4 |
|
||||
| **内部 LLM 计费分类** | Memory/Title/Summarization 三类 LLM 调用都计入 workspace 用量,区分 `usage_category`。 | ADR-006 §2.5 |
|
||||
| **role 扩到 owner/admin/member** | 团队 workspace 出现 → RBAC 真正发挥作用;`@require_permission` 装饰器升级。 | ADR-004 §5.4 |
|
||||
| **基础 audit log** | 写入业务 DB(暂不拆分),关键操作(quota 改、role 改、删 workspace)记录。 | — |
|
||||
| **基础 audit log** | 写入业务 DB(暂不拆分),关键操作(quota 改、role 改、删 workspace、API key 创建/吊销)记录。 | — |
|
||||
| **Webhook outbound** | `webhook_subscriptions` 表 + 重试机制;业务系统订阅 thread 完成 / run 失败 / quota 触底。Stage 1 推迟来的,此时业务系统已经开始要。 | headless-api §1 |
|
||||
| **API key 分维度 rate limit** | per-endpoint / per-LLM / per-sandbox 复合限速;引入 Redis。 | headless-api §5 |
|
||||
|
||||
### 不做(推迟到 Stage 3+)
|
||||
|
||||
@@ -255,14 +314,14 @@
|
||||
| Stage | 触发 | 时间盒 | 累计 |
|
||||
|---|---|---|---|
|
||||
| 0 | 现在 | 3–4 周 | 1 个月 |
|
||||
| 1 | 首批付费 | 6–10 周 | 4 个月 |
|
||||
| 2 | 增长期 | 10–16 周 | 8 个月 |
|
||||
| 3 | 成熟期 | 16–26 周 | 14 个月 |
|
||||
| 1 | 首批付费 + 业务系统集成(含自研 web 直连) | 10–15 周(headless API Pattern A+B 并行 4-5 周) | 4-5 个月 |
|
||||
| 2 | 增长期 | 10–16 周 | 8-9 个月 |
|
||||
| 3 | 成熟期 | 16–26 周 | 14-16 个月 |
|
||||
| 4 | 企业客户 | 单客户 4–8 周 | + 按需 |
|
||||
|
||||
**全功能落地**:~14 个月(Stage 0–3 累计),不含 Stage 4 enterprise 特性。
|
||||
**最小可付费**(Stage 0 + 1):~4 个月。
|
||||
**风险可控的增长**(Stage 0 + 1 + 2):~8 个月。
|
||||
**全功能落地**:~14-16 个月(Stage 0–3 累计),不含 Stage 4 enterprise 特性。
|
||||
**最小可付费 + 业务系统集成**(Stage 0 + 1):~4-5 个月。
|
||||
**风险可控的增长**(Stage 0 + 1 + 2):~8-9 个月。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user