Files
ai-write/src/stores/app.js
empty c3d47bb3aa feat: 实现结构化思维链注入 (Structured CoT Injection)
- paradigms.js: 为民主生活会添加专业级 System Prompt
  - 结构公式:定性判断 + 具体表现 + 后果分析
  - 关键词库:学用脱节、好人主义、本领恐慌等
  - 五维度法:思想、政治、作风、能力、纪律
- 大纲预填充:选择范式后自动生成五个带头框架
- promptBuilder.js: 新增 buildSystemPrompt() 支持范式专用 Prompt
- deepseek.js: generateContent 支持传入 paradigm 参数
- appStore.js: 传递 activeParadigm 到 API 层
2026-01-08 12:07:19 +08:00

434 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()
// 构建 PromptXML 结构化数据)
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
}
})