import type { DataProcessConfig, DataProcessDatasetSplit } from '@/types/dataProcess' import type { GenerationControlOptions, PreprocessOption, StructuredProcessOptions, UnstructuredPreprocessOption, UnstructuredProcessOptions, ProcessType, } from './types' import { normalizeQaPairsGenerationCount } from './types' const LEGACY_DEFAULT_GENERATION_PROMPT = '你是一名专业的数据生成助手。请根据输入内容生成准确、完整、可直接用于模型训练的问答数据。仅输出符合所选输出类型和目标格式的内容,答案应事实清晰、语言自然,不要添加无关说明。' const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT = '你是一名专业的数据生成助手。请严格依据输入内容生成准确、完整、可直接用于监督微调的问答数据。只生成问题和最终答案,不输出分析、推理过程或来源中不存在的信息;答案应事实清晰、语言自然。' const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT = '你是一名专业的推理数据生成助手。请严格依据输入内容生成问题、可核验的推理过程和最终答案。推理需要说明关键依据与必要步骤,不得引入来源中不存在的事实;最终答案应准确、完整且语言自然。' export const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_2 = `你是一名专业的数据生成助手。请根据下方来源内容生成可用于监督微调的问答数据。 来源内容: {{ content }} 要求:严格依据来源生成问题和最终答案,不输出分析、中间思考过程或来源中不存在的信息;答案应准确、完整、语言自然。` export const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_2 = `你是一名专业的推理数据生成助手。请根据下方来源内容生成可用于推理模型训练的问答数据。 来源内容: {{ content }} 要求:每条数据必须包含问题、可核验的中间思考过程和最终答案。中间思考过程需说明关键依据、必要步骤和中间计算,不得跳过推理只给结论,也不得引入来源中不存在的事实。` export const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_3 = `你是一名专业的数据生成专家。你的任务是根据下方的来源内容,提取核心信息并生成高质量的问答数据,用于大模型的监督微调(SFT)。 来源内容: {{ content }} 任务要求: 1. 问题构建:基于来源内容设计问题,问题应当具有实际意义,不仅限于表面的事实问答,可以包括对内容的主旨概括、具体细节的询问或应用场景的探讨。 2. 答案生成:严格依据来源内容生成最终答案,答案需准确无误、逻辑连贯、完整且表达自然流畅。 3. 忠于原文:不输出分析、中间思考过程或任何多余的解释,切勿引入来源内容中未提及的事实或主观臆造的信息。` export const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_3 = `你是一名具有深厚专业知识的推理数据生成专家。你的任务是根据给定的来源内容,生成高质量、逻辑严密且可用于训练推理大语言模型(如 O1 或 DeepSeek-R1)的问答数据。 来源内容: {{ content }} 任务要求: 1. 多维度提问:从来源内容中提取核心概念或关键逻辑,构建具有挑战性的问题,避免简单的信息抽取,提问可以涉及因果分析、方案设计、逻辑推断或跨段落信息整合。 2. 深度思维链(CoT): - 必须提供详尽的中间思考过程。 - 在思考过程中,需首先分析问题的核心需求,然后逐步拆解来源内容中的相关事实或数据。 - 对于涉及计算或多步推导的问题,必须一步步展示计算或推导过程,并说明每一步的理论依据或事实来源。 - 如果遇到矛盾或缺失的信息,需在思考过程中指出,并合理假设或推理,但最终答案不得编造来源中完全不相关的事实。 3. 最终答案:在充分推理后给出明确的最终结论。结论应当是对问题的直接回应,语言自然、结构清晰,并且结论必须能从前面的思考过程中自然得出。 数据格式要求: 请确保每条生成的数据清晰包含“问题”、“中间思考过程”和“最终答案”三个部分,且内容充实、专业。` export const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_4 = `你是一名专业的数据生成专家。请基于下方来源内容,生成高质量、可直接用于大模型监督微调(SFT)的问答数据。 来源内容: {{ content }} 任务要求: 1. 提问有价值:问题应聚焦来源内容的核心信息点,避免无意义的字面抄录或过于宽泛的提问;优先设计需要理解、归纳或推理才能回答的问题,例如主旨概括、细节追问、因果解释或场景应用。 2. 答案忠于来源:严格依据来源内容作答,不得引入来源中不存在的事实、观点或数据;若来源信息不足以完整回答该问题,请如实说明不足,不要编造或臆测。 3. 答案完整且自然:回答应直接回应问题,要点完整、逻辑清晰、语言流畅;涉及多项内容时建议分点陈述,但不必照搬原文措辞。` export const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_4 = `你是一名具有深厚专业知识的推理数据生成专家。请基于下方来源内容,生成包含完整思维链、可用于训练推理大语言模型(如 O1、DeepSeek-R1)的问答数据。 来源内容: {{ content }} 任务要求: 1. 提问有挑战性:从来源内容中提炼需要多步思考才能回答的问题,避免可直接定位答案的简单事实抽取;可涉及因果分析、逻辑推断或跨信息整合。 2. 推理可核验:逐步展开思考过程,每个关键结论都应标注其在来源中的依据或必要的中间推导;不得跳过关键步骤只给结论,也不得引入来源中完全不存在的事实。 3. 信息不足时如实处理:遇到来源信息缺失或相互矛盾时,需在推理中明确指出,并基于来源做出合理假设,但不得编造与来源无关的内容。 4. 最终答案须能从前面的推理过程中自然得出,语言简洁、结构清晰。` export const PREVIOUS_DEFAULT_DPO_GENERATION_PROMPT = `你是一名偏好数据构造专家。请基于下方来源内容,生成可用于直接偏好优化(DPO)的成对问答数据。 来源内容: {{ content }} 任务要求: 1. 问题应聚焦来源内容的核心信息,并具有实际训练价值。 2. Chosen 必须准确、完整、清晰且严格忠于来源内容。 3. Rejected 应当表面合理但包含可辨认的缺陷,例如遗漏关键条件、事实偏差、逻辑不完整或表达含混;不得用乱码、空话或危险内容凑数。 4. Chosen 与 Rejected 必须明显不同,且都直接回答同一个问题。` export const DEFAULT_STANDARD_GENERATION_PROMPT = `你是一名专业的数据生成专家。请基于下方来源内容,生成高质量、可直接用于大模型监督微调(SFT)的问答数据。 来源内容: {{ content }} 任务要求: 1. 提问有价值:问题应聚焦来源内容的核心信息点,避免无意义的字面抄录或过于宽泛的提问;优先设计需要理解、归纳或推理才能回答的问题,例如主旨概括、细节追问、因果解释或场景应用。 2. 问题表述自然:问题要像真实用户自然提出的问题——具体、口语化、直奔信息点;避免“请描述”“请说明”“根据文档”等模板化开头,不要把原文句子直接改成问句;多条问题应交替使用直接疑问、场景式提问、追问式等不同句式,不要只换关键词套用同一句式。 3. 答案忠于来源:严格依据来源内容作答,不得引入来源中不存在的事实、观点或数据;若来源信息不足以完整回答该问题,请如实说明不足,不要编造或臆测。 4. 答案完整且自然:回答应直接回应问题,要点完整、逻辑清晰、语言流畅;涉及多项内容时建议分点陈述,但不必照搬原文措辞。` export const DEFAULT_REASONING_GENERATION_PROMPT = `你是一名具有深厚专业知识的推理数据生成专家。请基于下方来源内容,生成包含完整思维链、可用于训练推理大语言模型(如 O1、DeepSeek-R1)的问答数据。 来源内容: {{ content }} 任务要求: 1. 提问有挑战性:从来源内容中提炼需要多步思考才能回答的问题,避免可直接定位答案的简单事实抽取;可涉及因果分析、逻辑推断或跨信息整合。 2. 问题表述自然:问题要像真实用户自然提出的问题——具体、口语化;避免“请分析”“请说明”等模板化开头连用;多条问题应交替使用不同句式,不要只换关键词套用同一句式。 3. 推理可核验:逐步展开思考过程,每个关键结论都应标注其在来源中的依据或必要的中间推导;不得跳过关键步骤只给结论,也不得引入来源中完全不存在的事实。 4. 信息不足时如实处理:遇到来源信息缺失或相互矛盾时,需在推理中明确指出,并基于来源做出合理假设,但不得编造与来源无关的内容。 5. 最终答案须能从前面的推理过程中自然得出,语言简洁、结构清晰。` export const DEFAULT_DPO_GENERATION_PROMPT = `你是一名偏好数据构造专家。请基于下方来源内容,生成可用于直接偏好优化(DPO)的成对问答数据。 来源内容: {{ content }} 任务要求: 1. 问题应聚焦来源内容的核心信息,并具有实际训练价值。 2. 问题表述自然:问题要像真实用户提出的问题——具体、口语化,避免“请描述”“请说明”等模板化开头。 3. Chosen 必须准确、完整、清晰且严格忠于来源内容。 4. Rejected 应当表面合理但包含可辨认的缺陷,例如遗漏关键条件、事实偏差、逻辑不完整或表达含混;不得用乱码、空话或危险内容凑数。 5. Chosen 与 Rejected 必须明显不同,且都直接回答同一个问题。` export function defaultGenerationPrompt(outputType: 'standard' | 'reasoning' | 'dpo') { if (outputType === 'reasoning') return DEFAULT_REASONING_GENERATION_PROMPT if (outputType === 'dpo') return DEFAULT_DPO_GENERATION_PROMPT return DEFAULT_STANDARD_GENERATION_PROMPT } export function isBuiltInGenerationPrompt(value: string) { const normalize = (s: string) => s.replace(/\r\n/g, '\n').trim() const normalizedValue = normalize(value) return [ LEGACY_DEFAULT_GENERATION_PROMPT, PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT, PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_2, PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_3, PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_4, PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT, PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_2, PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_3, PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_4, PREVIOUS_DEFAULT_DPO_GENERATION_PROMPT, DEFAULT_STANDARD_GENERATION_PROMPT, DEFAULT_REASONING_GENERATION_PROMPT, DEFAULT_DPO_GENERATION_PROMPT, ].map(normalize).includes(normalizedValue) } export function createDefaultStructuredOptions(): StructuredProcessOptions { return { preprocessOptions: [], semanticEnrichment: false, qaPairsPerRow: 1, datasetSplit: { train: 80, validation: 10, test: 10 }, generationModelId: '', generationPrompt: DEFAULT_STANDARD_GENERATION_PROMPT, outputType: 'standard', reasoningDetail: 'normal', temperature: 0.7, maxTokens: 1024, jsonMode: false, qualityFilterEnabled: false, filterLowQuality: true, filterShortContent: true, minOutputLength: 20, } } export function createDefaultUnstructuredOptions(): UnstructuredProcessOptions { return { preprocessOptions: [], chunkMethod: 'layout_hybrid', chunkSize: 800, chunkOverlap: 100, minChunkSize: 100, semanticBreakpointPercentile: 95, preserveTables: false, preserveCodeBlocks: false, preserveLists: false, semanticEnrichment: false, qaPairsPerChunk: 1, datasetSplit: { train: 80, validation: 10, test: 10 }, generationModelId: '', generationPrompt: DEFAULT_STANDARD_GENERATION_PROMPT, outputType: 'standard', reasoningDetail: 'normal', temperature: 0.7, maxTokens: 1024, jsonMode: false, qualityFilterEnabled: false, filterLowQuality: true, filterShortContent: true, minOutputLength: 20, } } function configValue(config: DataProcessConfig, key: string, fallback: T): T { return Object.prototype.hasOwnProperty.call(config, key) ? config[key] as T : fallback } function numberValue(config: DataProcessConfig, key: string, fallback: number): number { const value = Number(configValue(config, key, fallback)) return Number.isFinite(value) ? value : fallback } function datasetSplitValue(config: DataProcessConfig, fallback: DataProcessDatasetSplit) { const value = config.dataset_split if (!value || typeof value !== 'object') return { ...fallback } const split = value as unknown as Record const splitNumber = (key: keyof DataProcessDatasetSplit) => { const parsed = Number(split[key]) return Number.isFinite(parsed) ? parsed : fallback[key] } return { train: splitNumber('train'), validation: splitNumber('validation'), test: splitNumber('test'), } } function generationOptionsFromConfig( config: DataProcessConfig, defaults: GenerationControlOptions, ): GenerationControlOptions { const configuredOutputType = configValue(config, 'output_type', defaults.outputType) const outputType = configuredOutputType === 'reasoning' || configuredOutputType === 'dpo' ? configuredOutputType : 'standard' const configuredPrompt = String( configValue(config, 'generation_prompt', defaults.generationPrompt), ) return { generationModelId: configValue(config, 'generation_model_id', defaults.generationModelId), generationPrompt: isBuiltInGenerationPrompt(configuredPrompt) ? defaultGenerationPrompt(outputType) : configuredPrompt, outputType, reasoningDetail: configValue(config, 'reasoning_detail', defaults.reasoningDetail) === 'detailed' ? 'detailed' : 'normal', temperature: numberValue(config, 'temperature', defaults.temperature), maxTokens: numberValue(config, 'max_tokens', defaults.maxTokens), jsonMode: Boolean(configValue(config, 'json_mode', defaults.jsonMode)), qualityFilterEnabled: Boolean(configValue( config, 'quality_filter_enabled', defaults.qualityFilterEnabled, )), filterLowQuality: Boolean(configValue( config, 'filter_low_quality', defaults.filterLowQuality, )), filterShortContent: Boolean(configValue( config, 'filter_short_content', defaults.filterShortContent, )), minOutputLength: numberValue(config, 'min_output_length', defaults.minOutputLength), } } export function createStructuredOptionsFromConfig(config: DataProcessConfig): StructuredProcessOptions { const defaults = createDefaultStructuredOptions() const preprocessOptions = configValue(config, 'preprocess_options', []) const supportedPreprocessOptions = new Set([ 'clean_invalid', 'deduplicate', 'detect_structure', 'normalize_format', 'desensitize', 'filter_anomaly', ]) return { ...defaults, ...generationOptionsFromConfig(config, defaults), preprocessOptions: Array.isArray(preprocessOptions) ? Array.from(new Set(preprocessOptions.map(String).filter( (option): option is PreprocessOption => supportedPreprocessOptions.has(option as PreprocessOption), ))) : defaults.preprocessOptions, semanticEnrichment: Boolean(configValue( config, 'semantic_enrichment', defaults.semanticEnrichment, )), qaPairsPerRow: normalizeQaPairsGenerationCount( numberValue(config, 'qa_pairs_per_row', defaults.qaPairsPerRow), defaults.qaPairsPerRow, ), datasetSplit: datasetSplitValue(config, defaults.datasetSplit), } } export function createUnstructuredOptionsFromConfig(config: DataProcessConfig): UnstructuredProcessOptions { const defaults = createDefaultUnstructuredOptions() const preprocessOptions = configValue(config, 'preprocess_options', []) return { ...defaults, ...generationOptionsFromConfig(config, defaults), preprocessOptions: Array.isArray(preprocessOptions) ? preprocessOptions.map(String) as UnstructuredPreprocessOption[] : defaults.preprocessOptions, chunkMethod: configValue(config, 'chunk_method', defaults.chunkMethod), chunkSize: numberValue(config, 'chunk_size', defaults.chunkSize), chunkOverlap: numberValue(config, 'chunk_overlap', defaults.chunkOverlap), minChunkSize: numberValue(config, 'min_chunk_size', defaults.minChunkSize), semanticBreakpointPercentile: numberValue( config, 'semantic_breakpoint_percentile', defaults.semanticBreakpointPercentile, ), preserveTables: Boolean(configValue(config, 'preserve_tables', defaults.preserveTables)), preserveCodeBlocks: Boolean(configValue( config, 'preserve_code_blocks', defaults.preserveCodeBlocks, )), preserveLists: Boolean(configValue(config, 'preserve_lists', defaults.preserveLists)), semanticEnrichment: Boolean(configValue( config, 'semantic_enrichment', defaults.semanticEnrichment, )), qaPairsPerChunk: normalizeQaPairsGenerationCount( numberValue(config, 'qa_pairs_per_chunk', defaults.qaPairsPerChunk), defaults.qaPairsPerChunk, ), datasetSplit: datasetSplitValue(config, defaults.datasetSplit), } } export function previewAffectingOptionsFor( processType: ProcessType, structured: StructuredProcessOptions, unstructured: UnstructuredProcessOptions, ) { if (processType === 'structured') return { preprocessOptions: structured.preprocessOptions } if (processType !== 'unstructured') return null const { preprocessOptions, chunkMethod, chunkSize, chunkOverlap, minChunkSize, semanticBreakpointPercentile, preserveTables, preserveCodeBlocks, preserveLists, } = unstructured return { preprocessOptions, chunkMethod, chunkSize, chunkOverlap, minChunkSize, semanticBreakpointPercentile, preserveTables, preserveCodeBlocks, preserveLists, } } export function generationAffectingOptionsFor( processType: ProcessType, structured: StructuredProcessOptions, unstructured: UnstructuredProcessOptions, ) { const options = processType === 'unstructured' ? unstructured : structured if (processType === 'external') return null const common = { semanticEnrichment: options.semanticEnrichment, datasetSplit: options.datasetSplit, generationModelId: options.generationModelId, generationPrompt: options.generationPrompt, outputType: options.outputType, reasoningDetail: options.reasoningDetail, temperature: options.temperature, maxTokens: options.maxTokens, jsonMode: options.jsonMode, qualityFilterEnabled: options.qualityFilterEnabled, filterLowQuality: options.filterLowQuality, filterShortContent: options.filterShortContent, minOutputLength: options.minOutputLength, } return processType === 'unstructured' ? { ...common, qaPairsPerChunk: unstructured.qaPairsPerChunk } : { ...common, qaPairsPerRow: structured.qaPairsPerRow } }