3ef0807456
- Updated the regex for placeholder matching to support more flexible key formats. - Refactored variable storage to allow both ID and key lookups in rendering. - Improved schema validation to check for duplicate keys and enforce non-empty keys. - Added new utility functions for variable value lookup and display name generation. - Enhanced tests to cover new key-based variable scenarios. - Refactored KolPrompt repository to simplify prompt storage and management, including the removal of obsolete revision handling. - Introduced new API endpoints for saving, activating, and archiving prompts. - Added new utility functions for handling Kol placeholders and platform options in the admin web. - Implemented database migrations to simplify prompt storage structure and ensure data integrity.
857 lines
26 KiB
Markdown
857 lines
26 KiB
Markdown
# KOL 功能设计文档(v3 — 第一方 KOL 方案,不依赖 share codes)
|
||
|
||
> 本文档替代此前所有基于 `prompt_share_profiles` / `prompt_share_codes` 的 KOL 方案描述。
|
||
> `share_code_id` 仍然保留在 `article_templates` 现有表结构中作为历史遗留字段,但 **KOL 功能不再依赖它**。
|
||
|
||
## 概述
|
||
|
||
为 GEO 平台新增第一方 KOL 功能模块。KOL 是一种面向租户端产品能力的创作者身份:KOL 在自己的租户内编写、发布和运营精调 prompt 包,其他租户可以在市场中浏览并申请订阅;订阅成功后,订阅租户获得对对应 KOL prompt 的使用权,并通过专用 KOL 生成链路产出文章。
|
||
|
||
本方案的核心目标:
|
||
|
||
- **去旧方案依赖**:不再复用 `prompt_share_profiles`、`prompt_share_codes`、`share_code_id`
|
||
- **生成链路独立**:KOL 文章走独立的 `source_type='kol'` / `task_type='kol'`
|
||
- **权限边界清晰**:KOL 工作台属于租户端产品能力,审批与风控属于平台端能力
|
||
- **历史可追溯**:Prompt 发布版本、变量 schema、使用快照、任务和文章链路完整可追踪
|
||
|
||
### 核心概念
|
||
|
||
- **KOL Profile**:KOL 创作者身份,绑定一个 `user_id + tenant_id`
|
||
- **KOL Package**:KOL 发布的一组 prompt 的业务包
|
||
- **KOL Prompt**:包内单个 prompt 的逻辑实体
|
||
- **KOL Prompt Revision**:prompt 的不可变发布/草稿修订
|
||
- **KOL Subscription**:订阅租户对某个 package 的订阅关系
|
||
- **KOL Subscription Prompt Access**:订阅生效后,租户对包内某个 prompt 的访问授权
|
||
- **KOL Usage Log**:每次生成请求的审计与结算基础记录
|
||
|
||
### 设计原则
|
||
|
||
1. **KOL 是第一方模型,不是旧分享模型的变体**
|
||
2. **KOL 原始 prompt 不写入 `article_templates`**
|
||
3. **KOL 生成只走专用 API 与专用 worker 分支**
|
||
4. **V1 不接入通用模板选择器,避免绕过订阅鉴权**
|
||
5. **租户端能力留在 `tenant-api`,平台审批留在 `platform-api`**
|
||
|
||
---
|
||
|
||
## 1. 架构定位
|
||
|
||
### 1.1 边界划分
|
||
|
||
| 能力 | 归属 | 说明 |
|
||
|------|------|------|
|
||
| 市场浏览、包详情、申请订阅 | `tenant-api` | 面向租户端用户的产品能力 |
|
||
| KOL 工作台、Prompt 编辑、KOL 看板 | `tenant-api` | KOL 在自己租户内使用 |
|
||
| 订阅审批、KOL Profile 开通/冻结 | `platform-api` | 平台运营能力 |
|
||
| 文章生成执行 | `worker-generate` | 复用既有队列、额度、任务状态框架 |
|
||
|
||
### 1.2 与现有系统的关系
|
||
|
||
| 现有模块 | 复用方式 |
|
||
|---------|---------|
|
||
| `articles` | 复用,新增 `source_type='kol'` 和 `kol_prompt_id` |
|
||
| `generation_tasks` | 复用,新增 `task_type='kol'` |
|
||
| `article_versions` | 复用,生成成功后正常写入 |
|
||
| `quota_reservations` / `tenant_quota_ledgers` | 复用,遵守既有事务边界 |
|
||
| `task_records + SSE` | 复用,前端感知生成进度 |
|
||
| `template_assist_tasks` | 不复用,新建 `kol_assist_tasks` |
|
||
| `article_templates` | 不作为 KOL 原始 prompt 存储;`share_code_id` 对 KOL 无意义 |
|
||
|
||
### 1.3 V1 显式不做
|
||
|
||
- 不复活旧的 share code 导入/物化方案
|
||
- 不把 KOL prompt 接入通用 `/articles/template` 生成链路
|
||
- 不把 KOL prompt 存成订阅方的 `article_templates`
|
||
- 不做在线支付、分账、提现
|
||
- 不做 KOL 多租户归属切换
|
||
|
||
---
|
||
|
||
## 2. 数据模型
|
||
|
||
### 2.1 新增表
|
||
|
||
#### kol_profiles — KOL 创作者身份
|
||
|
||
```sql
|
||
CREATE TABLE kol_profiles (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
||
user_id BIGINT NOT NULL REFERENCES users(id),
|
||
display_name VARCHAR(100) NOT NULL,
|
||
bio TEXT,
|
||
avatar_url TEXT,
|
||
market_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active / suspended / closed
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
CREATE UNIQUE INDEX uk_kol_profile_user_active
|
||
ON kol_profiles(user_id)
|
||
WHERE deleted_at IS NULL;
|
||
CREATE INDEX idx_kol_profile_tenant
|
||
ON kol_profiles(tenant_id)
|
||
WHERE deleted_at IS NULL;
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- 一个用户在 V1 仅允许有一个 active KOL Profile
|
||
- `tenant_id` 是 KOL 的归属租户,也是 AI 辅助费用归属租户
|
||
- KOL Profile 不是 `platform_role`,也不是 `tenant_role`,而是第一方业务身份
|
||
|
||
#### kol_packages — KOL 订阅包
|
||
|
||
```sql
|
||
CREATE TABLE kol_packages (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
||
kol_profile_id BIGINT NOT NULL REFERENCES kol_profiles(id),
|
||
name VARCHAR(200) NOT NULL,
|
||
description TEXT,
|
||
cover_url TEXT,
|
||
industry VARCHAR(100),
|
||
tags JSONB NOT NULL DEFAULT '[]',
|
||
price_note VARCHAR(200),
|
||
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft / published / archived
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
CREATE INDEX idx_kol_package_profile
|
||
ON kol_packages(kol_profile_id, created_at DESC)
|
||
WHERE deleted_at IS NULL;
|
||
CREATE INDEX idx_kol_package_status
|
||
ON kol_packages(status, created_at DESC)
|
||
WHERE deleted_at IS NULL;
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- `tenant_id` 是创作者租户,不是订阅租户
|
||
- 包可以是全局可见资源,但 ownership 仍然属于创作者租户
|
||
- 市场只展示 `status='published'` 且 `kol_profiles.market_enabled=true`
|
||
- 市场列表/详情是**跨租户查询**;`/api/tenant/kol/marketplace/*` 是已知 tenant-scope 豁免点,实施时不能复用默认“按当前 tenant_id 过滤”的仓储查询
|
||
- 实施建议:优先在 service 层做跨租户聚合查询;若落在 tenant 仓储 SQL,则需为该查询显式标注 tenant-scope exemption,并同步调整 `check_tenant_scope.sh` 的豁免策略
|
||
|
||
#### kol_prompts — KOL Prompt 逻辑主表
|
||
|
||
```sql
|
||
CREATE TABLE kol_prompts (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
||
package_id BIGINT NOT NULL REFERENCES kol_packages(id),
|
||
name VARCHAR(200) NOT NULL,
|
||
platform_hint VARCHAR(100),
|
||
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft / active / archived
|
||
sort_order INT NOT NULL DEFAULT 0,
|
||
published_revision_no INT,
|
||
draft_revision_no INT,
|
||
created_by BIGINT NOT NULL REFERENCES users(id),
|
||
updated_by BIGINT NOT NULL REFERENCES users(id),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
CREATE INDEX idx_kol_prompt_package
|
||
ON kol_prompts(package_id, sort_order, created_at)
|
||
WHERE deleted_at IS NULL;
|
||
CREATE INDEX idx_kol_prompt_tenant_status
|
||
ON kol_prompts(tenant_id, status, created_at DESC)
|
||
WHERE deleted_at IS NULL;
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- `kol_prompts` 是逻辑实体,不直接存放 prompt 原文
|
||
- 当前可用版本通过 `published_revision_no` 指向
|
||
- KOL 编辑时创建新 revision;发布时切换 `published_revision_no`
|
||
|
||
#### kol_prompt_revisions — KOL Prompt 不可变修订
|
||
|
||
```sql
|
||
CREATE TABLE kol_prompt_revisions (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
||
prompt_id BIGINT NOT NULL REFERENCES kol_prompts(id),
|
||
revision_no INT NOT NULL,
|
||
prompt_asset_key TEXT NOT NULL, -- 受保护资产 key
|
||
schema_json JSONB NOT NULL DEFAULT '{}',
|
||
card_config_json JSONB NOT NULL DEFAULT '{}',
|
||
created_by BIGINT NOT NULL REFERENCES users(id),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
UNIQUE (prompt_id, revision_no)
|
||
);
|
||
CREATE INDEX idx_kol_prompt_revision_prompt
|
||
ON kol_prompt_revisions(prompt_id, revision_no DESC);
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- 原始 prompt 正文只存受保护资产,不回传前端
|
||
- `schema_json`、`card_config_json` 与 prompt 正文一起冻结在 revision 中
|
||
- 历史生成任务通过 `prompt_id + revision_no` 可精确回放
|
||
|
||
#### kol_subscriptions — 租户级订阅关系
|
||
|
||
```sql
|
||
CREATE TABLE kol_subscriptions (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id), -- 订阅方租户
|
||
package_id BIGINT NOT NULL REFERENCES kol_packages(id),
|
||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending / active / expired / revoked
|
||
approved_by BIGINT REFERENCES users(id),
|
||
start_at TIMESTAMPTZ,
|
||
end_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
CREATE UNIQUE INDEX uk_kol_subscription_active
|
||
ON kol_subscriptions(tenant_id, package_id)
|
||
WHERE status IN ('pending', 'active');
|
||
CREATE INDEX idx_kol_subscription_package
|
||
ON kol_subscriptions(package_id, status, created_at DESC);
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- 订阅主体是 tenant,不是单个 user
|
||
- 审批与撤销都以 tenant 为单位
|
||
- 结算、看板、风控也以 tenant 为统计口径
|
||
|
||
#### kol_subscription_prompts — 订阅生效后的 prompt 访问授权
|
||
|
||
```sql
|
||
CREATE TABLE kol_subscription_prompts (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
||
subscription_id BIGINT NOT NULL REFERENCES kol_subscriptions(id),
|
||
package_id BIGINT NOT NULL REFERENCES kol_packages(id),
|
||
prompt_id BIGINT NOT NULL REFERENCES kol_prompts(id),
|
||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active / revoked
|
||
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
revoked_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
CREATE UNIQUE INDEX uk_kol_subscription_prompt
|
||
ON kol_subscription_prompts(subscription_id, prompt_id);
|
||
CREATE INDEX idx_kol_subscription_prompt_tenant
|
||
ON kol_subscription_prompts(tenant_id, status, created_at DESC);
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- 这是新的“订阅后可用 prompt”映射表,用来替代旧的 share code / 物化模板模型
|
||
- 订阅审批通过时,为当前包内所有 active prompt 创建 access row
|
||
- 包后续新增 prompt 时,为所有 active 订阅补齐 access row
|
||
- 撤订或 prompt 下架时,access row 置为 revoked
|
||
|
||
#### kol_usage_logs — 使用与审计记录
|
||
|
||
```sql
|
||
CREATE TABLE kol_usage_logs (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
||
subscription_id BIGINT REFERENCES kol_subscriptions(id),
|
||
subscription_prompt_id BIGINT REFERENCES kol_subscription_prompts(id),
|
||
package_id BIGINT NOT NULL REFERENCES kol_packages(id),
|
||
prompt_id BIGINT NOT NULL REFERENCES kol_prompts(id),
|
||
prompt_revision_no INT NOT NULL,
|
||
article_id BIGINT REFERENCES articles(id),
|
||
generation_task_id BIGINT REFERENCES generation_tasks(id),
|
||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending / completed / failed
|
||
variables_snapshot JSONB,
|
||
schema_snapshot JSONB,
|
||
rendered_prompt_hash VARCHAR(64),
|
||
error_message TEXT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
finished_at TIMESTAMPTZ
|
||
);
|
||
CREATE INDEX idx_kol_usage_package
|
||
ON kol_usage_logs(package_id, created_at DESC);
|
||
CREATE INDEX idx_kol_usage_tenant
|
||
ON kol_usage_logs(tenant_id, created_at DESC);
|
||
CREATE INDEX idx_kol_usage_prompt
|
||
ON kol_usage_logs(prompt_id, created_at DESC);
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- `status` 明确进入表结构,解决 pending/completed/failed 口径不一致问题
|
||
- `prompt_revision_no + schema_snapshot + variables_snapshot + rendered_prompt_hash` 组成完整审计快照
|
||
- 与 `generation_task_id`、`article_id` 关联后可闭合结算与故障追踪链
|
||
|
||
#### kol_assist_tasks — KOL AI 辅助任务
|
||
|
||
```sql
|
||
CREATE TABLE kol_assist_tasks (
|
||
id VARCHAR(64) PRIMARY KEY,
|
||
tenant_id BIGINT NOT NULL REFERENCES tenants(id),
|
||
kol_profile_id BIGINT NOT NULL REFERENCES kol_profiles(id),
|
||
prompt_id BIGINT REFERENCES kol_prompts(id),
|
||
operator_id BIGINT NOT NULL REFERENCES users(id),
|
||
task_type VARCHAR(50) NOT NULL, -- ai_generate / ai_optimize
|
||
status VARCHAR(20) NOT NULL DEFAULT 'queued',
|
||
request_json JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||
result_json JSONB,
|
||
error_message TEXT,
|
||
started_at TIMESTAMPTZ,
|
||
completed_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
CREATE INDEX idx_kol_assist_tenant_profile
|
||
ON kol_assist_tasks(tenant_id, kol_profile_id, created_at DESC);
|
||
CREATE INDEX idx_kol_assist_status
|
||
ON kol_assist_tasks(status, created_at DESC);
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- `kol_assist_tasks.tenant_id` 是**计费租户**,必须始终写入 `kol_profiles.tenant_id`
|
||
- 实施时禁止直接使用当前请求的 `actor.TenantID` 作为兜底来源;即使 V1 中两者相同,也要先解析 profile 后再写任务记录
|
||
- 这样后续即使支持 KOL 多租户归属切换,历史费用归属也不会歧义
|
||
|
||
### 2.2 现有表变更
|
||
|
||
#### articles 表
|
||
|
||
新增字段:
|
||
|
||
- `kol_prompt_id BIGINT REFERENCES kol_prompts(id)`
|
||
|
||
新增 `source_type` 值:
|
||
|
||
- `kol`
|
||
|
||
说明:
|
||
|
||
- KOL 文章不依赖 `template_id`
|
||
- `template_id` 对 KOL 文章保持 `NULL`
|
||
|
||
#### generation_tasks 表
|
||
|
||
新增 `task_type` 值:
|
||
|
||
- `kol`
|
||
|
||
`input_params_json` 示例:
|
||
|
||
```json
|
||
{
|
||
"subscription_prompt_id": 901,
|
||
"prompt_id": 42,
|
||
"prompt_revision_no": 7,
|
||
"variables": {
|
||
"var_a1b2c3": "宜家家居",
|
||
"var_d4e5f6": "家具"
|
||
},
|
||
"schema_snapshot": {
|
||
"variables": []
|
||
},
|
||
"rendered_prompt_hash": "a1b2c3d4..."
|
||
}
|
||
```
|
||
|
||
#### article_templates 表
|
||
|
||
无 KOL 专属新增值。
|
||
|
||
说明:
|
||
|
||
- KOL 原始 prompt 不写入 `article_templates`
|
||
- 旧字段 `share_code_id` 继续保留,但 KOL 不使用
|
||
|
||
---
|
||
|
||
## 3. Prompt 变量与版本模型
|
||
|
||
### 3.1 schema_json 格式
|
||
|
||
```json
|
||
{
|
||
"variables": [
|
||
{
|
||
"id": "var_a1b2c3",
|
||
"key": "公司名称",
|
||
"label": "公司名称",
|
||
"type": "input",
|
||
"required": true,
|
||
"placeholder": "请输入您的公司名"
|
||
},
|
||
{
|
||
"id": "var_d4e5f6",
|
||
"key": "行业",
|
||
"label": "行业",
|
||
"type": "select",
|
||
"options": ["家具", "建材", "家电"],
|
||
"required": true
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
设计要点:
|
||
|
||
- 每个变量有不可变 `id`
|
||
- Prompt 正文内部只引用 `{{id}}`
|
||
- 前端展示 `label`,提交时按 `id -> value` 映射
|
||
- 修改 `key/label` 不会破坏既有 prompt 引用
|
||
|
||
### 3.2 修订规则
|
||
|
||
- 每次保存草稿,创建新的 `kol_prompt_revisions.revision_no`
|
||
- `kol_prompts.draft_revision_no` 指向最新草稿
|
||
- 点击发布时,把 `published_revision_no` 切到当前草稿修订
|
||
- `status='active'` 的 prompt 必须有 `published_revision_no`
|
||
|
||
### 3.3 历史复现
|
||
|
||
历史生成记录依赖以下字段重建:
|
||
|
||
- `prompt_id`
|
||
- `prompt_revision_no`
|
||
- `schema_snapshot`
|
||
- `variables_snapshot`
|
||
- `rendered_prompt_hash`
|
||
|
||
因此即使 KOL 后续继续编辑 prompt,历史记录仍可审计、对账和回放。
|
||
|
||
---
|
||
|
||
## 4. 页面结构与路由
|
||
|
||
### 4.1 侧边栏导航
|
||
|
||
```
|
||
现有导航:
|
||
Workspace
|
||
文章创作: 模版生成 | 自定义生成 | 自由创作 | 发布管理
|
||
品牌管理: 品牌库
|
||
品牌追踪: 追踪详情
|
||
内容管理: 知识库 | 图片库
|
||
|
||
新增:
|
||
KOL 市场: 模版市场 ← 所有用户可见
|
||
KOL 工作台: 提示词管理 | 数据看板 ← 仅 active kol_profile 可见
|
||
```
|
||
|
||
### 4.2 新增路由
|
||
|
||
| 路由 | 组件 | 角色 | 说明 |
|
||
|------|------|------|------|
|
||
| `/kol/marketplace` | `KolMarketplaceView` | 所有用户 | 浏览 KOL 包 |
|
||
| `/kol/packages/:id` | `KolPackageDetailView` | 所有用户 | 包详情与订阅 |
|
||
| `/kol/generate/:subscriptionPromptId` | `KolGenerateView` | 已订阅租户 | 动态表单生成 |
|
||
| `/kol/manage` | `KolManageView` | 仅 KOL | 包与 prompt 管理 |
|
||
| `/kol/dashboard` | `KolDashboardView` | 仅 KOL | KOL 数据看板 |
|
||
|
||
### 4.3 用户入口
|
||
|
||
1. 侧边栏「模版市场」进入市场浏览与订阅
|
||
2. Workspace 新增「KOL Prompt 卡片区」,展示当前租户可用的 `kol_subscription_prompts`
|
||
3. **V1 不把 KOL prompt 放入通用模板选择器**
|
||
|
||
---
|
||
|
||
## 5. API 设计
|
||
|
||
### 5.1 市场与订阅(tenant API)
|
||
|
||
```text
|
||
GET /api/tenant/kol/marketplace/packages
|
||
GET /api/tenant/kol/marketplace/packages/:id
|
||
POST /api/tenant/kol/marketplace/packages/:id/subscribe
|
||
GET /api/tenant/kol/subscriptions
|
||
GET /api/tenant/kol/subscription-prompts
|
||
GET /api/tenant/kol/subscription-prompts/:id
|
||
```
|
||
|
||
说明:
|
||
|
||
- `/marketplace/*` 返回全局已发布包
|
||
- `/subscriptions` 和 `/subscription-prompts` 只返回当前租户数据
|
||
- `/api/tenant/kol/marketplace/*` 属于 tenant-api 中少数跨租户读场景;实施时需显式声明 tenant-scope exemption
|
||
|
||
### 5.2 KOL 生成(tenant API)
|
||
|
||
```text
|
||
GET /api/tenant/kol/subscription-prompts/:id/schema
|
||
POST /api/tenant/kol/subscription-prompts/:id/generate
|
||
```
|
||
|
||
说明:
|
||
|
||
- `:id` 是 `kol_subscription_prompts.id`
|
||
- 后端根据 access row 反查 package、prompt、published revision
|
||
- 生成请求只接受变量值,不接受 prompt 原文
|
||
|
||
### 5.3 KOL 工作台(tenant API)
|
||
|
||
```text
|
||
GET /api/tenant/kol/manage/profile
|
||
|
||
GET /api/tenant/kol/manage/packages
|
||
POST /api/tenant/kol/manage/packages
|
||
PUT /api/tenant/kol/manage/packages/:id
|
||
DELETE /api/tenant/kol/manage/packages/:id
|
||
PUT /api/tenant/kol/manage/packages/:id/publish
|
||
PUT /api/tenant/kol/manage/packages/:id/archive
|
||
|
||
GET /api/tenant/kol/manage/packages/:id/prompts
|
||
POST /api/tenant/kol/manage/packages/:id/prompts
|
||
PUT /api/tenant/kol/manage/prompts/:id
|
||
DELETE /api/tenant/kol/manage/prompts/:id
|
||
POST /api/tenant/kol/manage/prompts/:id/publish
|
||
|
||
POST /api/tenant/kol/manage/assist/generate
|
||
POST /api/tenant/kol/manage/assist/optimize
|
||
```
|
||
|
||
说明:
|
||
|
||
- 工作台不走 `/api/platform/kol/*`
|
||
- 只有 `kol_profiles.tenant_id = actor.TenantID AND user_id = actor.UserID` 的用户可操作
|
||
|
||
### 5.4 KOL 看板(tenant API)
|
||
|
||
```text
|
||
GET /api/tenant/kol/dashboard/overview
|
||
GET /api/tenant/kol/dashboard/packages
|
||
GET /api/tenant/kol/dashboard/trend
|
||
```
|
||
|
||
### 5.5 平台管理员操作(platform API)
|
||
|
||
```text
|
||
POST /api/platform/admin/kol/profiles
|
||
PUT /api/platform/admin/kol/profiles/:id/suspend
|
||
PUT /api/platform/admin/kol/profiles/:id/activate
|
||
|
||
PUT /api/platform/admin/kol/subscriptions/:id/approve
|
||
POST /api/platform/admin/kol/subscriptions
|
||
PUT /api/platform/admin/kol/subscriptions/:id/revoke
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 订阅与授权生命周期
|
||
|
||
### 6.1 申请订阅
|
||
|
||
1. 用户在市场页点击「申请订阅」
|
||
2. 创建 `kol_subscriptions(status='pending')`
|
||
3. 等待平台管理员审批
|
||
|
||
### 6.2 审批通过
|
||
|
||
1. 平台管理员把订阅改为 `active`
|
||
2. 在同一事务或紧随其后的授权任务中,为该 package 当前所有 active prompt 创建 `kol_subscription_prompts`
|
||
3. Workspace 的 KOL 卡片区开始展示这些 prompt
|
||
|
||
### 6.3 包后续新增 prompt
|
||
|
||
1. KOL 在已发布包下新建并发布 prompt
|
||
2. 系统为所有 active 订阅补写 `kol_subscription_prompts`
|
||
3. 新 prompt 自动出现在订阅租户的 KOL 卡片区
|
||
|
||
实现建议:
|
||
|
||
- 订阅审批时可同步插入 access rows
|
||
- “已发布包新增 prompt” 建议走异步任务,避免大批量订阅阻塞发布请求
|
||
- 异步分发复用现有 `task_records` 可观测能力
|
||
|
||
### 6.4 订阅过期处理
|
||
|
||
V1 口径:
|
||
|
||
- **不做自动过期任务**
|
||
- `kol_subscriptions.end_at` 仅在读时与生成鉴权时实时校验
|
||
- 当 `end_at < now()` 时,即使 `status` 仍为 `active`,生成接口也必须拒绝访问
|
||
- 列表页、看板统计口径应把“`status='active'` 且当前时间仍在有效期内”视为有效订阅
|
||
- 后续如需引入 scheduler,再补“批量把过期订阅改为 `expired` + 回收 access rows”的异步清理任务
|
||
|
||
### 6.5 撤销订阅
|
||
|
||
1. 平台管理员把订阅改为 `revoked`
|
||
2. 把该订阅下所有 `kol_subscription_prompts` 置为 `revoked`
|
||
3. Workspace KOL 卡片区不再展示
|
||
4. 生成接口返回 403
|
||
|
||
### 6.6 Prompt 下架
|
||
|
||
1. KOL 把 prompt 标记为 `archived`
|
||
2. 对应的 `kol_subscription_prompts` 置为 `revoked`
|
||
3. 已生成文章保留,后续新生成禁止
|
||
|
||
---
|
||
|
||
## 7. 生成链路
|
||
|
||
严格遵循现有架构中的事务、额度、幂等和 worker 回写规则。
|
||
|
||
### 7.1 提交前校验
|
||
|
||
生成前必须逐项校验:
|
||
|
||
1. `kol_subscription_prompts.id` 存在且 `status='active'`
|
||
2. access row 的 `tenant_id = 当前租户`
|
||
3. 对应 `kol_subscriptions.status='active'`
|
||
4. 订阅在有效期内
|
||
5. `kol_packages.status='published'`
|
||
6. `kol_prompts.status='active'`
|
||
7. `kol_prompts.published_revision_no IS NOT NULL`
|
||
|
||
### 7.2 提交路径(单 PostgreSQL 事务)
|
||
|
||
1. 读取 `kol_prompts.published_revision_no`
|
||
2. 读取对应 `kol_prompt_revisions`
|
||
3. 渲染变量并计算 `rendered_prompt_hash`
|
||
4. 单事务内执行:
|
||
- 额度预扣与 reservation 创建
|
||
- 创建 `articles(source_type='kol', kol_prompt_id=...)`
|
||
- 创建 `generation_tasks(task_type='kol', request_hash=rendered_prompt_hash)`
|
||
- 创建 `kol_usage_logs(status='pending')`
|
||
- 创建 `task_records`
|
||
5. 事务提交后发送队列消息
|
||
|
||
### 7.3 Worker 执行路径
|
||
|
||
Worker 处理 `task_type='kol'` 时:
|
||
|
||
1. 从 `generation_tasks.input_params_json` 读取 `prompt_id + prompt_revision_no`
|
||
2. 加载对应 revision 的受保护 prompt 资产
|
||
3. 组装模型请求
|
||
4. 生成文章内容
|
||
|
||
### 7.4 成功路径(单 PostgreSQL 事务)
|
||
|
||
- 写入 `article_versions`
|
||
- 更新 `articles.generate_status='completed'`
|
||
- 确认额度消耗
|
||
- 更新 `kol_usage_logs.status='completed'`
|
||
- 回写 `article_id`、`finished_at`
|
||
|
||
### 7.5 失败路径(单 PostgreSQL 事务)
|
||
|
||
- 更新 `generation_tasks.status='failed'`
|
||
- 释放或退还额度
|
||
- 更新 `kol_usage_logs.status='failed'`
|
||
- 写入 `error_message` 与 `finished_at`
|
||
|
||
### 7.6 幂等键
|
||
|
||
幂等规则:
|
||
|
||
- `tenant_id + source_type('kol') + request_hash(rendered_prompt_hash) + schedule_slot(null)`
|
||
|
||
说明:
|
||
|
||
- 与现有生成体系一致
|
||
- KOL 不做定时调度,因此 `schedule_slot = null`
|
||
|
||
---
|
||
|
||
## 8. KOL Prompt 编辑器
|
||
|
||
### 8.1 包管理
|
||
|
||
- 仅展示当前 KOL 在当前租户下创建的包
|
||
- 支持创建、编辑、发布、归档
|
||
- 已发布包允许继续新增 prompt
|
||
|
||
### 8.2 Prompt 编辑器
|
||
|
||
三栏布局:
|
||
|
||
- 左栏:变量组件面板
|
||
- 中栏:Prompt 编辑区 + AI 辅助
|
||
- 右栏:变量配置面板
|
||
|
||
### 8.3 编辑规则
|
||
|
||
1. 拖拽变量组件到编辑区
|
||
2. 系统生成不可变 `var_xxx` 变量 ID
|
||
3. 编辑器显示 `{{公司名称}}`
|
||
4. 底层正文始终写成 `{{var_xxx}}`
|
||
5. 保存草稿时创建新 revision
|
||
6. 发布时切换 `published_revision_no`
|
||
|
||
### 8.4 AI 辅助
|
||
|
||
- AI 生成:根据场景描述生成 prompt 草稿
|
||
- AI 优化:对当前草稿 revision 进行优化建议
|
||
- 异步任务写入 `kol_assist_tasks`
|
||
- 费用记到 `kol_profiles.tenant_id`
|
||
|
||
### 8.5 Prompt 内容保护
|
||
|
||
- 正文存入受保护资产,前端不回传
|
||
- 普通用户只获取 `schema_json`
|
||
- 订阅租户无法查看原始 prompt 正文
|
||
|
||
---
|
||
|
||
## 9. 数据看板
|
||
|
||
### 9.1 指标
|
||
|
||
- 总订阅租户数
|
||
- 本月新增订阅
|
||
- 总使用次数
|
||
- 本月使用次数
|
||
- 失败次数与失败率
|
||
|
||
### 9.2 维度
|
||
|
||
- 按包:订阅租户数、使用次数、失败率
|
||
- 按 prompt:使用排行、失败排行
|
||
- 按时间:订阅趋势、使用趋势
|
||
|
||
### 9.3 数据来源
|
||
|
||
- 订阅:`kol_subscriptions`
|
||
- 授权:`kol_subscription_prompts`
|
||
- 使用:`kol_usage_logs`
|
||
|
||
---
|
||
|
||
## 10. Workspace 与文章集成
|
||
|
||
### 10.1 Workspace
|
||
|
||
新增接口:
|
||
|
||
```text
|
||
GET /api/tenant/workspace/kol-cards
|
||
```
|
||
|
||
返回当前租户可用的 KOL prompt 卡片:
|
||
|
||
- 包名
|
||
- Prompt 名称
|
||
- KOL 显示名
|
||
- 平台标识
|
||
- `subscription_prompt_id`
|
||
|
||
### 10.2 文章列表
|
||
|
||
文章列表对 `source_type='kol'` 展示:
|
||
|
||
- 来源标签:`KOL 生成`
|
||
- 关联 prompt 名称
|
||
- 关联 package 名称
|
||
|
||
### 10.3 明确不做的集成
|
||
|
||
- V1 不把 KOL prompt 混入 `template-cards`
|
||
- V1 不接入 `/api/tenant/templates/*`
|
||
- V1 不接入通用模板向导页面
|
||
|
||
这样可以避免普通模板链路绕过 KOL 的订阅鉴权和 usage 埋点。
|
||
|
||
---
|
||
|
||
## 11. 权限与鉴权
|
||
|
||
### 11.1 KOL 身份
|
||
|
||
满足以下条件才视为 KOL:
|
||
|
||
- `kol_profiles.user_id = actor.UserID`
|
||
- `kol_profiles.tenant_id = actor.TenantID`
|
||
- `kol_profiles.status = 'active'`
|
||
|
||
说明:
|
||
|
||
- `status='active'` 决定能否进入 KOL 工作台
|
||
- `market_enabled=true` 只决定是否能在市场中对外展示
|
||
|
||
### 11.2 接口鉴权
|
||
|
||
| 路径前缀 | 鉴权方式 | 说明 |
|
||
|---------|---------|------|
|
||
| `/api/tenant/kol/marketplace/*` | 标准 tenant 身份 | 市场浏览与订阅 |
|
||
| `/api/tenant/kol/subscription-prompts/*` | tenant + access row | 生成 |
|
||
| `/api/tenant/kol/manage/*` | tenant + active kol_profile | 工作台 |
|
||
| `/api/tenant/kol/dashboard/*` | tenant + active kol_profile | 看板 |
|
||
| `/api/platform/admin/kol/*` | `platform_role` | 平台运营 |
|
||
|
||
### 11.3 审计
|
||
|
||
高风险操作必须写审计日志:
|
||
|
||
- KOL Profile 开通、冻结、恢复
|
||
- 订阅审批、撤销
|
||
- Prompt 发布、归档
|
||
- KOL 包发布、归档
|
||
|
||
---
|
||
|
||
## 12. i18n
|
||
|
||
新增 `kol` 命名空间:
|
||
|
||
```text
|
||
kol.marketplace.title
|
||
kol.marketplace.filter.*
|
||
kol.package.subscribe
|
||
kol.package.pending
|
||
kol.package.subscribed
|
||
kol.generate.title
|
||
kol.generate.submit
|
||
kol.manage.title
|
||
kol.manage.createPackage
|
||
kol.manage.editor.*
|
||
kol.dashboard.title
|
||
kol.dashboard.totalSubs
|
||
kol.dashboard.monthSubs
|
||
kol.dashboard.totalUsage
|
||
kol.dashboard.monthUsage
|
||
kol.dashboard.failRate
|
||
nav.kolMarketplace
|
||
nav.kolManage
|
||
nav.kolDashboard
|
||
status.sourceType.kol
|
||
```
|
||
|
||
---
|
||
|
||
## 13. 第一版范围
|
||
|
||
### 第一版做
|
||
|
||
- `kol_profiles`
|
||
- `kol_packages`
|
||
- `kol_prompts + kol_prompt_revisions`
|
||
- `kol_subscriptions + kol_subscription_prompts`
|
||
- KOL 市场浏览与申请订阅
|
||
- KOL 工作台与三栏编辑器
|
||
- AI 辅助生成/优化
|
||
- KOL 专用生成链路
|
||
- `kol_usage_logs` 审计与看板
|
||
- Workspace KOL 卡片区
|
||
|
||
### 第一版不做
|
||
|
||
- 旧 share code 兼容层
|
||
- 通用模板选择器集成
|
||
- 在线支付
|
||
- 收益分账/提现
|
||
- KOL 自助入驻
|
||
- KOL 多租户迁移
|
||
- Prompt A/B 测试
|
||
- 订阅评价/评分系统
|
||
|
||
---
|
||
|
||
## 14. 关键决策总结
|
||
|
||
1. KOL 改为新的第一方模型,不再挂在旧分享能力上
|
||
2. KOL 原始 prompt 不写入 `article_templates`
|
||
3. 订阅后不再物化模板,改为 `kol_subscription_prompts` 授权映射
|
||
4. KOL 生成走独立 `source_type='kol'` / `task_type='kol'`
|
||
5. V1 不进入通用模板链路,先确保权限、审计、结算口径闭合
|