Files
YG_FT/frontend/src/views/data-process/create/dataProcessCreateState.ts
caoxiaozhu 78fb894307 feat(data_process): 优化问题生成提示语,提升问题自然度
- 系统提示语注入问题风格规则:像真实用户自然提问、避免"请描述/
  请说明/根据文档"等模板化开头、多条问题交替句式,并附正反示例;
  服务端注入对存量任务即时生效。
- 未配置提示语时的后端兜底从一句话充实为与前端同信息量的完整默认。
- 前端标准/思维链/DPO 三套内置默认提示语新增"问题表述自然"要求,
  旧版归档为 _4 常量并注册进 isBuiltInGenerationPrompt 迁移映射,
  自定义提示语不受影响。
- 新增兜底提示语专项测试与风格规则断言,生成测试 32/32 通过。
2026-08-18 15:49:00 +08:00

389 lines
19 KiB
TypeScript
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 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<T>(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<string, unknown>
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<unknown>(config, 'preprocess_options', [])
const supportedPreprocessOptions = new Set<PreprocessOption>([
'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<unknown>(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 }
}