Compare commits
11 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
45cc5a7808 | |
|
|
533507e4ae | |
|
|
63056170cc | |
|
|
54c0bfef68 | |
|
|
0aca637b06 | |
|
|
3a00bd64db | |
|
|
d3453f1a34 | |
|
|
3eb7e6c4d8 | |
|
|
9bc5332033 | |
|
|
081630f732 | |
|
|
8cb28745c3 |
|
|
@ -0,0 +1,250 @@
|
|||
---
|
||||
name: 试题讲解生成页面开发
|
||||
overview: 开发一个试题讲解生成页面,用户输入试题后,系统调用AI文本模型分析试题复杂度并拆分为多个讲解点,然后为每个讲解点调用豆包图片生成模型创建讲解图片,最后调用阿里云语音合成模型为每张图片生成讲解音频,实现幻灯片式自动播放。
|
||||
design:
|
||||
architecture:
|
||||
framework: vue
|
||||
styleKeywords:
|
||||
- Glassmorphism
|
||||
- Dark Theme
|
||||
- Modern
|
||||
- Smooth Animations
|
||||
fontSystem:
|
||||
fontFamily: PingFang SC
|
||||
heading:
|
||||
size: 32px
|
||||
weight: 600
|
||||
subheading:
|
||||
size: 18px
|
||||
weight: 500
|
||||
body:
|
||||
size: 16px
|
||||
weight: 400
|
||||
colorSystem:
|
||||
primary:
|
||||
- "#8b5cf6"
|
||||
- "#6366f1"
|
||||
background:
|
||||
- "#0f172a"
|
||||
- "#1e293b"
|
||||
text:
|
||||
- "#f8fafc"
|
||||
- "#94a3b8"
|
||||
functional:
|
||||
- "#10b981"
|
||||
- "#ef4444"
|
||||
- "#f59e0b"
|
||||
todos:
|
||||
- id: add-api-config
|
||||
content: 在config/index.js中添加试题讲解API配置(文本分析、图片生成、音频合成)
|
||||
status: completed
|
||||
- id: create-page-component
|
||||
content: 创建QuestionExplanation.vue页面组件,实现试题输入和状态管理
|
||||
status: completed
|
||||
dependencies:
|
||||
- add-api-config
|
||||
- id: implement-analysis-logic
|
||||
content: 实现试题分析逻辑,调用文本模型拆分讲解点并生成内容
|
||||
status: completed
|
||||
dependencies:
|
||||
- create-page-component
|
||||
- id: implement-resource-generation
|
||||
content: 实现图片生成和音频合成的批量生成逻辑
|
||||
status: completed
|
||||
dependencies:
|
||||
- implement-analysis-logic
|
||||
- id: implement-slideshow-player
|
||||
content: 实现幻灯片播放器和音频同步播放逻辑
|
||||
status: completed
|
||||
dependencies:
|
||||
- implement-resource-generation
|
||||
- id: add-route-and-card
|
||||
content: 在router添加路由配置并在HomePage添加功能卡片
|
||||
status: completed
|
||||
dependencies:
|
||||
- create-page-component
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
开发一个智能试题讲解生成页面,实现根据试题内容自动生成图文并茂的幻灯片式讲解。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- 试题输入:提供文本框供用户输入试题内容
|
||||
- 智能分析:调用AI文本模型分析试题复杂度,动态决定讲解点数量(3-8个)
|
||||
- 内容生成:为每个讲解点生成详细讲解文本和对应的图片生成提示词
|
||||
- 图片生成:调用豆包AI图片生成模型,批量生成讲解图片(支持组图生成)
|
||||
- 音频合成:调用阿里云语音合成模型,为每个讲解文本生成配套音频
|
||||
- 幻灯片播放:实现自动播放逻辑,图片与音频同步,音频播完自动切换下一张
|
||||
- 播放控制:支持播放/暂停、上一张/下一张、进度指示器等交互控制
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 前端框架:Vue 3 + Composition API
|
||||
- 路由管理:Vue Router
|
||||
- HTTP客户端:Axios
|
||||
- 样式方案:Scoped CSS + Glassmorphism UI风格(与现有项目保持一致)
|
||||
- API调用:前端直接调用(演示模式)
|
||||
|
||||
## 技术架构
|
||||
|
||||
### 系统流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[用户输入试题] --> B[调用文本模型分析]
|
||||
B --> C{分析试题复杂度}
|
||||
C -->|简单| D[生成3个讲解点]
|
||||
C -->|中等| E[生成5个讲解点]
|
||||
C -->|复杂| F[生成7-8个讲解点]
|
||||
D --> G[为每个讲解点生成详细文本和图片提示词]
|
||||
E --> G
|
||||
F --> G
|
||||
G --> H[批量生成图片]
|
||||
G --> I[批量合成音频]
|
||||
H --> J[加载图片资源]
|
||||
I --> K[加载音频资源]
|
||||
J --> L[幻灯片播放]
|
||||
K --> L
|
||||
L --> M{音频播放完成?}
|
||||
M -->|是| N[切换下一张]
|
||||
N --> L
|
||||
M -->|否| L
|
||||
```
|
||||
|
||||
### 模块划分
|
||||
|
||||
1. **输入模块**:试题文本输入框、提交按钮、清空按钮
|
||||
2. **处理模块**:试题分析、讲解文本生成、图片prompt生成
|
||||
3. **资源生成模块**:图片生成API调用、音频合成API调用
|
||||
4. **播放模块**:幻灯片展示、音频播放、自动切换逻辑
|
||||
5. **控制模块**:播放/暂停、上下切换、进度指示
|
||||
|
||||
### API集成
|
||||
|
||||
**文本分析模型**:doubao-seed-2-0-lite-260215
|
||||
|
||||
- 用途:分析试题复杂度、拆分讲解点、生成讲解文本和图片提示词
|
||||
- API:https://ark.cn-beijing.volces.com/api/v3/chat/completions
|
||||
- 认证:使用 DOUBAO_KEY
|
||||
|
||||
**图片生成模型**:doubao-seedream-5-0-260128
|
||||
|
||||
- 用途:批量生成讲解图片(支持组图生成)
|
||||
- API:https://ark.cn-beijing.volces.com/api/v3/images/generations
|
||||
- 认证:使用 DOUBAO_KEY
|
||||
- 特性:sequential_image_generation="auto",max_images=8
|
||||
|
||||
**语音合成模型**:qwen3-tts-flash
|
||||
|
||||
- 用途:为讲解文本生成音频
|
||||
- API:https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
|
||||
- 认证:使用 BAILIAN_API_KEY
|
||||
- 音色:Cherry(芊悦,阳光积极的小姐姐音色)
|
||||
|
||||
## 实现要点
|
||||
|
||||
### 性能优化
|
||||
|
||||
- 图片生成采用组图模式,一次API调用生成所有图片,减少网络请求
|
||||
- 音频合成采用并发请求,提升生成速度
|
||||
- 资源预加载:在播放第一张时预加载后续图片和音频
|
||||
- 使用URL.createObjectURL管理音频资源,避免内存泄漏
|
||||
|
||||
### 错误处理
|
||||
|
||||
- API调用失败重试机制(最多重试2次)
|
||||
- 部分资源生成失败时,显示占位图/文本提示,不影响其他讲解点播放
|
||||
- 超时处理:图片生成60秒超时,音频生成30秒超时
|
||||
|
||||
### 播放逻辑
|
||||
|
||||
- 使用HTML5 Audio API监听'ended'事件触发自动切换
|
||||
- 当前音频播放完毕后立即切换到下一张图片并播放对应音频
|
||||
- 播放完成后显示结束状态,提供重新播放按钮
|
||||
|
||||
### 状态管理
|
||||
|
||||
```
|
||||
- idle: 初始状态
|
||||
- analyzing: 正在分析试题
|
||||
- generating: 正在生成图片和音频
|
||||
- ready: 资源准备完成,等待播放
|
||||
- playing: 正在播放
|
||||
- paused: 暂停
|
||||
- completed: 播放完成
|
||||
- error: 错误状态
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
### 新增文件
|
||||
|
||||
```
|
||||
src/
|
||||
├── views/
|
||||
│ └── QuestionExplanation.vue # [NEW] 试题讲解页面主组件
|
||||
├── config/
|
||||
│ └── index.js # [MODIFY] 添加新API配置
|
||||
├── router/
|
||||
│ └── index.js # [MODIFY] 添加路由配置
|
||||
└── views/
|
||||
└── HomePage.vue # [MODIFY] 添加功能卡片
|
||||
```
|
||||
|
||||
### 详细文件说明
|
||||
|
||||
**QuestionExplanation.vue** - 试题讲解页面主组件
|
||||
|
||||
- 实现试题输入界面
|
||||
- 调用文本模型分析试题并生成讲解内容
|
||||
- 调用图片生成API批量生成讲解图片
|
||||
- 调用语音合成API生成配套音频
|
||||
- 实现幻灯片式自动播放逻辑
|
||||
- 提供播放控制(播放/暂停、上下切换、进度指示)
|
||||
- 采用glassmorphism UI风格,深色主题
|
||||
|
||||
**config/index.js** - 添加API配置
|
||||
|
||||
- 试题讲解文本分析API配置
|
||||
- 图片生成API配置(已有部分配置,可能需要补充)
|
||||
- 语音合成API配置(已有部分配置,可能需要补充)
|
||||
|
||||
**router/index.js** - 添加路由
|
||||
|
||||
- 路径:/question-explanation
|
||||
- 组件:QuestionExplanation
|
||||
|
||||
**HomePage.vue** - 添加功能卡片
|
||||
|
||||
- 标题:AI试题讲解生成
|
||||
- 描述:智能分析试题,自动生成图文讲解,幻灯片式播放,让解题过程清晰易懂
|
||||
- 图标:presentation/slides相关图标
|
||||
- 路由:/question-explanation
|
||||
|
||||
## 设计风格
|
||||
|
||||
采用现代深色主题 + Glassmorphism(玻璃态)设计风格,与现有项目保持视觉一致性。
|
||||
|
||||
## 页面布局
|
||||
|
||||
采用垂直布局,分为三个主要区域:
|
||||
|
||||
1. **顶部输入区**:标题 + 试题输入框 + 操作按钮
|
||||
2. **中间展示区**:幻灯片播放区域(图片 + 讲解文本)
|
||||
3. **底部控制区**:播放控制按钮 + 进度指示器
|
||||
|
||||
## 视觉特点
|
||||
|
||||
- 深色背景(#0f172a)配合玻璃态卡片
|
||||
- 渐变色彩点缀(紫色/蓝色渐变)
|
||||
- 流畅的过渡动画
|
||||
- 响应式设计,适配不同屏幕尺寸
|
||||
|
||||
## 交互设计
|
||||
|
||||
- 加载状态:骨架屏 + 进度提示
|
||||
- 播放状态:当前幻灯片高亮,进度条动态更新
|
||||
- 控制按钮:hover效果 + 点击反馈
|
||||
- 平滑过渡:图片切换使用淡入淡出动画
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
---
|
||||
name: 试题讲解视频生成功能开发
|
||||
overview: 在 QuestionExplanation.vue 页面添加视频生成功能,将已生成的图片和音频合成为完整视频,支持在线预览、下载,并显示详细的生成进度。
|
||||
design:
|
||||
architecture:
|
||||
framework: vue
|
||||
styleKeywords:
|
||||
- Dark Theme
|
||||
- Glassmorphism
|
||||
- Minimalist
|
||||
fontSystem:
|
||||
fontFamily: PingFang SC
|
||||
heading:
|
||||
size: 1.25rem
|
||||
weight: 600
|
||||
subheading:
|
||||
size: 1rem
|
||||
weight: 500
|
||||
body:
|
||||
size: 0.875rem
|
||||
weight: 400
|
||||
colorSystem:
|
||||
primary:
|
||||
- "#8b5cf6"
|
||||
- "#3b82f6"
|
||||
background:
|
||||
- "#0f172a"
|
||||
- "#1e293b"
|
||||
text:
|
||||
- "#ffffff"
|
||||
- rgba(255,255,255,0.6)
|
||||
functional:
|
||||
- "#22c55e"
|
||||
- "#ef4444"
|
||||
todos:
|
||||
- id: create-composer
|
||||
content: 创建 videoComposer.js 封装 ffmpeg.wasm 合成逻辑
|
||||
status: completed
|
||||
- id: add-video-config
|
||||
content: 在 config/index.js 添加视频相关配置
|
||||
status: completed
|
||||
- id: implement-compose-ui
|
||||
content: 在 QuestionExplanation.vue 添加合成按钮和进度显示
|
||||
status: completed
|
||||
dependencies:
|
||||
- create-composer
|
||||
- add-video-config
|
||||
- id: implement-preview-download
|
||||
content: 实现视频预览和下载功能
|
||||
status: completed
|
||||
dependencies:
|
||||
- implement-compose-ui
|
||||
- id: integrate-workflow
|
||||
content: 整合视频合成到现有生成流程
|
||||
status: completed
|
||||
dependencies:
|
||||
- implement-preview-download
|
||||
---
|
||||
|
||||
## 产品概述
|
||||
|
||||
在现有试题讲解页面基础上,新增视频合成功能,将已生成的图片和音频合成为完整的教学讲解视频。
|
||||
|
||||
## 核心功能
|
||||
|
||||
- 将多个讲解点的图片和音频按顺序合成为单一视频文件
|
||||
- 视频生成过程中显示详细进度(资源下载、片段合成、视频编码等步骤)
|
||||
- 支持在线预览生成的视频
|
||||
- 支持下载视频到本地
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 视频合成:ffmpeg.wasm(已安装 @ffmpeg/ffmpeg@0.12.15)
|
||||
- 现有框架:Vue 3 + Vite
|
||||
- HTTP 请求:axios(已有)
|
||||
|
||||
## 技术架构
|
||||
|
||||
### 实现方案
|
||||
|
||||
使用 ffmpeg.wasm 在浏览器端完成视频合成,无需后端服务:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[slides数据] --> B[下载图片/音频]
|
||||
B --> C[写入FFmpeg虚拟文件系统]
|
||||
C --> D[生成视频片段]
|
||||
D --> E[合并所有片段]
|
||||
E --> F[输出MP4视频]
|
||||
F --> G[预览/下载]
|
||||
```
|
||||
|
||||
### 核心流程
|
||||
|
||||
1. **资源准备阶段**:遍历 slides 数组,fetch 所有图片和音频到内存
|
||||
2. **文件系统写入**:将资源写入 ffmpeg.wasm 的虚拟文件系统
|
||||
3. **片段生成**:每个 slide 生成一个视频片段(图片+音频)
|
||||
4. **视频合并**:使用 concat demuxer 将所有片段合并为完整视频
|
||||
5. **输出展示**:生成 Blob URL 用于预览和下载
|
||||
|
||||
### 性能考量
|
||||
|
||||
- 音频时长检测:需要预先获取每个音频时长,用于视频片段时长控制
|
||||
- 内存管理:大文件处理时注意释放内存
|
||||
- 进度反馈:通过 ffmpeg 日志解析当前进度
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
src/
|
||||
├── views/
|
||||
│ └── QuestionExplanation.vue # [MODIFY] 添加视频合成功能
|
||||
├── utils/
|
||||
│ └── videoComposer.js # [NEW] ffmpeg.wasm 封装模块
|
||||
└── config/
|
||||
└── index.js # [MODIFY] 添加视频相关配置
|
||||
```
|
||||
|
||||
## 实现要点
|
||||
|
||||
### videoComposer.js 核心接口
|
||||
|
||||
- `initFFmpeg()`: 初始化并加载 ffmpeg.wasm
|
||||
- `composeVideo(slides, onProgress)`: 主合成函数,返回视频 Blob
|
||||
- `downloadVideo(blob, filename)`: 触发下载
|
||||
- `getAudioDuration(audioUrl)`: 获取音频时长
|
||||
|
||||
### 状态管理
|
||||
|
||||
新增状态:
|
||||
|
||||
- `videoStatus`: idle | preparing | composing | ready | error
|
||||
- `videoProgress`: { stage: string, current: number, total: number, message: string }
|
||||
- `videoBlobUrl`: 合成完成的视频 URL
|
||||
|
||||
## 设计说明
|
||||
|
||||
在现有播放器界面基础上,新增视频合成功能区,位于播放控制区域下方。采用深色主题风格与现有界面保持一致。
|
||||
|
||||
## 新增区块
|
||||
|
||||
1. **视频合成按钮区**:在播放控制区底部添加"生成视频"按钮
|
||||
2. **进度显示区**:合成过程中显示分步骤进度条和当前状态描述
|
||||
3. **视频预览区**:合成完成后显示视频播放器和下载按钮
|
||||
156
DEPLOY.md
156
DEPLOY.md
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
- **项目名称**:AI 英语学习辅助平台
|
||||
- **技术栈**:Vue 3 + Vite + Vue Router(History 模式)
|
||||
- **构建产物**:纯静态文件(HTML / CSS / JS),可部署到任意静态托管服务
|
||||
- **项目结构**:纯前端静态文件(HTML / CSS / JS),可部署到任意静态托管服务
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -12,12 +12,13 @@
|
|||
|
||||
### 1.1 环境要求
|
||||
|
||||
| 工具 | 版本要求 |
|
||||
|------|----------|
|
||||
| Node.js | >= 18.x(推荐 20.x LTS) |
|
||||
| npm | >= 9.x |
|
||||
| 工具 | 版本要求 | 用途 |
|
||||
|------|----------|------|
|
||||
| Node.js | >= 18.x(推荐 20.x LTS) | 前端构建 |
|
||||
| npm | >= 9.x | 包管理器 |
|
||||
|
||||
```bash
|
||||
# 检查 Node.js 和 npm 版本
|
||||
node -v
|
||||
npm -v
|
||||
```
|
||||
|
|
@ -25,6 +26,7 @@ npm -v
|
|||
### 1.2 安装依赖
|
||||
|
||||
```bash
|
||||
# 安装前端依赖
|
||||
cd AI_Demo
|
||||
npm install
|
||||
```
|
||||
|
|
@ -50,7 +52,7 @@ npm install
|
|||
|
||||
### 2.2 跨域响应头要求(重要)
|
||||
|
||||
项目使用了 `@ffmpeg/ffmpeg`(WebAssembly),需要服务器返回以下响应头,否则视频讲解功能将无法运行:
|
||||
项目使用了 `@ffmpeg/ffmpeg`(WebAssembly),需要服务器返回以下响应头:
|
||||
|
||||
```
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
|
|
@ -63,12 +65,12 @@ Cross-Origin-Embedder-Policy: require-corp
|
|||
|
||||
项目在开发环境通过 Vite 代理转发以下 API 请求,**生产环境必须在服务器/网关层配置对应的反向代理**:
|
||||
|
||||
| 前端请求路径前缀 | 代理目标 | 用途 |
|
||||
|-----------------|----------|------|
|
||||
| `/tts-api` | `https://openspeech.bytedance.com` | 豆包 TTS 语音合成 |
|
||||
| `/ark-api` | `https://ark.cn-beijing.volces.com` | 火山引擎 Ark 大模型 |
|
||||
| `/dashscope-api` | `https://dashscope.aliyuncs.com` | 阿里云百炼 |
|
||||
| `/asr-ws` | `wss://openspeech.bytedance.com` | 豆包 ASR WebSocket |
|
||||
| 前端请求路径前缀 | 代理目标 | 用途 | 备注 |
|
||||
|-----------------|----------|------|------|
|
||||
| `/tts-api` | `https://openspeech.bytedance.com` | 豆包 TTS 语音合成 | 需要反向代理 |
|
||||
| `/ark-api` | `https://ark.cn-beijing.volces.com` | 火山引擎 Ark 大模型 | 需要反向代理 |
|
||||
| `/dashscope-api` | `https://dashscope.aliyuncs.com` | 阿里云百炼 | 需要反向代理 |
|
||||
| `/asr-ws` | `wss://openspeech.bytedance.com` | 豆包 ASR WebSocket | 需要反向代理+注入鉴权 Header |
|
||||
|
||||
> **ASR WebSocket 特殊说明**:`/asr-ws` 代理需要在代理层注入以下鉴权 Header(浏览器原生 WebSocket 不支持自定义 Header):
|
||||
> - `X-Api-App-Key`
|
||||
|
|
@ -80,6 +82,8 @@ Cross-Origin-Embedder-Policy: require-corp
|
|||
|
||||
## 三、构建生产包
|
||||
|
||||
### 3.1 前端构建
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
|
@ -127,7 +131,7 @@ server {
|
|||
root /var/www/ai-demo;
|
||||
index index.html;
|
||||
|
||||
# 跨域响应头(FFmpeg WASM 必需)
|
||||
# 跨域响应头
|
||||
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||
|
||||
|
|
@ -180,6 +184,7 @@ server {
|
|||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
#### 4.A.3 启用配置并重启 Nginx
|
||||
|
||||
|
|
@ -204,17 +209,11 @@ certbot --nginx -d your-domain.com
|
|||
|
||||
---
|
||||
|
||||
### 方案 B:Node.js + Express 静态服务器
|
||||
### 方案 B:Node.js + Express 部署
|
||||
|
||||
适用于需要在 Node.js 环境中自定义响应头的场景。
|
||||
|
||||
#### 4.B.1 安装依赖
|
||||
|
||||
```bash
|
||||
npm install express http-proxy-middleware
|
||||
```
|
||||
|
||||
#### 4.B.2 创建服务器文件 `server.js`
|
||||
创建前端静态服务器文件 `frontend-server.js`(项目根目录):
|
||||
|
||||
```js
|
||||
import express from 'express'
|
||||
|
|
@ -227,14 +226,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|||
const app = express()
|
||||
const PORT = process.env.PORT || 3000
|
||||
|
||||
// 全局注入 COOP/COEP 响应头(FFmpeg WASM 必需)
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
|
||||
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp')
|
||||
next()
|
||||
})
|
||||
|
||||
// API 代理
|
||||
// 第三方 API 代理
|
||||
app.use('/tts-api', createProxyMiddleware({
|
||||
target: 'https://openspeech.bytedance.com',
|
||||
changeOrigin: true,
|
||||
|
|
@ -277,22 +269,38 @@ app.get('*', (req, res) => {
|
|||
})
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running at http://localhost:${PORT}`)
|
||||
console.log(`Frontend server running at http://localhost:${PORT}`)
|
||||
})
|
||||
```
|
||||
|
||||
#### 4.B.3 启动服务
|
||||
#### 4.B.3 安装前端服务器依赖
|
||||
|
||||
```bash
|
||||
DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx node server.js
|
||||
# 在项目根目录安装
|
||||
npm install express http-proxy-middleware
|
||||
```
|
||||
|
||||
#### 4.B.4 启动前端服务器
|
||||
|
||||
```bash
|
||||
# 在项目根目录启动
|
||||
DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx pm2 start frontend-server.js --name ai-demo-frontend
|
||||
|
||||
# 查看两个服务的状态
|
||||
pm2 status
|
||||
|
||||
# 设置开机自启
|
||||
pm2 save
|
||||
```
|
||||
|
||||
前端服务运行在 `http://localhost:3000`(提供静态文件和代理)
|
||||
|
||||
---
|
||||
|
||||
### 方案 C:EdgeOne Pages / Vercel / Netlify(纯静态托管)
|
||||
|
||||
> **注意**:此类平台**不支持服务端代理**,`/tts-api`、`/ark-api`、`/dashscope-api`、`/asr-ws` 等接口将因跨域或缺少鉴权 Header 而失败。
|
||||
> 建议仅用于演示或配合独立后端服务使用。
|
||||
> 建议仅用于演示。
|
||||
|
||||
#### 部署步骤(以 Vercel 为例)
|
||||
|
||||
|
|
@ -320,9 +328,11 @@ DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx node server.js
|
|||
|
||||
## 五、部署后验证清单
|
||||
|
||||
### 5.1 部署验证
|
||||
|
||||
| 验证项 | 检查方法 |
|
||||
|--------|----------|
|
||||
| 页面正常加载 | 访问首页,确认 6 个功能卡片显示正常 |
|
||||
| 页面正常加载 | 访问首页,确认功能卡片显示正常 |
|
||||
| 路由刷新不 404 | 直接访问 `/pronunciation`、`/speaking` 等子路由 |
|
||||
| COOP/COEP 响应头 | 浏览器 DevTools → Network → 查看 index.html 响应头 |
|
||||
| TTS 语音合成 | 进入发音练习页,测试语音播放 |
|
||||
|
|
@ -335,7 +345,7 @@ DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx node server.js
|
|||
|
||||
## 六、常见问题排查
|
||||
|
||||
### Q1:视频讲解页面报错 `SharedArrayBuffer is not defined`
|
||||
### Q1:页面报错 `SharedArrayBuffer is not defined`
|
||||
|
||||
**原因**:服务器未返回 `Cross-Origin-Opener-Policy: same-origin` 和 `Cross-Origin-Embedder-Policy: require-corp` 响应头。
|
||||
**解决**:参考第二节 2.2,在 Nginx 或 Node.js 服务器中添加对应响应头。
|
||||
|
|
@ -357,7 +367,7 @@ DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx node server.js
|
|||
|
||||
### Q5:构建时内存不足
|
||||
|
||||
**原因**:项目包含 FFmpeg WASM,构建产物较大。
|
||||
**原因**:项目依赖较多,构建产物较大。
|
||||
**解决**:
|
||||
|
||||
```bash
|
||||
|
|
@ -377,11 +387,17 @@ NODE_OPTIONS=--max-old-space-size=4096 npm run build
|
|||
| `/essay-correction` | 作文批改 | 图片/文本作文批改 |
|
||||
| `/exam-analysis` | 试题分析 | 图片题目分析解答 |
|
||||
| `/spell-practice` | 单词拼写 | 单词拼写练习检测 |
|
||||
| `/problem-solving` | 解题指导 | AI 解题步骤指导 |
|
||||
| `/question-generator` | 题目生成 | 自动生成练习题 |
|
||||
| `/question-variant` | 题目变体 | 生成相似题目变体 |
|
||||
| `/audio-to-text` | 语音转文字 | 音频转文本功能 |
|
||||
|
||||
---
|
||||
|
||||
## 八、依赖版本参考
|
||||
|
||||
### 前端依赖(package.json)
|
||||
|
||||
| 依赖包 | 版本 |
|
||||
|--------|------|
|
||||
| vue | ^3.5.30 |
|
||||
|
|
@ -393,14 +409,12 @@ NODE_OPTIONS=--max-old-space-size=4096 npm run build
|
|||
| marked | ^17.0.5 |
|
||||
| pako | ^2.1.0 |
|
||||
|
||||
---
|
||||
|
||||
## 九、目录结构参考
|
||||
|
||||
```
|
||||
AI_Demo/
|
||||
├── dist/ # 构建产物(部署此目录)
|
||||
├── src/
|
||||
├── dist/ # 前端构建产物(部署此目录)
|
||||
├── src/ # 前端源码
|
||||
│ ├── config/index.js # API 密钥配置(部署前确认)
|
||||
│ ├── router/index.js # 路由定义
|
||||
│ ├── views/ # 页面组件
|
||||
|
|
@ -409,11 +423,69 @@ AI_Demo/
|
|||
│ │ ├── Speaking.vue # 口语对话
|
||||
│ │ ├── EssayCorrection.vue # 作文批改
|
||||
│ │ ├── ExamAnalysis.vue # 试题分析
|
||||
│ │ └── SpellPractice.vue # 单词拼写
|
||||
│ │ ├── SpellPractice.vue # 单词拼写
|
||||
│ │ ├── ProblemSolving.vue # 解题指导
|
||||
│ │ ├── QuestionGenerator.vue # 题目生成
|
||||
│ │ ├── QuestionVariant.vue # 题目变体
|
||||
│ │ └── AudioToText.vue # 语音转文字
|
||||
│ ├── components/ # 公共组件
|
||||
│ ├── assets/ # 静态资源
|
||||
│ └── MainLayout.vue # 布局组件
|
||||
├── vite.config.js # 开发代理配置(生产环境需在服务器复现)
|
||||
├── package.json
|
||||
├── package.json # 前端依赖配置
|
||||
└── DEPLOY.md # 本文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、Docker 部署(可选)
|
||||
|
||||
### 10.1 构建镜像
|
||||
|
||||
```dockerfile
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 复制依赖文件
|
||||
COPY package*.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
# 复制源码
|
||||
COPY . ./
|
||||
|
||||
# 构建
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、安全建议
|
||||
|
||||
1. **HTTPS 强制**:生产环境务必启用 HTTPS
|
||||
2. **API 密钥保护**:不要将 API 密钥提交到代码仓库
|
||||
3. **输入验证**:对用户输入进行验证和过滤
|
||||
4. **日志审计**:记录关键操作和错误信息
|
||||
|
||||
---
|
||||
|
||||
## 十二、更新与维护
|
||||
|
||||
### 更新前端
|
||||
|
||||
```bash
|
||||
git pull
|
||||
npm install
|
||||
npm run build
|
||||
# 重新部署 dist/ 目录
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:v2.1
|
||||
**更新日期**:2025-04-16
|
||||
**更新内容**:移除视频讲解功能及相关后端服务
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>AI 英语学习辅助平台</title>
|
||||
<!-- COEP 策略下暂时禁用 umami(需要服务端支持 CORP 响应头) -->
|
||||
<!-- <script defer src="https://umami.23544.com/script.js" data-website-id="758c5bd3-4189-4dcb-a2a8-21531c92dcd8"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ffmpeg/ffmpeg": "^0.12.15",
|
||||
"@ffmpeg/util": "^0.12.2",
|
||||
"axios": "^1.13.6",
|
||||
"marked": "^17.0.5",
|
||||
"pako": "^2.1.0",
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -226,3 +226,4 @@ export const AUDIO_TEXT_OPTIMIZE_PROMPT = `你是一位专业的文字编辑和
|
|||
- 只输出优化后的文本,不要添加"优化后文本:"等前缀
|
||||
- 保持原文语言(中文/英文/日语)
|
||||
- 如果原文已经很好,只需做必要的格式调整`;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,722 @@
|
|||
通过文生图API,您可以基于文本描述创造出全新的图像。阿里云百炼提供两大系列模型:
|
||||
|
||||
- 千问(Qwen-Image): 擅长渲染复杂的中英文文本。
|
||||
|
||||
- 万相(Wan系列): 用于生成写实图像和摄影级视觉效果。
|
||||
|
||||
|
||||
**在线体验**:[北京](https://bailian.console.aliyun.com/?tab=model#/efm/model_experience_center/vision?currentTab=imageGenerate&modelId=qwen-image)|[新加坡](https://modelstudio.console.aliyun.com/?tab=dashboard#/efm/model_experience_center/vision?currentTab=imageGenerate)
|
||||
|
||||
## **模型效果**
|
||||
|
||||
#### **千问(Qwen-image)**
|
||||
|
||||
| **复杂布局**  | **超长段落**  | **写实人像**  |
|
||||
| --- | --- | --- |
|
||||
| **自然景观**  | **逻辑架构**  | **电商海报**  |
|
||||
|
||||
**点击查看提示词**
|
||||
|
||||
**复杂布局**:冬日北京的都市街景,青灰瓦顶、朱红色外墙的两间相邻中式商铺比肩而立,檐下悬挂印有剪纸马的暖光灯笼,在阴天漫射光中投下柔和光晕,映照湿润鹅卵石路面泛起细腻反光。左侧为书法店:靛蓝色老旧的牌匾上以遒劲行书刻着“**文字渲染**”。店门口的玻璃上挂着一幅字,自上而下,用田英章硬笔写着“**专业幻灯片 中英文海报 高级信息图**”,落款印章为“**1k token**”朱砂印。店内的墙上,可以模糊的辨认有三幅竖排的书法作品,第一幅写着着“**阿里巴巴**”,第二幅写着“**通义千问**”,第三福写着“**图像生成**”。一位白发苍苍的老人背对着镜头观赏。右侧为花店,牌匾上以鲜花做成文字“**真实质感**”;店内多层花架陈列红玫瑰、粉洋牡丹和绿植,门上贴了一个圆形花边标识,标识上写着“**2k resolution**”,门口摆放了一个彩色霓虹灯,上面写着“**细腻刻画 人物 自然 建筑**”。两家店中间堆放了一个雪人,举了一老式小黑板,上面用粉笔字写着“**Qwen-Image-2.0 正式发布**”。街道左侧,年轻情侣依偎在一起,女孩是瘦脸,身穿米白色羊绒大衣,肉色光腿神器。女孩举着心形透明气球,气球印有白色的字:“**生图编辑二合一**”。里面有一个毛茸茸的卡皮巴拉玩偶。男孩身着剪裁合体的深灰色呢子外套,内搭浅色高领毛衣。街道右侧,一个后背上写着“**更小模型,更快速度**”的骑手疾驰而过。整条街光影交织、动静相宜。
|
||||
|
||||
**超长段落**:中国古典水墨长卷风格,竖幅构图,画面自上而下、自右向左以行书题写柳永《雨霖铃·寒蝉凄切》全文(共12行,含标点与换行):“**寒蝉凄切,对长亭晚,骤雨初歇。都门帐饮无绪,留恋处、兰舟催发。执手相看泪眼,竟无语凝噎。念去去,千里烟波,暮霭沉沉楚天阔。多情自古伤离别,更那堪、冷落清秋节!今宵酒醒何处?杨柳岸,晓风残月。此去经年,应是良辰好景虚设。便纵有千种风情,更与何人说**?”书法墨色浓淡相宜,飞白自然,笔锋遒劲中见婉转,行气连贯如流水;字迹略带微洇,仿宣纸渗透效果。背景为极简留白水墨意境:右下角绘一叶孤舟泊于浅滩,舟头微翘,缆绳轻系枯柳;左侧远景以淡墨晕染出层叠低垂的暮霭与空阔楚天,天际线处一抹青灰远山若隐若现;近景岸边斜出三两枝细柳,枝条纤柔,叶已疏落,承袭清秋萧瑟之气;柳梢悬一弯将隐未隐的残月,清冷微光映照薄雾中拂面的晓风痕迹(以几缕轻扬的柳丝与水纹示意)。整幅画气息沉郁隽永,哀而不伤,严格遵循宋词意境与传统文人画\*\*“诗书画一体”\*\*范式,无印章、无题跋、无现代元素。
|
||||
|
||||
**写实人像**:一位约20岁出头的亚洲年轻女性,留着齐刘海、乌黑光滑的长直发,自然垂落于双肩两侧。她侧坐于一张复古碎花布艺沙发上,沙发图案为米白底色配粉色与绿色花卉,质地略显陈旧,带有生活感。她身穿一件宽松的浅绿色马海毛针织毛衣,质感蓬松柔软,下身搭配浅灰蓝色亚麻长裙,整体造型清新自然、慵懒随性。右手轻轻握住一颗红色番茄,抬至下巴附近,姿态随意,眼神直视镜头,神情平静、略带冷淡,带有一种漫不经心的疏离感。沙发右侧放有一个浅色陶盘,盘内盛放着三至四颗饱满鲜红的番茄,带有绿色蒂头,色彩鲜艳,与画面整体的冷绿色调形成强烈对比。背景为做旧的青绿色墙面,斑驳而有质感。窗外射入的自然光形成明显的光束,斜斜打在人物与背景上,光影层次丰富。窗台及背景角落摆有数盆绿植,左侧隐约可见一个深棕色老式木柜。整张照片色调偏冷绿,叠有明显的胶片颗粒感与轻微漏光效果,构图饱满,氛围静谧、文艺,带有强烈的复古胶片人文摄影风格。
|
||||
|
||||
**自然景观**:一幅写实风格的夏日森林场景,画面中央是一片幽深静谧的林间空地,高大挺拔的橡树与山毛榉构成主体乔木层,其浓密树冠呈现深邃厚重的墨绿色,叶片表面带有细微的蜡质反光;树冠间隙中透下柔和而强烈的阳光,在空气中形成清晰可见的丁达尔光束,光束边缘略带暖金色调,与冷调绿影形成微妙对比。中景处一丛新生的枫树嫩枝舒展着鲜亮明快的翠绿色叶片,叶脉清晰、半透明感强,边缘微微卷曲,仿佛刚经历晨露洗礼。前景左侧低矮的冬青与荚蒾灌木丛披覆着哑光柔和的橄榄绿色,枝叶交错,纹理细腻,部分叶片背面泛出浅灰绿光泽。地面覆盖着厚实湿润的苔藓层,由多种苔类组成:近处是绒状垂穗藓,呈现饱满润泽的青绿色,表面凝结细小露珠;稍远处为鳞叶藓与泥炭藓交织,显出微带蓝调的灰青绿与棕绿过渡;腐叶层隐约可见,呈深褐与墨绿混融的有机质感。所有植被表面均带有自然微湿反光,空气中有极细微的悬浮微粒在光束中浮动。背景林区渐次虚化,保留层次但不抢主体,远景融入一层薄薄的蓝绿雾霭。整体光影为上午10点左右的斜射日光,明暗对比适中,绿色系通过23种以上不同明度、饱和度、冷暖倾向与材质表现(如蜡质、绒面、革质、胶质)精确区分,毫无重复感,营造出丰饶、呼吸感强烈、充满生物细节与生态真实性的夏日森林秘境。
|
||||
|
||||
**逻辑架构:**一幅充满生活气息的插画,采用细腻、柔和的画风,色彩鲜艳且层次丰富,呈现出阳光明媚的街头景象,整体氛围轻松愉快。画面中是一条热闹的商业街道,天空湛蓝,点缀着几朵蓬松白云,几只海鸥在空中自由翱翔,为画面增添动感与生机。街道两旁的建筑风格现代而富有特色,外墙色彩明快,墙上悬挂着多个招牌,分别写着“**阿里巴巴**”、“**百炼**”、“**文生图**”,字体清晰可辨,排列错落有致,营造出浓厚的商业氛围。街道上人流如织,展现出繁忙而温馨的日常场景。画面前景中,一个穿着白色衬衫和短裤的男孩正站在一个摆满商品的货摊前专注挑选。他神情认真,身体微微前倾,体现出对商品的兴趣。货摊上陈列着各类饮料、零食和日用品,摆放整齐,细节丰富。摊主是一名中年男子,身穿深色围裙,神情专注地整理商品或与顾客交流,展现出市井生活的亲切感。货摊上方悬挂着一块木质标牌,清晰写着“**Qwen-Image**”,字体为手写风格,增添艺术感。整个场景通过细腻的描绘和温暖的色调,展现了日常生活中那些简单却美好的瞬间。画面风格为现实主义插画风格,带有轻微的手绘质感,强调光影与细节表现,整体构图饱满,空间感强。
|
||||
|
||||
**电商海报:**一张高质量的 C4D 风格电商海报,清新蓝色调。画面顶部为巨大的立体艺术字体 “**天猫 双十一 预售来了**”,极具视觉张力。主体是一袋蓝色包装的 “**萌宠家园**” 宠物粮,包装袋有透明窗口展示诱人的肉块,旁边有一只可爱的3D建模小猫。场景中点缀着精致的动物小模型和蓝色的科技感机械装置,营造热闹的促销氛围。底部醒目显示红色立体字 “**全场满 399 元减 99**”。明亮的商业工作室灯光,超高清,渲染细腻,质感通透,构图严谨。"
|
||||
|
||||
#### **万相**
|
||||
|
||||
| **人像写真**  | **写实摄影**  | **绘画流派**  |
|
||||
| --- | --- | --- |
|
||||
| **文字生成**  | **海报设计**  | **组图生成**  |
|
||||
|
||||
**点击查看提示词**
|
||||
|
||||
**人像写真**:照片,摄影人像,写实人像:背景是故宫红墙,女子身穿黑色旗袍,手握扇子,长曝光摄影,王家卫电影感,故事感,人来人往,迷离的光线,形成迷离的轨迹,柔焦摄影,眼神深邃神秘,艺术气息十足。
|
||||
|
||||
**写实摄影**:写实摄影,一只狐狸在森林中凝视镜头,鱼眼视角带来强烈的透视效果,毛发细节清晰,背景树木呈圆形拉伸,水彩风格,柔和色调。
|
||||
|
||||
**绘画流派**:一束野花插在旧陶罐中,背景是乡村厨房,印象派风格,柔和笔触,温暖光线,油画质感
|
||||
|
||||
**文字生成:**毛笔水墨画风格,宣纸纹理清晰可见,淡墨晕染出朦胧的客厅轮廓。一位身着素色长裙的东方少女盘坐于虚化边缘的旧式布艺沙发上,侧脸低垂,手持一卷展开的诗稿,窗外竹影婆娑,微风拂动帘栊。画面大量留白,右侧题有小楷诗句“闲坐悲双鬓,幽梦入青烟”,左下角钤朱文印章。墨色浓淡相宜,飞白笔触勾勒出光影流动感,意境空寂深远,似有古琴余音缭绕其间。
|
||||
|
||||
**海报设计:**扁平几何插画风格,一张端午节海报,杂志封面,色调与背景:以粉色渐变为主色调,营造出柔和且富有节日氛围的背景,奠定温馨且传统的基调。 文字元素:绿色字体搭配阴影效果,主文案突出"DRAGONBOAT FESTIVAL"与"端午"分两行不分开,正文信息下方"2025/05/31"、"农历五月初五"突出端午数字时间信息"2025/05/31"。 主体图案: 一艘绿色龙身搭配粉色龙鳍的龙船,高饱和色调,色彩对比强烈,高周围点缀祥云元素,船上坐着人物,进一步呼应赛龙舟的场景,增添节日活力。 细节点缀:添加 "中国传统节日" 字样,搭配小型粽子图标,丰富文化细节。高级简约排版方式,大师杰出作品。简约,时尚,大气,新中式传统海报,字体不要有阴影样式。
|
||||
|
||||
**组图生成**:四宫格日系Q版漫画,赛璐璐风格。第一格:戴黑框眼镜的程序员面对屏幕弹出的红色报错,瞳孔地震,冷汗飞溅,背景变为裂开的像素深渊。第二格:他撸起袖子敲击键盘,自信挑眉,头顶冒出“这不过是五行代码的事!”对话框。第三格:屏幕布满混乱的报错符号,他头发炸立,眼圈发黑,椅子后仰45度,天花板飘满废弃的流程图。第四格:误删一行灰色注释后,绿色对勾闪现,他歪头呆滞,屏幕上浮起问号气泡:“……所以它只是个幻觉?”
|
||||
|
||||
## **支持的模型**
|
||||
|
||||
- [千问文生图](https://help.aliyun.com/zh/model-studio/models#34e47bbcf57v1)
|
||||
|
||||
- [万相文生图](https://help.aliyun.com/zh/model-studio/models#b4eb59e706n17)
|
||||
|
||||
|
||||
## **模型选型**
|
||||
|
||||
- **复杂文字渲染**(如海报、对联):首选`**qwen-image-2.0-pro**`**、**`**wan2.6-t2i**`。
|
||||
|
||||
- **写实场景和摄影风格**(通用场景):可选万相模型,如`**wan2.6-t2i**`、`**wan2.5-t2i-preview**`。
|
||||
|
||||
- **需要自定义输出图像分辨率:**推荐`**qwen-image-2.0**`系列或万相模型。qwen-image-2.0系列支持自由设置宽高,输出图像总像素在\[512\*512, 2048\*2048\]之间;万相模型如`**wan2.6-t2i**`,输出图像总像素在\[1280\*1280, 1440\*1440\]之间。
|
||||
|
||||
> qwen-image-max、qwen-image-plus系列模型仅支持5种固定尺寸:1664\*928(16:9)、928\*1664(9:16)、1328\*1328(1:1)、1472\*1104(4:3)、1104\*1472(3:4)。
|
||||
|
||||
- **成本极度敏感,可接受基础质量:**可选择`**wanx2.0-t2i-turbo**`,价格较低,请参见[计费与限流](#a585cbf27dck8)。
|
||||
|
||||
|
||||
## 快速开始
|
||||
|
||||
#### **前提条件**
|
||||
|
||||
在调用前,请[获取API Key](https://help.aliyun.com/zh/model-studio/get-api-key),再[配置API Key到环境变量](https://help.aliyun.com/zh/model-studio/configure-api-key-through-environment-variables)。如果通过DashScope SDK进行调用,还需要[安装SDK](https://help.aliyun.com/zh/model-studio/install-sdk)。
|
||||
|
||||
#### **示例代码**
|
||||
|
||||
**调用方式说明**:
|
||||
|
||||
- 千问文生图模型均支持同步调用,其中qwen-image-plus、qwen-image模型支持异步调用,详情请参见[千问-文生图](https://help.aliyun.com/zh/model-studio/qwen-image-api)。
|
||||
|
||||
- 万相文生图模型均支持异步调用,其中wan2.6-t2i支持同步调用,详情请参见[万相-文生图V2](https://help.aliyun.com/zh/model-studio/text-to-image-v2-api-reference)。
|
||||
|
||||
|
||||
## 同步调用
|
||||
|
||||
## Python
|
||||
|
||||
### **请求示例**
|
||||
|
||||
```
|
||||
import json
|
||||
import os
|
||||
import dashscope
|
||||
from dashscope import MultiModalConversation
|
||||
|
||||
# 以下为北京地域url,若使用新加坡地域的模型,需将url替换为:https://dashscope-intl.aliyuncs.com/api/v1
|
||||
dashscope.base_http_api_url = 'https://dashscope.aliyuncs.com/api/v1'
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"text": "冬日北京的都市街景,青灰瓦顶、朱红色外墙的两间相邻中式商铺比肩而立,檐下悬挂印有剪纸马的暖光灯笼,在阴天漫射光中投下柔和光晕,映照湿润鹅卵石路面泛起细腻反光。左侧为书法店:靛蓝色老旧的牌匾上以遒劲行书刻着“文字渲染”。店门口的玻璃上挂着一幅字,自上而下,用田英章硬笔写着“专业幻灯片 中英文海报 高级信息图”,落款印章为“1k token”朱砂印。店内的墙上,可以模糊的辨认有三幅竖排的书法作品,第一幅写着着“阿里巴巴”,第二幅写着“通义千问”,第三福写着“图像生成”。一位白发苍苍的老人背对着镜头观赏。右侧为花店,牌匾上以鲜花做成文字“真实质感”;店内多层花架陈列红玫瑰、粉洋牡丹和绿植,门上贴了一个圆形花边标识,标识上写着“2k resolution”,门口摆放了一个彩色霓虹灯,上面写着“细腻刻画 人物 自然 建筑”。两家店中间堆放了一个雪人,举了一老式小黑板,上面用粉笔字写着“Qwen-Image-2.0 正式发布”。街道左侧,年轻情侣依偎在一起,女孩是瘦脸,身穿米白色羊绒大衣,肉色光腿神器。女孩举着心形透明气球,气球印有白色的字:“生图编辑二合一”。里面有一个毛茸茸的卡皮巴拉玩偶。男孩身着剪裁合体的深灰色呢子外套,内搭浅色高领毛衣。街道右侧,一个后背上写着“更小模型,更快速度”的骑手疾驰而过。整条街光影交织、动静相宜。"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
# 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
|
||||
# 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx"
|
||||
api_key = os.getenv("DASHSCOPE_API_KEY")
|
||||
|
||||
response = MultiModalConversation.call(
|
||||
api_key=api_key,
|
||||
model="qwen-image-2.0-pro",
|
||||
messages=messages,
|
||||
result_format='message',
|
||||
stream=False,
|
||||
watermark=False,
|
||||
prompt_extend=True,
|
||||
negative_prompt="低分辨率,低画质,肢体畸形,手指畸形,画面过饱和,蜡像感,人脸无细节,过度光滑,画面具有AI感。构图混乱。文字模糊,扭曲。",
|
||||
size='2048*2048'
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
print(json.dumps(response, ensure_ascii=False))
|
||||
else:
|
||||
print(f"HTTP返回码:{response.status_code}")
|
||||
print(f"错误码:{response.code}")
|
||||
print(f"错误信息:{response.message}")
|
||||
print("请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code")
|
||||
```
|
||||
|
||||
### **响应示例**
|
||||
|
||||
> 图像链接的有效期为24小时,请及时下载图像。
|
||||
|
||||
```
|
||||
{
|
||||
"status_code": 200,
|
||||
"request_id": "d2d1a8c0-325f-9b9d-8b90-xxxxxx",
|
||||
"code": "",
|
||||
"message": "",
|
||||
"output": {
|
||||
"text": null,
|
||||
"finish_reason": null,
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"image": "https://dashscope-result-wlcb.oss-cn-wulanchabu.aliyuncs.com/xxx.png?Expires=xxx"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"usage": {
|
||||
"input_tokens": 0,
|
||||
"output_tokens": 0,
|
||||
"width": 2048,
|
||||
"image_count": 1,
|
||||
"height": 2048
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Java
|
||||
|
||||
### **请求示例**
|
||||
|
||||
```
|
||||
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
|
||||
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
|
||||
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
|
||||
import com.alibaba.dashscope.common.MultiModalMessage;
|
||||
import com.alibaba.dashscope.common.Role;
|
||||
import com.alibaba.dashscope.exception.ApiException;
|
||||
import com.alibaba.dashscope.exception.NoApiKeyException;
|
||||
import com.alibaba.dashscope.exception.UploadFileException;
|
||||
import com.alibaba.dashscope.utils.Constants;
|
||||
import com.alibaba.dashscope.utils.JsonUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class QwenImage {
|
||||
|
||||
static {
|
||||
// 以下为北京地域url,若使用新加坡地域的模型,需将url替换为:https://dashscope-intl.aliyuncs.com/api/v1
|
||||
Constants.baseHttpApiUrl = "https://dashscope.aliyuncs.com/api/v1";
|
||||
}
|
||||
|
||||
// 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
|
||||
// 若没有配置环境变量,请用百炼API Key将下行替换为:static String apiKey ="sk-xxx"
|
||||
static String apiKey = System.getenv("DASHSCOPE_API_KEY");
|
||||
|
||||
public static void call() throws ApiException, NoApiKeyException, UploadFileException, IOException {
|
||||
|
||||
MultiModalConversation conv = new MultiModalConversation();
|
||||
|
||||
MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
|
||||
.content(Arrays.asList(
|
||||
Collections.singletonMap("text", "冬日北京的都市街景,青灰瓦顶、朱红色外墙的两间相邻中式商铺比肩而立,檐下悬挂印有剪纸马的暖光灯笼,在阴天漫射光中投下柔和光晕,映照湿润鹅卵石路面泛起细腻反光。左侧为书法店:靛蓝色老旧的牌匾上以遒劲行书刻着“文字渲染”。店门口的玻璃上挂着一幅字,自上而下,用田英章硬笔写着“专业幻灯片 中英文海报 高级信息图”,落款印章为“1k token”朱砂印。店内的墙上,可以模糊的辨认有三幅竖排的书法作品,第一幅写着着“阿里巴巴”,第二幅写着“通义千问”,第三福写着“图像生成”。一位白发苍苍的老人背对着镜头观赏。右侧为花店,牌匾上以鲜花做成文字“真实质感”;店内多层花架陈列红玫瑰、粉洋牡丹和绿植,门上贴了一个圆形花边标识,标识上写着“2k resolution”,门口摆放了一个彩色霓虹灯,上面写着“细腻刻画 人物 自然 建筑”。两家店中间堆放了一个雪人,举了一老式小黑板,上面用粉笔字写着“Qwen-Image-2.0 正式发布”。街道左侧,年轻情侣依偎在一起,女孩是瘦脸,身穿米白色羊绒大衣,肉色光腿神器。女孩举着心形透明气球,气球印有白色的字:“生图编辑二合一”。里面有一个毛茸茸的卡皮巴拉玩偶。男孩身着剪裁合体的深灰色呢子外套,内搭浅色高领毛衣。街道右侧,一个后背上写着“更小模型,更快速度”的骑手疾驰而过。整条街光影交织、动静相宜。")
|
||||
)).build();
|
||||
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("watermark", false);
|
||||
parameters.put("prompt_extend", true);
|
||||
parameters.put("negative_prompt", "低分辨率,低画质,肢体畸形,手指畸形,画面过饱和,蜡像感,人脸无细节,过度光滑,画面具有AI感。构图混乱。文字模糊,扭曲。");
|
||||
parameters.put("size", "2048*2048");
|
||||
|
||||
MultiModalConversationParam param = MultiModalConversationParam.builder()
|
||||
.apiKey(apiKey)
|
||||
.model("qwen-image-2.0-pro")
|
||||
.messages(Collections.singletonList(userMessage))
|
||||
.parameters(parameters)
|
||||
.build();
|
||||
|
||||
MultiModalConversationResult result = conv.call(param);
|
||||
System.out.println(JsonUtils.toJson(result));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
call();
|
||||
} catch (ApiException | NoApiKeyException | UploadFileException | IOException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **响应示例**
|
||||
|
||||
> 图像链接的有效期为24小时,请及时下载图像。
|
||||
|
||||
```
|
||||
{
|
||||
"requestId": "5b6f2d04-b019-40db-a5cc-xxxxxx",
|
||||
"usage": {
|
||||
"image_count": 1,
|
||||
"width": 2048,
|
||||
"height": 2048
|
||||
},
|
||||
"output": {
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"image": "https://dashscope-result-wlcb.oss-cn-wulanchabu.aliyuncs.com/xxx.png?Expires=xxx"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## curl
|
||||
|
||||
##### **请求示例**
|
||||
|
||||
```
|
||||
curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
|
||||
--data '{
|
||||
"model": "qwen-image-2.0-pro",
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"text": "冬日北京的都市街景,青灰瓦顶、朱红色外墙的两间相邻中式商铺比肩而立,檐下悬挂印有剪纸马的暖光灯笼,在阴天漫射光中投下柔和光晕,映照湿润鹅卵石路面泛起细腻反光。左侧为书法店:靛蓝色老旧的牌匾上以遒劲行书刻着“文字渲染”。店门口的玻璃上挂着一幅字,自上而下,用田英章硬笔写着“专业幻灯片 中英文海报 高级信息图”,落款印章为“1k token”朱砂印。店内的墙上,可以模糊的辨认有三幅竖排的书法作品,第一幅写着着“阿里巴巴”,第二幅写着“通义千问”,第三福写着“图像生成”。一位白发苍苍的老人背对着镜头观赏。右侧为花店,牌匾上以鲜花做成文字“真实质感”;店内多层花架陈列红玫瑰、粉洋牡丹和绿植,门上贴了一个圆形花边标识,标识上写着“2k resolution”,门口摆放了一个彩色霓虹灯,上面写着“细腻刻画 人物 自然 建筑”。两家店中间堆放了一个雪人,举了一老式小黑板,上面用粉笔字写着“Qwen-Image-2.0 正式发布”。街道左侧,年轻情侣依偎在一起,女孩是瘦脸,身穿米白色羊绒大衣,肉色光腿神器。女孩举着心形透明气球,气球印有白色的字:“生图编辑二合一”。里面有一个毛茸茸的卡皮巴拉玩偶。男孩身着剪裁合体的深灰色呢子外套,内搭浅色高领毛衣。街道右侧,一个后背上写着“更小模型,更快速度”的骑手疾驰而过。整条街光影交织、动静相宜。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"negative_prompt": "低分辨率,低画质,肢体畸形,手指畸形,画面过饱和,蜡像感,人脸无细节,过度光滑,画面具有AI感。构图混乱。文字模糊,扭曲。",
|
||||
"prompt_extend": true,
|
||||
"watermark": false,
|
||||
"size": "2048*2048"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
```
|
||||
curl --location 'https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
|
||||
--data '{
|
||||
"model": "qwen-image-2.0-pro",
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"text": "Healing-style hand-drawn poster featuring three puppies playing with a ball on lush green grass, adorned with decorative elements such as birds and stars. The main title “Come Play Ball!” is prominently displayed at the top in bold, blue cartoon font. Below it, the subtitle “Come [Show Off Your Skills]!” appears in green font. A speech bubble adds playful charm with the text: “Hehe, watch me amaze my little friends next!” At the bottom, supplementary text reads: “We get to play ball with our friends again!” The color palette centers on fresh greens and blues, accented with bright pink and yellow tones to highlight a cheerful, childlike atmosphere."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"negative_prompt": "低分辨率,低画质,肢体畸形,手指畸形,画面过饱和,蜡像感,人脸无细节,过度光滑,画面具有AI感。构图混乱。文字模糊,扭曲。",
|
||||
"prompt_extend": true,
|
||||
"watermark": false,
|
||||
"size": "2048*2048"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
##### **响应示例**
|
||||
|
||||
```
|
||||
{
|
||||
"output": {
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"message": {
|
||||
"content": [
|
||||
{
|
||||
"image": "https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/xxx.png?Expires=xxx"
|
||||
}
|
||||
],
|
||||
"role": "assistant"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"usage": {
|
||||
"height": 2048,
|
||||
"image_count": 1,
|
||||
"width": 2048
|
||||
},
|
||||
"request_id": "d0250a3d-b07f-49e1-bdc8-6793f4929xxx"
|
||||
}
|
||||
```
|
||||
|
||||
## 异步调用
|
||||
|
||||
> SDK 在底层封装了异步处理逻辑,上层接口表现为同步调用(即单次请求并等待最终结果返回);而 curl 示例则对应两个独立的异步 API 接口:一个用于提交任务,另一个用于查询结果。
|
||||
|
||||
## Python
|
||||
|
||||
### **请求示例**
|
||||
|
||||
```
|
||||
import os
|
||||
import dashscope
|
||||
from dashscope.aigc.image_generation import ImageGeneration
|
||||
from dashscope.api_entities.dashscope_response import Message
|
||||
|
||||
# 以下为北京地域url,各地域的base_url不同
|
||||
dashscope.base_http_api_url = 'https://dashscope.aliyuncs.com/api/v1'
|
||||
|
||||
# 若没有配置环境变量,请用百炼API Key将下行替换为:api_key="sk-xxx"
|
||||
# 各地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
|
||||
api_key = os.getenv("DASHSCOPE_API_KEY")
|
||||
|
||||
message = Message(
|
||||
role="user",
|
||||
content=[
|
||||
{
|
||||
'text': '一间有着精致窗户的花店,漂亮的木质门,摆放着花朵'
|
||||
}
|
||||
]
|
||||
)
|
||||
print("----sync call, please wait a moment----")
|
||||
rsp = ImageGeneration.call(
|
||||
model="wan2.6-t2i",
|
||||
api_key=api_key,
|
||||
messages=[message],
|
||||
negative_prompt="",
|
||||
prompt_extend=True,
|
||||
watermark=False,
|
||||
n=1,
|
||||
size="1280*1280"
|
||||
)
|
||||
print(rsp)
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
> url 有效期24小时,请及时下载图像。
|
||||
|
||||
```
|
||||
{
|
||||
"status_code": 200,
|
||||
"request_id": "820dd0db-eb42-4e05-8d6a-1ddb4axxxxxx",
|
||||
"code": "",
|
||||
"message": "",
|
||||
"output": {
|
||||
"text": null,
|
||||
"finish_reason": null,
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"image": "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/xxxxxx.png?Expires=xxxxxx",
|
||||
"type": "image"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"audio": null,
|
||||
"finished": true
|
||||
},
|
||||
"usage": {
|
||||
"input_tokens": 0,
|
||||
"output_tokens": 0,
|
||||
"characters": 0,
|
||||
"image_count": 1,
|
||||
"size": "1280*1280",
|
||||
"total_tokens": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Java
|
||||
|
||||
### **请求示例**
|
||||
|
||||
```
|
||||
import com.alibaba.dashscope.aigc.imagegeneration.*;
|
||||
import com.alibaba.dashscope.exception.ApiException;
|
||||
import com.alibaba.dashscope.exception.NoApiKeyException;
|
||||
import com.alibaba.dashscope.exception.UploadFileException;
|
||||
import com.alibaba.dashscope.utils.Constants;
|
||||
import com.alibaba.dashscope.utils.JsonUtils;
|
||||
import java.util.Collections;
|
||||
|
||||
public class Main {
|
||||
|
||||
static {
|
||||
// 以下为北京地域url,各地域的base_url不同
|
||||
Constants.baseHttpApiUrl = "https://dashscope.aliyuncs.com/api/v1";
|
||||
}
|
||||
|
||||
// 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey="sk-xxx"
|
||||
// 各地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key
|
||||
static String apiKey = System.getenv("DASHSCOPE_API_KEY");
|
||||
|
||||
public static void basicCall() throws ApiException, NoApiKeyException, UploadFileException {
|
||||
ImageGenerationMessage message = ImageGenerationMessage.builder()
|
||||
.role("user")
|
||||
.content(Collections.singletonList(
|
||||
Collections.singletonMap("text", "一间有着精致窗户的花店,漂亮的木质门,摆放着花朵")
|
||||
)).build();
|
||||
|
||||
ImageGenerationParam param = ImageGenerationParam.builder()
|
||||
.apiKey(apiKey)
|
||||
.model("wan2.6-t2i")
|
||||
.n(1)
|
||||
.size("1280*1280")
|
||||
.negativePrompt("")
|
||||
.promptExtend(true)
|
||||
.watermark(false)
|
||||
.messages(Collections.singletonList(message))
|
||||
.build();
|
||||
|
||||
ImageGeneration imageGeneration = new ImageGeneration();
|
||||
ImageGenerationResult result = null;
|
||||
try {
|
||||
System.out.println("---sync call, please wait a moment----");
|
||||
result = imageGeneration.call(param);
|
||||
} catch (ApiException | NoApiKeyException | UploadFileException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
System.out.println(JsonUtils.toJson(result));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
basicCall();
|
||||
} catch (ApiException | NoApiKeyException | UploadFileException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
> url 有效期24小时,请及时下载图像。
|
||||
|
||||
```
|
||||
{
|
||||
"status_code": 200,
|
||||
"request_id": "50b57166-eaaa-4f17-b1e0-35a5ca88672c",
|
||||
"code": "",
|
||||
"message": "",
|
||||
"output": {
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{
|
||||
"image": "https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/xxx.png?Expires=xxx",
|
||||
"type": "image"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"finished": true
|
||||
},
|
||||
"usage": {
|
||||
"input_tokens": 0,
|
||||
"output_tokens": 0,
|
||||
"image_count": 1,
|
||||
"size": "1280*1280",
|
||||
"total_tokens": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## curl
|
||||
|
||||
**说明**
|
||||
|
||||
- 异步调用必须设置 Header 参数`X-DashScope-Async` 为`enable`。
|
||||
|
||||
- 异步任务的 `task_id` 查询有效期为 24 小时,过期后任务状态将变为 `UNKNOWN`。
|
||||
|
||||
- 适用于所有模型,新手建议使用 [Postman](https://help.aliyun.com/zh/model-studio/first-call-to-image-and-video-api)调用API。
|
||||
|
||||
|
||||
##### **步骤1:发起创建任务请求**
|
||||
|
||||
该请求会返回一个任务ID(`task_id`)。
|
||||
|
||||
```
|
||||
curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/image-generation/generation' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
|
||||
--header 'X-DashScope-Async: enable' \
|
||||
--data '{
|
||||
"model": "wan2.6-t2i",
|
||||
"input": {
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"text": "一间有着精致窗户的花店,漂亮的木质门,摆放着花朵"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"prompt_extend": true,
|
||||
"watermark": false,
|
||||
"n": 1,
|
||||
"negative_prompt": "",
|
||||
"size": "1280*1280"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
##### **步骤2:根据任务ID查询结果**
|
||||
|
||||
使用上一步获取的 `task_id`,通过接口轮询任务状态,直到 `task_status` 变为 SUCCEEDED 或 FAILED。
|
||||
|
||||
将`{task_id}`完整替换为上一步接口返回的`task_id`的值。`task_id`查询有效期为24小时。
|
||||
|
||||
```
|
||||
curl -X GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id} \
|
||||
--header "Authorization: Bearer $DASHSCOPE_API_KEY"
|
||||
```
|
||||
|
||||
## 关键能力
|
||||
|
||||
### **1\. 指令遵循(提示词)**
|
||||
|
||||
参数:`input.prompt`(必选)、`input.negative_prompt`(可选)。
|
||||
|
||||
- **prompt(正向提示词)**:描述希望在画面中看到的内容、主体、场景、风格、光照和构图。文生图的核心控制参数。
|
||||
|
||||
- **negative\_prompt(反向提示词)**:描述不希望在画面中出现的内容,如“模糊”、“多余的手指”等。仅用于辅助优化生成质量。
|
||||
|
||||
|
||||
撰写技巧:一个结构化的 Prompt 通常能带来更好的效果,撰写技巧请参见[文生图Prompt指南](https://help.aliyun.com/zh/model-studio/text-to-image-prompt)。
|
||||
|
||||
### **2\. 开启prompt智能改写**
|
||||
|
||||
参数: `parameters.prompt_extend` (bool, **默认为 true**)。
|
||||
|
||||
此功能可自动扩展和优化**较短的Prompt**,提升出图效果。开启此功能额外耗时 3-5 秒。此耗时为使用大模型改写文本。
|
||||
|
||||
实践建议:
|
||||
|
||||
- 建议开启:当输入 Prompt 较简洁或宽泛时,此功能可显著提升图像效果。
|
||||
|
||||
- 建议关闭:若需控制画面细节、或已提供详细描述,或对响应延迟敏感。请将参数 `prompt_extend` **显式设为** `false`。
|
||||
|
||||
|
||||
### **3\. 设置输出图像分辨率**
|
||||
|
||||
参数: parameters.size (string),格式为 `**"宽*高"**`。
|
||||
|
||||
**qwen-image-2.0 系列**:支持自由设置宽高,输出图像总像素需在512\*512至2048\*2048之间。默认分辨率为2048\*2048。推荐分辨率:
|
||||
|
||||
- `2688*1536` :16:9
|
||||
|
||||
- `1536*2688` :9:16
|
||||
|
||||
- `2048*2048`(默认值):1:1
|
||||
|
||||
- `2368*1728` :4:3
|
||||
|
||||
- `1728*2368` :3:4
|
||||
|
||||
|
||||
**qwen-image-max、qwen-image-plus 系列**:仅支持以下 5 种固定的分辨率:
|
||||
|
||||
- `1664*928`(默认值):16:9
|
||||
|
||||
- `1472*1104`:4:3
|
||||
|
||||
- `1328*1328`:1:1
|
||||
|
||||
- `1104*1472`:3:4
|
||||
|
||||
- `928*1664`:9:16
|
||||
|
||||
|
||||
**万相 V2 版模型 (2.0 及以上版本)**:支持在 `[512, 1440]` 像素范围内任意组合宽高,总像素不超过 1440\*1440。常用分辨率:
|
||||
|
||||
- `1024*1024`(默认值):1:1
|
||||
|
||||
- `1440*810`: 16:9
|
||||
|
||||
- `810*1440`: 9:16
|
||||
|
||||
- `1440*1080`: 4:3
|
||||
|
||||
- `1080*1440`: 3:4
|
||||
|
||||
|
||||
## **应用于生产环境**
|
||||
|
||||
- **容错策略**
|
||||
|
||||
- **处理限流**:当 API 返回 `Throttling` 错误码或 HTTP 429 状态码时,表明已触发限流,限流处理请参见[限流](https://help.aliyun.com/zh/model-studio/rate-limit)。
|
||||
|
||||
- **异步任务轮询**:轮询查询异步任务结果时,建议采用合理的轮询策略(如前30秒每3秒一次,之后拉长间隔),避免因过于频繁的请求而触发限流。为任务设置一个最终超时时间(如 2 分钟),超时后标记为失败。
|
||||
|
||||
- **风险防范**
|
||||
|
||||
- **结果持久化**:API 返回的图片 URL 有 24 小时有效期。生产系统必须在获取 URL 后立即下载图片,并转存至您自己的持久化存储服务中(如阿里云对象存储 OSS)。
|
||||
|
||||
- **内容安全审核**:所有 `prompt` 和 `negative_prompt` 都会经过内容安全审核。若输入内容不合规,请求将被拦截并返回 `DataInspectionFailed` 错误。
|
||||
|
||||
- **生成内容的版权与合规风险**:请确保您的提示词内容符合相关法律法规。生成包含品牌商标、名人肖像、受版权保护的 IP 形象等内容可能涉及侵权风险,请您自行评估并承担相应责任。
|
||||
|
||||
|
||||
## **API文档**
|
||||
|
||||
- [千问 Qwen-Image](https://help.aliyun.com/zh/model-studio/qwen-image-api)
|
||||
|
||||
- [万相-文生图V2](https://help.aliyun.com/zh/model-studio/text-to-image-v2-api-reference)
|
||||
|
||||
|
||||
## **计费与限流**
|
||||
|
||||
- 模型免费额度和计费单价请参见[模型价格](https://help.aliyun.com/zh/model-studio/models#4611ffaa38hnp)。
|
||||
|
||||
- 模型限流请参见[限流-图像生成](https://help.aliyun.com/zh/model-studio/rate-limit#5998fd159df49)。
|
||||
|
||||
- 计费说明:按成功生成的 **图像张数** 计费。模型调用失败或处理错误不产生任何费用,也不消耗[新人免费额度](https://help.aliyun.com/zh/model-studio/new-free-quota)。
|
||||
|
||||
|
||||
## **错误码**
|
||||
|
||||
如果模型调用失败并返回报错信息,请参见[错误信息](https://help.aliyun.com/zh/model-studio/error-code)进行解决。
|
||||
|
||||
## **常见问题**
|
||||
|
||||
**Q: 图片 URL 多久会失效?我应该如何永久保存图片?**
|
||||
|
||||
A: 图片 URL 的有效期为 24 小时。您必须在获取到 URL 后,立即通过程序下载图片,并将其保存到您自己的持久化存储中,例如本地服务器或阿里云对象存储 OSS。
|
||||
|
||||
**Q: 调用API返回DataInspectionFailed错误,如何处理?**
|
||||
|
||||
A: 该错误表示输入文本触发了内容安全审核。请检查并修改prompt或negative\_prompt中的文本,移除可能违规的内容后重试。
|
||||
|
||||
**Q: prompt\_extend参数应该开启还是关闭?**
|
||||
|
||||
A: 当输入的prompt比较简洁或希望模型发挥更多创意时,建议保持开启(默认)。当prompt已经非常详细、专业,或对API响应延迟有严格要求时,建议显式设置为false。
|
||||
|
||||
**Q: 如何提升图像中文字的生成效果?**
|
||||
|
||||
A: 推荐使用`qwen-image-2.0-pro`模型,它具备更专业的文字渲染能力。
|
||||
|
||||
/\*表格图片设置为块元素(独占一行),居中展示,鼠标放在图片上可以点击查看原图\*/ .unionContainer .markdown-body .image.break { margin: 0px; display: inline-block; vertical-align: middle } /\* 让表格显示成类似钉钉文档的分栏卡片 \*/ table.help-table-card td { border: 10px solid #FFF !important; background: #F4F6F9; padding: 16px !important; vertical-align: top; } /\* 减少表格中的代码块 margin,让表格信息显示更紧凑 \*/ .unionContainer .markdown-body table .help-code-block { margin: 0 !important; } /\* 减少表格中的代码块字号,让表格信息显示更紧凑 \*/ .unionContainer .markdown-body .help-code-block pre { font-size: 12px !important; } /\* 减少表格中的代码块字号,让表格信息显示更紧凑 \*/ .unionContainer .markdown-body .help-code-block pre code { font-size: 12px !important; } /\* 表格中的引用上下间距调小,避免内容显示过于稀疏 \*/ .unionContainer .markdown-body table blockquote { margin: 4px 0 0 0; }
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -9,6 +9,7 @@ import ProblemSolving from '../views/ProblemSolving.vue'
|
|||
import QuestionGenerator from '../views/QuestionGenerator.vue'
|
||||
import QuestionVariant from '../views/QuestionVariant.vue'
|
||||
import AudioToText from '../views/AudioToText.vue'
|
||||
import SpeakingEvaluation from '../views/SpeakingEvaluation.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
|
|
@ -63,6 +64,11 @@ const router = createRouter({
|
|||
name: 'audio-to-text',
|
||||
component: AudioToText
|
||||
},
|
||||
{
|
||||
path: '/speaking-evaluation',
|
||||
name: 'speaking-evaluation',
|
||||
component: SpeakingEvaluation
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1028,6 +1028,30 @@ const reset = () => {
|
|||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* result-content 滚动条样式 */
|
||||
.result-content::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.result-content::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.result-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(139, 92, 246, 0.35);
|
||||
border-radius: 3px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.result-content::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(139, 92, 246, 0.55);
|
||||
}
|
||||
|
||||
.result-content::-webkit-scrollbar-thumb:active {
|
||||
background: rgba(139, 92, 246, 0.75);
|
||||
}
|
||||
|
||||
.result-card.is-optimizing {
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
box-shadow: 0 0 20px rgba(16, 185, 129, 0.1);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
import { ref, computed, onUnmounted } from "vue";
|
||||
import { ref, computed, onUnmounted, reactive } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import {
|
||||
DOUBAO_APP_ID,
|
||||
|
|
@ -90,6 +90,65 @@ const completedWords = ref(0);
|
|||
let audioInstance = null;
|
||||
let audioCache = new Map(); // 缓存已生成的音频
|
||||
|
||||
// 长按状态
|
||||
const longPressState = reactive({
|
||||
repeat: false, // 跟读按钮是否正在按住
|
||||
});
|
||||
|
||||
// 录音相关
|
||||
const isRecording = ref(false);
|
||||
let mediaRecorder = null;
|
||||
let recordedChunks = [];
|
||||
|
||||
// 开始录音
|
||||
const startRecording = async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
mediaRecorder = new MediaRecorder(stream);
|
||||
recordedChunks = [];
|
||||
|
||||
mediaRecorder.ondataavailable = (event) => {
|
||||
if (event.data.size > 0) {
|
||||
recordedChunks.push(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
mediaRecorder.onstop = () => {
|
||||
const blob = new Blob(recordedChunks, { type: 'audio/webm' });
|
||||
// 可以在这里处理录音结果,比如播放或上传
|
||||
console.log('录音完成,大小:', blob.size);
|
||||
// 停止所有音频轨道
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
};
|
||||
|
||||
mediaRecorder.start();
|
||||
isRecording.value = true;
|
||||
} catch (err) {
|
||||
console.error('无法访问麦克风:', err);
|
||||
alert('无法访问麦克风,请检查权限设置');
|
||||
}
|
||||
};
|
||||
|
||||
// 停止录音
|
||||
const stopRecording = () => {
|
||||
if (mediaRecorder && isRecording.value) {
|
||||
mediaRecorder.stop();
|
||||
isRecording.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 按住开始(按下即触发)
|
||||
const handlePressStart = () => {
|
||||
longPressState.repeat = true;
|
||||
startRecording();
|
||||
};
|
||||
|
||||
// 按住结束(松开即触发)
|
||||
const handlePressEnd = () => {
|
||||
longPressState.repeat = false;
|
||||
stopRecording();
|
||||
};
|
||||
|
||||
// 进度计算
|
||||
const progress = computed(() => {
|
||||
if (wordList.value.length === 0) return 0;
|
||||
|
|
@ -366,6 +425,10 @@ onUnmounted(() => {
|
|||
// 清理缓存的音频 URL
|
||||
audioCache.forEach((url) => URL.revokeObjectURL(url));
|
||||
audioCache.clear();
|
||||
// 停止录音
|
||||
if (isRecording.value) {
|
||||
stopRecording();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -506,7 +569,10 @@ onUnmounted(() => {
|
|||
|
||||
<!-- 播放中 -->
|
||||
<template v-else-if="dictationState === 'playing'">
|
||||
<button class="control-btn pause-btn" @click="pauseDictation">
|
||||
<button
|
||||
class="control-btn pause-btn"
|
||||
@click="pauseDictation"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
|
|
@ -521,7 +587,16 @@ onUnmounted(() => {
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="control-btn replay-btn" @click="replayCurrentWord">
|
||||
<button
|
||||
class="control-btn replay-btn"
|
||||
:class="{ pressing: longPressState.repeat, recording: isRecording }"
|
||||
@mousedown="handlePressStart"
|
||||
@mouseup="handlePressEnd"
|
||||
@mouseleave="handlePressEnd"
|
||||
@touchstart.prevent="handlePressStart"
|
||||
@touchend="handlePressEnd"
|
||||
@touchcancel="handlePressEnd"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
|
|
@ -532,7 +607,7 @@ onUnmounted(() => {
|
|||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
||||
d="M12 18.75a6 6 0 006-6v-1.5m-6 7.5a6 6 0 01-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 01-3-3V4.5a3 3 0 116 0v8.25a3 3 0 01-3 3z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
|
@ -570,7 +645,16 @@ onUnmounted(() => {
|
|||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="control-btn replay-btn" @click="replayCurrentWord">
|
||||
<button
|
||||
class="control-btn replay-btn"
|
||||
:class="{ pressing: longPressState.repeat, recording: isRecording }"
|
||||
@mousedown="handlePressStart"
|
||||
@mouseup="handlePressEnd"
|
||||
@mouseleave="handlePressEnd"
|
||||
@touchstart.prevent="handlePressStart"
|
||||
@touchend="handlePressEnd"
|
||||
@touchcancel="handlePressEnd"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
|
|
@ -581,7 +665,7 @@ onUnmounted(() => {
|
|||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
||||
d="M12 18.75a6 6 0 006-6v-1.5m-6 7.5a6 6 0 01-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 01-3-3V4.5a3 3 0 116 0v8.25a3 3 0 01-3 3z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
|
@ -1319,6 +1403,60 @@ onUnmounted(() => {
|
|||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* 长按状态 */
|
||||
.control-btn.pressing {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.replay-btn.pressing {
|
||||
background: linear-gradient(135deg, var(--accent-5), #d97706);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
|
||||
/* 录音中状态 */
|
||||
.replay-btn.recording {
|
||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4);
|
||||
animation: pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -50%) scale(0.8);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 6px 25px rgba(239, 68, 68, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.content-grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
|
|
|||
|
|
@ -17,10 +17,6 @@ export default defineConfig({
|
|||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
headers: {
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||
},
|
||||
proxy: {
|
||||
'/tts-api': {
|
||||
target: 'https://openspeech.bytedance.com',
|
||||
|
|
@ -51,6 +47,29 @@ export default defineConfig({
|
|||
proxyReq.setHeader('X-Api-Connect-Id', crypto.randomUUID())
|
||||
})
|
||||
}
|
||||
},
|
||||
// 阿里云OSS音频代理:解决COEP策略问题(支持多个OSS域名)
|
||||
'/oss-audio': {
|
||||
target: 'https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/oss-audio/, ''),
|
||||
configure: (proxy) => {
|
||||
proxy.on('proxyRes', (proxyRes) => {
|
||||
// 添加CORS头以符合COEP要求
|
||||
proxyRes.headers['access-control-allow-origin'] = '*'
|
||||
})
|
||||
}
|
||||
},
|
||||
// 阿里云OSS图片代理(支持多个OSS域名)
|
||||
'/oss-image': {
|
||||
target: 'https://dashscope-7c2c.oss-accelerate.aliyuncs.com',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/oss-image/, ''),
|
||||
configure: (proxy) => {
|
||||
proxy.on('proxyRes', (proxyRes) => {
|
||||
proxyRes.headers['access-control-allow-origin'] = '*'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue