- paradigms.js: 为民主生活会添加专业级 System Prompt - 结构公式:定性判断 + 具体表现 + 后果分析 - 关键词库:学用脱节、好人主义、本领恐慌等 - 五维度法:思想、政治、作风、能力、纪律 - 大纲预填充:选择范式后自动生成五个带头框架 - promptBuilder.js: 新增 buildSystemPrompt() 支持范式专用 Prompt - deepseek.js: generateContent 支持传入 paradigm 参数 - appStore.js: 传递 activeParadigm 到 API 层
434 lines
14 KiB
JavaScript
434 lines
14 KiB
JavaScript
import { defineStore } from 'pinia'
|
||
import { ref } from 'vue'
|
||
import { config } from '../utils/config.js'
|
||
import DeepSeekAPI from '../api/deepseek.js'
|
||
import { buildPrompt, createStreamParser, parseGhostwriterOutput } from '../utils/promptBuilder.js'
|
||
import { PARADIGMS, getParadigmById, buildParadigmConstraints } from '../config/paradigms.js'
|
||
|
||
export const useAppStore = defineStore('app', () => {
|
||
// 页面状态
|
||
const currentPage = ref('writer') // 'writer' 或 'analysis'
|
||
|
||
// API配置 - 从环境变量读取
|
||
const apiUrl = ref(config.apiUrl)
|
||
const apiKey = ref(config.apiKey)
|
||
|
||
// 写作相关
|
||
const inputTask = ref('请帮我写一篇关于"AI 辅助编程如何改变软件开发流程"的博客文章,面向中级程序员。')
|
||
const inputType = ref('text') // 'text' 或 'outline'
|
||
const outlinePoints = ref({
|
||
topic: '',
|
||
audience: '',
|
||
keyPoints: ''
|
||
})
|
||
const references = ref([
|
||
{
|
||
title: '示例:技术博客风格.txt',
|
||
content: '本文深入探讨了...(此处省略2000字,这是为了让AI模仿这种干练的技术风格)...',
|
||
styleTags: ['#技术干货', '#逻辑严密', '#客观中立']
|
||
}
|
||
])
|
||
const selectedTags = ref(['Markdown格式', '总分总结构'])
|
||
const customConstraint = ref('')
|
||
|
||
// 范式相关
|
||
const activeParadigm = ref(null) // 当前激活的范式配置
|
||
const expertGuidelines = ref([]) // 专家指令列表
|
||
|
||
// 质检报告(深度模式)
|
||
const qualityReport = ref(null) // { checks: [{key, title, status, message}], overall: 'pass'|'warning'|'fail' }
|
||
|
||
// 生成相关
|
||
const isGenerating = ref(false)
|
||
const isDeepMode = ref(false)
|
||
const generationStage = ref('') // 'thinking' | 'draft' | 'critique' | 'refine' | ''
|
||
const generatedContent = ref('')
|
||
const thinkingContent = ref('') // AI 思考过程(风格分析 + 大纲规划)
|
||
const rawStreamBuffer = ref('') // 原始流式输出缓冲
|
||
|
||
// 分析相关
|
||
const analysisText = ref('')
|
||
const analysisResult = ref(null)
|
||
const isAnalyzing = ref(false)
|
||
const styleAnalysis = ref('') // 存储具体的风格分析内容
|
||
|
||
// UI状态
|
||
const showPromptDebug = ref(false)
|
||
const showRefInput = ref(false)
|
||
const newRefTitle = ref('')
|
||
const newRefContent = ref('')
|
||
|
||
// 方法
|
||
const addReferenceFromAnalysis = (title, content) => {
|
||
references.value.push({
|
||
title: title || `参考范文_${new Date().toLocaleDateString()}`,
|
||
content: content,
|
||
styleTags: [],
|
||
isAnalyzing: false
|
||
})
|
||
// 自动触发风格分析
|
||
analyzeReferenceStyleAction(references.value.length - 1)
|
||
}
|
||
|
||
// 分析参考案例风格动作
|
||
const analyzeReferenceStyleAction = async (index) => {
|
||
const ref = references.value[index]
|
||
if (!ref || !ref.content) return
|
||
|
||
if (!apiUrl.value || !apiKey.value || apiKey.value === 'YOUR_KEY') {
|
||
console.warn('Store: Missing API Key for style analysis')
|
||
return
|
||
}
|
||
|
||
// 如果已经有标签,跳过(或者强制刷新?这里假设自动触发仅一次)
|
||
if (ref.styleTags && ref.styleTags.length > 0) return
|
||
|
||
ref.isAnalyzing = true
|
||
try {
|
||
const api = new DeepSeekAPI({ url: apiUrl.value, key: apiKey.value })
|
||
let fullTags = ''
|
||
|
||
console.log(`Store: Analyzing style for reference [${index}]...`)
|
||
await api.extractStyle(ref.content, (content) => {
|
||
fullTags += content
|
||
})
|
||
|
||
// 提取标签 (假设返回格式为 "#标签1 #标签2")
|
||
const tags = fullTags.match(/#[\w\u4e00-\u9fa5]+/g) || []
|
||
ref.styleTags = tags
|
||
console.log('Store: Style tags extracted:', tags)
|
||
} catch (error) {
|
||
console.error('Store: Style analysis failed', error)
|
||
} finally {
|
||
ref.isAnalyzing = false
|
||
}
|
||
}
|
||
|
||
// 生成内容动作
|
||
const generateContentAction = async () => {
|
||
if (!apiUrl.value || !apiKey.value || apiKey.value === 'YOUR_KEY') {
|
||
throw new Error('请先配置 API 地址和 API Key')
|
||
}
|
||
|
||
console.log('Store: generateContentAction 启动', { mode: isDeepMode.value ? 'Deep' : 'Standard' })
|
||
isGenerating.value = true
|
||
generatedContent.value = ''
|
||
thinkingContent.value = ''
|
||
rawStreamBuffer.value = ''
|
||
generationStage.value = 'thinking'
|
||
|
||
try {
|
||
const api = new DeepSeekAPI({ url: apiUrl.value, key: apiKey.value })
|
||
const streamParser = createStreamParser()
|
||
|
||
// 构建 Prompt(XML 结构化数据)
|
||
const taskContent = inputType.value === 'outline'
|
||
? `核心主题:${outlinePoints.value.topic}\n目标受众:${outlinePoints.value.audience}\n关键观点:\n${outlinePoints.value.keyPoints}`
|
||
: inputTask.value
|
||
|
||
const userMessage = buildPrompt(
|
||
taskContent,
|
||
[...selectedTags.value, customConstraint.value].filter(Boolean),
|
||
references.value
|
||
)
|
||
|
||
console.log('Store: 开始 Ghostwriter Protocol 生成流程...', {
|
||
hasParadigm: !!activeParadigm.value,
|
||
paradigmName: activeParadigm.value?.name
|
||
})
|
||
|
||
// 流式接收并实时解析 XML 结构,同时实时显示内容
|
||
// 传入范式配置以使用专用 System Prompt
|
||
await api.generateContent(userMessage, (chunk) => {
|
||
rawStreamBuffer.value += chunk
|
||
const { section, buffer } = 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: 进入草稿生成阶段')
|
||
}
|
||
|
||
// 实时更新 UI 显示(流式输出)
|
||
// 提取当前已接收的 thinking 和 draft 内容
|
||
const thinkingMatch = buffer.match(/<thinking>([\s\S]*?)(?:<\/thinking>|$)/)
|
||
const draftMatch = buffer.match(/<draft>([\s\S]*?)(?:<\/draft>|$)/)
|
||
|
||
if (thinkingMatch) {
|
||
thinkingContent.value = thinkingMatch[1].trim()
|
||
}
|
||
if (draftMatch) {
|
||
generatedContent.value = draftMatch[1].trim()
|
||
}
|
||
|
||
// 如果 AI 没有按 XML 格式输出,直接显示原始内容
|
||
if (!thinkingMatch && !draftMatch && buffer.length > 50) {
|
||
generatedContent.value = buffer
|
||
}
|
||
}, { paradigm: activeParadigm.value })
|
||
|
||
// 流式完成后,最终解析确保内容完整
|
||
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. 质检阶段(如果有专家指令)
|
||
if (activeParadigm.value && expertGuidelines.value.length > 0) {
|
||
generationStage.value = 'inspect'
|
||
console.log('Store: 进入质检阶段...')
|
||
|
||
// 构建质检 Prompt
|
||
const guidelinesText = expertGuidelines.value
|
||
.map((g, i) => `${i + 1}. 【${g.title}】${g.description}`)
|
||
.join('\n')
|
||
|
||
const inspectPrompt = `你是一名严格的质检专家。请根据以下专家评价标准,逐条检查这篇文章的质量。
|
||
|
||
# 专家评价标准
|
||
${guidelinesText}
|
||
|
||
# 待检查的文章
|
||
${draft}
|
||
|
||
# 输出要求
|
||
请严格按照以下 JSON 格式输出检查结果(不要输出其他内容):
|
||
{
|
||
"checks": [
|
||
{"key": "对应的检查项英文key", "title": "检查项标题", "status": "pass|warning|fail", "message": "具体评价说明"}
|
||
],
|
||
"overall": "pass|warning|fail",
|
||
"summary": "整体评价总结(一句话)"
|
||
}`
|
||
|
||
let inspectResult = ''
|
||
await api.generateContent(inspectPrompt, (content) => {
|
||
inspectResult += content
|
||
}, { temperature: 0.3 })
|
||
|
||
// 解析质检结果
|
||
try {
|
||
// 提取 JSON(可能被包裹在 markdown 代码块中)
|
||
const jsonMatch = inspectResult.match(/\{[\s\S]*\}/)
|
||
if (jsonMatch) {
|
||
qualityReport.value = JSON.parse(jsonMatch[0])
|
||
console.log('Store: 质检完成', qualityReport.value)
|
||
}
|
||
} catch (e) {
|
||
console.warn('Store: 质检结果解析失败', e)
|
||
qualityReport.value = {
|
||
checks: expertGuidelines.value.map(g => ({
|
||
key: g.checkKey,
|
||
title: g.title,
|
||
status: 'warning',
|
||
message: '质检解析异常,请人工复核'
|
||
})),
|
||
overall: 'warning',
|
||
summary: '质检结果解析失败,建议人工复核'
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3. 批判阶段
|
||
generationStage.value = 'critique'
|
||
console.log('Store: 进入批判阶段...')
|
||
generatedContent.value += '\n\n---\n\n💡 **AI 深度反思 (Critique Stage)**:\n正在分析逻辑漏洞与改进空间...\n\n'
|
||
|
||
const critiquePrompt = `你是一个严厉的主编。请分析以下文章的逻辑漏洞、论证强度和风格一致性。请列出3-5条具体的修改建议。不要重写文章,只提供建议。\n\n文章内容:\n${draft}`
|
||
|
||
let critiqueContent = ''
|
||
await api.generateContent(critiquePrompt, (content) => {
|
||
generatedContent.value += content
|
||
critiqueContent += content
|
||
})
|
||
|
||
// 4. 修正阶段
|
||
generationStage.value = 'refine'
|
||
console.log('Store: 进入修正阶段...')
|
||
generatedContent.value += '\n\n---\n\n✨ **深度润色 (Refinement Stage)**:\n正在根据反思意见重写...\n\n'
|
||
|
||
const refinePrompt = `你是一个专业的写作专家。请根据以下修改建议,重写这篇文章。保持原文的优秀风格,同时修复提到的问题。\n\n原文:\n${draft}\n\n修改建议:\n${critiqueContent}\n\n请直接输出重写后的正文:`
|
||
|
||
await api.generateContent(refinePrompt, (content) => {
|
||
generatedContent.value += content
|
||
})
|
||
}
|
||
|
||
console.log('Store: 生成流程完成')
|
||
} catch (error) {
|
||
console.error('Store: 生成内容失败:', error)
|
||
throw error
|
||
} finally {
|
||
isGenerating.value = false
|
||
generationStage.value = ''
|
||
console.log('Store: isGenerating 重置为 false')
|
||
}
|
||
}
|
||
|
||
// 分析文章动作
|
||
const analyzeArticleAction = async (text, detectParadigmFn) => {
|
||
if (!text.trim()) {
|
||
throw new Error('请输入要分析的文章内容')
|
||
}
|
||
|
||
console.log('Store: analyzeArticleAction 启动')
|
||
isAnalyzing.value = true
|
||
analysisResult.value = {
|
||
paradigm: '分析中...',
|
||
paradigmType: null,
|
||
analysis: '',
|
||
timestamp: new Date()
|
||
}
|
||
|
||
try {
|
||
const api = new DeepSeekAPI({ url: apiUrl.value, key: apiKey.value })
|
||
let fullContent = ''
|
||
|
||
console.log('Store: 调用 API 分析文章...')
|
||
await api.analyzeContent(text, (content) => {
|
||
fullContent += content
|
||
// 实时更新分析结果,用于 UI 展示流式效果
|
||
analysisResult.value = {
|
||
paradigm: '分析中...',
|
||
paradigmType: null,
|
||
analysis: fullContent,
|
||
timestamp: new Date()
|
||
}
|
||
})
|
||
console.log('Store: 分析文章流式接收完成')
|
||
|
||
// 分析完成后检测范式类型
|
||
const detectedParadigm = detectParadigmFn(fullContent)
|
||
console.log('Store: 检测到范式类型:', detectedParadigm.name)
|
||
|
||
analysisResult.value = {
|
||
paradigm: detectedParadigm.name,
|
||
paradigmType: detectedParadigm.type,
|
||
analysis: fullContent,
|
||
timestamp: new Date()
|
||
}
|
||
|
||
return { paradigm: detectedParadigm.name, content: fullContent }
|
||
} catch (error) {
|
||
console.error('Store: 分析失败:', error)
|
||
analysisResult.value = {
|
||
error: true,
|
||
message: error.message
|
||
}
|
||
throw error
|
||
} finally {
|
||
isAnalyzing.value = false
|
||
console.log('Store: isAnalyzing 重置为 false')
|
||
}
|
||
}
|
||
|
||
// 切换页面
|
||
const switchPage = (page) => {
|
||
currentPage.value = page
|
||
}
|
||
|
||
// 加载范式预设
|
||
const loadParadigmPreset = (paradigmId) => {
|
||
const paradigm = getParadigmById(paradigmId)
|
||
if (!paradigm) {
|
||
console.warn('Store: Paradigm not found:', paradigmId)
|
||
return
|
||
}
|
||
|
||
console.log('Store: Loading paradigm preset:', paradigm.name)
|
||
|
||
// 1. 设置当前范式
|
||
activeParadigm.value = paradigm
|
||
expertGuidelines.value = paradigm.expertGuidelines || []
|
||
|
||
// 2. 预填充大纲模板(如果有)
|
||
if (paradigm.outlineTemplate) {
|
||
inputType.value = 'outline'
|
||
inputTask.value = paradigm.outlineTemplate
|
||
} else {
|
||
inputType.value = 'text'
|
||
inputTask.value = ''
|
||
}
|
||
|
||
// 3. 自动挂载默认参考案例
|
||
if (paradigm.defaultReference) {
|
||
references.value = [{
|
||
title: paradigm.defaultReference.title,
|
||
content: paradigm.defaultReference.content,
|
||
styleTags: [],
|
||
isAnalyzing: false
|
||
}]
|
||
}
|
||
|
||
// 4. 自动选中推荐标签
|
||
if (paradigm.recommendedTags) {
|
||
selectedTags.value = [...paradigm.recommendedTags]
|
||
}
|
||
|
||
// 5. 注入系统约束到自定义要求(简化版,完整版在 specializedPrompt)
|
||
if (paradigm.systemConstraints?.length > 0) {
|
||
customConstraint.value = paradigm.systemConstraints.join(';')
|
||
}
|
||
|
||
// 6. 清空之前的生成内容
|
||
generatedContent.value = ''
|
||
thinkingContent.value = ''
|
||
qualityReport.value = null
|
||
|
||
// 7. 跳转到写作页面
|
||
currentPage.value = 'writer'
|
||
}
|
||
|
||
// 清除当前范式
|
||
const clearParadigm = () => {
|
||
activeParadigm.value = null
|
||
expertGuidelines.value = []
|
||
qualityReport.value = null
|
||
}
|
||
|
||
return {
|
||
// 状态
|
||
currentPage,
|
||
apiUrl,
|
||
apiKey,
|
||
inputTask,
|
||
inputType,
|
||
outlinePoints,
|
||
references,
|
||
selectedTags,
|
||
customConstraint,
|
||
activeParadigm,
|
||
expertGuidelines,
|
||
qualityReport,
|
||
isGenerating,
|
||
isDeepMode,
|
||
generationStage,
|
||
generatedContent,
|
||
thinkingContent,
|
||
rawStreamBuffer,
|
||
analysisText,
|
||
analysisResult,
|
||
isAnalyzing,
|
||
styleAnalysis,
|
||
showPromptDebug,
|
||
showRefInput,
|
||
newRefTitle,
|
||
newRefContent,
|
||
|
||
// 方法
|
||
switchPage,
|
||
addReferenceFromAnalysis,
|
||
generateContentAction,
|
||
analyzeArticleAction,
|
||
loadParadigmPreset,
|
||
clearParadigm
|
||
}
|
||
})
|