2026-03-03 17:06:08 +08:00
|
|
|
|
# AI创作系统设计文档
|
|
|
|
|
|
|
|
|
|
|
|
## 一、功能矩阵
|
|
|
|
|
|
|
|
|
|
|
|
| 功能 | 入口 | 输入 | 输出 | 配额消耗 |
|
|
|
|
|
|
|-----|------|------|------|---------|
|
|
|
|
|
|
| AI改写结局 | 结局页 | 原结局+用户指令 | 新结局文本 | 1次 |
|
|
|
|
|
|
| AI改写节点 | 章节选择页 | 原节点+用户指令 | 新节点+选项 | 1次 |
|
|
|
|
|
|
| AI续写 | 故事播放页 | 当前节点+用户指令 | 后续2-3个节点 | 2次 |
|
|
|
|
|
|
| AI创作大纲 | 创作中心 | 题材/关键词 | 标题+简介+大纲 | 1次 |
|
|
|
|
|
|
| AI完整创作 | 创作中心 | 大纲确认 | 完整故事节点树 | 5次 |
|
|
|
|
|
|
| AI润色 | 编辑器 | 原文本 | 优化后文本 | 1次 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、核心流程
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ AI创作完整流程 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
|
|
|
|
|
|
用户触发 ──► 配额检查 ──► 构建Prompt ──► 调用AI ──► 解析响应 ──► 存储记录
|
|
|
|
|
|
│ │ │ │
|
|
|
|
|
|
│ ▼ ▼ ▼
|
|
|
|
|
|
│ 配额不足? [ai_generations] 解析失败?
|
|
|
|
|
|
│ │ 记录调用 │
|
|
|
|
|
|
│ ▼ ▼
|
|
|
|
|
|
│ 引导充值/看广告 重试/降级
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
展示结果 ◄── 用户操作 ──► 采纳? ──► 写入故事表 ──► 进入审核流程
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
放弃/编辑
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 三、Prompt模板设计
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 改写结局
|
|
|
|
|
|
```
|
|
|
|
|
|
[系统提示]
|
|
|
|
|
|
你是一个互动故事创作专家。根据用户的改写指令,重新创作故事结局。
|
|
|
|
|
|
要求:
|
|
|
|
|
|
- 保持原故事的世界观和人物性格
|
|
|
|
|
|
- 结局要有张力和情感冲击
|
|
|
|
|
|
- 字数控制在200-400字
|
|
|
|
|
|
- 输出格式:纯文本
|
|
|
|
|
|
|
|
|
|
|
|
[用户提示]
|
|
|
|
|
|
故事标题:{title}
|
|
|
|
|
|
故事分类:{category}
|
|
|
|
|
|
原结局名称:{ending_name}
|
|
|
|
|
|
原结局内容:{ending_content}
|
|
|
|
|
|
---
|
|
|
|
|
|
用户改写指令:{user_prompt}
|
|
|
|
|
|
---
|
|
|
|
|
|
请创作新的结局:
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 续写剧情
|
|
|
|
|
|
```
|
|
|
|
|
|
[系统提示]
|
|
|
|
|
|
你是一个互动故事创作专家。根据当前剧情,续写后续发展。
|
|
|
|
|
|
要求:
|
|
|
|
|
|
- 提供2-3个剧情走向选项
|
|
|
|
|
|
- 每个选项后续写1个节点内容
|
|
|
|
|
|
- 保持悬念和代入感
|
|
|
|
|
|
- 输出JSON格式
|
|
|
|
|
|
|
|
|
|
|
|
[用户提示]
|
|
|
|
|
|
故事标题:{title}
|
|
|
|
|
|
当前剧情:{current_content}
|
|
|
|
|
|
已做选择:{choices_history}
|
|
|
|
|
|
---
|
|
|
|
|
|
用户期望:{user_prompt}
|
|
|
|
|
|
---
|
|
|
|
|
|
请续写剧情,输出格式:
|
|
|
|
|
|
{
|
|
|
|
|
|
"choices": [
|
|
|
|
|
|
{"text": "选项1文本", "content": "选择后的剧情内容", "speaker": "角色名"},
|
|
|
|
|
|
{"text": "选项2文本", "content": "选择后的剧情内容", "speaker": "角色名"}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3.3 完整创作
|
|
|
|
|
|
```
|
|
|
|
|
|
[系统提示]
|
|
|
|
|
|
你是一个互动故事创作专家。根据用户提供的关键词,创作一个完整的互动故事。
|
|
|
|
|
|
要求:
|
|
|
|
|
|
- 故事有3-5个关键分支点
|
|
|
|
|
|
- 至少2个不同结局(好结局/坏结局)
|
|
|
|
|
|
- 每个节点100-200字
|
|
|
|
|
|
- 输出完整的节点树JSON
|
|
|
|
|
|
|
|
|
|
|
|
[用户提示]
|
|
|
|
|
|
题材:{genre}
|
|
|
|
|
|
关键词:{keywords}
|
|
|
|
|
|
主角设定:{protagonist}
|
|
|
|
|
|
核心冲突:{conflict}
|
|
|
|
|
|
---
|
|
|
|
|
|
请创作完整故事,输出格式:
|
|
|
|
|
|
{
|
|
|
|
|
|
"title": "故事标题",
|
|
|
|
|
|
"description": "故事简介",
|
|
|
|
|
|
"nodes": {
|
|
|
|
|
|
"start": {"content": "开头内容", "speaker": "", "choices": [...]},
|
|
|
|
|
|
"node_1": {...},
|
|
|
|
|
|
"ending_good": {"content": "好结局", "is_ending": true, "ending_type": "good"},
|
|
|
|
|
|
"ending_bad": {"content": "坏结局", "is_ending": true, "ending_type": "bad"}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、API设计
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 AI改写结局
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /api/ai/rewrite-ending
|
|
|
|
|
|
Request:
|
|
|
|
|
|
{
|
|
|
|
|
|
"story_id": 123,
|
|
|
|
|
|
"ending_name": "双向奔赴",
|
|
|
|
|
|
"ending_content": "原结局内容...",
|
|
|
|
|
|
"prompt": "让主角逆袭"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"generation_id": 456,
|
|
|
|
|
|
"content": "新结局内容...",
|
|
|
|
|
|
"ending_name": "双向奔赴(改写版)",
|
|
|
|
|
|
"tokens_used": 580,
|
|
|
|
|
|
"quota_remaining": 4
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 AI续写
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /api/ai/continue
|
|
|
|
|
|
Request:
|
|
|
|
|
|
{
|
|
|
|
|
|
"story_id": 123,
|
|
|
|
|
|
"current_node_key": "node_5",
|
|
|
|
|
|
"choices_history": ["选项A", "选项B"],
|
|
|
|
|
|
"prompt": "希望有反转"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"generation_id": 457,
|
|
|
|
|
|
"choices": [
|
|
|
|
|
|
{"text": "追上去", "content": "你快步追上...", "next_key": "ai_node_1"},
|
|
|
|
|
|
{"text": "放手离开", "content": "你转身离去...", "next_key": "ai_node_2"}
|
|
|
|
|
|
],
|
|
|
|
|
|
"nodes": {
|
|
|
|
|
|
"ai_node_1": {"content": "...", "choices": [...]},
|
|
|
|
|
|
"ai_node_2": {"content": "...", "is_ending": true}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 AI完整创作
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /api/ai/create
|
|
|
|
|
|
Request:
|
|
|
|
|
|
{
|
|
|
|
|
|
"genre": "都市言情",
|
|
|
|
|
|
"keywords": "霸总,契约婚姻,追妻火葬场",
|
|
|
|
|
|
"protagonist": "独立女性设计师",
|
|
|
|
|
|
"conflict": "假结婚变真爱"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"generation_id": 458,
|
|
|
|
|
|
"draft_story_id": 789,
|
|
|
|
|
|
"title": "契约总裁的心动法则",
|
|
|
|
|
|
"description": "...",
|
|
|
|
|
|
"node_count": 12,
|
|
|
|
|
|
"ending_count": 3
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.4 配额查询
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /api/user/ai-quota
|
|
|
|
|
|
|
|
|
|
|
|
Response:
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"daily_free_remaining": 3,
|
|
|
|
|
|
"purchased_remaining": 10,
|
|
|
|
|
|
"vip_bonus": 5,
|
|
|
|
|
|
"total_available": 18,
|
|
|
|
|
|
"reset_time": "2026-03-04 00:00:00"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 五、配额与计费
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 配额规则
|
|
|
|
|
|
| 用户类型 | 每日免费 | 购买包 | 说明 |
|
|
|
|
|
|
|---------|---------|-------|------|
|
|
|
|
|
|
| 普通用户 | 3次 | 10次/6元 | 看广告+1次 |
|
|
|
|
|
|
| 月卡VIP | 10次 | 同上 | 月费18元 |
|
|
|
|
|
|
| 年卡VIP | 20次 | 同上 | 年费168元 |
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 消耗逻辑
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// 扣费优先级:每日免费 > 赠送 > 购买 > VIP额外
|
|
|
|
|
|
async function consumeQuota(userId, amount = 1) {
|
|
|
|
|
|
const quota = await UserAIQuota.findByPk(userId);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否需要重置每日配额
|
|
|
|
|
|
if (quota.daily_reset_date !== today) {
|
|
|
|
|
|
quota.daily_free_used = 0;
|
|
|
|
|
|
quota.daily_reset_date = today;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算可用配额
|
|
|
|
|
|
const dailyFreeRemain = quota.daily_free_total - quota.daily_free_used;
|
|
|
|
|
|
const available = dailyFreeRemain + quota.gift_quota + quota.purchased_quota + quota.vip_daily_bonus;
|
|
|
|
|
|
|
|
|
|
|
|
if (available < amount) {
|
|
|
|
|
|
throw new Error('QUOTA_INSUFFICIENT');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 按优先级扣除
|
|
|
|
|
|
let toConsume = amount;
|
|
|
|
|
|
if (dailyFreeRemain > 0) {
|
|
|
|
|
|
const use = Math.min(toConsume, dailyFreeRemain);
|
|
|
|
|
|
quota.daily_free_used += use;
|
|
|
|
|
|
toConsume -= use;
|
|
|
|
|
|
}
|
|
|
|
|
|
// ... 依次扣除其他配额
|
|
|
|
|
|
|
|
|
|
|
|
await quota.save();
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 六、审核流程
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
用户发布 ──► 机器审核 ──► 通过?──► 直接上架
|
|
|
|
|
|
│ │
|
|
|
|
|
|
▼ ▼
|
|
|
|
|
|
疑似违规 人工审核 ──► 通过/拒绝
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
进入人工队列
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 机器审核维度
|
|
|
|
|
|
- 敏感词检测(sensitive_words表)
|
|
|
|
|
|
- 内容安全API(腾讯云/阿里云)
|
|
|
|
|
|
- AI生成内容标记检测
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 审核状态流转
|
|
|
|
|
|
```
|
|
|
|
|
|
草稿(0) ──► 提交审核 ──► 审核中(1) ──► 已发布(2)
|
|
|
|
|
|
│
|
|
|
|
|
|
▼
|
|
|
|
|
|
已拒绝(4) ──► 修改后重新提交
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 七、数据统计
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 核心指标
|
|
|
|
|
|
- AI调用成功率
|
|
|
|
|
|
- 平均响应时间
|
|
|
|
|
|
- 用户采纳率
|
|
|
|
|
|
- 生成内容发布率
|
|
|
|
|
|
- Token消耗/成本
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 日报表聚合
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 每日定时任务聚合到 ai_daily_stats
|
|
|
|
|
|
INSERT INTO ai_daily_stats (stat_date, gen_type, model_name, call_count, ...)
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
DATE(created_at),
|
|
|
|
|
|
gen_type,
|
|
|
|
|
|
model_name,
|
|
|
|
|
|
COUNT(*),
|
|
|
|
|
|
SUM(CASE WHEN status=1 THEN 1 ELSE 0 END),
|
|
|
|
|
|
SUM(input_tokens),
|
|
|
|
|
|
SUM(output_tokens),
|
|
|
|
|
|
AVG(latency_ms)
|
|
|
|
|
|
FROM ai_generations
|
|
|
|
|
|
WHERE DATE(created_at) = CURDATE() - INTERVAL 1 DAY
|
|
|
|
|
|
GROUP BY DATE(created_at), gen_type, model_name;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 八、技术实现要点
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 服务端架构
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────────┐
|
|
|
|
|
|
│ API Gateway │
|
|
|
|
|
|
└──────────────────────┬──────────────────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
┌──────────────┼──────────────┐
|
|
|
|
|
|
▼ ▼ ▼
|
|
|
|
|
|
┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
|
|
|
|
│ 故事服务 │ │ AI服务 │ │ 用户服务 │
|
|
|
|
|
|
└─────────┘ └────┬────┘ └─────────┘
|
|
|
|
|
|
│
|
|
|
|
|
|
┌────────────┼────────────┐
|
|
|
|
|
|
▼ ▼ ▼
|
|
|
|
|
|
┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
|
|
|
|
│ OpenAI │ │ Claude │ │ 本地模型 │
|
|
|
|
|
|
└─────────┘ └─────────┘ └─────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 AI服务封装
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// services/ai.js
|
|
|
|
|
|
class AIService {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.providers = {
|
|
|
|
|
|
openai: new OpenAIProvider(),
|
|
|
|
|
|
claude: new ClaudeProvider(),
|
|
|
|
|
|
local: new LocalProvider()
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async generate(options) {
|
|
|
|
|
|
const { type, provider = 'openai', ...params } = options;
|
|
|
|
|
|
const template = await this.getPromptTemplate(type);
|
|
|
|
|
|
const prompt = this.buildPrompt(template, params);
|
|
|
|
|
|
|
|
|
|
|
|
const startTime = Date.now();
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await this.providers[provider].chat(prompt);
|
|
|
|
|
|
return {
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
content: result.content,
|
|
|
|
|
|
tokens: result.usage,
|
|
|
|
|
|
latency: Date.now() - startTime
|
|
|
|
|
|
};
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return { success: false, error: error.message };
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.3 前端交互优化
|
|
|
|
|
|
- 流式输出:使用SSE实时展示生成过程
|
|
|
|
|
|
- 骨架屏:生成中显示打字动画
|
|
|
|
|
|
- 失败重试:自动重试2次,超时30秒
|
|
|
|
|
|
- 结果缓存:相同输入5分钟内复用
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 九、后续迭代
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 1(当前)
|
2026-03-04 18:31:48 +08:00
|
|
|
|
- [x] AI改写结局(后端接口已完成,模拟实现)
|
|
|
|
|
|
- [x] 后端基础框架(Python + FastAPI)
|
|
|
|
|
|
- [x] 故事/用户数据模型
|
|
|
|
|
|
- [x] 游玩记录接口
|
2026-03-03 17:06:08 +08:00
|
|
|
|
- [ ] 配额系统接入
|
|
|
|
|
|
- [ ] 基础审核流程
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 2
|
|
|
|
|
|
- [ ] AI续写功能
|
|
|
|
|
|
- [ ] AI创作大纲
|
|
|
|
|
|
- [ ] 创作中心入口
|
|
|
|
|
|
|
|
|
|
|
|
### Phase 3
|
|
|
|
|
|
- [ ] AI完整创作
|
|
|
|
|
|
- [ ] UGC发布流程
|
|
|
|
|
|
- [ ] 创作者认证
|
|
|
|
|
|
- [ ] 收益分成
|