forked from b2894lxlx/tuanwei-admin
fix(ai): 写作入口采集上游主控字段,用户诉求不再被默认值覆盖(issue 13)
聊天页「写作」模式没有表单,只按「键:值」解析输入,普通自然语言解析为空后一律回落 doc_type=新闻稿 / Number=800。上游「AI内容创作与宣传助手」的 prompt 把这两项当主控指令, 实测模型会主动忽略用户原话里的冲突要求,思维链原文: I will ignore the conflicting "300字推文" instruction inside the bullet points because the main task says "文稿类型为:新闻稿" and "篇幅要求(字数限制):800" 这就是用户报的「写作模块听不懂要求」——不是模型听不懂,是把「新闻稿/800字」当硬指令发给了它。 - 新增 src/views/oa/chatgpt/writing-params.ts:从自然语言抽取文体/字数/主题/时间/地点/ 人员/活动类型,保留既有「键:值」入参,界面显式选择优先于自动识别;纯函数零依赖, 可直接用 node 运行测试 - 文体识别对齐上游真实存在的 4 个模板(新闻稿/活动总结/微信推文/通知公告);认不出的文体 (发言稿、短视频脚本等)原样下发交给模型发挥,强行映射成新闻稿会更糟 - 用户没提字数时按文体给常规篇幅,而不是不下发——上游对空字数的处理会在正文里留一行 「(未设定有效字数限制)」 - 写作模式输入框上方新增「文体 / 字数」两组按钮,复用既有 ratio-panel 样式 验证:解析用例 8/8(.scratch/ai-assistant/probe/test-writing-params.ts);同一句输入做端到端 对照,修复前得 800 字新闻稿,修复后得 281 字微信推文(引用块 + ## 模块小标题 + 【互动时间】)。
This commit is contained in:
@@ -213,6 +213,32 @@
|
||||
<span>{{ assistantTitle }}</span>
|
||||
</div>
|
||||
<div class="input-shell">
|
||||
<div v-if="activeChatMode === 'writing'" class="ratio-panel">
|
||||
<span class="ratio-label">文体</span>
|
||||
<button
|
||||
v-for="item in WRITING_DOC_TYPE_OPTIONS"
|
||||
:key="item.value"
|
||||
class="ratio-option"
|
||||
type="button"
|
||||
:class="{ 'is-active': writingDocType === item.value }"
|
||||
:disabled="loading"
|
||||
@click="writingDocType = item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</button>
|
||||
<span class="ratio-label">字数</span>
|
||||
<button
|
||||
v-for="item in WRITING_WORD_COUNT_OPTIONS"
|
||||
:key="item.value"
|
||||
class="ratio-option"
|
||||
type="button"
|
||||
:class="{ 'is-active': writingWordCount === item.value }"
|
||||
:disabled="loading"
|
||||
@click="writingWordCount = item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="activeChatMode === 'image'" class="ratio-panel">
|
||||
<span class="ratio-label">图片比例</span>
|
||||
<button
|
||||
@@ -316,6 +342,12 @@ import { listAiChatList, removeAiChatList } from "@/api/ai/aiChatList";
|
||||
import type { AiChatList } from "@/api/ai/aiChatList/model";
|
||||
import { sendMessage, stopMessage } from "@/api/oa/chatgpt";
|
||||
import { manuscriptGen } from "@/api/ai/manuscriptGen";
|
||||
import {
|
||||
parseWritingRequest,
|
||||
WRITING_DOC_TYPE_OPTIONS,
|
||||
WRITING_WORD_COUNT_OPTIONS
|
||||
} from "./writing-params";
|
||||
import type { WritingOverrides } from "./writing-params";
|
||||
import { creativeAssistant } from "@/api/ai/creativeAssistant";
|
||||
import { API_BASE_URL } from "@/config/setting";
|
||||
import type { Menu } from "@/api/system/menu/model";
|
||||
@@ -360,6 +392,9 @@ const stopping = ref(false);
|
||||
const content = ref("");
|
||||
const activeChatMode = ref<ChatMode>("chat");
|
||||
const imageRatio = ref("1:1");
|
||||
// 写作模式的显式选择:默认 auto,即由 writing-params 从用户输入里自动识别
|
||||
const writingDocType = ref("auto");
|
||||
const writingWordCount = ref("auto");
|
||||
const wsConnected = ref(false);
|
||||
const deletingChatId = ref<number>();
|
||||
const messages = ref<ChatMessageItem[]>([]);
|
||||
@@ -413,7 +448,7 @@ const imageRatioOptions = [
|
||||
|
||||
const inputPlaceholder = computed(() => {
|
||||
if (activeChatMode.value === "writing") {
|
||||
return "请输入写作需求,例如新闻稿、推文文案、小论文、发言稿,也可以补充标题、受众、字数和风格";
|
||||
return "直接描述需求即可,例如:帮我写一篇300字关于迎新晚会的推文,风格活泼,面向大一新生(也可在下方直接指定文体和字数)";
|
||||
}
|
||||
if (activeChatMode.value === "image") {
|
||||
return "请输入图片生成需求,例如校园活动海报、公众号头图、志愿服务插画,并选择图片比例";
|
||||
@@ -960,32 +995,19 @@ const pickString = (...values: unknown[]) => {
|
||||
return "";
|
||||
};
|
||||
|
||||
const parseKeyValueInput = (value: string) => {
|
||||
const result: Record<string, 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 buildWritingPayload = (query: string) => {
|
||||
const fields = parseKeyValueInput(query);
|
||||
// 界面上显式选了文体/字数就用显式值,否则由 writing-params 从自然语言里识别。
|
||||
// 过去这里没有表单,一律回落 doc_type=新闻稿 / Number=800,而上游提示词把这两项
|
||||
// 当作主控指令,会直接压掉用户原话里的诉求(实测见 .scratch/ai-assistant/probe/)。
|
||||
const overrides: WritingOverrides = {};
|
||||
if (writingDocType.value !== "auto") {
|
||||
overrides.docType = writingDocType.value;
|
||||
}
|
||||
if (writingWordCount.value !== "auto") {
|
||||
overrides.number = Number(writingWordCount.value);
|
||||
}
|
||||
return {
|
||||
docType: fields["稿件类型"] || fields["文体"] || fields["类型"] || "新闻稿",
|
||||
eventType: fields["活动类型"] || fields["场景"] || "校园内容",
|
||||
theme: fields["稿件标题"] || fields["标题"] || fields["主题"] || query,
|
||||
time: fields["活动时间"] || fields["时间"] || "",
|
||||
location: fields["活动地点"] || fields["地点"] || "",
|
||||
participants: fields["参与人员"] || fields["受众"] || fields["人员"] || "",
|
||||
highlights: fields["亮点内容"] || fields["要点"] || fields["亮点"] || fields["内容"] || query,
|
||||
number: Number(fields["建议篇幅"] || fields["字数"] || 800),
|
||||
...parseWritingRequest(query, overrides),
|
||||
files: []
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user