Files
geo/docs/superpowers/specs/2026-04-16-kol-feature-design.md
T
root 3ef0807456 fix: Enhance Kol Variable Rendering and Management
- 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.
2026-04-18 00:47:57 +08:00

857 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 不进入通用模板链路,先确保权限、审计、结算口径闭合