feat: 范式检查和复检功能支持智能粒度过滤和批量处理
## 核心功能 - 范式检查和复检支持自动分批处理(15句/批) - 智能粒度过滤:根据选中句子数量自动调整检查粒度 * 1-5句:仅句子级检查(术语、语气、表达) * 6-20句:句子级+段落级检查(逻辑结构、递进关系) * 21+句:全文级检查(章节完整性、篇幅占比等) ## 新增文件 - src/stores/paradigm.js:自定义范式管理 store * inferScope:智能推断检查项粒度 * ensureGuidelinesHasScope:为旧范式补充 scope 字段 - src/utils/requirementParser.js:需求文档解析工具 * buildRequirementParserPrompt:生成解析 prompt * validateParadigm:范式验证(兼容字符串/对象格式) - src/components/RequirementParserPanel.vue:需求解析面板 * 支持粘贴文本或选择文件 * AI 自动生成范式配置(specializedPrompt + expertGuidelines) * 可视化编辑检查项(title/description/scope) ## 主要改进 - ArticleRewritePanel.vue: * 范式检查支持粒度过滤和批量处理 * 复检功能支持粒度过滤和批量处理 * 集成 paradigm store 自动补充 scope 字段 * 添加批次进度显示 - RequirementParserPanel.vue: * 修复对象格式 expertGuidelines 显示问题 * 支持编辑 title/description/scope 三个字段 * 添加粒度下拉选择器(句子级/段落级/全文级) ## 技术细节 - 向后兼容:支持字符串和对象两种 expertGuidelines 格式 - 自动转换:旧范式字符串格式自动转换为对象格式并推断 scope - 错误处理:JSON 解析失败时提供降级方案 - 控制台日志:[复检] 前缀标识复检相关日志 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
199
src/utils/requirementParser.js
Normal file
199
src/utils/requirementParser.js
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 需求文档解析工具
|
||||
* 将需求文档转换为范式配置
|
||||
*/
|
||||
|
||||
/**
|
||||
* 构建需求解析的 Prompt
|
||||
*/
|
||||
export function buildRequirementParserPrompt(requirementText) {
|
||||
return `你是一位专业的文档分析专家,擅长提取文档中的核心要求并将其转化为结构化的写作范式配置。
|
||||
|
||||
【任务】
|
||||
分析以下需求文档,提取关键要求并生成"范式配置",用于指导AI检查和润色文稿。
|
||||
|
||||
【需求文档】
|
||||
${requirementText}
|
||||
|
||||
【输出要求】
|
||||
请生成以下三个部分,使用JSON格式输出:
|
||||
|
||||
1. **specializedPrompt** (string): 系统提示词,包含:
|
||||
- 会议主题/文档目标
|
||||
- 核心要求(列表形式)
|
||||
- 论述规范(结构要求、术语规范、语气要求等)
|
||||
- 维度要求(如五维度溯源等)
|
||||
长度:300-500字
|
||||
|
||||
2. **expertGuidelines** (array): 专家检查指令,每条指令应该:
|
||||
- 针对需求文档中的具体要求
|
||||
- 可直接用于检查文稿是否符合标准
|
||||
- 清晰、可执行
|
||||
- **包含 scope 字段**(指定检查粒度):
|
||||
* "sentence" - 句子级检查(如术语规范、语气分寸、表达方式)
|
||||
* "paragraph" - 段落级检查(如逻辑结构、递进关系、论述层次)
|
||||
* "document" - 全文级检查(如章节完整性、篇幅占比、结构要求)
|
||||
数量:8-12条
|
||||
|
||||
3. **metadata** (object): 元数据,包含:
|
||||
- name (string): 范式名称(简短,10字以内)
|
||||
- description (string): 范式描述(30字以内)
|
||||
- keyRequirements (array): 核心要求关键词(3-5个)
|
||||
|
||||
【输出格式】
|
||||
\`\`\`json
|
||||
{
|
||||
"specializedPrompt": "你是一位资深的...",
|
||||
"expertGuidelines": [
|
||||
{"title": "党内术语规范", "description": "检查是否使用...", "scope": "sentence"},
|
||||
{"title": "递进式结构", "description": "检查段落是否符合...", "scope": "paragraph"},
|
||||
{"title": "章节完整性", "description": "检查是否包含会前准备...", "scope": "document"},
|
||||
...
|
||||
],
|
||||
"metadata": {
|
||||
"name": "范式名称",
|
||||
"description": "范式描述",
|
||||
"keyRequirements": ["要求1", "要求2", "要求3"]
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
【注意事项】
|
||||
1. specializedPrompt 应该完整、系统,涵盖所有关键要求
|
||||
2. expertGuidelines 应该具体、可操作,每条针对一个检查点
|
||||
3. 保留原文中的专业术语和标准表述
|
||||
4. 如果需求文档提到篇幅要求、格式要求等,务必在 specializedPrompt 中明确体现
|
||||
5. **scope 分配原则**:
|
||||
- sentence 级:适用于任何句子片段(1-5句)
|
||||
- paragraph 级:需要段落上下文(6-20句)
|
||||
- document 级:需要完整文档或大段落(21+句)
|
||||
|
||||
请开始分析并生成范式配置。`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析AI返回的范式配置
|
||||
* @param {string} aiResponse - AI返回的文本
|
||||
* @returns {Object|null} 解析后的范式配置,失败返回null
|
||||
*/
|
||||
export function parseParadigmConfig(aiResponse) {
|
||||
try {
|
||||
// 调试:输出原始响应
|
||||
console.log('AI 原始响应长度:', aiResponse.length);
|
||||
console.log('AI 原始响应内容(前500字符):', aiResponse.substring(0, 500));
|
||||
|
||||
// 检查响应是否为空
|
||||
if (!aiResponse || aiResponse.trim().length === 0) {
|
||||
console.error('AI 返回内容为空');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试提取 JSON 代码块
|
||||
const jsonMatch = aiResponse.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
let jsonText = jsonMatch ? jsonMatch[1] : aiResponse;
|
||||
|
||||
console.log('提取的 JSON 文本(前200字符):', jsonText.substring(0, 200));
|
||||
|
||||
// 移除可能的注释
|
||||
jsonText = jsonText.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*/g, '');
|
||||
|
||||
const config = JSON.parse(jsonText);
|
||||
|
||||
// 验证必需字段
|
||||
if (!config.specializedPrompt || !Array.isArray(config.expertGuidelines) || !config.metadata) {
|
||||
console.error('配置缺少必需字段:', {
|
||||
hasPrompt: !!config.specializedPrompt,
|
||||
hasGuidelines: Array.isArray(config.expertGuidelines),
|
||||
hasMetadata: !!config.metadata
|
||||
});
|
||||
throw new Error('缺少必需字段');
|
||||
}
|
||||
|
||||
return config;
|
||||
} catch (error) {
|
||||
console.error('解析范式配置失败:', error);
|
||||
console.error('完整的 AI 响应:', aiResponse);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将解析的配置转换为完整的范式对象
|
||||
* @param {Object} parsedConfig - 解析后的配置
|
||||
* @param {string} sourceDocPath - 源需求文档路径(可选)
|
||||
* @returns {Object} 完整的范式对象
|
||||
*/
|
||||
export function buildParadigmObject(parsedConfig, sourceDocPath = null) {
|
||||
const timestamp = Date.now();
|
||||
const id = `custom-${timestamp}`;
|
||||
|
||||
return {
|
||||
id,
|
||||
name: parsedConfig.metadata.name,
|
||||
description: parsedConfig.metadata.description,
|
||||
type: 'custom', // 标记为自定义范式
|
||||
createdAt: new Date().toISOString(),
|
||||
sourceDoc: sourceDocPath,
|
||||
|
||||
specializedPrompt: parsedConfig.specializedPrompt,
|
||||
expertGuidelines: parsedConfig.expertGuidelines,
|
||||
|
||||
// 可选:继承默认的逻辑范式和维度集
|
||||
logicParadigms: null, // 由使用者决定是否继承
|
||||
dimensionSetId: null,
|
||||
defaultReference: null,
|
||||
|
||||
// 元数据
|
||||
metadata: {
|
||||
...parsedConfig.metadata,
|
||||
customGenerated: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证范式配置的完整性
|
||||
* @param {Object} paradigm - 范式对象
|
||||
* @returns {Object} 验证结果 { valid: boolean, errors: string[] }
|
||||
*/
|
||||
export function validateParadigm(paradigm) {
|
||||
const errors = [];
|
||||
|
||||
if (!paradigm.name || paradigm.name.trim().length === 0) {
|
||||
errors.push('范式名称不能为空');
|
||||
}
|
||||
|
||||
if (!paradigm.specializedPrompt || paradigm.specializedPrompt.length < 100) {
|
||||
errors.push('specializedPrompt 内容过短,建议至少300字');
|
||||
}
|
||||
|
||||
if (!Array.isArray(paradigm.expertGuidelines) || paradigm.expertGuidelines.length < 5) {
|
||||
errors.push('expertGuidelines 至少需要5条指令');
|
||||
}
|
||||
|
||||
if (paradigm.expertGuidelines) {
|
||||
paradigm.expertGuidelines.forEach((guideline, index) => {
|
||||
// 兼容字符串和对象格式
|
||||
if (typeof guideline === 'string') {
|
||||
if (guideline.trim().length === 0) {
|
||||
errors.push(`第${index + 1}条指令为空`);
|
||||
}
|
||||
} else if (typeof guideline === 'object') {
|
||||
// 对象格式:检查 description 字段
|
||||
if (!guideline.description || guideline.description.trim().length === 0) {
|
||||
errors.push(`第${index + 1}条指令的描述为空`);
|
||||
}
|
||||
if (!guideline.scope || !['sentence', 'paragraph', 'document'].includes(guideline.scope)) {
|
||||
errors.push(`第${index + 1}条指令的 scope 字段无效`);
|
||||
}
|
||||
} else {
|
||||
errors.push(`第${index + 1}条指令格式错误`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user