营销智能体(Marketing Agent)
功能概述
营销智能体是系统两大创作模式之一(另一为短剧模式),面向营销内容创作场景。它提供对话式交互能力,集成 LLM 对话、图片生成、视频生成三大核心功能,帮助用户创作带货脚本、广告创意、产品文案等营销内容。
双模式系统
用户首次进入系统时通过模式选择弹窗选择创作模式,选择结果保存在 localStorage 的 creation_mode 字段中(short_drama 或 marketing)。两种模式共享后端基础设施,但前端入口和交互风格不同:
| 模式 | 入口 | 主题 | 适用场景 |
|---|---|---|---|
| 短剧模式 | /video-workflow-list | 深色主题 | AI 短剧创作全流程 |
| 营销模式 | /marketing-inspiration(首页"开始创作"落地页)→ /marketing-agent(生成对话页) | 浅色主题(白底蓝调) | 营销内容创作 |
模式切换入口位于 web/index.html 的模式选择弹窗,selectCreationMode(mode) 方法仅保存模式状态,不自动跳转。
页面架构
技术栈
- 框架:Vue 3(CDN 引入
vue.global.prod.js,使用 Composition API) - HTTP 客户端:axios(
axios.min.js) - Markdown 渲染:marked.js(
marked.min.js) - 代码高亮:highlight.js(
github.min.css主题) - 任务配置:
task_config.js(统一任务类型/模型/算力配置模块) - 视频压缩:
video_compressor.js(Canvas + MediaRecorder 方案,480p 压缩) - 国际化:
i18n-core.js+i18n-vue-plugin.js+i18n-dom.js
三栏布局
页面为 Vue 3 单页应用(SPA),采用三栏布局,整体高度 100vh:
+--------+------------------+--------------------------------+
| 左侧 | 左侧边栏 | 主内容区 |
| 窄导航 | | |
| (64px) | (260px) | (flex: 1) |
+--------+------------------+--------------------------------+
| 区块 | 类名 | 宽度 | 说明 |
|---|---|---|---|
| 左侧窄导航 | .marketing-nav | 64px | Logo、灵感、生成(高亮)、资产 |
| 左侧边栏 | .sidebar | 260px | 新对话按钮、搜索框、最近对话列表、用户信息 |
| 主内容区 | .main-content | flex: 1 | 顶部栏 + 消息流 + 底部输入区 |
响应式:768px 以下侧边栏变为固定定位的抽屉,通过 .sidebar.open 类控制显示。
组件结构
页面内所有 UI 均在单个 Vue 3 createApp() 实例中实现,无外部组件拆分。主要 UI 区块:
- 顶部栏 (
.top-bar):当前日期、搜索框、算力余额显示 - 消息流 (
.chat-messages):欢迎卡片、用户/AI 消息气泡、打字指示器、继续按钮 - 底部输入区 (
.input-area):文件上传按钮、文本输入框、媒体缩略图条、类型选择、模型选择、比例/分辨率选择、发送按钮 - 弹窗层:联系反馈弹窗、算力日志弹窗(iframe)、算力充值弹窗、图片放大模态框
- 资产库视图 (
.asset-library):通过左侧导航切换activeView为assets显示,展示用户历史生成结果(图片/视频),每页 60 条,支持分页。图片资产支持"生成视频"操作(useAssetForVideo),会将图片带入生成页输入区并自动切换到视频模式。图片放大弹窗中的"生成视频"按钮(imageToVideo)同样会自动切换回生成视图。
核心功能详解
三种创作类型
底部输入栏的类型选择器提供三种创作模式:
| 类型 | key | 图标 | 说明 |
|---|---|---|---|
| Agent 模式 | agent | ✨ | LLM 对话驱动,可调用图片/视频生成工具,支持多模态输入 |
| 图片生成 | image | 🖼️ | 直接调用文生图/图生图 API,轮询获取结果 |
| 视频生成 | video | 📹 | 直接调用文生视频/图生视频 API,轮询获取结果 |
Agent 模式为默认推荐模式,走 LLM 对话流程,后端 PM Agent 可自主调用图片/视频生成工具。图片/视频模式为直接生成模式,前端直接调用生成 API 并轮询状态。
会话管理
创建会话
- 调用
POST /api/session/create,传入user_id、world_id、auth_token、session_type: 2(营销模式标识) - 营销智能体使用固定
world_id = '1',无需多世界概念 - 创建成功后将新会话加入本地
sessions列表头部并持久化到localStorage(marketing_sessions键)
搜索
侧边栏搜索框通过 v-model 绑定 searchQuery,filteredSessions 计算属性对会话标题进行大小写不敏感的模糊过滤。
重命名
- 悬浮会话项显示
···按钮,点击弹出上下文菜单 - 选择"重命名"后,会话标题变为内联输入框(
.history-rename-input) - 支持 Enter 确认、Escape 取消、失焦自动确认
- 调用
PUT /api/session/{session_id}/title保存到后端
删除
- 调用
DELETE /api/session/{session_id}进行软删除 - 至少保留一个会话,删除最后一个时提示
keep_one_session - 删除当前活跃会话后自动切换到列表中的第一个会话
加载会话列表
页面初始化时调用 GET /api/sessions?user_id=xxx&world_id=1&session_type=2&limit=50 从后端加载,失败时回退到 localStorage 缓存。
消息交互
消息类型
| 角色 | 头像 | 气泡样式 | 说明 |
|---|---|---|---|
user | 👤 | 蓝色背景,右对齐 | 用户输入 |
ai | 🤖 | 白色背景,左对齐 | AI 回复 |
system | - | 居中标签 | 任务状态标签(已完成等) |
多模态消息
- 用户上传的图片在消息气泡中以
<img>标签渲染,最大高度 160px,点击可放大 - 视频以
<video>标签渲染,支持 controls - 音频以
<audio>标签渲染 - 历史消息中的多模态内容(JSON 数组格式的
image_url+text)在parseHistoryMessage中解析为文本+图片分别渲染
Markdown 渲染
renderMarkdown(text) 函数的处理流程:
- 保护已有的 Markdown 图片语法
!alt和链接语法text(占位符替换) - 将裸图片 URL(以
.png/.jpg/.gif/.webp等结尾)自动转换为 Markdown 图片格式 - 处理 URL 后紧跟中文等非空白字符的边界问题
- 恢复占位符
- 调用
marked.parse()解析为 HTML - 为所有
<img>标签添加点击放大事件和样式
Verification 交互(ask_user)
当后端 Agent 通过 ask_user 工具向用户提问时,SSE 流会推送 human_verification_required 事件:
- 前端解析
verification对象,提取title、description、options - 渲染为特殊消息气泡,包含预设选项按钮和"其他"自由输入按钮
- 用户选择后调用
POST /api/verification/{verification_id}提交回答 - 验证期间(
pendingVerificationId不为 null)禁止正常发送消息
切换会话或刷新页面后,前端会从会话历史中查找最后一个未被验证回答覆盖、且对应验证记录状态仍为 pending 的验证请求,并恢复 pendingVerificationId。只有 verificationId 等于当前 pendingVerificationId 的问题卡片可点击,历史中的旧问题保持禁用,避免误把旧选项提交到当前等待项。主输入框在等待用户回答时仍可提交自定义答案,但不会发送新的普通对话消息。
如果用户切换会话期间 ask_user 已超时,后端会在历史接口中为该验证项返回 verification_status='cancelled'。前端加载到非 pending 状态时不会恢复等待状态,因此输入框和发送按钮会恢复为普通对话模式;超时后的历史问题仍可展示,但不再阻塞用户继续输入。
SSE 流式响应
连接建立
Agent 模式下发送消息的完整流程:
POST /api/session/{session_id}/task创建任务,获取task_idEventSource连接到/api/task/{task_id}/stream- 超时设置为 900 秒(15 分钟)
事件类型
| 事件类型 | 处理逻辑 |
|---|---|
message | 追加文本到当前 AI 消息气泡 |
status | 忽略 |
progress | 忽略 |
human_verification_required | 触发 Verification 交互流程 |
verification_timeout | 清除验证状态,提示超时 |
context_compression | 忽略 |
image_task_submitted | 前端开始轮询图片生成状态 |
video_task_submitted | 前端开始轮询视频生成状态 |
error | 关闭 SSE,显示错误消息 |
done | 关闭 SSE,显示"继续"按钮,刷新算力余额 |
去重与续传
/api/task/{task_id}/stream会为数据库中的任务消息输出 SSEid,取值为相关数据表的主键。- 浏览器原生重连时,后端会读取
Last-Event-ID;手动恢复连接时也可通过last_id查询参数指定已处理的最后一条消息。 - 前端维护已处理的 SSE 消息 id 集合,重复 id 会被跳过,避免重连重放时把同一条完整
message再次拼接到 AI 气泡中。 - 当页面或会话切换后恢复活跃 Agent 任务时,前端还会对比当前历史中已展示的 assistant 内容;如果恢复流重放了同样的完整
message,会跳过该消息,避免"历史气泡 + 恢复流气泡"重复显示。 - Agent SSE 文本回复的持久化以任务完成回调中的
session_storage.save_session()为准;前端收到done后不再调用/session/{session_id}/message追加同一段 assistant 文本,避免"后端保存 + 前端追加"产生相邻重复历史。 - Agent 模式下用户消息的持久化以后端增强版为准:前端发送时只在当前页面即时展示用户气泡,不再调用
/session/{session_id}/message保存展示版;任务完成后 PM Agent 保存包含媒体 URL 标签和用户偏好的增强版 user 消息。历史加载时前端会把[用户图片偏好]、[用户视频偏好]折叠为"查看发送偏好",避免同一用户输入以 UI 展示版和 LLM 增强版各保存一次。 /session/{session_id}/message也会跳过与最后一条历史记录 role 和 content 都相同的消息,作为其他追加路径的兜底保护。call_agent成功返回的专家结果会在_handle_agent_call()阶段作为 SSEmessage推送给前端,确保"图片内容分析"等专家输出立即可见;结果仍会进入 PM 上下文和会话历史,若 PM 后续 assistant 回复复述同一段内容,前端会通过内容去重跳过重复气泡。handleStream在建立连接时捕获发起会话streamSessionId,并在onmessage顶部校验:若用户已切换到其他会话,残留的 SSE 事件(message/image_task_submitted/video_task_submitted/verification_timeout等)会被丢弃并关闭旧连接,避免把上一会话的 AI 回复、任务结果或验证提示串到当前对话。切回原会话时由会话恢复机制重新建立 SSE。
任务状态轮询
Agent 提交图片/视频生成任务后,前端通过 setInterval 轮询 GET /api/get-status/{project_ids}:
- 图片轮询间隔:5 秒(启动后立即查询一次,不必等待第一个周期)
- 视频轮询间隔:10 秒(启动后立即查询一次)
- 任务完成后将结果(图片/视频 URL)渲染到消息气泡中
- 使用
sessionTaskRegistry注册表跟踪所有活跃任务,支持会话切换后恢复 - 直接图片/视频生成使用
directGenerationTasks为每次提交创建独立任务实例,实例内绑定type、project_ids、msgUid、sessionId和intervalId。轮询只读取实例内的projectIds,避免多轮对话中后提交的图片任务覆盖先提交的视频任务。 - 直接生成任务完成后优先调用
replacePendingTask(sessionId, task_type, project_ids, content)精确替换原__PENDING_TASK__历史行;只有匹配失败时才 fallback 追加 assistant 消息,且追加到任务所属的原 session。 - 生成结果统一通过媒体类型过滤后渲染:图片结果会排除
.mp4/.webm/.mov/.avi/.mkv,视频结果会排除图片扩展名,避免视频 URL 被<img class="generated-image">包裹。 GET /api/get-status/{project_ids}对 CDN 结果采用 CDN 优先策略;如果生成任务已完成但 CDN 仍处于 pending,会先返回本地result_url作为file_url,并附带cdn_status: "pending",避免聊天框一直显示生成中- 前端轮询统一识别
SUCCESS/COMPLETED/DONE等完成状态,并从 task 本身和results[]中提取file_url、result_url、video_url、image_url、output_url、download_url等字段 - 如果
video_task_submittedSSE 事件缺失,但专家工作总结中包含project_ids: [...]且文本语义为视频任务,前端会自动补启动视频轮询;加载历史会话时也会执行同样的兜底恢复 - 如果
image_task_submittedSSE 事件缺失,但专家回复中包含project_ids: [...]或"项目ID: 744"这类图片任务标识,前端会自动补启动图片轮询;图片轮询会绑定触发时的会话,避免切换会话后把结果写入当前对话 - 文本兜底恢复会优先识别视频摘要,包含"视频参数""图生视频""文生视频""时长""first_last_frame""multi_reference"等视频信号的工作总结不会再触发图片轮询,避免"图片模式 first_last_frame"被误判为图片生成任务
- Agent 图片/视频轮询启动后均立即查询一次状态,已完成的任务可直接渲染结果,不必等待第一个轮询周期
- 视频兜底恢复按会话绑定轮询结果,异步返回时如果用户已切换到其他会话,不会把结果写入当前对话;历史加载时会过滤空 AI 气泡和重复的视频结果,避免切换会话后重复显示
- 直接发起模式(
checkDirectGenerationStatus)的轮询在每次await获取状态返回后,会再次校验currentSessionId是否仍等于任务实例的task.sessionId:任务进行中切换会话时,旧轮询回调立即停止并清理,不再向当前(已切换的)messages或全局imageResults/videoResults写入,杜绝"视频生成失败/图片生成失败"提示串到其他对话 handleImageTaskSubmitted/handleVideoTaskSubmitted支持显式explicitSessionId入参;由 SSE 流派发任务时传入该流的streamSessionId,使轮询与结果持久化归属发起会话,而非事件被处理时的"当前会话"
任务恢复机制
页面刷新或切换会话时,系统通过以下机制恢复未完成的任务:
__PENDING_TASK__标记:后端在相关数据表中保存__PENDING_TASK__:{type}:{project_ids}标记(message_type='pending_task')recoverPendingTasks():加载历史消息后检测标记,轮询任务状态;完成时优先替换 pending 消息(通过PUT /session/{id}/message/{message_id}),替换失败则 fallback 追加新消息sessionActiveTaskId注册表:记录每个 session 的活跃 Agent task_id,切换回来时检查任务状态并重连 SSEdirectGenerationTasks/ Agent 轮询注册表:直接生成任务在内存中按任务实例跟踪 project_ids;Agent 图片/视频轮询按sessionId:type:project_ids去重,支持会话内恢复和并发任务隔离- 并发安全:
clean-pending-tasks支持按task_type+project_ids精确清理,并发任务互不影响。轮询和recoverPendingTasks(sessionId)的 fallback 追加也必须显式传入原始 sessionId,避免用户切换会话后把上一会话的图片/视频结果写入当前会话。
模型选择
图片模型
通过 TaskConfig.getModelOptionsForCategory('text_to_image') 动态加载,默认优先选择默认生图模型。用户选择保存到 localStorage(marketing_selected_image_model 键),并通过 POST /api/text-to-image-model 同步到后端相关数据表。
视频模型
- 文生视频:
TaskConfig.getModelOptionsForCategory('text_to_video') - 图生视频:
TaskConfig.getModelOptionsForCategory('image_to_video'),根据videoImageMode过滤支持的模型 - 模型偏好从后端
GET /api/video-model获取,回退到localStorage
模型选择状态
页面 UI 仍保持一个可见的模型选择器,但内部状态已拆分为图片模型和视频模型两套。selectedImageModel / selectedImageModelKey 保存生图选择,selectedVideoModelName / selectedVideoModelKey 保存生视频选择,selectedModel / selectedModelKey 仅作为当前模式的可写计算属性供模板和发送逻辑读取。用户在 Agent 模式下切换"生图 / 生视频"时,不会再用视频模型覆盖图片模型;image_preferences.model_name 始终取图片模型,video_preferences.model_name 始终取视频模型。
LLM 模型(Agent 模式)
调用 GET /api/models 加载所有可用 LLM 模型,支持 VL(视觉理解)和 Thinking(深度思考)标签。同名模型可能来自不同供应商,前端使用 model_id + vendor_id 作为唯一选择键,避免同名模型同时高亮。默认大语言模型优先路由到默认第三方大语言模型服务,该服务未配置时回退到 API 聚合服务或其他供应商。用户选择保存到 localStorage(marketing_selected_llm_model_id + marketing_selected_llm_vendor_id 键),发送消息时通过 model、model_id、vendor_id 字段传给后端。
图像比例选择
比例选项通过 TaskConfig.getRatioOptions(modelKey) 动态获取,支持的比例如下(定义在 aspectRatioMap 中):
| 比例 | 可视化尺寸 |
|---|---|
| auto(智能) | 20x20 |
| 21:9 | 28x12 |
| 16:9 | 24x14 |
| 3:2 | 22x15 |
| 4:3 | 20x15 |
| 1:1 | 18x18 |
| 3:4 | 15x20 |
| 2:3 | 14x22 |
| 9:16 | 12x24 |
用户选择保存到 localStorage(marketing_selected_ratio 键),并通过 POST /api/text-to-image-model 同步到后端。全新用户没有保存偏好时,前端按当前生图模型的 default_ratio 初始化;默认生图模型使用 9:16 竖屏比例。
分辨率选择
图片模式下显示分辨率选项,通过 TaskConfig.getSizeOptions(modelKey) 动态获取。选项前自动添加 auto(自动根据模型选择最佳分辨率)。分辨率映射:1K -> 1K,2K -> 高清 2K,4K -> 超清 4K。
视频时长和生成方式
视频时长
通过 TaskConfig 的 supported_durations 动态获取,默认选项 ['auto', 3, 5, 8, 10, 15]。
生成方式(videoImageMode)
视频模式下有图片时可选择:
| 模式 | key | 说明 |
|---|---|---|
| 首尾帧 | first_last_frame | 第一张为首帧,第二张为尾帧(可选) |
| 全能参考 | multi_reference | 所有图片作为风格参考,最多 5 张 |
文件上传
Agent 对话模式
支持同时上传多种媒体文件:
| 类型 | 最大数量 | 大小限制 | 时长限制 | 说明 |
|---|---|---|---|---|
| 图片 | 9 张 | 10MB(可配置) | - | 上传到 /api/upload-agent-image 获取 HTTP URL |
| 视频 | 3 个 | 50MB(可配置) | 15 秒(可配置) | 前端压缩到 480p 后上传到 /api/upload-agent-video |
| 音频 | 5 个 | 20MB | 15 秒(可配置) | 上传到 /api/upload-agent-audio |
Agent 视频模式
支持上传参考图(首尾帧/全能参考)、参考视频和参考音频。图片上传到 /api/upload-agent-image,通过 image_urls 字段传给后端。
Agent 视频模式下,主图和后续参考图都会等待上传完成并转换为 HTTP URL;发送给后端时按输入区显示顺序去重收集到 image_urls,避免 blob 预览地址或未上传完成的参考图遗漏。
当前页面即时渲染的用户气泡统一通过 collectCurrentMessageMedia() 收集媒体,再由 buildUserMessageContent() 渲染;图片、视频、音频模式和 Agent 模式不再分别拼接预览 HTML。Agent 发送给后端的 image_urls、video_urls、audio_urls 也复用同一媒体集合,确保即时显示、请求 payload 和后端保存的增强版用户消息尽量一致。
纯视频参考文件上传到 /api/upload-agent-video,只进入 video_urls 流程,不会设置图片上传状态,也不会触发图片 HTTP URL 等待。
会话历史从后端相关数据表恢复时依赖媒体标签重新渲染图片、视频和音频。/api/session/{session_id}/task 写入用户消息时必须把上传接口返回的 URL 拼入 [图片N](URL: ... thumb: ...)、[视频N](URL: ...)、[音频N](URL: ...) 标签后再落库;上传接口已经根据 server.is_local 决定返回本地 URL 还是 CDN URL,历史持久化层只保存返回值,不重新判断或改写 CDN。
生成状态轮询和 Agent SSE 流使用连续失败保护。图片/视频状态轮询、Agent 图片/视频轮询,以及 SSE 连接错误/超时在同一任务上连续失败 3 次后,会停止对应定时器或断开流,并清理活跃任务归属,避免对话区持续追加"请求超时,请稍后重试"之类的错误气泡。
图片/视频模式
图片直接通过 FormData 上传到 /api/text-to-image 或 /api/image-edit。视频上传到 /api/ai-app-run-image(图生视频)或 /api/ai-app-run(文生视频)。
视频压缩
使用 VIDEO_COMPRESSOR 模块(Canvas + MediaRecorder 方案):
- 目标短边:480px
- 帧率:30fps
- 视频码率:1.5Mbps
- 压缩阈值:文件 > 10MB 或短边 > 480px
- 支持 WebM (VP9/VP8) 和 MP4 格式
- 超过最大时长的视频自动截断
@ 引用系统
输入框支持 @ 触发媒体引用下拉框,列出已上传的媒体文件。选中后插入引用标签到文本中,支持模糊搜索过滤。mentionDropdown 状态管理下拉框的显示、查询和选中索引。
算力显示
余额显示
顶部栏右侧显示算力余额,每 30-45 秒随机间隔自动刷新(startComputingPowerRefresh)。余额根据数值自动变色:
| 余额范围 | CSS 类 | 颜色 |
|---|---|---|
| < 100 | .low-power | 红色 |
| 100-999 | .medium-power | 黄色 |
| >= 1000 | .high-power | 绿色 |
点击余额打开算力日志弹窗(iframe 加载 /computing_power_logs.html)。
消耗预估
输入区右侧根据当前选中模型和参数实时计算本次操作的算力消耗,使用 TaskConfig.getComputingPower() 方法。
算力充值
算力日志弹窗中点击"算力充值"按钮打开充值弹窗:
- 调用
GET /api/recharge/packages加载套餐列表 - 用户选择套餐后调用
POST /api/recharge/wechat-pay创建支付订单 - 通过第三方 QR 码服务生成微信支付二维码
- 支持自动检测到账
i18n 国际化
使用自研 i18n 框架(i18n-core.js),支持:
- Vue 模板中的
$t('key')翻译 - JS 代码中的
window.t('key')翻译 - 带参数的翻译:
$t('duration_seconds', { dur: 5 })->5秒 - DOM 属性的
data-i18n自动翻译 - 语言切换器组件(
i18n-switcher.js)
翻译文件位于 web/i18n/locales/zh-CN/marketing_agent.json,共约 220 个翻译键,覆盖页面标题、导航、侧边栏、输入区、模型选择、错误提示等所有用户可见文本。
API 接口列表
会话管理
| 端点 | 方法 | 说明 | 请求参数 |
|---|---|---|---|
/api/session/create | POST | 创建新会话 | { user_id, world_id, auth_token, session_type: 2 } |
/api/sessions | GET | 获取会话列表 | ?user_id=&world_id=&session_type=2&limit=50 |
/api/session/{session_id}/history | GET | 获取会话历史 | Headers: Authorization, X-User-Id |
/api/session/{session_id}/message | POST | 追加消息到会话 | { role, content } |
/api/session/{session_id}/title | PUT | 更新会话标题 | { title } |
/api/session/{session_id} | DELETE | 删除会话(软删除) | - |
/api/session/{session_id}/latest-task | GET | 获取最新活跃任务 | - |
/api/session/{session_id}/clean-pending-tasks | POST | 精确清理 pending task 标记 | { task_type?, project_ids? }(不传则清理全部) |
/api/session/{session_id}/message/{message_id} | PUT | 更新消息内容(pending→结果替换) | { content, message_type? } |
智能体任务
| 端点 | 方法 | 说明 |
|---|---|---|
/api/session/{session_id}/task | POST | 创建 Agent 任务 |
/api/task/{task_id}/stream | GET (SSE) | 流式获取任务消息 |
/api/task/{task_id}/status | GET | 查询任务状态 |
/api/verification/{verification_id} | POST | 提交人工验证回答 |
任务创建请求体
{
"message": "用户输入文本",
"auth_token": "认证令牌",
"image_urls": ["http://..."],
"video_urls": ["http://..."],
"audio_urls": ["http://..."],
"model": "LLM模型名称",
"model_id": 1,
"vendor_id": 1,
"image_preferences": {
"ratio": "9:16",
"model_name": "第三方图像生成模型",
"resolution": "2K"
},
"video_preferences": {
"ratio": "16:9",
"duration": 5,
"image_mode": "first_last_frame",
"model_name": "第三方视频生成模型"
}
}
Agent 模式下,image_preferences.ratio、image_preferences.resolution 会在创建任务时同步写入后端图片偏好,后续专家调用 generate_text_to_image / edit_image 工具时会由工具层强制应用当前偏好。ratio: "auto" 也会被保存,表示本次任务不强制覆盖专家或模型默认比例。
媒体上传
| 端点 | 方法 | 说明 |
|---|---|---|
/api/upload-agent-image | POST | 上传 Agent 图片,返回 HTTP URL |
/api/upload-agent-video | POST | 上传 Agent 视频 |
/api/upload-agent-audio | POST | 上传 Agent 音频 |
生成任务
| 端点 | 方法 | 说明 |
|---|---|---|
/api/text-to-image | POST | 文生图 |
/api/image-edit | POST | 图生图/图片编辑 |
/api/ai-app-run | POST | 文生视频 |
/api/ai-app-run-image | POST | 图生视频 |
/api/get-status/{project_ids} | GET | 查询生成任务状态 |
模型和配置
| 端点 | 方法 | 说明 |
|---|---|---|
/api/system/task-configs | GET | 获取任务配置(模型/算力/比例等) |
/api/system/server-config | GET | 获取服务器配置(文件大小限制等) |
/api/models | GET | 获取可用 LLM 模型列表 |
/api/video-model | GET | 获取用户视频模型偏好 |
/api/text-to-image-model | POST | 同步图片模型偏好到后端 |
用户和算力
| 端点 | 方法 | 说明 |
|---|---|---|
/api/user/computing_power | GET | 查询算力余额 |
/api/user/computing_power_logs | GET | 查询算力日志 |
/api/recharge/packages | GET | 获取充值套餐列表 |
/api/recharge/wechat-pay | POST | 创建微信支付订单 |
认证与登录跳转
登录状态检查
页面初始化时检查 localStorage 中的 user_id 和 auth_token,如果缺失则自动跳转到登录页面。
401 自动跳转
所有带 Authorization 头的 API 请求均通过 checkAuthResponse() 函数统一检查 HTTP 状态码。当后端返回 401(未授权/token 过期)时,自动跳转到登录页面并携带当前页面路径,登录成功后可跳回原页面。
跳转目标格式:/index.html?redirect_url={当前页面路径}
index.html 已内置 redirect_url 参数支持:
- 解析
redirect_url参数并保存到localStorage的redirect_after_login - 未登录时自动弹出登录窗口
- 登录成功后检查
redirect_after_login,存在则跳转到指定路径
世界观
| 端点 | 方法 | 说明 |
|---|---|---|
/api/worlds | GET | 获取世界列表 |
/api/worlds | POST | 创建世界 |
前端依赖库
| 文件 | 用途 |
|---|---|
web/js/vue.global.prod.js | Vue 3 生产版 |
web/js/axios.min.js | HTTP 客户端 |
web/js/marked.min.js | Markdown 解析器 |
web/js/task_config.js | 任务配置统一管理模块 |
web/js/video_compressor.js | 前端视频压缩模块 |
web/css/github.min.css | 代码高亮主题 |
web/i18n/i18n-core.js | i18n 核心模块 |
web/i18n/i18n-vue-plugin.js | i18n Vue 插件 |
web/i18n/i18n-dom.js | i18n DOM 翻译 |
web/i18n/i18n-switcher.js | 语言切换器组件 |
配置项
localStorage 键
| 键名 | 用途 | 示例值 |
|---|---|---|
creation_mode | 当前创作模式 | marketing |
marketing_sessions | 本地会话历史缓存 | JSON 数组 |
marketing_selected_image_model | 用户选择的图片模型名称 | 第三方图像生成模型 |
marketing_selected_image_model_key | 用户选择的图片模型 key | 模型标识 |
marketing_selected_t2v_model | 文生视频模型名称 | 第三方视频生成模型 |
marketing_selected_i2v_model | 图生视频模型名称 | 第三方视频生成模型 |
marketing_selected_ratio | 用户选择的比例 | 9:16 |
marketing_selected_llm_model_id | LLM 模型 ID | 1 |
marketing_selected_llm_vendor_id | LLM 供应商 ID | 1 |
marketing_media_type | Agent 模式的生成偏好 | image / video |
auth_token | 认证令牌 | JWT token |
user_id | 用户 ID | 数字字符串 |
phone | 用户手机号 | 手机号 |
服务端配置(/api/system/server-config)
| 字段 | 默认值 | 说明 |
|---|---|---|
max_image_size_mb | 10 | 图片上传大小限制(MB) |
max_video_size_mb | 50 | 视频上传大小限制(MB) |
max_video_duration_seconds | 15 | 视频/音频最大时长(秒) |
前端常量
| 常量 | 值 | 说明 |
|---|---|---|
AGENT_IMAGE_MAX_COUNT | 9 | Agent 模式最大图片数 |
AGENT_VIDEO_MAX_COUNT | 3 | Agent 模式最大视频数 |
AGENT_AUDIO_MAX_COUNT | 5 | Agent 模式最大音频数 |
VIDEO_COMPRESSOR.TARGET_SHORT_EDGE | 480 | 视频压缩目标短边(px) |
VIDEO_COMPRESSOR.FPS | 30 | 视频压缩帧率 |
VIDEO_COMPRESSOR.VIDEO_BITRATE | 1500000 | 视频压缩码率(bps) |
样式与主题
主题色变量(CSS 自定义属性)
定义于 web/css/marketing_agent.css 的 :root 中:
--bg-primary: #f5f5f5; /* 主背景 */
--bg-secondary: #ffffff; /* 卡片/输入框背景 */
--bg-sidebar: #fafafa; /* 侧边栏背景 */
--accent-color: #00a8e6; /* 主题色(亮蓝) */
--accent-hover: #0095cc; /* 主题色悬停 */
--accent-light: #e6f7ff; /* 主题色浅底 */
--user-bubble: #e6f7ff; /* 用户气泡背景 */
--ai-bubble: #ffffff; /* AI 气泡背景 */
--text-primary: #1a1a1a; /* 主文本 */
--text-secondary: #666666; /* 次要文本 */
--text-muted: #999999; /* 弱化文本 */
--border-color: #e8e8e8; /* 边框 */
--shadow: 0 2px 8px rgba(0,0,0,0.06);
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--header-height: 56px;
--sidebar-width: 260px;
下拉菜单和弹窗
所有下拉菜单(类型选择、模型选择、比例选择等)采用统一的向上弹出定位(position: absolute; bottom: 100%),带 fadeInUp 动画。大面板弹窗使用 box-shadow: 0 -8px 32px 阴影。
与其他模块的关系
世界观系统
营销智能体使用固定 world_id = '1',复用 script_writer 的世界观基础设施。会话通过 session_type: 2 区分营销模式和短剧模式(session_type: 1)。
算力系统
- 模型选择时通过
TaskConfig.getComputingPower()实时预估消耗 - 发送消息时后端扣减算力,SSE 流结束后前端刷新余额
- 算力不足时后端返回错误,前端提示充值
PM Agent 后端
Agent 模式的消息通过 PM Agent(pm_agent.py)处理:
- PM Agent 根据用户意图委托专家(如
marketing-image专家生图) - 专家返回结果后,PM Agent 自动提取图片 URL 并注入对话历史(多模态消息)
- PM Agent 通过
call_agent委托专家时会透传本次任务的image_urls、audio_urls、video_urls;专家会以[图片N]、[音频N]、[视频N]标签注入上下文,避免数字人等任务只知道"用户已提供音频"但拿不到真实 URL - Agent 前端发送任务前会等待音频上传完成,并只把真实 HTTP 音频 URL 写入
audio_urls;上传完成后会同步更新mediaItems.serverUrl和mediaItems.fileUrl - 数字人任务提交音频前会检查音频地址:
localhost、内网地址和本地文件路径会先上传到视频生成服务的文件存储,并改用返回的文件标识;已是视频生成服务标准格式的文件名会直接复用,公网 URL 保持原值 - 通过
ask_user工具实现向用户提问的交互 - 专家通过
script_writer_core/config/agents_config.json中的expert_type声明所属模式;剧本 PM 只允许script类型专家,营销 PM 只允许marketing类型专家 - 可调用专家列表会按当前 PM 的
allowed_expert_types动态过滤,后端也会做校验,避免营销模式误调用剧本专家或反向串线 - 营销视频克隆必须走
sop-video-clone并委托marketing-video;数字人口播必须走sop-digital-human并委托digital-human-creator,且数字人专家必须实际调用generate_digital_human并返回非空project_ids才算提交成功 - 数字人口播缺少用户音频时,专家应调用通用
generate_reference_audio生成参考音频;数字人专家不暴露角色耦合的generate_character_reference_audio
用户偏好同步
用户在前端选择的图片模型、比例、分辨率等偏好通过 API 同步到后端相关数据表,确保跨设备/会话的一致性。
Agent 对话任务还会在 /api/session/{session_id}/task 入口同步本次请求携带的 image_preferences,避免只把"9:16"等偏好写入提示词、但专家实际调用生图工具时仍读取旧偏好。LLM 调用日志会输出 Agent 与 Agent scope 字段,可在 llm.log 中按 Agent scope: expert 或具体专家 ID 筛选专家智能体;第三方大语言模型接口返回的 reasoning_content(思考内容)会按日志截断规则记录正文,便于排查专家决策过程。
灵感发布页(Inspiration)
灵感页(/marketing-inspiration,由 web/marketing_inspiration.html + web/js/marketing_inspiration.js + web/css/marketing_inspiration.css 实现)展示已审核通过的公开作品(存于相关数据表),支持瀑布流浏览、Lightbox 详情、"做同款/用作参考图"(携带参数跳转到生成页),以及上传参考图直接发起 Agent 创作。
首页(web/index.html)在营销模式下点击"开始创作"横幅(handleStartCreation)即跳转到本页(/marketing-inspiration?user_id=...),灵感页左侧导航再进入生成对话页/marketing-agent。
发布与资产固化
用户在生成页将生成记录"发布为灵感"时,POST /api/marketing-publications 触发资产固化服务执行 promote_assets:
- 将生成结果、参考图、参考音视频从临时缓存(
/upload/cache/...、/upload/temp/...)复制到长期目录/upload/marketing_publications/{publication_id}/,避免缓存过期导致链接失效。 - 为每个固化文件在相关数据表中创建一条记录(按 label 区分生成结果/参考图/参考音频/参考视频,采用永不过期的存储策略)。
- 发布时写入数据库的仍是本地路径(指向
/upload/marketing_publications/...)。
CDN 透明加速
灵感页图片/视频统一通过 server.py 的 cdn_redirect_middleware 提供 CDN 访问,前端无需感知 URL 变化:
- 浏览器请求任意
/upload/*.{媒体扩展名}时,中间件按本地路径指纹查相关数据表;若记录已有云存储路径,则 302 重定向到新鲜的对象存储签名 URL(28 小时有效),否则直接返回本地文件。 promote_assets在创建映射后,通过守护线程 fire-and-forget 调用CDNUtil.trigger_cdn_upload触发异步上传。因发布接口为 async 端点,此处用独立守护线程而非直接调用,避免内部线程池阻塞事件循环(Web 接口不得阻塞)。- 上传是否触发受
server.auto_upload_to_cdn配置门控:未启用时只建映射不上传,中间件也直接走本地文件,行为与无 CDN 一致。
设计说明:无需在 API 层(list_public/to_dict)替换 CDN URL。这与生成记录的处理方式一致(保持本地路径,依赖中间件透明重定向)。在 API 层每次生成签名 URL 既冗余又会绕过中间件统一的刷新逻辑。
做同款/参考图跳转
Lightbox 中"做同款"调用 GET /api/marketing-inspirations/{id}/template 获取参数快照与输入媒体(注意:模板返回的是参考图/参考视频,不包含生成的结果本身),再跳转到生成页;"用作参考图"将作品 URL 作为参考图带入 Agent 模式。跳转前对 src 做绝对 URL 判断(/^https?:\/\//),避免已是完整 URL 时重复拼接 window.location.origin。
文件清单
| 文件路径 | 类型 | 说明 |
|---|---|---|
web/marketing_agent.html | 页面 | 营销智能体对话页面(Vue 3 SPA) |
web/css/marketing_agent.css | 样式 | 对话页面样式(浅色主题) |
web/js/task_config.js | 脚本 | 任务配置统一管理模块 |
web/js/video_compressor.js | 脚本 | 前端视频压缩模块(Canvas + MediaRecorder) |
web/i18n/locales/zh-CN/marketing_agent.json | 翻译 | 中文翻译文件(约 220 个键) |
web/i18n/locales/en/marketing_agent.json | 翻译 | 英文翻译文件 |
server.py | 路由 | /marketing-agent 静态页面路由;cdn_redirect_middleware 为 /upload/ 媒体提供透明 CDN 重定向 |
api/script_writer.py | API | 会话管理、任务创建、流式响应、文件上传等 API |
api/marketing_publications.py | API | 灵感发布/审核/列表/模板等接口(/api/marketing-publications、/api/marketing-inspirations) |
model/marketing_publications.py | 模型 | 灵感发布数据模型与查询 |
services/marketing_publication_asset_service.py | 服务 | 发布时资产固化(复制到长期目录)+ 异步触发 CDN 上传 |
web/marketing_inspiration.html | 页面 | 灵感瀑布流页面 |
web/js/marketing_inspiration.js | 脚本 | 灵感页交互(瀑布流、Lightbox、工具栏、做同款/参考图跳转) |
web/css/marketing_inspiration.css | 样式 | 灵感页专用样式 |
web/i18n/locales/{zh-CN,en}/marketing_inspiration.json | 翻译 | 灵感页国际化文案 |
web/index.html | 页面 | 模式选择弹窗入口 |