AI 辅助游戏开发实战流水线
一、AI 游戏开发流水线全景
MiniPlay Studio 的核心愿景之一是 “让开发者用自然语言描述游戏,AI 负责实现细节”。这条流水线包含五个关键环节:
graph LR
A[自然语言 Prompt] --> B[LLM 结构化生成<br/>Level JSON]
B --> C[场景自动搭建<br/>ECS Entity 生成]
C --> D[Diffusion 素材生成<br/>风格迁移与后处理]
D --> E[运行时集成<br/>加载与渲染]
E --> F[玩家数据收集]
F --> G[RLHF 数值平衡<br/>自动调参]
G --> B
style A fill:#e3f2fd
style B fill:#e8f5e9
style D fill:#fff3e0
style G fill:#fce4ec
本文将逐一拆解每个环节的技术实现。
二、LLM 关卡生成:Prompt Engineering 到结构化 JSON
2.1 关卡数据 Schema
首先定义 AI 必须遵守的输出结构:
interface LevelSchema {
version: '1.0';
title: string;
difficulty: 'easy' | 'normal' | 'hard' | 'expert';
gridSize: { width: number; height: number };
tileSize: number;
entities: Array<{
type: 'player' | 'enemy' | 'platform' | 'coin' | 'spike' | 'exit' | 'powerup';
x: number; y: number;
properties?: Record<string, any>;
}>;
metadata: {
parTime: number; // 目标通关时间(秒)
maxCoins: number;
theme: string;
};
}
2.2 Few-shot Prompt 模板
const LEVEL_GENERATION_PROMPT = `You are a professional game level designer. Generate a JSON level description based on the user's request.
## Rules
1. Output MUST be valid JSON matching the LevelSchema exactly.
2. All coordinates must be within grid bounds.
3. The player must be able to reach the exit (path validity).
4. Enemy count scales with difficulty: easy(0-2), normal(2-5), hard(5-10), expert(10+).
5. Include at least one checkpoint in hard/expert levels.
## LevelSchema
${JSON.stringify(getLevelSchema(), null, 2)}
## Examples
### Example 1: Easy forest level
Input: "A peaceful forest with a few coins"
Output:
{\n "version": "1.0",\n "title": "Forest Walk",\n "difficulty": "easy",\n "gridSize": { "width": 20, "height": 12 },\n "tileSize": 32,\n "entities": [\n { "type": "player", "x": 2, "y": 10 },\n { "type": "exit", "x": 18, "y": 10 },\n { "type": "coin", "x": 5, "y": 8 },\n { "type": "coin", "x": 10, "y": 6 },\n { "type": "coin", "x": 15, "y": 9 },\n { "type": "platform", "x": 8, "y": 7, "properties": { "width": 4 } },\n { "type": "platform", "x": 14, "y": 5, "properties": { "width": 3 } }\n ],\n "metadata": { "parTime": 30, "maxCoins": 3, "theme": "forest" }\n}
### Example 2: Hard lava dungeon
Input: "A dangerous lava dungeon with many enemies"
Output:
{\n "version": "1.0",\n "title": "Inferno Depths",\n "difficulty": "hard",\n "gridSize": { "width": 30, "height": 15 },\n "tileSize": 32,\n "entities": [\n { "type": "player", "x": 2, "y": 13 },\n { "type": "exit", "x": 28, "y": 2 },\n { "type": "enemy", "x": 8, "y": 13, "properties": { "patrolRange": 4, "enemyType": "slime" } },\n { "type": "enemy", "x": 15, "y": 10, "properties": { "patrolRange": 3, "enemyType": "bat" } },\n { "type": "enemy", "x": 22, "y": 7, "properties": { "patrolRange": 5, "enemyType": "skeleton" } },\n { "type": "spike", "x": 10, "y": 13 },\n { "type": "spike", "x": 11, "y": 13 },\n { "type": "spike", "x": 20, "y": 8 },\n { "type": "powerup", "x": 12, "y": 6, "properties": { "type": "double_jump" } },\n { "type": "platform", "x": 5, "y": 11, "properties": { "width": 3 } },\n { "type": "platform", "x": 18, "y": 8, "properties": { "width": 5, "moving": true, "moveRange": 4 } },\n { "type": "platform", "x": 25, "y": 4, "properties": { "width": 3 } }\n ],\n "metadata": { "parTime": 90, "maxCoins": 0, "theme": "lava_dungeon" }\n}
## Now generate
Input: "{USER_PROMPT}"
Output:`;
2.3 LevelGenerator:服务端生成服务
class LevelGenerator {
constructor(
private llmClient: LLMClient, // OpenAI / Claude / 本地模型
private cache: CacheManager,
private validator: LevelValidator
) {}
async generate(prompt: string): Promise<LevelSchema> {
const cacheKey = `level_${hashPrompt(prompt)}`;
const cached = await this.cache.get<LevelSchema>(cacheKey);
if (cached) return cached;
const fullPrompt = LEVEL_GENERATION_PROMPT.replace('{USER_PROMPT}', prompt);
for (let attempt = 0; attempt < 3; attempt++) {
try {
const response = await this.llmClient.complete({
prompt: fullPrompt,
temperature: 0.7 + attempt * 0.1, // 逐步增加随机性
maxTokens: 2000,
});
const jsonStr = this.extractJSON(response.text);
const level = JSON.parse(jsonStr) as LevelSchema;
// 三层校验
const validation = this.validator.validate(level);
if (validation.isValid) {
await this.cache.set(cacheKey, level, 86400); // 缓存 1 天
return level;
}
console.warn(`[LevelGen] Attempt ${attempt + 1} failed validation:`, validation.errors);
} catch (e) {
console.error(`[LevelGen] Attempt ${attempt + 1} error:`, e);
}
}
throw new Error('Failed to generate valid level after 3 attempts');
}
private extractJSON(text: string): string {
// 提取 ```json ... ``` 或纯 JSON
const codeBlock = text.match(/```json\s*([\s\S]*?)```/);
if (codeBlock) return codeBlock[1].trim();
const firstBrace = text.indexOf('{');
const lastBrace = text.lastIndexOf('}');
if (firstBrace >= 0 && lastBrace > firstBrace) {
return text.slice(firstBrace, lastBrace + 1);
}
throw new Error('No JSON found in response');
}
}
2.4 关卡合法性校验器
class LevelValidator {
validate(level: LevelSchema): { isValid: boolean; errors: string[] } {
const errors: string[] = [];
// 1. Schema 校验
if (!level.entities || !Array.isArray(level.entities)) {
errors.push('Missing entities array');
return { isValid: false, errors };
}
// 2. 边界校验
for (const e of level.entities) {
if (e.x < 0 || e.x >= level.gridSize.width || e.y < 0 || e.y >= level.gridSize.height) {
errors.push(`Entity ${e.type} at (${e.x},${e.y}) out of bounds`);
}
}
// 3. 必备元素校验
const hasPlayer = level.entities.some(e => e.type === 'player');
const hasExit = level.entities.some(e => e.type === 'exit');
if (!hasPlayer) errors.push('Missing player spawn');
if (!hasExit) errors.push('Missing exit');
// 4. 路径可达性校验(BFS 简化版)
if (hasPlayer && hasExit) {
const player = level.entities.find(e => e.type === 'player')!;
const exit = level.entities.find(e => e.type === 'exit')!;
if (!this.isReachable(level, player, exit)) {
errors.push('Exit is not reachable from player spawn');
}
}
// 5. 难度一致性校验
const enemyCount = level.entities.filter(e => e.type === 'enemy').length;
const expectedEnemyRange = { easy: [0,2], normal: [2,5], hard: [5,10], expert: [10,50] }[level.difficulty];
if (expectedEnemyRange && (enemyCount < expectedEnemyRange[0] || enemyCount > expectedEnemyRange[1])) {
errors.push(`Enemy count ${enemyCount} does not match difficulty ${level.difficulty}`);
}
return { isValid: errors.length === 0, errors };
}
private isReachable(level: LevelSchema, start: any, end: any): boolean {
const { width, height } = level.gridSize;
const obstacles = new Set<string>();
for (const e of level.entities) {
if (e.type === 'spike' || e.type === 'wall') {
obstacles.add(`${e.x},${e.y}`);
}
}
const visited = new Set<string>();
const queue = [{ x: start.x, y: start.y }];
visited.add(`${start.x},${start.y}`);
const dirs = [[0,1], [0,-1], [1,0], [-1,0]];
while (queue.length > 0) {
const { x, y } = queue.shift()!;
if (x === end.x && y === end.y) return true;
for (const [dx, dy] of dirs) {
const nx = x + dx, ny = y + dy;
const key = `${nx},${ny}`;
if (nx >= 0 && nx < width && ny >= 0 && ny < height &&
!obstacles.has(key) && !visited.has(key)) {
visited.add(key);
queue.push({ x: nx, y: ny });
}
}
}
return false;
}
}
三、场景自动搭建:JSON → ECS Entity
生成关卡 JSON 后,引擎需要将其转换为实际的 ECS Entity。
3.1 LevelBuilder:关卡建造器
class LevelBuilder {
constructor(
private em: EntityManager,
private world: ComponentWorld,
private assetManager: AssetManager
) {}
build(level: LevelSchema): void {
// 清理旧关卡 Entity
this.clearCurrentLevel();
// 创建背景
this.createBackground(level.metadata.theme);
// 按类型分组创建
for (const entityDef of level.entities) {
switch (entityDef.type) {
case 'player':
this.createPlayer(entityDef.x, entityDef.y);
break;
case 'enemy':
this.createEnemy(entityDef);
break;
case 'platform':
this.createPlatform(entityDef);
break;
case 'coin':
this.createCoin(entityDef.x, entityDef.y);
break;
case 'spike':
this.createSpike(entityDef.x, entityDef.y);
break;
case 'exit':
this.createExit(entityDef.x, entityDef.y);
break;
case 'powerup':
this.createPowerup(entityDef);
break;
}
}
// 设置相机边界
this.setupCamera(level.gridSize.width * level.tileSize, level.gridSize.height * level.tileSize);
}
private createPlayer(x: number, y: number): Entity {
const e = this.em.create();
const tileSize = 32;
this.world.get<Transform>('transform')!.add(e, {
x: x * tileSize, y: y * tileSize,
rotation: 0, scaleX: 1, scaleY: 1,
});
this.world.get<Sprite>('sprite')!.add(e, {
textureId: 'player_idle',
srcX: 0, srcY: 0, srcW: 32, srcH: 32,
tint: 0xFFFFFFFF,
});
this.world.get<RigidBody>('rigidBody')!.add(e, {
vx: 0, vy: 0,
mass: 1,
isStatic: false,
});
// 玩家标签 Component
this.world.get<{ isPlayer: boolean }>('tag')!.add(e, { isPlayer: true });
return e;
}
private createEnemy(def: any): Entity {
const e = this.em.create();
const tileSize = 32;
this.world.get<Transform>('transform')!.add(e, {
x: def.x * tileSize, y: def.y * tileSize,
rotation: 0, scaleX: 1, scaleY: 1,
});
const enemyType = def.properties?.enemyType || 'slime';
this.world.get<Sprite>('sprite')!.add(e, {
textureId: `enemy_${enemyType}`,
srcX: 0, srcY: 0, srcW: 32, srcH: 32,
tint: 0xFFFFFFFF,
});
this.world.get<RigidBody>('rigidBody')!.add(e, {
vx: 20, vy: 0,
mass: 1,
isStatic: false,
});
// AI 巡逻行为
this.world.get<{ patrolRange: number; startX: number; direction: number }>('patrol')!.add(e, {
patrolRange: def.properties?.patrolRange || 3,
startX: def.x * tileSize,
direction: 1,
});
return e;
}
private createPlatform(def: any): Entity {
const e = this.em.create();
const tileSize = 32;
const width = def.properties?.width || 1;
this.world.get<Transform>('transform')!.add(e, {
x: def.x * tileSize + (width * tileSize) / 2 - tileSize / 2,
y: def.y * tileSize,
rotation: 0, scaleX: width, scaleY: 1,
});
this.world.get<Sprite>('sprite')!.add(e, {
textureId: 'platform_grass',
srcX: 0, srcY: 0, srcW: 32, srcH: 32,
tint: 0xFFFFFFFF,
});
this.world.get<RigidBody>('rigidBody')!.add(e, {
vx: 0, vy: 0,
mass: 0,
isStatic: true,
});
// 移动平台
if (def.properties?.moving) {
this.world.get<{ moveRange: number; speed: number; axis: 'x' | 'y' }>('movingPlatform')!.add(e, {
moveRange: def.properties.moveRange * tileSize,
speed: 50,
axis: 'x',
});
}
return e;
}
private clearCurrentLevel(): void {
// 标记所有非 UI Entity 为销毁
const tagPool = this.world.get<{ isPlayer?: boolean; isUI?: boolean }>('tag');
for (const entity of [...this.em.entities]) {
const tag = tagPool?.get(entity);
if (!tag?.isUI) {
this.em.destroy(entity);
}
}
}
// ... createCoin, createSpike, createExit, createPowerup 类似
}
四、Diffusion 素材生成与风格迁移
4.1 AssetStylizer:服务端素材服务
interface StyleConfig {
baseStyle: 'pixel_art' | 'cartoon' | 'flat' | 'realistic' | 'anime';
colorPalette: string[]; // 主色调
resolution: { width: number; height: number };
}
class AssetStylizer {
constructor(
private diffusionAPI: DiffusionAPI, // Stable Diffusion / Midjourney / DALL-E
private rembgAPI: BackgroundRemovalAPI,
private cache: CDNCache
) {}
async generateAsset(
description: string,
style: StyleConfig,
options: { transparent?: boolean; variations?: number } = {}
): Promise<{ url: string; seed: number }[]> {
const cacheKey = `asset_${hash(description + JSON.stringify(style))}`;
const cached = await this.cache.get(cacheKey);
if (cached) return cached;
const prompt = this.buildPrompt(description, style);
const negativePrompt = this.buildNegativePrompt(style);
const results: { url: string; seed: number }[] = [];
const variations = options.variations || 1;
for (let i = 0; i < variations; i++) {
const response = await this.diffusionAPI.generate({
prompt,
negativePrompt,
width: style.resolution.width,
height: style.resolution.height,
seed: Math.floor(Math.random() * 2147483647),
steps: 25,
cfgScale: 7.5,
});
let imageUrl = response.url;
// 自动抠图
if (options.transparent) {
imageUrl = await this.rembgAPI.remove(imageUrl);
}
// 上传 CDN
const cdnUrl = await this.cache.upload(imageUrl, `${cacheKey}_v${i}.png`);
results.push({ url: cdnUrl, seed: response.seed });
}
await this.cache.set(cacheKey, results, 604800); // 缓存 7 天
return results;
}
private buildPrompt(description: string, style: StyleConfig): string {
const styleModifiers: Record<string, string> = {
pixel_art: 'pixel art, 16-bit, game sprite, sharp pixels, clean edges',
cartoon: 'cartoon style, bright colors, clean outlines, game asset',
flat: 'flat design, minimal, vector-like, solid colors, game UI',
realistic: 'realistic 3D render, game asset, detailed texture, studio lighting',
anime: 'anime style, cel shaded, vibrant colors, game character art',
};
const colorHint = style.colorPalette.length > 0
? `color palette: ${style.colorPalette.join(', ')}`
: '';
return `${description}, ${styleModifiers[style.baseStyle]}, ${colorHint}, ` +
`isolated on solid background, game asset, centered composition, high quality`;
}
private buildNegativePrompt(style: StyleConfig): string {
return 'blurry, low quality, deformed, extra limbs, watermark, signature, ' +
'text, cluttered background, cropped, out of frame';
}
}
4.2 风格一致性校验(CLIP Embedding 比对)
class StyleConsistencyChecker {
constructor(private clipAPI: CLIPAPI) {}
async checkConsistency(
originalUrl: string,
newUrl: string,
threshold: number = 0.85
): Promise<{ consistent: boolean; similarity: number }> {
const [emb1, emb2] = await Promise.all([
this.clipAPI.encodeImage(originalUrl),
this.clipAPI.encodeImage(newUrl),
]);
// 余弦相似度
const similarity = this.cosineSimilarity(emb1, emb2);
return { consistent: similarity >= threshold, similarity };
}
private cosineSimilarity(a: number[], b: number[]): number {
let dot = 0, normA = 0, normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
}
五、数值平衡:RLHF 简化版实现
5.1 玩家数据收集
interface PlayerSession {
levelId: string;
attempts: number;
completionTime: number;
deathPositions: Array<{ x: number; y: number }>;
coinsCollected: number;
powerupsUsed: number;
finalScore: number;
}
class BalanceDataCollector {
private sessions: PlayerSession[] = [];
record(session: PlayerSession): void {
this.sessions.push(session);
}
getLevelStats(levelId: string): {
avgAttempts: number;
completionRate: number;
avgTime: number;
deathHotspots: Array<{ x: number; y: number; count: number }>;
} {
const levelSessions = this.sessions.filter(s => s.levelId === levelId);
if (levelSessions.length === 0) {
return { avgAttempts: 0, completionRate: 0, avgTime: 0, deathHotspots: [] };
}
const completions = levelSessions.filter(s => s.attempts > 0);
const avgAttempts = levelSessions.reduce((s, v) => s + v.attempts, 0) / levelSessions.length;
// 死亡热点聚类(简单网格聚合)
const hotspotMap = new Map<string, number>();
for (const s of levelSessions) {
for (const dp of s.deathPositions) {
const key = `${Math.floor(dp.x / 32)},${Math.floor(dp.y / 32)}`;
hotspotMap.set(key, (hotspotMap.get(key) || 0) + 1);
}
}
const deathHotspots = Array.from(hotspotMap.entries())
.map(([key, count]) => {
const [x, y] = key.split(',').map(Number);
return { x, y, count };
})
.filter(h => h.count >= 3)
.sort((a, b) => b.count - a.count)
.slice(0, 10);
return {
avgAttempts,
completionRate: completions.length / levelSessions.length,
avgTime: levelSessions.reduce((s, v) => s + v.completionTime, 0) / levelSessions.length,
deathHotspots,
};
}
}
5.2 自动调参引擎
interface LevelParameters {
enemyHealth: number;
enemySpeed: number;
platformGap: number;
coinValue: number;
spikeDamage: number;
}
class BalanceOptimizer {
constructor(private collector: BalanceDataCollector) {}
optimize(levelId: string, currentParams: LevelParameters): LevelParameters {
const stats = this.collector.getLevelStats(levelId);
const params = { ...currentParams };
// 完成率 < 30%:太难,降低难度
if (stats.completionRate < 0.30) {
params.enemyHealth *= 0.8;
params.enemySpeed *= 0.85;
params.platformGap *= 0.9;
params.coinValue *= 1.2;
console.log(`[Balance] ${levelId}: Too hard, reducing difficulty`);
}
// 完成率 > 90%:太简单,增加难度
else if (stats.completionRate > 0.90) {
params.enemyHealth *= 1.15;
params.enemySpeed *= 1.1;
params.platformGap *= 1.1;
params.coinValue *= 0.9;
console.log(`[Balance] ${levelId}: Too easy, increasing difficulty`);
}
// 平均尝试次数 > 10:微调
else if (stats.avgAttempts > 10) {
params.enemyHealth *= 0.9;
params.spikeDamage *= 0.8;
}
// 死亡热点:在死亡密集区添加道具或调整平台
if (stats.deathHotspots.length > 0) {
const topHotspot = stats.deathHotspots[0];
console.log(`[Balance] ${levelId}: Death hotspot at (${topHotspot.x},${topHotspot.y}) with ${topHotspot.count} deaths`);
// 通知 LevelBuilder 在该位置增加 checkpoint 或降低敌人
}
return params;
}
}
六、完整流水线集成
class AIGamePipeline {
constructor(
private levelGenerator: LevelGenerator,
private levelBuilder: LevelBuilder,
private stylizer: AssetStylizer,
private optimizer: BalanceOptimizer
) {}
async createLevelFromPrompt(prompt: string): Promise<void> {
// Step 1: LLM 生成关卡 JSON
console.time('level-generation');
const levelData = await this.levelGenerator.generate(prompt);
console.timeEnd('level-generation');
// Step 2: 判断是否需要生成新素材
const theme = levelData.metadata.theme;
const existingAssets = await this.checkAssets(theme);
if (!existingAssets) {
// Step 3: Diffusion 生成素材(异步,不阻塞关卡搭建)
this.stylizer.generateAsset(
`${theme} background for platformer game`,
{ baseStyle: 'pixel_art', colorPalette: ['#4CAF50', '#8BC34A', '#795548'], resolution: { width: 640, height: 360 } },
{ transparent: false }
);
}
// Step 4: 搭建场景
console.time('level-build');
this.levelBuilder.build(levelData);
console.timeEnd('level-build');
// Step 5: 记录关卡参数用于后续平衡优化
this.currentLevelParams = this.extractParams(levelData);
}
async analyzeAndOptimize(levelId: string): Promise<void> {
const optimized = this.optimizer.optimize(levelId, this.currentLevelParams);
this.applyParams(optimized);
}
private checkAssets(theme: string): Promise<boolean> {
// 检查 CDN 是否已有该主题素材
return Promise.resolve(false); // 简化
}
private extractParams(level: LevelSchema): LevelParameters {
const enemies = level.entities.filter(e => e.type === 'enemy');
return {
enemyHealth: enemies.length > 0 ? 100 : 0,
enemySpeed: 50,
platformGap: 64,
coinValue: 10,
spikeDamage: 50,
};
}
private applyParams(params: LevelParameters): void {
// 应用到当前关卡的 ECS Entity
console.log('[Pipeline] Applying optimized params:', params);
}
private currentLevelParams: LevelParameters = {
enemyHealth: 100, enemySpeed: 50, platformGap: 64, coinValue: 10, spikeDamage: 50,
};
}
七、AI Pipeline 性能指标
| 环节 | 目标延迟 | 实际延迟(缓存命中) | 实际延迟(实时生成) | 质量保证 |
|---|---|---|---|---|
| 关卡 JSON 生成 | < 1s | < 50ms | 800–1500ms | Schema + BFS 校验 |
| 素材生成 | < 5s | < 100ms | 3–8s | CLIP 一致性检查 |
| 场景搭建 | < 100ms | — | 20–50ms | 运行时边界检查 |
| 数值优化 | < 50ms | — | 10ms | 统计分析驱动 |
八、总结与扩展方向
本文构建的 AI 游戏开发流水线实现了:
- 自然语言 → 结构化关卡:通过 Few-shot Prompt 引导 LLM 输出可校验的 JSON
- JSON → ECS 场景:LevelBuilder 将数据自动转化为 Entity/Component
- AI 素材风格迁移:Diffusion + 自动抠图 + 一致性校验
- 数据驱动平衡:RLHF 简化版实现难度自适应
下一步扩展:
- 关卡进化算法:使用遗传算法(GA)基于玩家数据进化关卡布局
- Narrative AI:接入故事生成模型,为关卡生成背景叙事和 NPC 对话
- Audio AI:使用 AudioLDM / MusicGen 生成与关卡主题匹配的背景音乐
延伸阅读
- 小游戏引擎 ECS 架构深度解析 — 本文 LevelBuilder 的上层 ECS 基础
- 小游戏性能剖析与内存管理 — AI 生成大批量 Entity 时的性能注意事项
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。
「games」更多文章
小游戏开发者增长与获客体系:裂变邀请、Game Jam 与开发者社区运营
系统讲解小游戏平台的开发者增长与获客策略。涵盖裂变邀请机制(模板 Remix 传播设计)、Game Jam 赛事运营全攻略、教育渠道 BD(高校实验室合作)、SEO/ASO 策略、开发者社区运营(Discord/Discourse)、增长漏斗模型与 Cohort 留存分析。提供可落地的运营 SOP 与数据指标追踪体系。
小游戏商业化全栈设计:广告聚合、IAP 道具经济与 LTV 预测模型
系统讲解小游戏平台的商业化全栈架构。涵盖广告聚合层设计(Waterfall + Header Bidding 混合出价)、IAP 道具经济体系(消耗品/订阅/战令)、LTV/CAC 预测模型、归因分析(Adjust/AppsFlyer 对接)、防作弊检测与 eCPM 优化策略。提供 AdMediator、IAPManager 的完整 TypeScript 实现与财务模型数据表。
小游戏创作者经济生态设计:插件商店、收益分润与创作者成长体系
深入讲解小游戏平台创作者经济生态的架构设计。涵盖插件商店技术架构(版本管理、签名验证、依赖解析)、收益分润算法(阶梯抽成与动态分成)、创作者等级体系(GMV/质量/活跃三维评估)、UGC 内容审核流水线。提供 MarketplaceEngine 核心 TypeScript 实现与智能推荐算法。