feat: recommend task-aware defaults for creative forms
Some checks failed
StoryForge CI / Baseline checks (push) Has been cancelled
StoryForge CI / Backend tests (push) Has been cancelled
StoryForge CI / Web tests (push) Has been cancelled

This commit is contained in:
kris
2026-04-05 12:31:21 +08:00
parent d0ae34ae4a
commit 571be69571
3 changed files with 30 additions and 4 deletions

View File

@@ -27,6 +27,12 @@
- `创建 AI 视频` 会按来源任务自动推荐风格、画幅和单镜头时长;`创建实拍剪辑` 会自动推荐目标时长和画幅。
- 这样从主 Agent、任务详情或最近完成任务继续往下做时表单默认值会更贴近当前任务本身而不是每次都从通用模板起步。
### 高优先级创作表单开始自动补标题和剪辑目标
- `创建 AI 视频 / 创建实拍剪辑 / 写复盘` 现在会优先基于来源任务自动带出更合理的标题,而不是总让用户自己再补一遍。
- `创建实拍剪辑` 还会基于来源任务摘要自动生成更贴近当前任务的默认剪辑目标。
- 这样从某条任务继续派生后续动作时,表单不仅默认值更合理,连标题和目标文案也更像是承接当前任务的自然下一步。
### 主 Agent 抖音相似搜索与对标关系 live 修复
- 修复 `search-similar-accounts` / `save-benchmark-link` 在抖音 live 数据上错误按 `project_id` 查询账号导致的 500。

View File

@@ -430,6 +430,20 @@ function recommendAiVideoShotDuration(sourceJob) {
return 5;
}
function recommendDerivativeJobTitle(sourceJob, suffix, fallback) {
const base = String(sourceJob?.title || "").trim();
if (base) return `${base} · ${suffix}`;
return fallback;
}
function recommendRealCutObjective(sourceJob) {
const briefText = brief(getJobSeedBrief(sourceJob), 48);
if (briefText && briefText !== "暂无") {
return `围绕「${briefText}」保留高信息密度片段,输出适合短视频平台的粗剪结果`;
}
return "保留高信息密度片段,输出适合短视频平台的粗剪结果";
}
function formatDateTime(value) {
if (!value) return "-";
const date = new Date(value);
@@ -11437,7 +11451,7 @@ function openCreateAiVideoAction(defaults = {}) {
fields: [
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) },
...(sourceJob ? [{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml(sourceJob) }] : []),
{ name: "title", label: "任务标题", value: defaults.title || (sourceJob ? `${sourceJob.title} · AI 视频` : ""), placeholder: "例如:创业口播 AI 视频测试" },
{ name: "title", label: "任务标题", value: defaults.title || recommendDerivativeJobTitle(sourceJob, "AI 视频", ""), placeholder: "例如:创业口播 AI 视频测试" },
{ name: "brief", label: "视频 brief", type: "textarea", rows: 5, value: defaults.brief || getJobSeedBrief(sourceJob), placeholder: "写明主题、风格、镜头和目标受众" },
{ name: "sourceJobId", label: "关联源任务", type: "select", value: defaults.sourceJobId || sourceJob?.id || "", options: [{ value: "", label: "不关联" }, ...getCompletedJobOptions()] },
{
@@ -11554,11 +11568,11 @@ function openCreateRealCutAction(defaults = {}) {
fields: [
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, assistant?.id || "") },
...(sourceJob ? [{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml(sourceJob) }] : []),
{ name: "title", label: "任务标题", value: defaults.title || (sourceJob ? `${sourceJob.title} · 实拍剪辑` : ""), placeholder: "例如:创业素材粗剪" },
{ name: "title", label: "任务标题", value: defaults.title || recommendDerivativeJobTitle(sourceJob, "实拍剪辑", ""), placeholder: "例如:创业素材粗剪" },
{ name: "sourceJobId", label: "源任务", type: "select", value: defaults.sourceJobId || sourceJob?.id || getCompletedJobOptions()[0]?.value || "", options: getCompletedJobOptions() },
{ name: "targetDurationSec", label: "目标时长(秒)", type: "number", value: defaults.targetDurationSec || recommendRealCutDuration(sourceJob), min: 10, max: 300 },
{ name: "aspectRatio", label: "画幅", value: defaults.aspectRatio || sourceJob?.artifacts?.aspect_ratio || recommendAspectRatioForPlatform(defaultPlatform) },
{ name: "objective", label: "目标", type: "textarea", rows: 4, value: defaults.objective || "", placeholder: "例如:保留高信息密度片段,输出适合短视频平台的粗剪结果" }
{ name: "objective", label: "目标", type: "textarea", rows: 4, value: defaults.objective || recommendRealCutObjective(sourceJob), placeholder: "例如:保留高信息密度片段,输出适合短视频平台的粗剪结果" }
],
onSubmit: async (values) => {
if (!values.title?.trim()) throw new Error("请填写任务标题");
@@ -11819,7 +11833,7 @@ function openReviewAction(defaults = {}) {
fields: [
{ name: "context", label: "当前上下文", type: "html", html: renderIntakeActionContextHtml(project.id, defaultAssistantId) },
...(sourceJob ? [{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml(sourceJob) }] : []),
{ name: "title", label: "标题", value: existingReview?.title || defaults.title || sourceJob?.title || "", placeholder: "例如:创业口播 3 月 22 日复盘" },
{ name: "title", label: "标题", value: existingReview?.title || defaults.title || recommendDerivativeJobTitle(sourceJob, "复盘", ""), placeholder: "例如:创业口播 3 月 22 日复盘" },
{ name: "sourceJobId", label: "关联任务", type: "select", value: existingReview?.source_job_id || defaults.sourceJobId || sourceJob?.id || "", options: [{ value: "", label: "不关联任务" }, ...getCompletedJobOptions()] },
{ name: "assistantId", label: "负责 Agent", type: "select", value: existingReview?.assistant_id || defaultAssistantId, options: [{ value: "", label: "先不绑定" }, ...assistants] },
{ name: "platform", label: "平台", type: "select", value: normalizePlatformValue(existingReview?.platform || defaults.platform || sourceJob?.platform || "douyin"), options: getPlatformOptions() },

View File

@@ -1153,6 +1153,8 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(APP, /function recommendCreativeStyle\(sourceJob\)/);
assert.match(APP, /function recommendRealCutDuration\(sourceJob\)/);
assert.match(APP, /function recommendAiVideoShotDuration\(sourceJob\)/);
assert.match(APP, /function recommendDerivativeJobTitle\(sourceJob, suffix, fallback\)/);
assert.match(APP, /function recommendRealCutObjective\(sourceJob\)/);
assert.match(importHomepage, /label: "当前上下文", type: "html"/);
assert.match(importHomepage, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
assert.match(importHomepage, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
@@ -1185,6 +1187,7 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(createAiVideo, /const defaultPlatform = normalizePlatformValue\(defaults\.platform \|\| sourceJob\?\.platform \|\| "douyin"\)/);
assert.match(createAiVideo, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
assert.match(createAiVideo, /sourceJob \? \[\{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml\(sourceJob\) \}\] : \[\]/);
assert.match(createAiVideo, /value: defaults\.title \|\| recommendDerivativeJobTitle\(sourceJob, "AI 视频", ""\)/);
assert.match(createAiVideo, /value: defaults\.style \|\| recommendCreativeStyle\(sourceJob\)/);
assert.match(createAiVideo, /recommendAspectRatioForPlatform\(defaultPlatform\)/);
assert.match(createAiVideo, /value: defaults\.duration \|\| recommendAiVideoShotDuration\(sourceJob\)/);
@@ -1192,12 +1195,15 @@ test("input-heavy intake sheets surface current context and smarter defaults", (
assert.match(createRealCut, /const defaultPlatform = normalizePlatformValue\(defaults\.platform \|\| sourceJob\?\.platform \|\| "douyin"\)/);
assert.match(createRealCut, /renderIntakeActionContextHtml\(project\.id, assistant\?\.id \|\| ""\)/);
assert.match(createRealCut, /sourceJob \? \[\{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml\(sourceJob\) \}\] : \[\]/);
assert.match(createRealCut, /value: defaults\.title \|\| recommendDerivativeJobTitle\(sourceJob, "实拍剪辑", ""\)/);
assert.match(createRealCut, /value: defaults\.targetDurationSec \|\| recommendRealCutDuration\(sourceJob\)/);
assert.match(createRealCut, /recommendAspectRatioForPlatform\(defaultPlatform\)/);
assert.match(createRealCut, /value: defaults\.objective \|\| recommendRealCutObjective\(sourceJob\)/);
assert.match(reviewAction, /label: "当前上下文", type: "html"/);
assert.match(reviewAction, /const defaultAssistantId = getSelectedAssistant\(\)\?\.id \|\| assistants\[0\]\?\.value \|\| ""/);
assert.match(reviewAction, /renderIntakeActionContextHtml\(project\.id, defaultAssistantId\)/);
assert.match(reviewAction, /sourceJob \? \[\{ name: "sourceJobContext", label: "来源任务", type: "html", html: renderSourceJobContextHtml\(sourceJob\) \}\] : \[\]/);
assert.match(reviewAction, /value: existingReview\?\.title \|\| defaults\.title \|\| recommendDerivativeJobTitle\(sourceJob, "复盘", ""\)/);
assert.match(reviewAction, /normalizePlatformValue\(existingReview\?\.platform \|\| defaults\.platform \|\| sourceJob\?\.platform \|\| "douyin"\)/);
});