feat(imitation): simplify form fields and make brand_name required
Remove industry, target_audience, content_goal, tone, and length_goal from the imitation form; brand_name is now mandatory with autocomplete from brand library, keywords are pre-populated from the selected brand's search keywords, and output length is hardcoded to ~2000 characters in the prompt. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -723,14 +723,9 @@ const enUS = {
|
|||||||
sourceURL: 'Article URL',
|
sourceURL: 'Article URL',
|
||||||
sourceTitle: 'Source title',
|
sourceTitle: 'Source title',
|
||||||
language: 'Article language',
|
language: 'Article language',
|
||||||
industry: 'Industry / scenario',
|
|
||||||
brandName: 'Brand / subject',
|
brandName: 'Brand / subject',
|
||||||
region: 'Region',
|
region: 'Region',
|
||||||
targetAudience: 'Target audience',
|
keywords: 'Optimization keywords',
|
||||||
contentGoal: 'Content goal',
|
|
||||||
tone: 'Tone',
|
|
||||||
lengthGoal: 'Length',
|
|
||||||
keywords: 'Keywords',
|
|
||||||
preservePoints: 'Keep / emphasize',
|
preservePoints: 'Keep / emphasize',
|
||||||
avoidPoints: 'Avoid',
|
avoidPoints: 'Avoid',
|
||||||
extraRequirements: 'Extra requirements',
|
extraRequirements: 'Extra requirements',
|
||||||
@@ -743,21 +738,17 @@ const enUS = {
|
|||||||
'Selected groups are retrieved in the backend and passed to generation as reference context.',
|
'Selected groups are retrieved in the backend and passed to generation as reference context.',
|
||||||
sourceURLPlaceholder: 'https://example.com/article',
|
sourceURLPlaceholder: 'https://example.com/article',
|
||||||
sourceTitlePlaceholder: 'Optional. Citation titles are prefilled automatically.',
|
sourceTitlePlaceholder: 'Optional. Citation titles are prefilled automatically.',
|
||||||
industryPlaceholder: 'For example: home improvement, education, SaaS, healthcare',
|
brandNamePlaceholder: 'Required. Type the target subject, or choose from the brand library.',
|
||||||
brandNamePlaceholder: 'Optional. Helps the article fit a specific brand or subject.',
|
|
||||||
regionPlaceholder: 'For example: Shanghai, East China, nationwide, North America',
|
regionPlaceholder: 'For example: Shanghai, East China, nationwide, North America',
|
||||||
targetAudiencePlaceholder:
|
keywordsPlaceholder:
|
||||||
'For example: first-time buyers, channel partners, procurement leads',
|
'Type optimization keywords and press Enter. Brand-library options appear after choosing a subject.',
|
||||||
contentGoalPlaceholder: 'For example: education, conversion, brand trust, selection advice',
|
|
||||||
tonePlaceholder: 'For example: professional, conversational, comparative, news analysis',
|
|
||||||
lengthGoalPlaceholder: 'For example: around 1200 words, short post, long-form guide',
|
|
||||||
keywordsPlaceholder: 'Type keywords and press Enter',
|
|
||||||
preservePointsPlaceholder: 'List facts, data, arguments, or examples that must be kept.',
|
preservePointsPlaceholder: 'List facts, data, arguments, or examples that must be kept.',
|
||||||
avoidPointsPlaceholder: 'List claims, competitors, compliance risks, or wording to avoid.',
|
avoidPointsPlaceholder: 'List claims, competitors, compliance risks, or wording to avoid.',
|
||||||
extraRequirementsPlaceholder:
|
extraRequirementsPlaceholder:
|
||||||
'Add channel, structure, title style, or industry-specific instructions.',
|
'Add channel, structure, title style, or industry-specific instructions.',
|
||||||
queued: 'Rewrite task submitted and queued.',
|
queued: 'Rewrite task submitted and queued.',
|
||||||
sourceURLRequired: 'Enter the source article URL first.',
|
sourceURLRequired: 'Enter the source article URL first.',
|
||||||
|
brandNameRequired: 'Enter the brand / subject first.',
|
||||||
submitError: 'Failed to submit rewrite task.',
|
submitError: 'Failed to submit rewrite task.',
|
||||||
backToList: 'Back to rewrites',
|
backToList: 'Back to rewrites',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -691,14 +691,9 @@ const zhCN = {
|
|||||||
sourceURL: "文章 URL",
|
sourceURL: "文章 URL",
|
||||||
sourceTitle: "来源标题",
|
sourceTitle: "来源标题",
|
||||||
language: "文章语言",
|
language: "文章语言",
|
||||||
industry: "行业/场景",
|
|
||||||
brandName: "品牌/主体",
|
brandName: "品牌/主体",
|
||||||
region: "地域",
|
region: "地域",
|
||||||
targetAudience: "目标读者",
|
keywords: "优化关键词",
|
||||||
contentGoal: "内容目标",
|
|
||||||
tone: "语气风格",
|
|
||||||
lengthGoal: "篇幅要求",
|
|
||||||
keywords: "关键词",
|
|
||||||
preservePoints: "必须保留/强调",
|
preservePoints: "必须保留/强调",
|
||||||
avoidPoints: "需要规避",
|
avoidPoints: "需要规避",
|
||||||
extraRequirements: "其他要求",
|
extraRequirements: "其他要求",
|
||||||
@@ -709,19 +704,15 @@ const zhCN = {
|
|||||||
knowledgeBaseHint: "选择后,后台会检索相关知识片段并作为生成参考,未选择则只基于来源文章和当前设置生成。",
|
knowledgeBaseHint: "选择后,后台会检索相关知识片段并作为生成参考,未选择则只基于来源文章和当前设置生成。",
|
||||||
sourceURLPlaceholder: "https://example.com/article",
|
sourceURLPlaceholder: "https://example.com/article",
|
||||||
sourceTitlePlaceholder: "可选,引用来源标题会自动带入",
|
sourceTitlePlaceholder: "可选,引用来源标题会自动带入",
|
||||||
industryPlaceholder: "例如:家居建材、教育培训、SaaS、医疗健康",
|
brandNamePlaceholder: "必填,可输入目标达成词,或从品牌库选择",
|
||||||
brandNamePlaceholder: "可选,填写后文章会自然贴合该品牌或主体",
|
|
||||||
regionPlaceholder: "例如:上海、华东、全国、北美市场",
|
regionPlaceholder: "例如:上海、华东、全国、北美市场",
|
||||||
targetAudiencePlaceholder: "例如:首次购买用户、渠道代理、企业采购负责人",
|
keywordsPlaceholder: "输入优化关键词后回车,可多选;选择品牌库主体后会显示搜索词候选",
|
||||||
contentGoalPlaceholder: "例如:科普说明、转化种草、品牌信任、选型建议",
|
|
||||||
tonePlaceholder: "例如:专业可信、轻松口语、评测对比、新闻解读",
|
|
||||||
lengthGoalPlaceholder: "例如:1200字左右、短文、深度长文",
|
|
||||||
keywordsPlaceholder: "输入关键词后回车,可多选",
|
|
||||||
preservePointsPlaceholder: "列出必须保留的事实、数据、观点或案例",
|
preservePointsPlaceholder: "列出必须保留的事实、数据、观点或案例",
|
||||||
avoidPointsPlaceholder: "列出不能出现的说法、竞品、合规禁区或表达方式",
|
avoidPointsPlaceholder: "列出不能出现的说法、竞品、合规禁区或表达方式",
|
||||||
extraRequirementsPlaceholder: "补充行业、渠道、结构、标题风格等自由要求",
|
extraRequirementsPlaceholder: "补充行业、渠道、结构、标题风格等自由要求",
|
||||||
queued: "仿写任务已提交,正在排队生成",
|
queued: "仿写任务已提交,正在排队生成",
|
||||||
sourceURLRequired: "请先填写来源文章 URL",
|
sourceURLRequired: "请先填写来源文章 URL",
|
||||||
|
brandNameRequired: "请先填写品牌/主体",
|
||||||
submitError: "仿写任务提交失败",
|
submitError: "仿写任务提交失败",
|
||||||
backToList: "返回仿写列表",
|
backToList: "返回仿写列表",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { LeftOutlined, LinkOutlined } from '@ant-design/icons-vue'
|
import { LeftOutlined, LinkOutlined } from '@ant-design/icons-vue'
|
||||||
import { useMutation, useQueryClient } from '@tanstack/vue-query'
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { computed, reactive, watch } from 'vue'
|
import { computed, reactive, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import KnowledgeGroupSelect from '@/components/KnowledgeGroupSelect.vue'
|
import KnowledgeGroupSelect from '@/components/KnowledgeGroupSelect.vue'
|
||||||
import { articlesApi } from '@/lib/api'
|
import { articlesApi, brandsApi } from '@/lib/api'
|
||||||
import { formatError } from '@/lib/errors'
|
import { formatError } from '@/lib/errors'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -19,13 +19,8 @@ const form = reactive({
|
|||||||
source_url: '',
|
source_url: '',
|
||||||
source_title: '',
|
source_title: '',
|
||||||
locale: 'zh-CN',
|
locale: 'zh-CN',
|
||||||
industry: '',
|
|
||||||
brand_name: '',
|
brand_name: '',
|
||||||
region: '',
|
region: '',
|
||||||
target_audience: '',
|
|
||||||
content_goal: '',
|
|
||||||
tone: '',
|
|
||||||
length_goal: '',
|
|
||||||
keywords: [] as string[],
|
keywords: [] as string[],
|
||||||
preserve_points: '',
|
preserve_points: '',
|
||||||
avoid_points: '',
|
avoid_points: '',
|
||||||
@@ -34,8 +29,40 @@ const form = reactive({
|
|||||||
knowledge_group_ids: [] as number[],
|
knowledge_group_ids: [] as number[],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const brandsQuery = useQuery({
|
||||||
|
queryKey: ['brands'],
|
||||||
|
queryFn: () => brandsApi.list(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const brandNameOptions = computed(() =>
|
||||||
|
(brandsQuery.data.value ?? []).map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.name,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectedBrand = computed(
|
||||||
|
() => brandsQuery.data.value?.find((item) => item.name === form.brand_name.trim()) ?? null,
|
||||||
|
)
|
||||||
|
|
||||||
|
const questionsQuery = useQuery({
|
||||||
|
queryKey: computed(() => ['brands', selectedBrand.value?.id, 'questions', 'imitation']),
|
||||||
|
enabled: computed(() => Boolean(selectedBrand.value?.id)),
|
||||||
|
queryFn: () => brandsApi.listQuestions(selectedBrand.value?.id as number),
|
||||||
|
})
|
||||||
|
|
||||||
|
const keywordOptions = computed(() =>
|
||||||
|
(questionsQuery.data.value ?? []).map((item) => ({
|
||||||
|
label: item.question_text,
|
||||||
|
value: item.question_text,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
|
||||||
const canSubmit = computed(
|
const canSubmit = computed(
|
||||||
() => form.source_url.trim().length > 0 && !generateMutation.isPending.value,
|
() =>
|
||||||
|
form.source_url.trim().length > 0 &&
|
||||||
|
form.brand_name.trim().length > 0 &&
|
||||||
|
!generateMutation.isPending.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
const generateMutation = useMutation({
|
const generateMutation = useMutation({
|
||||||
@@ -44,13 +71,8 @@ const generateMutation = useMutation({
|
|||||||
source_url: form.source_url.trim(),
|
source_url: form.source_url.trim(),
|
||||||
source_title: form.source_title.trim(),
|
source_title: form.source_title.trim(),
|
||||||
locale: form.locale,
|
locale: form.locale,
|
||||||
industry: form.industry.trim(),
|
|
||||||
brand_name: form.brand_name.trim(),
|
brand_name: form.brand_name.trim(),
|
||||||
region: form.region.trim(),
|
region: form.region.trim(),
|
||||||
target_audience: form.target_audience.trim(),
|
|
||||||
content_goal: form.content_goal.trim(),
|
|
||||||
tone: form.tone.trim(),
|
|
||||||
length_goal: form.length_goal.trim(),
|
|
||||||
keywords: form.keywords.map((item) => item.trim()).filter(Boolean),
|
keywords: form.keywords.map((item) => item.trim()).filter(Boolean),
|
||||||
preserve_points: form.preserve_points.trim(),
|
preserve_points: form.preserve_points.trim(),
|
||||||
avoid_points: form.avoid_points.trim(),
|
avoid_points: form.avoid_points.trim(),
|
||||||
@@ -95,6 +117,10 @@ function submit(): void {
|
|||||||
message.warning(t('imitation.create.sourceURLRequired'))
|
message.warning(t('imitation.create.sourceURLRequired'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!form.brand_name.trim()) {
|
||||||
|
message.warning(t('imitation.create.brandNameRequired'))
|
||||||
|
return
|
||||||
|
}
|
||||||
generateMutation.mutate()
|
generateMutation.mutate()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -204,17 +230,11 @@ function submit(): void {
|
|||||||
|
|
||||||
<div class="imitation-generate-page__field-grid">
|
<div class="imitation-generate-page__field-grid">
|
||||||
<div class="imitation-generate-page__field">
|
<div class="imitation-generate-page__field">
|
||||||
<label>{{ t('imitation.create.industry') }}</label>
|
<label class="required-asterisk">{{ t('imitation.create.brandName') }}</label>
|
||||||
<a-input
|
<a-auto-complete
|
||||||
v-model:value="form.industry"
|
|
||||||
:placeholder="t('imitation.create.industryPlaceholder')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="imitation-generate-page__field">
|
|
||||||
<label>{{ t('imitation.create.brandName') }}</label>
|
|
||||||
<a-input
|
|
||||||
v-model:value="form.brand_name"
|
v-model:value="form.brand_name"
|
||||||
|
allow-clear
|
||||||
|
:options="brandNameOptions"
|
||||||
:placeholder="t('imitation.create.brandNamePlaceholder')"
|
:placeholder="t('imitation.create.brandNamePlaceholder')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -227,44 +247,13 @@ function submit(): void {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="imitation-generate-page__field">
|
|
||||||
<label>{{ t('imitation.create.targetAudience') }}</label>
|
|
||||||
<a-input
|
|
||||||
v-model:value="form.target_audience"
|
|
||||||
:placeholder="t('imitation.create.targetAudiencePlaceholder')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="imitation-generate-page__field">
|
|
||||||
<label>{{ t('imitation.create.contentGoal') }}</label>
|
|
||||||
<a-input
|
|
||||||
v-model:value="form.content_goal"
|
|
||||||
:placeholder="t('imitation.create.contentGoalPlaceholder')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="imitation-generate-page__field">
|
|
||||||
<label>{{ t('imitation.create.tone') }}</label>
|
|
||||||
<a-input
|
|
||||||
v-model:value="form.tone"
|
|
||||||
:placeholder="t('imitation.create.tonePlaceholder')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="imitation-generate-page__field">
|
|
||||||
<label>{{ t('imitation.create.lengthGoal') }}</label>
|
|
||||||
<a-input
|
|
||||||
v-model:value="form.length_goal"
|
|
||||||
:placeholder="t('imitation.create.lengthGoalPlaceholder')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="imitation-generate-page__field imitation-generate-page__field--wide">
|
<div class="imitation-generate-page__field imitation-generate-page__field--wide">
|
||||||
<label>{{ t('imitation.create.keywords') }}</label>
|
<label>{{ t('imitation.create.keywords') }}</label>
|
||||||
<a-select
|
<a-select
|
||||||
v-model:value="form.keywords"
|
v-model:value="form.keywords"
|
||||||
mode="tags"
|
mode="tags"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
|
:options="keywordOptions"
|
||||||
:placeholder="t('imitation.create.keywordsPlaceholder')"
|
:placeholder="t('imitation.create.keywordsPlaceholder')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -278,7 +278,8 @@ runtime:
|
|||||||
- 文章结构不动,文章分析核心维度不动,只动目标达成词(也就是brand_name)
|
- 文章结构不动,文章分析核心维度不动,只动目标达成词(也就是brand_name)
|
||||||
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
||||||
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
||||||
- 如果给出了地域、品牌、目标读者或内容目标,标题和正文都要自然贴合这些上下文。
|
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
||||||
|
- 全文篇幅控制在 2000 字左右。
|
||||||
%s
|
%s
|
||||||
|
|
||||||
输出格式:Markdown。正文结构清晰,可直接进入文章编辑器。
|
输出格式:Markdown。正文结构清晰,可直接进入文章编辑器。
|
||||||
@@ -301,14 +302,10 @@ runtime:
|
|||||||
zh-CN: "输出语言:中文简体。"
|
zh-CN: "输出语言:中文简体。"
|
||||||
en-US: "Output language: English."
|
en-US: "Output language: English."
|
||||||
article_imitation_setting_labels:
|
article_imitation_setting_labels:
|
||||||
industry: "行业/场景"
|
|
||||||
brand_name: "品牌/主体"
|
brand_name: "品牌/主体"
|
||||||
region: "地域"
|
region: "地域"
|
||||||
target_audience: "目标读者"
|
|
||||||
content_goal: "内容目标"
|
|
||||||
tone: "语气风格"
|
|
||||||
length_goal: "篇幅要求"
|
length_goal: "篇幅要求"
|
||||||
keywords: "关键词"
|
keywords: "优化关键词"
|
||||||
preserve_points: "必须保留/强调"
|
preserve_points: "必须保留/强调"
|
||||||
avoid_points: "需要规避"
|
avoid_points: "需要规避"
|
||||||
extra_requirements: "其他要求"
|
extra_requirements: "其他要求"
|
||||||
|
|||||||
+3
-6
@@ -278,7 +278,8 @@ runtime:
|
|||||||
- 文章结构不动,文章分析核心维度不动,只动目标达成词(也就是brand_name)
|
- 文章结构不动,文章分析核心维度不动,只动目标达成词(也就是brand_name)
|
||||||
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
||||||
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
||||||
- 如果给出了地域、品牌、目标读者或内容目标,标题和正文都要自然贴合这些上下文。
|
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
||||||
|
- 全文篇幅控制在 2000 字左右。
|
||||||
%s
|
%s
|
||||||
|
|
||||||
输出格式:Markdown。正文结构清晰,可直接进入文章编辑器。
|
输出格式:Markdown。正文结构清晰,可直接进入文章编辑器。
|
||||||
@@ -301,14 +302,10 @@ runtime:
|
|||||||
zh-CN: "输出语言:中文简体。"
|
zh-CN: "输出语言:中文简体。"
|
||||||
en-US: "Output language: English."
|
en-US: "Output language: English."
|
||||||
article_imitation_setting_labels:
|
article_imitation_setting_labels:
|
||||||
industry: "行业/场景"
|
|
||||||
brand_name: "品牌/主体"
|
brand_name: "品牌/主体"
|
||||||
region: "地域"
|
region: "地域"
|
||||||
target_audience: "目标读者"
|
|
||||||
content_goal: "内容目标"
|
|
||||||
tone: "语气风格"
|
|
||||||
length_goal: "篇幅要求"
|
length_goal: "篇幅要求"
|
||||||
keywords: "关键词"
|
keywords: "优化关键词"
|
||||||
preserve_points: "必须保留/强调"
|
preserve_points: "必须保留/强调"
|
||||||
avoid_points: "需要规避"
|
avoid_points: "需要规避"
|
||||||
extra_requirements: "其他要求"
|
extra_requirements: "其他要求"
|
||||||
|
|||||||
@@ -748,13 +748,8 @@ export interface GenerateImitationRequest {
|
|||||||
source_url: string
|
source_url: string
|
||||||
source_title?: string
|
source_title?: string
|
||||||
locale?: string
|
locale?: string
|
||||||
industry?: string
|
brand_name: string
|
||||||
brand_name?: string
|
|
||||||
region?: string
|
region?: string
|
||||||
target_audience?: string
|
|
||||||
content_goal?: string
|
|
||||||
tone?: string
|
|
||||||
length_goal?: string
|
|
||||||
keywords?: string[]
|
keywords?: string[]
|
||||||
preserve_points?: string
|
preserve_points?: string
|
||||||
avoid_points?: string
|
avoid_points?: string
|
||||||
|
|||||||
@@ -278,7 +278,8 @@ runtime:
|
|||||||
- 文章结构不动,文章分析核心维度不动,只动目标达成词(也就是brand_name)
|
- 文章结构不动,文章分析核心维度不动,只动目标达成词(也就是brand_name)
|
||||||
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
- 保留源文章中的事实信息、论证逻辑和可复用观点。
|
||||||
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
- 不要逐句同义替换,不要复制源文的独特措辞,不要编造源文、知识库和补充要求之外的事实。
|
||||||
- 如果给出了地域、品牌、目标读者或内容目标,标题和正文都要自然贴合这些上下文。
|
- 如果给出了地域、品牌/主体或优化关键词,标题和正文都要自然贴合这些上下文。
|
||||||
|
- 全文篇幅控制在 2000 字左右。
|
||||||
%s
|
%s
|
||||||
|
|
||||||
输出格式:Markdown。正文结构清晰,可直接进入文章编辑器。
|
输出格式:Markdown。正文结构清晰,可直接进入文章编辑器。
|
||||||
@@ -301,14 +302,10 @@ runtime:
|
|||||||
zh-CN: "输出语言:中文简体。"
|
zh-CN: "输出语言:中文简体。"
|
||||||
en-US: "Output language: English."
|
en-US: "Output language: English."
|
||||||
article_imitation_setting_labels:
|
article_imitation_setting_labels:
|
||||||
industry: "行业/场景"
|
|
||||||
brand_name: "品牌/主体"
|
brand_name: "品牌/主体"
|
||||||
region: "地域"
|
region: "地域"
|
||||||
target_audience: "目标读者"
|
|
||||||
content_goal: "内容目标"
|
|
||||||
tone: "语气风格"
|
|
||||||
length_goal: "篇幅要求"
|
length_goal: "篇幅要求"
|
||||||
keywords: "关键词"
|
keywords: "优化关键词"
|
||||||
preserve_points: "必须保留/强调"
|
preserve_points: "必须保留/强调"
|
||||||
avoid_points: "需要规避"
|
avoid_points: "需要规避"
|
||||||
extra_requirements: "其他要求"
|
extra_requirements: "其他要求"
|
||||||
|
|||||||
@@ -47,13 +47,8 @@ type GenerateImitationRequest struct {
|
|||||||
SourceURL string `json:"source_url" binding:"required"`
|
SourceURL string `json:"source_url" binding:"required"`
|
||||||
SourceTitle string `json:"source_title"`
|
SourceTitle string `json:"source_title"`
|
||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
Industry string `json:"industry"`
|
|
||||||
BrandName string `json:"brand_name"`
|
BrandName string `json:"brand_name"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
TargetAudience string `json:"target_audience"`
|
|
||||||
ContentGoal string `json:"content_goal"`
|
|
||||||
Tone string `json:"tone"`
|
|
||||||
LengthGoal string `json:"length_goal"`
|
|
||||||
Keywords []string `json:"keywords"`
|
Keywords []string `json:"keywords"`
|
||||||
PreservePoints string `json:"preserve_points"`
|
PreservePoints string `json:"preserve_points"`
|
||||||
AvoidPoints string `json:"avoid_points"`
|
AvoidPoints string `json:"avoid_points"`
|
||||||
@@ -135,6 +130,9 @@ func (s *ArticleImitationService) Generate(ctx context.Context, req GenerateImit
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if strings.TrimSpace(req.BrandName) == "" {
|
||||||
|
return nil, response.ErrBadRequest(40001, "brand_name_required", "brand_name is required")
|
||||||
|
}
|
||||||
|
|
||||||
quotaRepo := repository.NewQuotaRepository(s.pool)
|
quotaRepo := repository.NewQuotaRepository(s.pool)
|
||||||
balance, err := quotaRepo.GetCurrentBalance(ctx, actor.TenantID, "article_generation")
|
balance, err := quotaRepo.GetCurrentBalance(ctx, actor.TenantID, "article_generation")
|
||||||
@@ -587,13 +585,9 @@ func buildImitationInputParams(sourceURL string, req GenerateImitationRequest) m
|
|||||||
"source_url": sourceURL,
|
"source_url": sourceURL,
|
||||||
"source_title": strings.TrimSpace(req.SourceTitle),
|
"source_title": strings.TrimSpace(req.SourceTitle),
|
||||||
"locale": normalizeImitationLocale(req.Locale),
|
"locale": normalizeImitationLocale(req.Locale),
|
||||||
"industry": strings.TrimSpace(req.Industry),
|
|
||||||
"brand_name": strings.TrimSpace(req.BrandName),
|
"brand_name": strings.TrimSpace(req.BrandName),
|
||||||
"region": strings.TrimSpace(req.Region),
|
"region": strings.TrimSpace(req.Region),
|
||||||
"target_audience": strings.TrimSpace(req.TargetAudience),
|
"length_goal": "2000字左右",
|
||||||
"content_goal": strings.TrimSpace(req.ContentGoal),
|
|
||||||
"tone": strings.TrimSpace(req.Tone),
|
|
||||||
"length_goal": strings.TrimSpace(req.LengthGoal),
|
|
||||||
"keywords": normalizeImitationKeywords(req.Keywords),
|
"keywords": normalizeImitationKeywords(req.Keywords),
|
||||||
"preserve_points": strings.TrimSpace(req.PreservePoints),
|
"preserve_points": strings.TrimSpace(req.PreservePoints),
|
||||||
"avoid_points": strings.TrimSpace(req.AvoidPoints),
|
"avoid_points": strings.TrimSpace(req.AvoidPoints),
|
||||||
@@ -631,12 +625,8 @@ func buildImitationGenerationPrompt(params map[string]interface{}, sourceContent
|
|||||||
}
|
}
|
||||||
|
|
||||||
var settings strings.Builder
|
var settings strings.Builder
|
||||||
appendPromptLine(&settings, "industry", extractString(params, "industry"))
|
|
||||||
appendPromptLine(&settings, "brand_name", extractString(params, "brand_name"))
|
appendPromptLine(&settings, "brand_name", extractString(params, "brand_name"))
|
||||||
appendPromptLine(&settings, "region", extractString(params, "region"))
|
appendPromptLine(&settings, "region", extractString(params, "region"))
|
||||||
appendPromptLine(&settings, "target_audience", extractString(params, "target_audience"))
|
|
||||||
appendPromptLine(&settings, "content_goal", extractString(params, "content_goal"))
|
|
||||||
appendPromptLine(&settings, "tone", extractString(params, "tone"))
|
|
||||||
appendPromptLine(&settings, "length_goal", extractString(params, "length_goal"))
|
appendPromptLine(&settings, "length_goal", extractString(params, "length_goal"))
|
||||||
if len(keywords) > 0 {
|
if len(keywords) > 0 {
|
||||||
appendPromptLine(&settings, "keywords", strings.Join(keywords, "、"))
|
appendPromptLine(&settings, "keywords", strings.Join(keywords, "、"))
|
||||||
@@ -674,12 +664,8 @@ func buildImitationKnowledgeQuery(params map[string]interface{}) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
appendValue(extractString(params, "source_title"))
|
appendValue(extractString(params, "source_title"))
|
||||||
appendValue(extractString(params, "industry"))
|
|
||||||
appendValue(extractString(params, "brand_name"))
|
appendValue(extractString(params, "brand_name"))
|
||||||
appendValue(extractString(params, "region"))
|
appendValue(extractString(params, "region"))
|
||||||
appendValue(extractString(params, "target_audience"))
|
|
||||||
appendValue(extractString(params, "content_goal"))
|
|
||||||
appendValue(extractString(params, "tone"))
|
|
||||||
appendValue(strings.Join(extractStringList(params["keywords"], 16), "\n"))
|
appendValue(strings.Join(extractStringList(params["keywords"], 16), "\n"))
|
||||||
appendValue(extractString(params, "extra_requirements"))
|
appendValue(extractString(params, "extra_requirements"))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user