From e8dcca25d84d3c1f13b80ab4a243b813f2121d3e Mon Sep 17 00:00:00 2001 From: empty Date: Thu, 8 Jan 2026 11:25:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20Ghostwriter=20Prot?= =?UTF-8?q?ocol=20=E6=80=9D=E7=BB=B4=E9=93=BE=E9=A9=B1=E5=8A=A8=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 promptBuilder.js:实现完整的 Chain of Thought 系统提示词模板 - 新增 XML 输出解析器:parseGhostwriterOutput 和 createStreamParser - 实现动态 Few-Shot:智能裁剪参考案例防止 Token 爆炸 - 更新 DeepSeekAPI:使用 Ghostwriter Protocol 系统提示词 - 更新 appStore:分离 thinking 和 draft 内容,新增流式解析状态机 - 更新 MainContent.vue:新增 AI 思考路径折叠面板,显示风格分析与大纲规划 - 新增生成阶段指示器:thinking -> draft -> critique -> refine --- src/api/deepseek.js | 5 +- src/components/MainContent.vue | 69 ++++++++++++- src/stores/app.js | 49 ++++++--- src/utils/promptBuilder.js | 180 ++++++++++++++++++++++++++++----- 4 files changed, 263 insertions(+), 40 deletions(-) diff --git a/src/api/deepseek.js b/src/api/deepseek.js index acd6ff3..15ead04 100644 --- a/src/api/deepseek.js +++ b/src/api/deepseek.js @@ -1,3 +1,5 @@ +import { GHOSTWRITER_SYSTEM_PROMPT } from '../utils/promptBuilder.js' + class DeepSeekAPI { constructor(config) { this.baseURL = config.url @@ -87,10 +89,11 @@ class DeepSeekAPI { } async generateContent(prompt, onContent, options = {}) { + // 使用 Ghostwriter Protocol 系统提示词 return this._streamRequest([ { role: 'system', - content: '你是一个资深的专业写作助手,请严格按照用户的要求进行创作。' + content: GHOSTWRITER_SYSTEM_PROMPT }, { role: 'user', diff --git a/src/components/MainContent.vue b/src/components/MainContent.vue index 9b11d40..c50d200 100644 --- a/src/components/MainContent.vue +++ b/src/components/MainContent.vue @@ -44,9 +44,48 @@

在左侧配置参数,点击生成开始写作

-
-
- +
+ +
+ +
+
+
+
+
+
+ + +
+ + {{ stageLabel }} +
+ + +
+
+ +
@@ -137,6 +176,8 @@ const { currentPage, showPromptDebug, generatedContent, + thinkingContent, + generationStage, isGenerating, analysisResult, inputTask, @@ -145,6 +186,10 @@ const { references } = storeToRefs(appStore) +// AI 思考过程折叠状态 +import { ref } from 'vue' +const isThinkingExpanded = ref(false) + // 范式定义 const paradigms = [ { @@ -214,7 +259,23 @@ const constructedPrompt = computed(() => { // 渲染Markdown const renderedMarkdown = computed(() => { - return marked.parse(generatedContent.value) + return marked.parse(generatedContent.value || '') +}) + +// 渲染 AI 思考过程 +const renderedThinking = computed(() => { + return marked.parse(thinkingContent.value || '') +}) + +// 生成阶段标签 +const stageLabel = computed(() => { + const labels = { + 'thinking': '🧠 风格解构 & 大纲规划中...', + 'draft': '✍️ 正在撰写初稿...', + 'critique': '💡 AI 深度反思中...', + 'refine': '✨ 深度润色中...' + } + return labels[generationStage.value] || '' }) // 复制内容 diff --git a/src/stores/app.js b/src/stores/app.js index 05c674e..3c30356 100644 --- a/src/stores/app.js +++ b/src/stores/app.js @@ -2,7 +2,7 @@ import { defineStore } from 'pinia' import { ref } from 'vue' import { config } from '../utils/config.js' import DeepSeekAPI from '../api/deepseek.js' -import { buildPrompt } from '../utils/promptBuilder.js' +import { buildPrompt, createStreamParser, parseGhostwriterOutput } from '../utils/promptBuilder.js' export const useAppStore = defineStore('app', () => { // 页面状态 @@ -33,8 +33,10 @@ export const useAppStore = defineStore('app', () => { // 生成相关 const isGenerating = ref(false) const isDeepMode = ref(false) - const generationStage = ref('') // 'draft' | 'critique' | 'refine' | '' + const generationStage = ref('') // 'thinking' | 'draft' | 'critique' | 'refine' | '' const generatedContent = ref('') + const thinkingContent = ref('') // AI 思考过程(风格分析 + 大纲规划) + const rawStreamBuffer = ref('') // 原始流式输出缓冲 // 分析相关 const analysisText = ref('') @@ -103,29 +105,50 @@ export const useAppStore = defineStore('app', () => { console.log('Store: generateContentAction 启动', { mode: isDeepMode.value ? 'Deep' : 'Standard' }) isGenerating.value = true generatedContent.value = '' - generationStage.value = 'draft' + thinkingContent.value = '' + rawStreamBuffer.value = '' + generationStage.value = 'thinking' try { const api = new DeepSeekAPI({ url: apiUrl.value, key: apiKey.value }) + const streamParser = createStreamParser() - // 构建初稿 Prompt + // 构建 Prompt(XML 结构化数据) const taskContent = inputType.value === 'outline' ? `核心主题:${outlinePoints.value.topic}\n目标受众:${outlinePoints.value.audience}\n关键观点:\n${outlinePoints.value.keyPoints}` : inputTask.value - const draftPrompt = buildPrompt( + const userMessage = buildPrompt( taskContent, [...selectedTags.value, customConstraint.value].filter(Boolean), references.value ) - console.log('Store: 生成初稿...') - let draftContent = '' - await api.generateContent(draftPrompt, (content) => { - generatedContent.value += content - draftContent += content + console.log('Store: 开始 Ghostwriter Protocol 生成流程...') + + // 流式接收并实时解析 XML 结构 + await api.generateContent(userMessage, (chunk) => { + rawStreamBuffer.value += chunk + const { section } = streamParser.process(chunk) + + // 根据当前 section 更新 UI 状态 + if (section === 'thinking' && generationStage.value !== 'thinking') { + generationStage.value = 'thinking' + console.log('Store: 进入思考阶段 (Style Analysis + Planning)') + } + if (section === 'draft' && generationStage.value !== 'draft') { + generationStage.value = 'draft' + console.log('Store: 进入草稿生成阶段') + } }) + // 流式完成后,解析最终结果 + const { thinking, draft, hasStructuredOutput } = streamParser.getResult() + console.log('Store: XML 解析完成', { hasStructuredOutput, thinkingLength: thinking.length, draftLength: draft.length }) + + thinkingContent.value = thinking + generatedContent.value = draft + // 如果是深度模式,进入 Agentic Workflow if (isDeepMode.value) { // 2. 批判阶段 @@ -133,7 +156,7 @@ export const useAppStore = defineStore('app', () => { console.log('Store: 进入批判阶段...') generatedContent.value += '\n\n---\n\n💡 **AI 深度反思 (Critique Stage)**:\n正在分析逻辑漏洞与改进空间...\n\n' - const critiquePrompt = `你是一个严厉的主编。请分析以下文章的逻辑漏洞、论证强度和风格一致性。请列出3-5条具体的修改建议。不要重写文章,只提供建议。\n\n文章内容:\n${draftContent}` + const critiquePrompt = `你是一个严厉的主编。请分析以下文章的逻辑漏洞、论证强度和风格一致性。请列出3-5条具体的修改建议。不要重写文章,只提供建议。\n\n文章内容:\n${draft}` let critiqueContent = '' await api.generateContent(critiquePrompt, (content) => { @@ -146,7 +169,7 @@ export const useAppStore = defineStore('app', () => { console.log('Store: 进入修正阶段...') generatedContent.value += '\n\n---\n\n✨ **深度润色 (Refinement Stage)**:\n正在根据反思意见重写...\n\n' - const refinePrompt = `你是一个专业的写作专家。请根据以下修改建议,重写这篇文章。保持原文的优秀风格,同时修复提到的问题。\n\n原文:\n${draftContent}\n\n修改建议:\n${critiqueContent}\n\n请直接输出重写后的正文:` + const refinePrompt = `你是一个专业的写作专家。请根据以下修改建议,重写这篇文章。保持原文的优秀风格,同时修复提到的问题。\n\n原文:\n${draft}\n\n修改建议:\n${critiqueContent}\n\n请直接输出重写后的正文:` await api.generateContent(refinePrompt, (content) => { generatedContent.value += content @@ -241,6 +264,8 @@ export const useAppStore = defineStore('app', () => { isDeepMode, generationStage, generatedContent, + thinkingContent, + rawStreamBuffer, analysisText, analysisResult, isAnalyzing, diff --git a/src/utils/promptBuilder.js b/src/utils/promptBuilder.js index b8f9732..020fc25 100644 --- a/src/utils/promptBuilder.js +++ b/src/utils/promptBuilder.js @@ -1,32 +1,166 @@ +// ============================================ +// Ghostwriter Protocol - Chain of Thought Template +// ============================================ + +export const GHOSTWRITER_SYSTEM_PROMPT = `# Role +你是一名世界级的"影子写手"和"文体分析专家"。你的核心能力是完美复刻给定的写作风格,并将其应用于新的主题创作。 + +# Input Data +你将收到三部分输入: +1. : 需要模仿的目标范文(可能包含多篇)。 +2. : 必须遵守的硬性规范(格式、禁忌、字数)。 +3. : 用户希望撰写的具体内容要求。 + +# Process (Thinking Chain) +不要直接开始写作!你必须严格按照以下步骤进行思维推理: + +Step 1: 【风格解构】 (Style Analysis) +仔细阅读 ,从以下维度提取"风格指纹": +- 语调 (Tone): 是严肃、幽默、辛辣还是温情? +- 句式 (Sentence Structure): 喜欢长短句结合,还是大量使用从句?是否喜欢反问? +- 词汇 (Vocabulary): 偏向学术词汇、互联网黑话还是平实口语? +- 结构 (Structure): 文章的起承转合是如何安排的? + +Step 2: 【大纲构建】 (Structural Planning) +基于 的要求,结合 Step 1 分析出的结构特点,规划文章大纲。 + +Step 3: 【草稿生成】 (Drafting) +撰写正文。在这一步,你需要时刻回看 Step 1 的分析,确保每一个段落的"味道"都与参考范文一致。 +同时,必须严格遵守 中的所有要求。 + +Step 4: 【自我审查】 (Refinement) +检查生成的内容: +- 是否出现了 中禁止的词汇? +- 风格是否真的像参考范文?如果不够像,请进行局部重写。 + +# Output Format +请严格按照以下 XML 格式输出你的思考过程和最终结果: + + +在此处输出你的 Step 1 风格分析 和 Step 2 大纲规划。 +简要说明你打算如何模仿这种风格。 + + + +在此处输出最终的文章内容。 +注意:仅输出正文,不要包含任何"好的,这是文章"之类的废话。 + +` + +// 动态 Few-Shot 配置 +const MAX_REFERENCES = 3 +const MAX_REFERENCE_LENGTH = 2000 + +// 智能裁剪参考案例 +const selectRepresentativeReferences = (references) => { + if (!references || references.length === 0) return [] + if (references.length <= MAX_REFERENCES) { + return references.map(ref => ({ + ...ref, + content: ref.content.length > MAX_REFERENCE_LENGTH + ? ref.content.substring(0, MAX_REFERENCE_LENGTH) + '...(已截断)' + : ref.content + })) + } + // 优先选择有风格标签的、内容较长的 + const sorted = [...references].sort((a, b) => { + const aScore = (a.styleTags?.length || 0) * 10 + Math.min(a.content.length, 500) + const bScore = (b.styleTags?.length || 0) * 10 + Math.min(b.content.length, 500) + return bScore - aScore + }) + return sorted.slice(0, MAX_REFERENCES).map(ref => ({ + ...ref, + content: ref.content.length > MAX_REFERENCE_LENGTH + ? ref.content.substring(0, MAX_REFERENCE_LENGTH) + '...(已截断)' + : ref.content + })) +} + +// 构建 User Message(XML 结构化数据) export const buildPrompt = (task, constraints, references) => { - let prompt = `# Role\n你是一个资深的专业写作专家。你具备极强的风格模仿能力和逻辑组织能力,能够根据提供的参考资料和风格分析,创作出高度一致的高质量文稿。\n\n`; + const selectedRefs = selectRepresentativeReferences(references) - // 1. 注入规范与风格分析 - prompt += `# Instructions & Style Constraints\n`; - prompt += `\n`; - constraints.forEach(tag => prompt += `- ${tag}\n`); - prompt += `\n\n`; - - // 2. 注入参考案例 (Few-Shot) - if (references.length > 0) { - prompt += `# Style Reference Cases\n`; - prompt += `请深度学习并模仿以下参考资料的语调、用词习惯、句式结构和情感色彩:\n`; - references.forEach((ref, idx) => { - const styleInfo = ref.styleTags && ref.styleTags.length > 0 - ? `\nStyle Tags: ${ref.styleTags.join(', ')}` - : ''; - prompt += `${styleInfo}\n${ref.content}\n\n\n`; - }); + // 1. 处理参考范文 + let refText + if (selectedRefs.length > 0) { + refText = selectedRefs.map((ref, idx) => { + const styleInfo = ref.styleTags?.length > 0 + ? `\n[已识别风格: ${ref.styleTags.join(', ')}]` + : '' + return `--- 范文 ${idx + 1}: ${ref.title} ---${styleInfo}\n${ref.content}` + }).join('\n\n') + } else { + refText = '无特定参考风格,请使用清晰、专业的通用风格。' } - // 3. 注入用户任务 - prompt += `# Current Writing Task\n`; - prompt += `请基于上述所有风格约束和参考案例,执行以下写作任务:\n`; - prompt += `\n${task}\n\n\n`; + // 2. 处理约束条件 + const constraintsText = constraints.length > 0 + ? constraints.map(c => `- ${c}`).join('\n') + : '- 无特殊约束' + + // 3. 组装 XML 结构化 User Message + return ` +${refText} + + + +${constraintsText} + + + +${task} +` +} + +// XML 输出解析器 - 提取 内容 +export const parseGhostwriterOutput = (fullText) => { + const thinkingMatch = fullText.match(/([\s\S]*?)<\/thinking>/) + const draftMatch = fullText.match(/([\s\S]*?)<\/draft>/) - prompt += `直接开始输出正文内容,无需任何开场白或解释。`; + return { + thinking: thinkingMatch ? thinkingMatch[1].trim() : '', + draft: draftMatch ? draftMatch[1].trim() : fullText.trim(), + hasStructuredOutput: !!(thinkingMatch && draftMatch) + } +} + +// 实时流式解析状态机 +export const createStreamParser = () => { + let buffer = '' + let currentSection = 'pre' // 'pre' | 'thinking' | 'between' | 'draft' | 'post' - return prompt; + return { + // 处理新的流式内容片段 + process(chunk) { + buffer += chunk + + // 检测状态转换 + if (currentSection === 'pre' && buffer.includes('')) { + currentSection = 'thinking' + } + if (currentSection === 'thinking' && buffer.includes('')) { + currentSection = 'between' + } + if ((currentSection === 'between' || currentSection === 'pre') && buffer.includes('')) { + currentSection = 'draft' + } + if (currentSection === 'draft' && buffer.includes('')) { + currentSection = 'post' + } + + return { section: currentSection, buffer } + }, + + // 获取最终解析结果 + getResult() { + return parseGhostwriterOutput(buffer) + }, + + // 获取当前 section + getCurrentSection() { + return currentSection + } + } } export const parseStreamResponse = (chunk) => {