feat: 实现 Ghostwriter Protocol 思维链驱动指令集

- 重构 promptBuilder.js:实现完整的 Chain of Thought 系统提示词模板
- 新增 XML 输出解析器:parseGhostwriterOutput 和 createStreamParser
- 实现动态 Few-Shot:智能裁剪参考案例防止 Token 爆炸
- 更新 DeepSeekAPI:使用 Ghostwriter Protocol 系统提示词
- 更新 appStore:分离 thinking 和 draft 内容,新增流式解析状态机
- 更新 MainContent.vue:新增 AI 思考路径折叠面板,显示风格分析与大纲规划
- 新增生成阶段指示器:thinking -> draft -> critique -> refine
This commit is contained in:
empty
2026-01-08 11:25:40 +08:00
parent 0b80285c74
commit e8dcca25d8
4 changed files with 263 additions and 40 deletions

View File

@@ -1,32 +1,166 @@
// ============================================
// Ghostwriter Protocol - Chain of Thought Template
// ============================================
export const GHOSTWRITER_SYSTEM_PROMPT = `# Role
你是一名世界级的"影子写手"和"文体分析专家"。你的核心能力是完美复刻给定的写作风格,并将其应用于新的主题创作。
# Input Data
你将收到三部分输入:
1. <reference_material>: 需要模仿的目标范文(可能包含多篇)。
2. <constraints>: 必须遵守的硬性规范(格式、禁忌、字数)。
3. <user_task>: 用户希望撰写的具体内容要求。
# Process (Thinking Chain)
不要直接开始写作!你必须严格按照以下步骤进行思维推理:
Step 1: 【风格解构】 (Style Analysis)
仔细阅读 <reference_material>,从以下维度提取"风格指纹"
- 语调 (Tone): 是严肃、幽默、辛辣还是温情?
- 句式 (Sentence Structure): 喜欢长短句结合,还是大量使用从句?是否喜欢反问?
- 词汇 (Vocabulary): 偏向学术词汇、互联网黑话还是平实口语?
- 结构 (Structure): 文章的起承转合是如何安排的?
Step 2: 【大纲构建】 (Structural Planning)
基于 <user_task> 的要求,结合 Step 1 分析出的结构特点,规划文章大纲。
Step 3: 【草稿生成】 (Drafting)
撰写正文。在这一步,你需要时刻回看 Step 1 的分析,确保每一个段落的"味道"都与参考范文一致。
同时,必须严格遵守 <constraints> 中的所有要求。
Step 4: 【自我审查】 (Refinement)
检查生成的内容:
- 是否出现了 <constraints> 中禁止的词汇?
- 风格是否真的像参考范文?如果不够像,请进行局部重写。
# Output Format
请严格按照以下 XML 格式输出你的思考过程和最终结果:
<thinking>
在此处输出你的 Step 1 风格分析 和 Step 2 大纲规划。
简要说明你打算如何模仿这种风格。
</thinking>
<draft>
在此处输出最终的文章内容。
注意:仅输出正文,不要包含任何"好的,这是文章"之类的废话。
</draft>
`
// 动态 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 MessageXML 结构化数据)
export const buildPrompt = (task, constraints, references) => {
let prompt = `# Role\n你是一个资深的专业写作专家。你具备极强的风格模仿能力和逻辑组织能力,能够根据提供的参考资料和风格分析,创作出高度一致的高质量文稿。\n\n`;
const selectedRefs = selectRepresentativeReferences(references)
// 1. 注入规范与风格分析
prompt += `# Instructions & Style Constraints\n`;
prompt += `<global_rules>\n`;
constraints.forEach(tag => prompt += `- ${tag}\n`);
prompt += `</global_rules>\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 += `<case_${idx + 1} title="${ref.title}">${styleInfo}\n${ref.content}\n</case_${idx + 1}>\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 += `<task_content>\n${task}\n</task_content>\n\n`;
// 2. 处理约束条件
const constraintsText = constraints.length > 0
? constraints.map(c => `- ${c}`).join('\n')
: '- 无特殊约束'
// 3. 组装 XML 结构化 User Message
return `<reference_material>
${refText}
</reference_material>
<constraints>
${constraintsText}
</constraints>
<user_task>
${task}
</user_task>`
}
// XML 输出解析器 - 提取 <thinking> 和 <draft> 内容
export const parseGhostwriterOutput = (fullText) => {
const thinkingMatch = fullText.match(/<thinking>([\s\S]*?)<\/thinking>/)
const draftMatch = fullText.match(/<draft>([\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('<thinking>')) {
currentSection = 'thinking'
}
if (currentSection === 'thinking' && buffer.includes('</thinking>')) {
currentSection = 'between'
}
if ((currentSection === 'between' || currentSection === 'pre') && buffer.includes('<draft>')) {
currentSection = 'draft'
}
if (currentSection === 'draft' && buffer.includes('</draft>')) {
currentSection = 'post'
}
return { section: currentSection, buffer }
},
// 获取最终解析结果
getResult() {
return parseGhostwriterOutput(buffer)
},
// 获取当前 section
getCurrentSection() {
return currentSection
}
}
}
export const parseStreamResponse = (chunk) => {