From a7a143bb268f95c642e3dc342e6ee255c48b0e56 Mon Sep 17 00:00:00 2001 From: weicw1996 Date: Wed, 21 Oct 2026 12:39:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20=E5=86=99=E4=BD=9C=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E6=8B=86=E6=88=90=E8=87=AA=E7=94=B1=E5=88=9B=E4=BD=9C?= =?UTF-8?q?=E4=B8=8E=E6=A8=A1=E6=9D=BF=E4=B8=A4=E6=9D=A1=E8=B7=AF=EF=BC=88?= =?UTF-8?q?issue=2018=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 自由创作不再解析自然语言:原话整段作为 userContent 下发,docType 留空,由后端转 「AI 创意助手」。选了文体才提交 docType 等结构化条件,走「内容创作与宣传助手」。 - writing-params.js 删掉 parseWritingRequest 与关键字表,只留选项常量;`auto` 改语义为「自由创作」。 - 字数选择器只在选中文体后显示;未选文体时不下发 number。 - 撤掉图片比例选择器:上游创意助手开始节点没有比例变量,只能拼进 user_content, 那等于替上游写 prompt。要恢复需先在上游加变量。 - 稿件页同步:默认「自由创作」,选了模板才展开标题、活动类型、时间地点等可选项。 --- src/pages/assistant/index.vue | 121 ++++----- src/pages/assistant/writing-params.js | 337 ++------------------------ src/pages/manuscript/index.vue | 106 ++++++-- 3 files changed, 151 insertions(+), 413 deletions(-) diff --git a/src/pages/assistant/index.vue b/src/pages/assistant/index.vue index efef659..59345ca 100644 --- a/src/pages/assistant/index.vue +++ b/src/pages/assistant/index.vue @@ -134,8 +134,9 @@ + - - - 比例 - {{ item.label }} - - - {{ item.name }} @@ -272,9 +255,9 @@ uploadAiFile } from './chat-service' import { + WRITING_DOC_TYPE_FREE, WRITING_DOC_TYPE_OPTIONS, - WRITING_WORD_COUNT_OPTIONS, - parseWritingRequest + WRITING_WORD_COUNT_OPTIONS } from './writing-params' import { FALLBACK_TEXT, @@ -291,19 +274,9 @@ { label: '写作', value: 'writing' }, { label: '图片', value: 'image' } ] - /** - * 图片比例选项。 - * - * 上游「AI 创意助手」开始节点**没有**比例变量,只能把它作为一行文本追加进 - * `user_content`(与 web 端 `buildImagePrompt` 同一做法)。 - */ - const IMAGE_RATIO_OPTIONS = [ - { label: '1:1', value: '1:1' }, - { label: '4:3', value: '4:3' }, - { label: '3:4', value: '3:4' }, - { label: '16:9', value: '16:9' }, - { label: '9:16', value: '9:16' } - ] + // 图片比例选择器已撤掉:上游创意助手开始节点没有比例变量(实测只有 user_content / + // user_files),前端只能把它拼进 user_content —— 那是替上游写 prompt。 + // 要恢复这个功能,先在上游开始节点加变量,再按变量契约接上来。 /** 参考素材上限,与 Dify 开始节点 file-list 变量 user_files 的 max_length 一致 */ const IMAGE_FILE_LIMIT = 5 /** 单个附件大小上限(与既有 pages/creative-assistant 口径一致) */ @@ -319,12 +292,11 @@ chatModeOptions: CHAT_MODE_OPTIONS, writingDocTypeOptions: WRITING_DOC_TYPE_OPTIONS, writingWordCountOptions: WRITING_WORD_COUNT_OPTIONS, - imageRatioOptions: IMAGE_RATIO_OPTIONS, IMAGE_FILE_LIMIT, - // 写作模式的显式选择:默认 auto,即由 writing-params 从用户输入里自动识别 - writingDocType: 'auto', + // 写作模式:默认「自由创作」——不套模板,原话直接交给 AI 创意助手 + writingDocType: WRITING_DOC_TYPE_FREE, + // 字数只在选了文体后才有意义,默认不下发 writingWordCount: 'auto', - imageRatio: '1:1', // 图片模式的参考素材(Dify 开始节点 file-list 变量 user_files) imageFiles: [], uploadingFiles: false, @@ -374,6 +346,10 @@ canPickImageFiles() { return !this.loading && !this.uploadingFiles && this.imageFiles.length < IMAGE_FILE_LIMIT }, + /** 选中文体 = 套模板走「内容创作与宣传助手」;否则是自由创作走「AI 创意助手」 */ + writingTemplateMode() { + return this.writingDocType !== WRITING_DOC_TYPE_FREE + }, assistantTitle() { if (this.activeChatMode === 'writing') { return 'AI 写作助手' @@ -385,10 +361,10 @@ }, assistantHint() { if (this.activeChatMode === 'writing') { - return '直接说需求就行,例如「帮我写一篇300字关于迎新晚会的推文,风格活泼」;也可以在下方指定文体和字数。' + return '默认「自由创作」:说清想写什么就行,由 AI 创意助手直接生成;选了文体则按该体裁生成,并可在下方指定字数。' } if (this.activeChatMode === 'image') { - return '描述你要的图片,例如校园活动海报、公众号头图;还可以选择图片比例并添加参考素材。' + return '描述你要的图片,例如校园活动海报、公众号头图;还可以添加参考素材。' } return '你可以直接提问团务流程问题,我会通过实时消息回复。' }, @@ -397,10 +373,10 @@ return '请先登录后使用 AI 助手' } if (this.activeChatMode === 'writing') { - return '直接描述需求即可,例如:帮我写一篇300字关于迎新晚会的推文,风格活泼,面向大一新生(也可在下方直接指定文体和字数)' + return '描述你想创作的内容,例如:帮我写一段迎新晚会的主持人开场词,口语化一点(选了文体后才需要再定字数)' } if (this.activeChatMode === 'image') { - return '请输入图片生成需求,例如校园活动海报、公众号头图、志愿服务插画,并选择图片比例' + return '请输入图片生成需求,例如校园活动海报、公众号头图、志愿服务插画' } return '说说你想问的问题' }, @@ -987,29 +963,33 @@ return '' }, /** - * 写作模式入参。 + * 写作模式入参(2026-10-21 改造后)。 * - * 优先级:**界面显式选择 > 「键:值」入参 > 自然语言推断 > 默认值**。 - * 解析规则全部在 `./writing-params`(与 web 端 `writing-params.ts` 等价移植)。 + * 两条路,都不解析自然语言: + * - 自由创作(未选文体):原话整段作为 `userContent`,后端见 `docType` 为空 + * 就转「AI 创意助手」(只吃 `user_content`); + * - 选了文体:原话整段作为 `highlights`(核心亮点与具体要求),只下发界面 + * 上显式选过的 docType / number,其余可选字段留空交给上游。 * - * 过去这里没有表单,只按「键:值」解析,解析不到就一律回落 - * `doc_type=新闻稿` / `Number=800`;而上游提示词把这两项当**主控指令**, - * 会直接压掉用户原话里的诉求(实测见 issue 13 / 15)。 + * 过去这里用 `parseWritingRequest` 从原话猜 doc_type / 字数 / 主题, + * 猜错会把垃圾值当主控指令(issue 13);现已废弃该解析,见 `./writing-params`。 */ buildWritingPayload(query) { - const overrides = {} - if (this.writingDocType !== 'auto') { - overrides.docType = this.writingDocType + const content = String(query || '').trim() + if (this.writingDocType === WRITING_DOC_TYPE_FREE) { + return { docType: '', userContent: content } } - if (this.writingWordCount !== 'auto') { - overrides.number = Number(this.writingWordCount) - } - return { - ...parseWritingRequest(query, overrides), + const payload = { + docType: this.writingDocType, + highlights: content, files: [] } + if (this.writingWordCount !== 'auto') { + payload.number = Number(this.writingWordCount) + } + return payload }, - formatWritingResult(payload, query) { + formatWritingResult(payload) { if (typeof payload === 'string') { return payload } @@ -1042,26 +1022,23 @@ data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content ) if (title || body) { - return `${title || this.buildWritingPayload(query).theme}\n\n${body}` + // 不再从入参里补标题:改造后主题只来自界面显式填写, + // 自由创作与问答式写作都没有主题,拼空标题只会在正文前多两行空行。 + return title ? `${title}\n\n${body}` : body } return this.buildFallbackText() }, - /** 图片模式:把比例与「直接出图」的要求作为文本追加进 user_content */ - buildImagePrompt(query) { - return [ - query, - `图片比例:${this.imageRatio}`, - '请直接生成图片,优先输出图片文件或图片链接。' - ].join('\n') - }, /** - * 图片模式入参:`user_content` 一段文本 + `user_files`(Dify file-list 变量)。 + * 图片模式入参:`user_content` 就是**用户原话**,外加 `user_files`(Dify file-list 变量)。 * * 附件上限 5、类型取 image/video/audio/document —— 都来自上游开始节点的变量定义。 + * + * 不往 `user_content` 里追加任何我们自己写的话(如「图片比例:1:1」 + * 「请直接生成图片…」):上游没有比例变量,拼进去等于替上游写 prompt。 */ buildImagePayload(query) { return { - user_content: this.buildImagePrompt(query), + user_content: query, user_files: this.imageFiles.map((item) => ({ transfer_method: 'local_file', type: item.type, @@ -1118,12 +1095,6 @@ } this.writingWordCount = value }, - pickImageRatio(value) { - if (this.loading) { - return - } - this.imageRatio = value - }, /** * 选参考素材。 * @@ -1351,7 +1322,7 @@ } if (this.activeChatMode === 'writing') { const result = await manuscriptGen(this.buildWritingPayload(query)) - setResult(this.formatWritingResult(result, query)) + setResult(this.formatWritingResult(result)) return } if (this.activeChatMode === 'image') { diff --git a/src/pages/assistant/writing-params.js b/src/pages/assistant/writing-params.js index 1e78777..2c584f3 100644 --- a/src/pages/assistant/writing-params.js +++ b/src/pages/assistant/writing-params.js @@ -1,341 +1,40 @@ /** - * 「AI 写作助手」入参解析:把用户自然语言诉求抽取成上游 Dify 真正读取的字段。 + * 「AI 写作助手」的可选项定义(2026-10-21 改造后)。 * - * 本文件是 `tuanwei-admin/src/views/oa/chatgpt/writing-params.ts` 的**逐行等价移植** - * (去掉 TS 类型标注,风格对齐小程序仓库:Tab 缩进、单引号、无分号)。 - * 两端共用同一套解析规则,避免行为漂移;改动其一请同步另一侧。 + * 改造前这里还负责把用户原话解析成「键:值」入参,再据此填 doc_type / Number。 + * 现在写作模式只有两条路,都不解析自然语言: * - * 背景(实测记录见 .scratch/ai-assistant/probe/): - * 上游应用「AI 内容创作与宣传助手」是按**结构化表单**设计的,它的 user prompt 模板是 + * 1. 自由创作(value = `auto`):原话整段作为 `user_content`,交给「AI 创意助手」; + * 2. 选了文体:原话整段作为 `highlights`(核心亮点与具体要求),连同 doc_type + * 交给「内容创作与宣传助手」,字数只认界面上的显式选择。 * - * 本次需要生成的文稿类型为:{doc_type} - * 篇幅要求(字数限制):{Number} - * 以下是本次活动的核心要素,请严格据此生成稿件: - * - 活动主题:{theme} - * - 活动类型:{event_type} - * - 举办时间:{time} / 举办地点:{location} / 参与人员:{participants} - * - 核心亮点与具体要求:{highlights} + * 为什么不猜:上游提示词里 doc_type / Number 是**主控指令**,模型会显式服从、 + * 甚至压掉用户原话里的冲突要求(实测思维链:「I will ignore the conflicting + * "300字推文" instruction … because the main task says "文稿类型为:新闻稿"」)。 + * 从原话里推断还会在短祈使句上产出「帮我写个」这类残片当活动主题 + * (见 issue 13 的「2026-10-19 契约修正」)。 * - * 其中 doc_type 与 Number 是**主控指令**,模型会显式服从、甚至忽略用户原话里的冲突要求。 - * 实测思维链原文: - * I will ignore the conflicting "300字推文" instruction inside the bullet points - * because the main task says "文稿类型为:新闻稿" and "篇幅要求(字数限制):800" - * - * 而小程序问答助手页过去的「稿件」入口没有表单,只按「键:值」解析,解析不到就一律回落 - * doc_type=新闻稿 / Number=800,用户自然语言里的真实诉求(推文、300 字)因此被覆盖, - * 表现为「写作模块听不懂要求」(issue 13 / 15)。 - * - * 本模块做三件事: - * 1. 保留既有的「键:值」入参(向后兼容,老用法不受影响); - * 2. 从自然语言里抽取文体、字数、主题、时间、地点、人员、活动类型; - * 3. 允许用户在界面上显式指定文体 / 字数,显式选择优先于自动识别。 - * - * 纯函数、零依赖。 + * 本模块只剩选项常量:纯数据、零依赖。 */ +/** 「自由创作」的取值,同时也是 `writingDocType` 的默认值 */ +export const WRITING_DOC_TYPE_FREE = 'auto' + /** 上游 Dify 提示词里真正定义了 Markdown 结构与文风的 4 种文体 */ export const WRITING_DOC_TYPE_OPTIONS = [ - { label: '自动识别', value: 'auto' }, + { label: '自由创作', value: WRITING_DOC_TYPE_FREE }, { label: '新闻稿', value: '新闻稿' }, { label: '活动总结', value: '活动总结' }, { label: '微信推文', value: '微信推文' }, { label: '通知公告', value: '通知公告' } ] +/** 字数只在选了文体之后才显示;`auto` 表示不下发 */ export const WRITING_WORD_COUNT_OPTIONS = [ - { label: '自动识别', value: 'auto' }, + { label: '默认', value: 'auto' }, { label: '300字', value: '300' }, { label: '500字', value: '500' }, { label: '800字', value: '800' }, { label: '1000字', value: '1000' }, { label: '1500字', value: '1500' } ] - -const DEFAULT_DOC_TYPE = '新闻稿' -const DEFAULT_EVENT_TYPE = '校园内容' - -/** - * 用户没提字数时的兜底:按文体给一个常规篇幅。 - * - * 之所以不干脆不下发 Number —— 上游提示词对空字数的处理是「按文体常规篇幅撰写, - * 并在文末用一行注明(未设定有效字数限制)」,那行尾注会直接出现在用户看到的正文里。 - */ -const DEFAULT_WORD_COUNT_BY_DOC_TYPE = { - 新闻稿: 800, - 活动总结: 1000, - 微信推文: 600, - 通知公告: 500 -} - -const FIELD_ALIASES = { - docType: ['稿件类型', '文稿类型', '文体', '类型'], - eventType: ['活动类型', '场景'], - theme: ['稿件标题', '标题', '主题', '活动主题'], - time: ['活动时间', '举办时间', '时间'], - location: ['活动地点', '举办地点', '地点'], - participants: ['参与人员', '参加人员', '受众', '人员'], - highlights: ['亮点内容', '要点', '亮点', '具体要求', '要求', '内容'], - number: ['建议篇幅', '字数', '篇幅'] -} - -/** 活动类型关键词(与 gxmu/manuscriptGen 表单页的选项保持一致的措辞) */ -const EVENT_TYPE_KEYWORDS = [ - '志愿服务', - '社会实践', - '文体活动', - '会议论坛', - '品牌项目', - '校园活动', - '团日活动', - '迎新', - '讲座', - '培训', - '比赛', - '表彰', - '招聘', - '宣讲' -] - -/** 既有的「键:值」解析:按行取 `键:值`,普通自然语言(无冒号)解析为空对象 */ -export const parseKeyValueInput = (value) => { - const result = {} - String(value || '') - .split(/\n+/) - .map((item) => item.trim()) - .filter(Boolean) - .forEach((line) => { - const match = line.match(/^([^::]+)[::]\s*(.*)$/) - if (match) { - result[match[1].trim()] = match[2].trim() - } - }) - return result -} - -const pickField = (fields, aliases) => { - for (let index = 0; index < aliases.length; index += 1) { - const value = fields[aliases[index]] - if (value) { - return value - } - } - return '' -} - -/** 上游没有内置模板、但用户会说的其它文体;命中就原样交给模型发挥 */ -const EXTRA_DOC_TYPE_WORDS = [ - '发言稿', - '演讲稿', - '致辞', - '主持词', - '短视频脚本', - '视频脚本', - '脚本', - '策划书', - '活动方案', - '简报', - '心得体会' -] - -/** - * 从一段文本里识别文体关键词,认不出来返回空串。 - * - * 只认「明确的文体词」,不做任何兜底 —— 它会被喂进用户的**整句话**, - * 若像 `normalizeDocType` 那样原样返回,整句话就会变成一个非法的文稿类型。 - */ -export const inferDocType = (text) => { - if (/推文|公众号|微信|朋友圈|新媒体/.test(text)) { - return '微信推文' - } - if (/总结|复盘|综述/.test(text)) { - return '活动总结' - } - if (/通知|公告|告示|通告|启事/.test(text)) { - return '通知公告' - } - if (/新闻|通讯|通稿|报道|简讯|宣传稿|消息稿/.test(text)) { - return '新闻稿' - } - for (let index = 0; index < EXTRA_DOC_TYPE_WORDS.length; index += 1) { - if (text.includes(EXTRA_DOC_TYPE_WORDS[index])) { - return EXTRA_DOC_TYPE_WORDS[index] - } - } - return '' -} - -/** - * 把用户给出的文体归一化到上游定义过的 4 种。 - * - * 认不出来的类型(如「发言稿」「短视频脚本」)**原样返回**,交给模型自行发挥 —— - * 强行映射成「新闻稿」会生成一篇完全不是发言稿的文章,比不映射更糟。 - * 注意:入参应当是「文体名」(键值或界面选择),不要传用户整句话。 - */ -export const normalizeDocType = (raw) => { - const value = String(raw || '').trim() - if (!value) { - return '' - } - return inferDocType(value) || value -} - -/** - * 从文本里抽取目标字数。 - * - * 覆盖「300字」「300字左右」「不少于500字」「800-1000字」等写法; - * 区间取上界(用户给区间时通常不希望写少)。50~10000 之外视为无效。 - */ -export const parseWordCount = (text) => { - const valid = (raw) => { - if (!raw) { - return undefined - } - const value = Number(raw) - return value >= 50 && value <= 10000 ? value : undefined - } - const source = String(text || '') - // 「键:值」入参里的字数常常就是一个裸数字(字数:500) - const bare = source.trim().match(/^(\d{2,5})\s*(?:字|字符)?$/) - if (bare) { - return valid(bare[1]) - } - const range = source.match(/(\d{2,5})\s*[-~~—-至到]\s*(\d{2,5})\s*(?:字|字符)/) - if (range) { - return valid(range[2]) - } - const single = source.match(/(\d{2,5})\s*(?:字|字符)/) - if (single) { - return valid(single[1]) - } - const target = source.match( - /(?:不少于|不低于|至少|不超过|大约|约|控制在|限制在|需要|要)\s*(\d{2,5})\s*(?:字|字符)?/ - ) - return valid(target ? target[1] : undefined) -} - -/** - * 主题清洗:把抽到的短语里混进来的时间、地点、指令动词和助词去掉。 - * 例如「2026年3月5日在学校礼堂举办的学习雷锋志愿服务活动」→「学习雷锋志愿服务活动」。 - */ -const tidyTheme = (value) => { - const cleaned = value - .replace(/^\s*(?:\d{4}\s*年)?\s*\d{1,2}\s*月\s*\d{1,2}\s*[日号]\s*/, '') - .replace(/^\s*(?:在|于)[^,。;!?\n]{2,20}?(?:举办|举行|开展|召开|进行)的?/, '') - .replace(/^\s*(?:开展|举办|举行|组织|进行|召开)/, '') - .replace(/(?:上的|中的|里的|的)$/, '') - .replace(/[,。;!?、\s]+$/, '') - .trim() - return cleaned || value.trim() -} - -/** 从「关于X的」「以X为主题」「写一篇X总结」等说法里抽取活动主题 */ -export const parseTheme = (query) => { - const source = String(query || '') - const patterns = [ - /关于[「“"'《]?([^,。;!?\n]{2,40}?)[」”"'》]?的/, - /以[「“"'《]?([^,。;!?\n]{2,40}?)[」”"'》]?(?:为主题|为题|为标题)/, - /(?:写|撰写|生成|创作|拟写|草拟|做)(?:一篇|一份|一段|一个|一则|条)?[「“"'《]?([^,。;!?\n]{2,40}?)[」”"'》]?(?:上的|中的|里的|的|之)?(?:新闻稿|通讯稿|通稿|报道|推文|总结|通知|公告|文案|稿件|宣传稿|发言稿|脚本|简讯)/, - /([^,。;!?\n]{2,30}?)(?:活动)?(?:总结|新闻稿|推文|通知|公告|报道|简讯)/ - ] - for (let index = 0; index < patterns.length; index += 1) { - const match = source.match(patterns[index]) - const value = match && match[1] ? match[1].trim() : '' - if (value) { - return tidyTheme(value) - } - } - return '' -} - -/** 兜底主题:清洗掉指令性词汇与字数要求后截断 */ -const fallbackTheme = (query) => { - const source = String(query || '') - const cleaned = source - .replace(/[\r\n]+/g, ' ') - .replace( - /(?:请|帮我|麻烦|辛苦|需要)?\s*(?:写|撰写|生成|创作|拟写|草拟|出一?)(?:一篇|一份|一段|一个|条)?/g, - '' - ) - .replace(/(?:关于|围绕|以)/g, '') - .replace(/\d{2,5}\s*(?:[-~~—-至到]\s*\d{2,5})?\s*(?:字|字符)(?:左右|以内|以下|以上)?/g, '') - .replace(/[,。;!?、,.;!?]/g, ' ') - .replace(/\s+/g, ' ') - .trim() - return (cleaned || source.trim()).slice(0, 30) -} - -const parseTime = (query) => { - const match = String(query || '').match(/(\d{4}\s*年)?\s*\d{1,2}\s*月\s*\d{1,2}\s*[日号]/) - return match ? match[0].replace(/\s+/g, '') : '' -} - -const parseLocation = (query) => { - const match = String(query || '').match( - /(?:在|于)\s*([^,。;!?\n]{2,20}?)\s*(?:举办|举行|开展|召开|进行)/ - ) - return match && match[1] ? match[1].trim() : '' -} - -const parseParticipants = (query) => { - const match = String(query || '').match(/面向\s*([^,。;!?\n]{2,20})/) - return match && match[1] ? match[1].trim() : '' -} - -const parseEventType = (query) => { - const source = String(query || '') - for (let index = 0; index < EVENT_TYPE_KEYWORDS.length; index += 1) { - if (source.includes(EVENT_TYPE_KEYWORDS[index])) { - return EVENT_TYPE_KEYWORDS[index] - } - } - return '' -} - -/** - * 把用户输入解析成上游工作流的入参。 - * - * @param {string} query 用户在写作框里输入的内容(自然语言或「键:值」) - * @param {{ docType?: string, number?: number }} overrides 界面上的显式选择,优先于自动识别 - */ -export const parseWritingRequest = (query, overrides) => { - const options = overrides || {} - const fields = parseKeyValueInput(query) - - const explicitDocType = options.docType && options.docType !== 'auto' ? options.docType : '' - const keyedDocType = normalizeDocType(pickField(fields, FIELD_ALIASES.docType)) - const inferredDocType = inferDocType(query) || DEFAULT_DOC_TYPE - const docType = explicitDocType || keyedDocType || inferredDocType - - const explicitNumber = - typeof options.number === 'number' && options.number > 0 ? options.number : undefined - const keyedNumber = parseWordCount(pickField(fields, FIELD_ALIASES.number)) - const inferredNumber = parseWordCount(query) - // 不用 ?? / ?: 链:0 是合法字数但被判为「无」,这里显式按 undefined 判定 - let number = explicitNumber - if (number === undefined) { - number = keyedNumber - } - if (number === undefined) { - number = inferredNumber - } - if (number === undefined) { - number = DEFAULT_WORD_COUNT_BY_DOC_TYPE[docType] - } - if (number === undefined) { - number = 800 - } - - const theme = pickField(fields, FIELD_ALIASES.theme) || parseTheme(query) || fallbackTheme(query) - - return { - docType, - eventType: pickField(fields, FIELD_ALIASES.eventType) || parseEventType(query) || DEFAULT_EVENT_TYPE, - theme, - time: pickField(fields, FIELD_ALIASES.time) || parseTime(query), - location: pickField(fields, FIELD_ALIASES.location) || parseLocation(query), - participants: pickField(fields, FIELD_ALIASES.participants) || parseParticipants(query), - // 「核心亮点与具体要求」保留用户原话:这里放原话比放抽取结果更贴合上游用途 - highlights: pickField(fields, FIELD_ALIASES.highlights) || query, - number - } -} diff --git a/src/pages/manuscript/index.vue b/src/pages/manuscript/index.vue index 88c15e6..60bd98c 100644 --- a/src/pages/manuscript/index.vue +++ b/src/pages/manuscript/index.vue @@ -5,13 +5,14 @@ AI CONTENT STUDIO AI 内容创作与宣传助手 - 面向团委宣传、活动报道、新闻稿和推文草稿的快速生成场景。填写创作条件后,页面会调用 - `/ai/manuscriptGen` 并通过 WebSocket 实时接收生成内容。 + 两种用法:不选模板(自由创作)时只需描述想写什么,由「AI 创意助手」生成;选了稿件类型后再展开活动信息等可选项, + 由「内容创作与宣传助手」按体裁生成。结果通过 WebSocket 实时回传。 活动新闻稿 公众号推文 发言稿提纲 + 自由创作示例 进入问答助手 @@ -19,17 +20,6 @@ 创作配置 - - 稿件标题 - - - 稿件类型 @@ -37,9 +27,22 @@ {{ form.docType || '请选择稿件类型' }} + 选「自由创作」= 不套稿件模板,只描述你想写什么;选具体模板后才会展开下方其它可选项。 - + + +