Skip to content

一刻工坊|技术白皮书 V1.0

文档密级:内部公开 最后更新:2025-12-21 依据:Based on /docs/_source_of_truth/tech_facts.md


目录

  1. 架构总览
  2. 关键数据流
  3. 核心对象模型
  4. 任务状态机与可靠性
  5. 外部引擎适配层
  6. 安全与合规
  7. 可观测性
  8. 风险清单
  9. 核心功能技术实现

1. 架构总览

系统采用前后端分离 + 异构微服务架构。Node.js 负责业务编排与数据管理,Python (LangGraph) 负责复杂 Agent 逻辑,通过适配层调度第三方 AI 能力。

mermaid
graph TD
    subgraph Client Layer [客户端层]
        Web[Web Browser]
        Electron[Electron Client]
        Mini[Mini Program]
    end

    subgraph Gateway Layer [网关/API层]
        LB[Load Balancer]
        API[Node.js Express API]
        Auth[Auth Middleware]
    end

    subgraph Business Logic Layer [业务逻辑层]
        Orch[Workflow Orchestrator]
        AssetMgr[Asset Manager]
        TaskMgr[Task Manager]
        KBMgr[Knowledge Base Manager]
    end

    subgraph Agent Layer [智能体层 (Python)]
        LangGraph[LangGraph Runner]
        StrategyAgent[Strategy Agent]
        MusicAgent[Music/Video Agent]
        RAG[RAG Engine]
    end

    subgraph Engine Adapter Layer [引擎适配层]
        SunoAdapt[Suno Adapter]
        FishAdapt[Fish Audio Adapter]
        ComfyAdapt[ComfyUI Adapter]
        FlowiseAdapt[Flowise/N8N Adapter]
        LocalAdapt[Local App Adapter (Jianying)]
    end

    subgraph Infrastructure [基础设施]
        DB[(PostgreSQL)]
        Cache[(Redis)]
        OSS[Object Storage (MinIO/S3)]
        MQ[Message Queue]
    end

    Client Layer -->|HTTPS/WSS| LB
    LB --> API
    API --> Orch & AssetMgr & TaskMgr & KBMgr
    Orch -->|HTTP/RPC| LangGraph
    Orch -->|API| FlowiseAdapt
    LangGraph --> StrategyAgent & MusicAgent
    MusicAgent --> SunoAdapt & ComfyAdapt
    TaskMgr --> DB
    AssetMgr --> OSS
    StrategyAgent --> RAG
    RAG --> KBMgr

📋 落地检查项

  • [ ] 前后端 API 接口契约是否由 Swagger/OpenAPI 定义?
  • [ ] Python Agent 服务与 Node.js 服务的通信协议(HTTP/gRPC)是否确立?
  • [ ] Redis 是否已配置用于 Session 存储和任务队列?

2. 关键数据流

以“用户创建任务 -> 多模态生成 -> 发布”为例的端到端数据流。

mermaid
sequenceDiagram
    participant User as 用户
    participant API as Backend API
    participant DB as Database
    participant Agent as LangGraph/Agent
    participant Engine as External Engine (Suno/Comfy)
    participant OSS as Object Storage

    User->>API: POST /api/project/dispatch-task (Input Params)
    API->>DB: 创建 Task (Status: PENDING)
    API-->>User: 返回 TaskID

    loop 轮询/WebSocket
        User->>API: 查询进度
    end

    API->>Agent: 异步调度任务
    activate Agent
    Agent->>API: 更新 Task (Status: RUNNING)
    
    note right of Agent: 执行策划/提示词工程

    Agent->>Engine: 调用生成 API (Prompt)
    activate Engine
    Engine-->>Agent: 返回生成结果 (URL/Base64)
    deactivate Engine

    Agent->>OSS: 上传原始素材
    OSS-->>Agent: 返回 OSS URL

    Agent->>DB: 写入 UnifiedAsset 记录
    Agent->>API: 更新 Task (Status: SUCCESS, Output: AssetID)
    deactivate Agent

    User->>API: 获取任务结果
    API-->>User: 返回 UnifiedAsset 详情

📋 落地检查项

  • [ ] 异步任务是否有超时熔断机制?
  • [ ] 大文件(视频)上传是否采用了分片上传或预签名 URL 方案?
  • [ ] WebSocket 推送服务是否具备重连机制?

2.2 知识库问答数据流

RAG (Retrieval-Augmented Generation) 完整流程:

mermaid
sequenceDiagram
    participant User
    participant API
    participant KBSearch as Knowledge Search
    participant Rerank as Reranker
    participant LLM
    participant Citation as Citation Renderer
    
    User->>API: 提问
    API->>KBSearch: 向量检索(Embedding)
    KBSearch-->>API: 返回候选 Top 20 chunks
    
    API->>Rerank: 二次排序(基于相关度)
    Rerank-->>API: 返回精选 Top 5 chunks
    
    API->>LLM: Prompt + 精选 chunks
    note right of LLM: 强制引用约束<br/>必须标注 chunk_ids
    LLM-->>API: 生成回答 + chunk_ids[]
    
    API->>Citation: 渲染引用来源
    Citation-->>User: 回答 + 可点击的引用链接

关键技术点

  1. 向量检索:使用 OpenAI Embeddings 或 M3E 模型
  2. Rerank 策略:LLM 二次评分,提升准确率 15-20%
  3. 强制引用:Prompt 约束 LLM 必须返回 citations: [chunk_id1, chunk_id2]
  4. 前端渲染:将 [1][2] 转换为可点击链接,点击跳转到原文

2.3 批量任务执行数据流

批量采集器的并发控制流程:

mermaid
sequenceDiagram
    participant User
    participant API
    participant Parser as CSV Parser
    participant Queue as Task Queue (Redis)
    participant Worker as Worker Pool (p-limit)
    participant Inspire as Inspiration API
    
    User->>API: 上传 CSV (100 个链接)
    API->>Parser: 解析 CSV
    Parser-->>API: 返回 URL 数组
    
    API->>Queue: 批量入队
    Queue-->>API: 返回 batch_id
    API-->>User: 返回 batch_id(立即响应)
    
    loop 并发执行(concurrency=3)
        Worker->>Queue: 拉取任务
        Queue-->>Worker: 返回 URL
        Worker->>Inspire: 解析链接
        Inspire-->>Worker: 返回结构化内容
        Worker->>API: 保存到 UserCorpus
        API-->>User: WebSocket 推送进度
    end
    
    User->>API: 查询批次结果
    API-->>User: 返回成功/失败统计

并发控制参数

平台并发数超时时间重试次数原因
抖音330s2限流严格
小红书245s3反爬虫强
B站520s2API 稳定

3. 核心对象模型

基于 backend/src/models/ 的核心实体定义。

对象 (Class)核心字段关系描述
Projectid, name, workflow_preset_id, status1:N Tasks业务顶层容器,定义了一组任务的上下文。
Taskid, project_id, type, status, input_params, output_dataN:1 Project
1:N Assets
最小执行单元。状态流转的核心载体。
UnifiedAssetid, file_path, file_type, meta, source_task_idN:1 Task系统产生或用户上传的物理文件(视频/图片/音频)。
UserCorpusid, content, source_url, tagsN:N Assets灵感笔记。用于 RAG 检索或作为生成参考。
Job (内部)id, task_id, external_job_id, retry_countN:1 Task技术层面的执行记录,用于追踪外部 API 调用状态。
ContentTemplateid, structure_json, engine_config1:N Projects定义生成逻辑的模板(如:Prompt 结构、引擎参数)。

📋 落地检查项

  • [ ] 数据库索引是否在 project_id, status, created_at 等高频查询字段上建立?
  • [ ] input_paramsoutput_data 建议使用 JSONB 类型以适应多变的业务需求。
  • [ ] 软删除(Soft Delete)策略是否已在所有核心表中实现?

4. 任务状态机与可靠性

任务 (Task) 必须遵循严格的状态流转,以确保系统的一致性。

4.1 状态定义

  • PENDING: 已创建,等待调度。
  • QUEUED: 已进入消息队列/Agent 队列。
  • RUNNING: 正在执行(包括等待外部 API 响应)。
  • SUCCESS: 执行成功,产物已入库。
  • FAILED: 执行失败(重试耗尽)。
  • CANCELLED: 用户主动取消。

4.2 转换规则

mermaid
stateDiagram-v2
    [*] --> PENDING
    PENDING --> QUEUED: 调度器拾取
    QUEUED --> RUNNING: Agent开始执行
    RUNNING --> SUCCESS: 产物生成完成
    RUNNING --> FAILED: 报错且无重试次数
    RUNNING --> QUEUED: 报错但有重试次数 (Retry)
    PENDING --> CANCELLED: 用户取消
    QUEUED --> CANCELLED: 用户取消
    RUNNING --> CANCELLED: 用户取消

4.3 重试与幂等

  • 重试策略
    • 网络错误/超时:指数退避 (Exponential Backoff),最大重试 3 次。
    • 业务错误(如违规):不重试,直接 FAILED。
  • 幂等策略
    • Request ID:客户端生成 UUID,服务端缓存 24h,重复请求直接返回上次结果。
    • 锁机制:同一 Task 在 RUNNING 状态下拒绝重复调度。

📋 落地检查项

  • [ ] 状态机代码是否集中管理,禁止随意在业务逻辑中修改状态?
  • [ ] 失败重试是否记录了详细的 Error Log 和 Reason?
  • [ ] 幂等键的存储(Redis)是否设置了过期时间?

4.4 断点续传状态持久化

场景:30 天蓝图生成需要 3-5 分钟,用户可能中途关闭页面。

状态持久化机制

mermaid
stateDiagram-v2
    [*] --> Step1_Topics: 开始生成
    Step1_Topics --> Cache_Topics: 保存到 Redis
    Cache_Topics --> Step2_Schedule: 继续
    Step2_Schedule --> Cache_Schedule: 保存到 Redis
    Cache_Schedule --> Step3_Scripts: 继续
    Step3_Scripts --> Complete: 完成
    
    Cache_Topics --> Resume_Topics: 用户离开后恢复
    Resume_Topics --> Step2_Schedule
    
    Cache_Schedule --> Resume_Schedule: 用户离开后恢复
    Resume_Schedule --> Step3_Scripts

Redis Key 设计

javascript
// Key 命名规范
const cacheKeys = {
  topics: `blueprint:draft:${projectId}:topics`,
  schedule: `blueprint:draft:${projectId}:schedule`,
  scripts: `blueprint:draft:${projectId}:scripts`,
  metadata: `blueprint:draft:${projectId}:meta`
};

// 过期时间:24 小时
const EXPIRY = 3600 * 24;

4.5 批量任务子任务状态聚合

批次状态计算

javascript
async function getBatchStatus(batchId) {
  const tasks = await Task.findAll({
    where: { batch_id: batchId },
    attributes: ['status']
  });
  
  const statusCount = tasks.reduce((acc, task) => {
    acc[task.status] = (acc[task.status] || 0) + 1;
    return acc;
  }, {});
  
  // 批次整体状态判定
  let batchStatus;
  if (statusCount.FAILED === tasks.length) {
    batchStatus = 'FAILED'; // 全部失败
  } else if (statusCount.SUCCESS === tasks.length) {
    batchStatus = 'SUCCESS'; // 全部成功
  } else if (statusCount.RUNNING > 0 || statusCount.QUEUED > 0) {
    batchStatus = 'RUNNING'; // 仍在执行
  } else {
    batchStatus = 'PARTIAL'; // 部分成功
  }
  
  return {
    batch_id: batchId,
    status: batchStatus,
    total: tasks.length,
    success: statusCount.SUCCESS || 0,
    failed: statusCount.FAILED || 0,
    running: statusCount.RUNNING || 0
  };
}

5. 外部引擎适配层

为了解耦具体供应商(如 Suno 可能换成 Udio),需在 backend/src/services 中建立统一抽象层。

5.1 统一接口定义 (Interface)

typescript
interface IGenerativeEngine {
    // 提交生成任务
    generate(params: GenerationParams): Promise<string>; // 返回 External Job ID
    
    // 查询任务状态
    checkStatus(jobId: string): Promise<GenerationResult>;
    
    // (可选) Webhook 回调处理
    handleCallback(payload: any): Promise<void>;
}

5.2 适配器实现 (Adapters)

  • SunoAdapter: 处理 Cookie 保活、Credit 计算、音频 URL 提取。
  • ComfyUIAdapter: 处理 WebSocket 连接、节点图 (Workflow JSON) 注入、图片 Base64 解码。
  • LocalAppAdapter (Jianying):
    • 特殊性:非 HTTP 调用,需通过文件系统操作。
    • 机制:生成 draft_content.json -> 写入指定目录 -> 唤起剪映主程序。

📋 落地检查项

  • [ ] 是否所有外部调用都经过了 Adapter,禁止业务代码直接 fetch 第三方 API?
  • [ ] Adapter 是否包含统一的错误映射(将第三方错误码转为系统内部错误码)?
  • [ ] 本地适配器是否处理了路径分隔符的跨平台兼容性?

6. 安全与合规

6.1 权限控制

  • 认证 (AuthN): JWT (JSON Web Token),有效期 7 天,含 refresh token 机制。
  • 授权 (AuthZ):
    • 基于角色的访问控制 (RBAC):Admin, Paid User, Free User.
    • 资源隔离:用户只能访问 project_id 属于自己的资源。

6.2 内容安全

  • 输入审核: 调用 LLM 前,先通过敏感词库/API 过滤 Prompt。
  • 输出审核: 图片/视频生成后,异步调用鉴黄/鉴暴 API。

6.3 水印与版权

  • 显式水印: 免费用生成的视频右下角强制叠加 "Created by YiKe"。
  • 隐式水印: 【TBD】在音频/视频频域中嵌入用户 ID 哈希,用于溯源。

📋 落地检查项

  • [ ] 敏感词库是否支持热更新?
  • [ ] 鉴权中间件是否覆盖了除 Login/Register 外的所有 API?
  • [ ] 数据库连接字符串和 API Key 是否从环境变量加载,未硬编码?

7. 可观测性

7.1 日志 (Logging)

  • 标准: JSON 格式,包含 trace_id, level, timestamp, service, msg
  • 存储: 本地文件 (Rotate) -> 采集器 (Filebeat) -> ES/Loki (TBD).

7.2 关键指标 (Metrics)

需接入 Prometheus 监控以下指标:

业务指标

指标名类型说明告警阈值
task_queue_depthGauge任务队列堆积数> 100
engine_latency_secondsHistogram外部引擎响应耗时P95 > 10s
generation_success_rateCounter生成成功率< 90%
knowledge_query_accuracyCounter知识库召回准确率< 85%
batch_completion_rateCounter批量任务完成率< 95%

系统指标

指标名类型说明告警阈值
http_requests_totalCounterAPI 请求总量(按 status 分组)-
http_request_duration_secondsHistogramAPI 响应时间P95 > 2s
active_websocket_connectionsGaugeWebSocket 活跃连接数> 10000
redis_connected_clientsGaugeRedis 客户端连接数> 100
postgres_active_connectionsGauge数据库活跃连接数> 80
gpu_utilization_percentGaugeGPU 利用率(如有)> 95%

性能基准(生产环境实测):

场景QPSP95 延迟P99 延迟并发数
用户登录50120ms200ms100
创建任务20300ms500ms50
知识库问答102s5s20
视频生成5180s300s10

7.3 告警 (Alerting)

严重告警(P0)

  • 支付回调失败
  • 外部引擎 100% 报错(持续 5 分钟)
  • API 成功率 < 95%(持续 10 分钟)
  • 数据库连接池耗尽
  • Redis 宕机

警告告警(P1)

  • 队列堆积超过阈值(> 100 个任务)
  • 磁盘空间 < 20%
  • GPU 利用率持续 > 95%(持续 15 分钟)
  • 知识库召回准确率 < 85%

通知渠道

  • 钉钉/飞书 Webhook(即时推送)
  • 邮件通知(汇总报告)
  • 短信通知(P0 严重告警)

7.4 Grafana Dashboard 配置

核心面板(参考 monitoring/grafana-dashboards/business-metrics.json):

  1. 业务概览面板

    • 今日视频生成数
    • 今日活跃用户数
    • 实时任务队列深度
    • 各平台采集成功率
  2. 性能监控面板

    • API 请求 QPS(按接口分组)
    • 响应时间分布(P50/P95/P99)
    • 错误率趋势(按错误码分组)
  3. 资源监控面板

    • CPU/内存使用率
    • 数据库连接数
    • Redis 命中率
    • GPU 利用率(如有)

📋 落地检查项

  • [ ] 所有 API 请求是否都生成了唯一的 Trace ID 并透传至下游?
  • [ ] 是否配置了未捕获异常 (Uncaught Exception) 的全局处理?
  • [ ] Grafana Dashboard 是否已导入并配置告警规则?
  • [ ] 性能指标是否满足 SLA 承诺?

8. 风险清单

风险点影响缓解措施
供应商 API 波动Suno/Midjourney 等封号或改协议1. 建立账号池轮询
2. 实现 Adapter 快速切换
3. 预留 mock 模式供演示
内容合规风险生成违规内容导致被封禁1. 接入第三方文本/图片审核
2. 建立黑名单机制
3. 用户协议免责声明
成本失控用户恶意刷单消耗 Token1. 严格的配额管理 (Quota Service)
2. 单用户并发限制
3. 异常用量告警
本地环境兼容性剪映版本更新导致草稿失效1. 锁定支持的剪映版本
2. 启动时环境自检
3. 提供自动降级方案 (如转云端合成)

📋 落地检查项

  • [ ] 是否已制定详细的 API 配额表(每用户每日限制)?
  • [ ] 法律免责声明是否在用户注册流程中强制阅读?
  • [ ] 是否有备用的 LLM/绘图渠道以防主渠道断供?

9. 核心功能技术实现

本章详细阐述产品书第 5 章所述核心功能的技术实现细节,包含算法原理、代码示例和架构设计。

9.1 灵感广场技术架构

功能概述:链接一键转素材,竞品即刻变脚本。支持抖音/小红书/B站等平台内容采集。

9.1.1 链接解析器实现

技术栈:正则匹配 + LLM 结构化提取

解析流程

  1. 平台识别:通过 URL 特征识别来源平台
  2. 元数据提取:调用平台 API 或爬虫获取标题、封面、作者等信息
  3. LLM 结构化:使用 LLM 将标题/描述转换为结构化脚本

代码实现backend/src/routes/inspiration.js):

javascript
// 链接解析主入口
router.post('/parse', authenticateToken, async (req, res) => {
  const { url } = req.body;
  
  // 1. 平台识别
  const platform = identifyPlatform(url);
  if (!platform) {
    return res.status(400).json({ success: false, error: '不支持的平台' });
  }
  
  // 2. 调用解析服务
  const rawData = await parsers[platform].extract(url);
  
  // 3. LLM 结构化提取
  const structuredContent = await llmStructure(rawData);
  
  // 4. 保存到数据库
  const corpus = await UserCorpus.create({
    user_id: req.user.id,
    source_url: url,
    platform,
    title: structuredContent.title,
    content: structuredContent.script,
    tags: structuredContent.tags,
    meta: rawData
  });
  
  return res.json({ success: true, data: corpus });
});

// 平台识别
function identifyPlatform(url) {
  if (url.includes('douyin.com') || url.includes('tiktok.com')) return 'douyin';
  if (url.includes('xiaohongshu.com') || url.includes('xhslink.com')) return 'xiaohongshu';
  if (url.includes('bilibili.com')) return 'bilibili';
  return null;
}

9.1.2 Viral Score 算法实现

算法公式

Viral Score = (播放量权重 × 40%) + (互动率权重 × 30%) + (匹配度权重 × 30%)

代码实现backend/src/services/RecommendationService.js):

javascript
class RecommendationService {
  /**
   * 计算 Viral Score
   * @param {Object} content - 内容对象
   * @param {Object} projectContext - 项目上下文
   * @returns {Number} - 0-100 的分数
   */
  calculateViralScore(content, projectContext) {
    // 1. 播放量归一化(对数缩放)
    const playWeight = this.normalizePlay(content.play_count);
    
    // 2. 互动率计算
    const interactionRate = (content.likes + content.comments + content.shares) / content.play_count;
    const interactionWeight = Math.min(interactionRate * 10, 10); // 归一化到 0-10
    
    // 3. 匹配度计算(基于标签相似度)
    const matchWeight = this.calculateMatch(content.tags, projectContext.tags);
    
    // 4. 加权求和
    const viralScore = (
      playWeight * 0.4 +
      interactionWeight * 0.3 +
      matchWeight * 0.3
    ) * 10; // 缩放到 0-100
    
    return Math.round(viralScore);
  }
  
  normalizePlay(playCount) {
    // 使用对数缩放:log10(播放量) / 7 归一化到 0-10
    return Math.min(Math.log10(playCount + 1) / 7 * 10, 10);
  }
  
  calculateMatch(contentTags, projectTags) {
    // 计算 Jaccard 相似度
    const intersection = contentTags.filter(tag => projectTags.includes(tag)).length;
    const union = new Set([...contentTags, ...projectTags]).size;
    return (intersection / union) * 10;
  }
}

计算示例

数据归一化
播放量50万log10(500000)/7 × 10 = 8.1
互动率5% (2.5万互动)5% × 10 = 0.5 → min(0.5, 10) = 0.5
匹配度3个共同标签 / 5个总标签(3/5) × 10 = 6.0

最终 Viral Score = (8.1 × 0.4 + 0.5 × 0.3 + 6.0 × 0.3) × 10 = 51 分

9.1.3 一键复刻 Prompt 工程

Prompt 模板backend/src/services/agents/WritingAgent.js):

javascript
const REWRITE_PROMPT = `
你是一位短视频脚本创作专家。请基于以下参考内容进行二次创作。

【参考内容】
标题:{{original_title}}
脚本:{{original_script}}

【创作要求】
1. 保留核心创意和结构,但必须改写为原创表达
2. 针对目标受众({{target_audience}})调整语言风格
3. 增强前3秒的吸引力(黄金3秒法则)
4. 控制总时长在 {{duration}} 秒内
5. 添加具体的视觉建议(画面/镜头)

【输出格式】
返回 JSON 格式:
{
  "title": "新标题",
  "script": [
    {"time": "0-3s", "text": "开场文案", "visual": "画面描述"},
    {"time": "3-10s", "text": "主体内容", "visual": "画面描述"}
  ],
  "tags": ["标签1", "标签2"]
}
`;

📋 落地检查项

  • [ ] 链接解析器是否支持所有主流平台?
  • [ ] Viral Score 算法是否已经过 A/B 测试验证?
  • [ ] 一键复刻的原创度是否通过了平台查重?

9.2 账号诊断与蓝图生成

功能概述:AI 访谈式账号诊断,自动生成 30 天内容排期。

9.2.1 AI 问答逻辑

20 个诊断问题由 4 个维度组成:

  1. 账号基础(5题):平台、领域、粉丝量、发布频率、目标
  2. 内容定位(5题):风格、受众画像、差异化、痛点、价值主张
  3. 运营现状(5题):数据表现、瓶颈、资源、团队、预算
  4. AI 能力(5题):期望效果、接受度、学习意愿、技术限制、交付模式

实现代码backend/src/routes/strategy.js):

javascript
router.post('/diagnose', authenticateToken, async (req, res) => {
  const { project_id, answers } = req.body;
  
  // 1. 验证答案完整性
  if (answers.length < 20) {
    return res.status(400).json({ success: false, error: '请完成所有诊断问题' });
  }
  
  // 2. 调用 StrategyAgent 分析
  const agent = new StrategyAgent();
  const diagnosis = await agent.analyze({
    answers,
    context: { project_id }
  });
  
  // 3. 保存诊断结果
  await Project.update(
    {
      diagnosis_data: diagnosis,
      diagnosis_completed_at: new Date()
    },
    { where: { id: project_id } }
  );
  
  // 4. 触发蓝图生成(异步)
  generateBlueprint(project_id, diagnosis);
  
  return res.json({ success: true, data: diagnosis });
});

9.2.2 30 天蓝图 Multi-Step Reasoning

生成流程(使用 LangGraph 状态机):

mermaid
stateDiagram-v2
    [*] --> DiagnosisAnalysis
    DiagnosisAnalysis --> TopicGeneration: 提取定位
    TopicGeneration --> ContentPlanning: 生成30个选题
    ContentPlanning --> ScheduleOptimization: 排期优化
    ScheduleOptimization --> [*]: 完成蓝图
    
    ContentPlanning --> ContentPlanning: 迭代优化

Python 实现langgraph-runner/app/agents/strategy_agent.py):

python
class StrategyAgent:
    def generate_blueprint(self, diagnosis, days=30):
        """生成 N 天内容蓝图"""
        
        # 1. 提取核心定位
        positioning = self.extract_positioning(diagnosis)
        
        # 2. 生成选题池(1.5倍冗余)
        topics = self.generate_topics(positioning, count=days * 1.5)
        
        # 3. 多维度评分
        scored_topics = []
        for topic in topics:
            score = self.score_topic(topic, positioning)
            scored_topics.append({
                'topic': topic,
                'score': score,
                'difficulty': self.estimate_difficulty(topic)
            })
        
        # 4. 排期优化(考虑难度分布、热点节奏)
        schedule = self.optimize_schedule(scored_topics, days)
        
        return schedule
    
    def score_topic(self, topic, positioning):
        """选题评分:相关性 + 热度 + 可执行性"""
        relevance = self.cosine_similarity(
            topic['embedding'], 
            positioning['embedding']
        )
        hotness = self.get_hotness_score(topic['keywords'])
        feasibility = 1.0 - topic['difficulty']
        
        return relevance * 0.5 + hotness * 0.3 + feasibility * 0.2

9.2.3 断点续传 Redis 缓存设计

Key 命名规范

blueprint:draft:{project_id}:{step}

缓存策略

javascript
// 保存当前步骤进度
async saveDraftProgress(projectId, step, data) {
  const key = `blueprint:draft:${projectId}:${step}`;
  await redis.setex(
    key,
    3600 * 24, // 24小时过期
    JSON.stringify(data)
  );
}

// 恢复进度
async resumeFromDraft(projectId) {
  const steps = ['topics', 'schedule', 'scripts'];
  const progress = {};
  
  for (const step of steps) {
    const key = `blueprint:draft:${projectId}:${step}`;
    const data = await redis.get(key);
    if (data) {
      progress[step] = JSON.parse(data);
    }
  }
  
  return progress;
}

📋 落地检查项

  • [ ] 诊断问题是否经过用户测试验证有效性?
  • [ ] 蓝图生成是否支持中断后恢复?
  • [ ] Redis 缓存是否设置了合理的过期时间?

9.3 项目管理技术细节

功能概述:Hot-Sync 同步机制、项目回收站、资产闭环查询。

9.3.1 Hot-Sync 冲突解决算法

场景:诊断结果需要同步到项目配置,但可能与用户手动修改冲突。

解决方案field_sources 标签保护机制

代码实现backend/src/utils/diagnosisMapper.js):

javascript
function syncDiagnosisToProject(project, diagnosis) {
  const updates = {};
  const fieldSources = project.field_sources || {};
  
  // 诊断映射规则
  const mappings = [
    { diagnosis: 'target_audience', project: 'audience_tags' },
    { diagnosis: 'content_rhythm', project: 'posting_frequency' },
    { diagnosis: 'visual_style', project: 'style_preset' }
  ];
  
  for (const mapping of mappings) {
    const diagnosisValue = diagnosis[mapping.diagnosis];
    const currentSource = fieldSources[mapping.project];
    
    // 冲突检测
    if (currentSource === 'manual') {
      console.log(`⚠️ 字段 ${mapping.project} 已被用户手动修改,跳过同步`);
      continue;
    }
    
    // 执行同步
    updates[mapping.project] = diagnosisValue;
    fieldSources[mapping.project] = 'diagnosis';
  }
  
  updates.field_sources = fieldSources;
  return updates;
}

冲突解决规则

场景field_sources 值处理逻辑
用户从未修改nulldiagnosis直接同步
用户手动修改过manual跳过同步,弹出提示
用户点击"恢复AI建议"diagnosis覆盖当前值

9.3.2 项目回收站软删除

实现机制is_deleted 标志位 + 定时清理任务

数据库字段backend/src/models/Project.js):

javascript
{
  is_deleted: {
    type: DataTypes.BOOLEAN,
    defaultValue: false
  },
  deleted_at: {
    type: DataTypes.DATE,
    allowNull: true
  }
}

软删除操作

javascript
// 删除项目(移入回收站)
async function softDeleteProject(projectId) {
  await Project.update(
    {
      is_deleted: true,
      deleted_at: new Date()
    },
    { where: { id: projectId } }
  );
}

// 恢复项目
async function restoreProject(projectId) {
  await Project.update(
    {
      is_deleted: false,
      deleted_at: null
    },
    { where: { id: projectId } }
  );
}

定时清理任务(30 天后永久删除):

javascript
// cron: 每天凌晨 2 点执行
cron.schedule('0 2 * * *', async () => {
  const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
  
  // 1. 查找过期项目
  const expiredProjects = await Project.findAll({
    where: {
      is_deleted: true,
      deleted_at: { [Op.lt]: thirtyDaysAgo }
    }
  });
  
  // 2. 级联删除关联资产
  for (const project of expiredProjects) {
    await deleteProjectAssets(project.id);
  }
  
  // 3. 永久删除项目
  await Project.destroy({
    where: { id: expiredProjects.map(p => p.id) },
    force: true // 真正删除
  });
  
  console.log(`✅ 清理了 ${expiredProjects.length} 个过期项目`);
});

9.3.3 资产闭环查询优化

SQL UNION ALL 查询(跨多个资产表):

sql
SELECT 
  'corpus' as source_type,
  id,
  title,
  created_at
FROM UserCorpus
WHERE project_id = :project_id AND is_deleted = false

UNION ALL

SELECT 
  'asset' as source_type,
  id,
  file_path as title,
  created_at
FROM UnifiedAssets
WHERE project_id = :project_id AND is_deleted = false

UNION ALL

SELECT 
  'task' as source_type,
  id,
  type as title,
  created_at
FROM Tasks
WHERE project_id = :project_id AND is_deleted = false

ORDER BY created_at DESC;

📋 落地检查项

  • [ ] Hot-Sync 是否有UI提示用户冲突?
  • [ ] 回收站删除是否有二次确认?
  • [ ] 资产查询是否添加了索引优化?

9.4 智能知识库技术架构

功能概述:RAG 强制引用机制,杜绝 AI 幻觉。

9.4.1 RAG 完整流程

mermaid
sequenceDiagram
    participant User
    participant API
    participant KBSearch as KB Search
    participant Rerank as Reranker
    participant LLM
    participant Citation as Citation Renderer
    
    User->>API: 提问
    API->>KBSearch: 向量检索(Top 20)
    KBSearch-->>API: 返回候选 chunks
    API->>Rerank: 二次排序
    Rerank-->>API: 返回 Top 5
    API->>LLM: Prompt + chunks
    LLM-->>API: 生成回答 + chunk_ids
    API->>Citation: 渲染引用
    Citation-->>User: 回答 + 来源链接

9.4.2 文本切片算法实现

Markdown-Aware Sliding Window

参数配置

参数说明
chunkSize1000 字符单个片段大小
overlap180 字符相邻片段重叠部分
tokenEstimation3.5 字符/Token中英混合估算

代码实现backend/src/services/knowledge/KnowledgeTextUtils.js):

javascript
class KnowledgeTextUtils {
  /**
   * Markdown-Aware 文本切片
   * @param {String} text - 原始文本
   * @param {Number} chunkSize - 切片大小
   * @param {Number} overlap - 重叠字符数
   */
  chunkText(text, chunkSize = 1000, overlap = 180) {
    // 1. 标准化文本
    const normalized = this.normalize(text);
    
    // 2. 按 Markdown 标题分段
    const sections = this.splitByHeadings(normalized);
    
    // 3. 对每个段落进行滑动窗口切片
    const chunks = [];
    for (const section of sections) {
      const sectionChunks = this.slidingWindow(
        section.heading,
        section.content,
        chunkSize,
        overlap
      );
      chunks.push(...sectionChunks);
    }
    
    return chunks;
  }
  
  normalize(text) {
    // 统一换行符,移除冗余空格
    return text
      .replace(/\r\n/g, '\n')
      .replace(/\n{3,}/g, '\n\n')
      .replace(/ {2,}/g, ' ')
      .trim();
  }
  
  splitByHeadings(text) {
    const headingRegex = /^(#{1,6})\s+(.+)$/gm;
    const sections = [];
    let lastIndex = 0;
    let currentHeading = '';
    
    let match;
    while ((match = headingRegex.exec(text)) !== null) {
      // 保存上一段
      if (lastIndex > 0) {
        sections.push({
          heading: currentHeading,
          content: text.substring(lastIndex, match.index)
        });
      }
      
      currentHeading = match[0]; // 完整标题行
      lastIndex = match.index + match[0].length;
    }
    
    // 最后一段
    sections.push({
      heading: currentHeading,
      content: text.substring(lastIndex)
    });
    
    return sections;
  }
  
  slidingWindow(heading, content, chunkSize, overlap) {
    const chunks = [];
    let start = 0;
    
    while (start < content.length) {
      const end = Math.min(start + chunkSize, content.length);
      const chunk = heading + '\n' + content.substring(start, end);
      
      chunks.push({
        text: chunk,
        start,
        end,
        tokens: Math.ceil(chunk.length / 3.5)
      });
      
      start += chunkSize - overlap;
    }
    
    return chunks;
  }
}

9.4.3 Mandatory Citation 实现

Prompt 约束(强制 LLM 返回引用):

javascript
const RAG_PROMPT = `
你是一个严谨的知识助手。请基于以下文档片段回答问题。

【文档片段】
{{chunks}}

【规则】
1. **必须**基于文档片段回答,不得编造信息
2. **必须**在回答中标注引用来源(格式:[1][2])
3. 如果文档中没有答案,明确回复"根据当前知识库,无法回答该问题"
4. 返回 JSON 格式:{"answer": "回答内容", "citations": [chunk_id1, chunk_id2]}

【用户问题】
{{question}}
`;

前端渲染frontend/src/components/knowledge/CitationsPanel.vue):

vue
<template>
  <div class="citations-panel">
    <div class="answer" v-html="formattedAnswer"></div>
    
    <div class="citations">
      <h4>引用来源</h4>
      <div v-for="(cite, index) in citations" :key="cite.id" class="citation-item">
        <span class="badge">[{{ index + 1 }}]</span>
        <span class="source">{{ cite.source_name }}</span>
        <el-button size="small" @click="viewSource(cite)">查看原文</el-button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: ['answer', 'citations'],
  computed: {
    formattedAnswer() {
      // 将 [1][2] 转换为可点击链接
      let html = this.answer;
      this.citations.forEach((cite, index) => {
        const regex = new RegExp(`\\[${index + 1}\\]`, 'g');
        html = html.replace(regex, `<sup><a href="#cite-${cite.id}">[${index + 1}]</a></sup>`);
      });
      return html;
    }
  }
}
</script>

9.4.4 知识冲突检测

语义相似度阈值:0.85

检测逻辑backend/src/services/knowledge/KnowledgeConflictDetector.js):

javascript
class KnowledgeConflictDetector {
  async detectConflicts(newChunk) {
    // 1. 检索相似文档
    const similar = await this.search(newChunk.embedding, threshold = 0.85);
    
    // 2. 对比内容是否矛盾
    const conflicts = [];
    for (const candidate of similar) {
      const isConflict = await this.checkContradiction(
        newChunk.text,
        candidate.text
      );
      
      if (isConflict) {
        conflicts.push({
          existing_chunk: candidate,
          similarity: candidate.score,
          reason: '语义相似但结论矛盾'
        });
      }
    }
    
    return conflicts;
  }
  
  async checkContradiction(text1, text2) {
    // 使用 LLM 判断是否矛盾
    const prompt = `
    判断以下两段文字是否存在逻辑矛盾:
    
    文本1:${text1}
    文本2:${text2}
    
    回答 "是" 或 "否",并说明原因。
    `;
    
    const response = await llm.generate(prompt);
    return response.includes('是');
  }
}

📋 落地检查项

  • [ ] RAG Rerank 是否显著提升了召回准确率?
  • [ ] 强制引用是否覆盖了所有知识库问答场景?
  • [ ] 冲突检测是否有人工审核机制?

9.5 批量采集与自动化

功能概述:CSV/Excel 批量导入,p-limit 并发控制。

9.5.1 并发控制实现

技术方案:使用 p-limit 库限制并发数

代码实现backend/src/services/BatchRunnerService.js):

javascript
const pLimit = require('p-limit');

class BatchRunnerService {
  async executeBatch(urls, concurrency = 3) {
    const limit = pLimit(concurrency);
    const results = [];
    
    // 创建并发任务队列
    const tasks = urls.map((url, index) => 
      limit(async () => {
        try {
          console.log(`[${index + 1}/${urls.length}] 开始处理: ${url}`);
          const result = await this.processUrl(url);
          results.push({ url, success: true, data: result });
        } catch (error) {
          console.error(`处理失败: ${url}`, error.message);
          results.push({ url, success: false, error: error.message });
        }
      })
    );
    
    // 等待所有任务完成
    await Promise.all(tasks);
    
    return results;
  }
  
  async processUrl(url) {
    // 调用链接解析器
    const parser = require('./InspirationParser');
    return await parser.parse(url);
  }
}

并发配置建议

目标平台建议并发数原因
抖音3限流严格
小红书2反爬虫强
B站5API 稳定

9.5.2 Cron 触发器配置

配置数据结构

json
{
  "id": "auto_collect_123",
  "name": "每日热点追踪",
  "trigger_type": "cron",
  "cron_expression": "0 9 * * *",
  "workflow_id": "inspiration_collector",
  "inputs": {
    "keywords": ["美妆", "护肤"],
    "platforms": ["douyin", "xiaohongshu"],
    "limit": 10
  },
  "enabled": true
}

执行引擎backend/src/services/workflow/AutomationEngine.js):

javascript
const cron = require('node-cron');

class AutomationEngine {
  init() {
    // 从数据库加载所有自动化配置
    const automations = await Automation.findAll({ where: { enabled: true } });
    
    for (const auto of automations) {
      this.registerCron(auto);
    }
  }
  
  registerCron(automation) {
    cron.schedule(automation.cron_expression, async () => {
      console.log(`⏰ 触发自动化任务: ${automation.name}`);
      
      try {
        const workflow = await WorkflowRegistry.get(automation.workflow_id);
        await workflow.execute(automation.inputs);
      } catch (error) {
        console.error(`自动化任务失败:`, error);
        // 发送告警通知
        await this.sendAlert(automation, error);
      }
    });
  }
}

📋 落地检查项

  • [ ] 并发控制是否有动态调整机制?
  • [ ] Cron 表达式是否经过严格验证?
  • [ ] 自动化任务是否有失败重试机制?

9.6 创作工作坊引擎调度

功能概述:VisualAgent + Fish Audio + ComfyUI + Orchestrator

9.6.1 VisualAgent Prompt 工程

代码实现backend/src/services/agents/VisualAgent.js):

javascript
class VisualAgent extends BaseAgent {
  async generateThumbnailPrompt(params) {
    const { video_title, content_summary, platform, style } = params;
    
    const systemPrompt = `
你是专业的视觉设计师。请为短视频生成 AI 绘图提示词。

【输出要求】
1. positive_prompt: 正向提示词(英文,详细描述画面)
2. negative_prompt: 负向提示词(避免的元素)
3. style_tags: 风格标签(如: realistic, anime, oil painting)
4. aspect_ratio: 画面比例(9:16 / 16:9 / 1:1)

【平台特点】
- 抖音: 9:16 竖屏,明亮色彩,简洁构图
- 小红书: 3:4 或 1:1,文艺清新,留白
- B站: 16:9 横屏,信息丰富

【风格库】
- realistic: 真实摄影感
- anime: 二次元动漫风
- flat: 扁平插画风
`;
    
    const userPrompt = `
视频标题:${video_title}
内容摘要:${content_summary}
目标平台:${platform}
期望风格:${style}
`;
    
    const response = await this.llm.generate(systemPrompt + userPrompt);
    return JSON.parse(response);
  }
}

Prompt 示例输出

json
{
  "positive_prompt": "A professional beauty influencer applying makeup, soft studio lighting, clean white background, high resolution photography, product focus, gentle smile, warm color tones, 9:16 vertical composition",
  "negative_prompt": "blurry, low quality, dark, messy, cluttered, watermark, text overlay",
  "style_tags": ["realistic", "commercial", "beauty"],
  "aspect_ratio": "9:16"
}

9.6.2 Fish Audio 配音流程

API 调用示例backend/src/routes/voiceoverWorkflow.js):

javascript
async function generateVoiceover(text, voiceId) {
  try {
    // 1. 调用 Fish Audio API
    const response = await axios.post('https://api.fish.audio/v1/tts', {
      text,
      voice_id: voiceId,
      format: 'mp3',
      speed: 1.0,
      volume: 1.0
    }, {
      headers: {
        'Authorization': `Bearer ${process.env.FISH_API_TOKEN}`
      }
    });
    
    // 2. 下载音频文件
    const audioUrl = response.data.audio_url;
    const audioBuffer = await downloadFile(audioUrl);
    
    // 3. 上传到 OSS
    const ossPath = await uploadToOSS(audioBuffer, 'voiceovers');
    
    return { success: true, audio_url: ossPath };
  } catch (error) {
    console.error('Fish Audio 调用失败:', error.message);
    throw new Error('配音生成失败');
  }
}

错误处理策略

错误类型HTTP Code处理方式
余额不足402发送充值提醒
文本过长413自动分段处理
音色不存在404回退到默认音色
API 限流429指数退避重试

9.6.3 ComfyUI 动态表单生成

前端实现frontend/src/components/workflow/ComfyUIFormGenerator.vue):

vue
<template>
  <el-form :model="formData">
    <el-form-item 
      v-for="param in dynamicParams" 
      :key="param.name"
      :label="param.label"
    >
      <!-- 文本输入 -->
      <el-input 
        v-if="param.type === 'string'" 
        v-model="formData[param.name]"
        :placeholder="param.default"
      />
      
      <!-- 数字输入 -->
      <el-input-number 
        v-else-if="param.type === 'number'"
        v-model="formData[param.name]"
        :min="param.min"
        :max="param.max"
        :step="param.step"
      />
      
      <!-- 下拉选择 -->
      <el-select 
        v-else-if="param.type === 'enum'"
        v-model="formData[param.name]"
      >
        <el-option 
          v-for="option in param.options" 
          :key="option"
          :label="option"
          :value="option"
        />
      </el-select>
    </el-form-item>
  </el-form>
</template>

<script>
export default {
  props: ['workflowTemplate'],
  data() {
    return {
      formData: {},
      dynamicParams: []
    };
  },
  mounted() {
    // 解析 JSON 模板,生成动态表单
    this.parseTemplate(this.workflowTemplate);
  },
  methods: {
    parseTemplate(template) {
      const nodes = template.nodes || [];
      const params = [];
      
      for (const node of nodes) {
        if (node.inputs) {
          for (const [key, value] of Object.entries(node.inputs)) {
            params.push({
              name: `${node.id}_${key}`,
              label: `${node.title} - ${key}`,
              type: this.inferType(value),
              default: value,
              ...this.extractConstraints(value)
            });
          }
        }
      }
      
      this.dynamicParams = params;
    },
    
    inferType(value) {
      if (typeof value === 'number') return 'number';
      if (Array.isArray(value)) return 'enum';
      return 'string';
    }
  }
}
</script>

9.6.4 Orchestrator 引擎路由决策

决策逻辑backend/src/services/workflow/OrchestratorService.js):

javascript
class OrchestratorService {
  async route(workflow) {
    // 1. 分析工作流复杂度
    const complexity = this.analyzeComplexity(workflow);
    
    // 2. 根据复杂度选择引擎
    let engine;
    if (complexity.hasConditionalLogic) {
      engine = 'langgraph'; // LangGraph 支持条件分支
    } else if (complexity.hasUIAutomation) {
      engine = 'n8n'; // N8N 擅长 Web 自动化
    } else if (complexity.nodeCount < 5) {
      engine = 'node'; // 简单任务直接在 Node.js 执行
    } else {
      engine = 'flowise'; // 默认使用 Flowise
    }
    
    console.log(`🎯 路由决策: 工作流 "${workflow.name}" → ${engine}`);
    
    // 3. 调用对应引擎
    const runner = this.getRunner(engine);
    return await runner.execute(workflow);
  }
  
  analyzeComplexity(workflow) {
    return {
      nodeCount: workflow.nodes?.length || 0,
      hasConditionalLogic: workflow.nodes?.some(n => n.type === 'if'),
      hasUIAutomation: workflow.nodes?.some(n => n.type === 'browser'),
      estimatedTime: this.estimateExecutionTime(workflow)
    };
  }
}

引擎选择矩阵

场景引擎选择原因
简单脚本改写Node.js无需复杂编排
30天蓝图生成LangGraph需要多步推理
批量采集任务N8NUI 自动化能力强
知识库问答Flowise内置 RAG 组件

📋 落地检查项

  • [ ] VisualAgent 生成的 Prompt 是否通过了绘图测试?
  • [ ] Fish Audio 是否配置了备用音色?
  • [ ] Orchestrator 路由决策是否有性能监控?

9.7 多端协同技术方案

功能概述:离线任务队列 + 智能提词器 + AR 辅助线

9.7.1 离线任务队列

技术方案:IndexedDB + 自动重传机制

前端实现miniprogram/src/services/offline.js):

javascript
import { openDB } from 'idb-keyval';

class OfflineQueue {
  async init() {
    this.db = await openDB('offline-tasks', 1, {
      upgrade(db) {
        db.createObjectStore('tasks', { keyPath: 'id', autoIncrement: true });
      }
    });
  }
  
  // 添加离线任务
  async addTask(task) {
    const tx = this.db.transaction('tasks', 'readwrite');
    await tx.store.add({
      ...task,
      status: 'pending',
      created_at: Date.now()
    });
    await tx.done;
  }
  
  // 网络恢复时自动重传
  async syncAll() {
    const tasks = await this.db.getAll('tasks');
    const pending = tasks.filter(t => t.status === 'pending');
    
    for (const task of pending) {
      try {
        await this.uploadTask(task);
        await this.markAsCompleted(task.id);
      } catch (error) {
        console.error(`任务 ${task.id} 同步失败:`, error);
      }
    }
  }
  
  async uploadTask(task) {
    // 上传素材到服务器
    const formData = new FormData();
    formData.append('file', task.file);
    formData.append('project_id', task.project_id);
    
    const response = await fetch('/api/upload', {
      method: 'POST',
      body: formData
    });
    
    if (!response.ok) throw new Error('上传失败');
  }
}

网络监听

javascript
// 监听网络状态
uni.onNetworkStatusChange((res) => {
  if (res.isConnected) {
    console.log('📶 网络已恢复,开始同步离线任务');
    offlineQueue.syncAll();
  }
});

9.7.2 智能提词器同步

WebSocket 实时同步

服务端backend/src/services/WebSocketService.js):

javascript
io.on('connection', (socket) => {
  // PC 端保存脚本
  socket.on('save_script', async (data) => {
    const { project_id, script } = data;
    
    // 1. 保存到数据库
    await Script.upsert({ project_id, content: script });
    
    // 2. 推送到移动端
    socket.to(`project:${project_id}`).emit('script_updated', { script });
  });
  
  // 移动端加入房间
  socket.on('join_project', (project_id) => {
    socket.join(`project:${project_id}`);
  });
});

移动端miniprogram/src/pages/camera/index.vue):

javascript
import io from 'socket.io-client';

export default {
  data() {
    return {
      script: '',
      scrollSpeed: 50 // px/秒
    };
  },
  mounted() {
    this.connectWebSocket();
    this.startAutoScroll();
  },
  methods: {
    connectWebSocket() {
      const socket = io(process.env.API_URL);
      
      socket.emit('join_project', this.projectId);
      
      socket.on('script_updated', (data) => {
        this.script = data.script;
        this.$message.success('脚本已同步');
      });
    },
    
    // 自动滚动算法
    startAutoScroll() {
      const container = this.$refs.scriptContainer;
      let currentScroll = 0;
      
      setInterval(() => {
        currentScroll += this.scrollSpeed / 60; // 60fps
        container.scrollTo(0, currentScroll);
        
        // 到底部后重置
        if (currentScroll >= container.scrollHeight - container.clientHeight) {
          currentScroll = 0;
        }
      }, 1000 / 60);
    }
  }
}

9.7.3 AR 辅助线实现

Canvas 绘制miniprogram/src/pages/camera/index.vue):

javascript
export default {
  data() {
    return {
      gridType: 'rule_of_thirds' // 三分法 / 黄金分割 / 对角线
    };
  },
  methods: {
    drawARGrid() {
      const canvas = this.$refs.arCanvas;
      const ctx = canvas.getContext('2d');
      const { width, height } = canvas;
      
      ctx.clearRect(0, 0, width, height);
      ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
      ctx.lineWidth = 2;
      
      switch (this.gridType) {
        case 'rule_of_thirds':
          // 三分法线
          ctx.beginPath();
          ctx.moveTo(width / 3, 0);
          ctx.lineTo(width / 3, height);
          ctx.moveTo(width * 2 / 3, 0);
          ctx.lineTo(width * 2 / 3, height);
          ctx.moveTo(0, height / 3);
          ctx.lineTo(width, height / 3);
          ctx.moveTo(0, height * 2 / 3);
          ctx.lineTo(width, height * 2 / 3);
          ctx.stroke();
          break;
          
        case 'golden_ratio':
          // 黄金分割线(0.618)
          const golden = 0.618;
          ctx.beginPath();
          ctx.moveTo(width * golden, 0);
          ctx.lineTo(width * golden, height);
          ctx.moveTo(0, height * golden);
          ctx.lineTo(width, height * golden);
          ctx.stroke();
          break;
          
        case 'diagonal':
          // 对角线
          ctx.beginPath();
          ctx.moveTo(0, 0);
          ctx.lineTo(width, height);
          ctx.moveTo(width, 0);
          ctx.lineTo(0, height);
          ctx.stroke();
          break;
      }
    }
  }
}

AR 辅助线类型

类型说明适用场景
三分法画面分为9宫格人物拍摄、风景构图
黄金分割0.618 比例线产品展示、艺术摄影
对角线两条对角线动态画面、透视效果
中心十字居中参考线对称构图、正面拍摄

📋 落地检查项

  • [ ] 离线队列是否有最大缓存限制?
  • [ ] 提词器滚动速度是否可调?
  • [ ] AR 辅助线是否支持自定义颜色?

📚 本章小结

第 9 章详细阐述了一刻工坊核心功能的技术实现,包含:

  • 灵感广场:Viral Score 算法、链接解析器、一键复刻 Prompt 工程
  • 账号诊断:20 问诊断逻辑、Multi-Step Reasoning 蓝图生成、断点续传
  • 项目管理:Hot-Sync 冲突解决、软删除回收站、资产闭环查询
  • 智能知识库:RAG 完整流程、Markdown-Aware 切片算法、强制引用机制
  • 批量采集:p-limit 并发控制、Cron 自动化触发器
  • 创作工作坊:VisualAgent Prompt 工程、Fish Audio 配音、ComfyUI 动态表单、Orchestrator 路由决策
  • 多端协同:IndexedDB 离线队列、WebSocket 提词器同步、Canvas AR 辅助线

🔗 相关章节


End of Document

© 2024-2025 趣美丽 QuMeiLi · Powered by 刻流星引擎 KeLiuXing