feat(data_process): 优化问题生成提示语,提升问题自然度

- 系统提示语注入问题风格规则:像真实用户自然提问、避免"请描述/
  请说明/根据文档"等模板化开头、多条问题交替句式,并附正反示例;
  服务端注入对存量任务即时生效。
- 未配置提示语时的后端兜底从一句话充实为与前端同信息量的完整默认。
- 前端标准/思维链/DPO 三套内置默认提示语新增"问题表述自然"要求,
  旧版归档为 _4 常量并注册进 isBuiltInGenerationPrompt 迁移映射,
  自定义提示语不受影响。
- 新增兜底提示语专项测试与风格规则断言,生成测试 32/32 通过。
This commit is contained in:
caoxiaozhu
2026-08-18 15:48:36 +08:00
parent 91b4ae2287
commit 78fb894307
3 changed files with 119 additions and 7 deletions

View File

@@ -45,6 +45,25 @@ MINIMAX_M3_API_HOSTS = {"api.minimax.io", "api.minimaxi.com"}
MINIMAX_M3_MIN_COMPLETION_TOKENS = 4096
logger = logging.getLogger(__name__)
# 问题表述风格规则:防止模型产出“请描述/请说明”式模板化问句。
_QUESTION_STYLE_RULE = (
"各条问题必须覆盖不同的信息点并使用不同的句式,只替换关键词套用同一句式视为重复。"
"问题表述要像真实用户自然提出的问题:具体、口语化、直奔信息点,"
"避免“请描述”“请说明”“根据文档”等模板化开头,"
"也不要把原文句子直接改成问句;多条问题时交替使用直接疑问、场景式提问、追问式等句式。"
"表述示例(仅示意风格,不要照搬内容):"
"避免——“请描述系统的权限控制机制”;"
"推荐——“不同角色能看到的菜单不一样,平台是怎么控制的?”"
)
# 任务配置未提供提示语时的兜底,与前端内置默认提示语保持同等信息量。
_DEFAULT_GENERATION_PROMPT = (
"你是一名专业的数据生成专家。请基于来源内容生成高质量、"
"可直接用于监督微调的问答数据:问题聚焦核心信息点、"
"表述像真实用户自然提出的问题,具体、口语化,多条问题使用不同句式;"
"答案严格依据来源内容,准确、完整、语言自然,不引入来源之外的信息。"
)
def _is_retryable_generation_error(exc: Exception) -> bool:
if isinstance(exc, _TerminalModelGenerationError):
@@ -310,11 +329,11 @@ def _prompt_messages(
)
schema_instruction = (
f"必须只返回 JSON 对象,格式为 {schema}items 必须包含 {count} 条。"
f"这是总计 {total_count} 条中的第 {start_index}-{end_index}"
"各条必须使用不同的提问角度和表述,避免重复。"
f"{output_rule}不要输出 Markdown 代码围栏或 JSON 之外的说明。"
f"这是总计 {total_count} 条中的第 {start_index}-{end_index}"
f"{_QUESTION_STYLE_RULE}{output_rule}"
"不要输出 Markdown 代码围栏或 JSON 之外的说明。"
)
base_prompt = normalize_text(prompt) or "请根据来源内容生成可用于监督微调的问答数据。"
base_prompt = normalize_text(prompt) or _DEFAULT_GENERATION_PROMPT
if "{{ content }}" in base_prompt:
user_prompt = base_prompt.replace("{{ content }}", content)
return [

View File

@@ -35,6 +35,8 @@ def test_generate_model_records_uses_prompt_auth_and_stable_split() -> None:
assert "客户反馈页面加载慢" in payload["messages"][1]["content"]
assert "你正在生成标准监督微调问答数据" in payload["messages"][0]["content"]
assert "禁止输出分析、推理过程" in payload["messages"][0]["content"]
assert "真实用户自然提出的问题" in payload["messages"][0]["content"]
assert "模板化开头" in payload["messages"][0]["content"]
return httpx.Response(
200,
json={
@@ -88,6 +90,59 @@ def test_generate_model_records_uses_prompt_auth_and_stable_split() -> None:
assert progress_updates == [(1, 1)]
def test_generate_model_records_falls_back_to_rich_default_prompt() -> None:
captured: dict[str, str] = {}
def handler(request: httpx.Request) -> httpx.Response:
payload = json.loads(request.content)
captured["system"] = payload["messages"][0]["content"]
captured["user"] = payload["messages"][1]["content"]
return httpx.Response(
200,
json={
"choices": [
{
"message": {
"content": json.dumps(
{
"items": [
{
"instruction": "平台如何控制不同角色的菜单可见性?",
"input": "",
"output": "按角色分配权限。",
}
]
},
ensure_ascii=False,
)
}
}
]
},
)
client = httpx.Client(transport=httpx.MockTransport(handler))
records = generate_model_records(
[{"id": "preview-1", "edited_content": "平台按角色分配菜单权限。"}],
model={
"name": "Qwen",
"online_model_name": "qwen-plus",
"api_url": "model.example",
},
config={},
task_id="task-fallback",
split={"train": 100, "validation": 0, "test": 0},
qa_pairs_per_item=1,
client=client,
)
assert len(records) == 1
assert records[0]["status"] == "valid"
assert "数据生成专家" in captured["system"]
assert "真实用户自然提出的问题" in captured["system"]
assert "平台按角色分配菜单权限。" in captured["user"]
def test_generate_model_records_builds_native_dpo_pair() -> None:
def handler(request: httpx.Request) -> httpx.Response:
payload = json.loads(request.content)

View File

@@ -52,7 +52,7 @@ export const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_3 = `你是一名具
数据格式要求:
请确保每条生成的数据清晰包含“问题”、“中间思考过程”和“最终答案”三个部分,且内容充实、专业。`
export const DEFAULT_STANDARD_GENERATION_PROMPT = `你是一名专业的数据生成专家。请基于下方来源内容生成高质量、可直接用于大模型监督微调SFT的问答数据。
export const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_4 = `你是一名专业的数据生成专家。请基于下方来源内容生成高质量、可直接用于大模型监督微调SFT的问答数据。
来源内容:
{{ content }}
@@ -62,7 +62,7 @@ export const DEFAULT_STANDARD_GENERATION_PROMPT = `你是一名专业的数据
2. 答案忠于来源:严格依据来源内容作答,不得引入来源中不存在的事实、观点或数据;若来源信息不足以完整回答该问题,请如实说明不足,不要编造或臆测。
3. 答案完整且自然:回答应直接回应问题,要点完整、逻辑清晰、语言流畅;涉及多项内容时建议分点陈述,但不必照搬原文措辞。`
export const DEFAULT_REASONING_GENERATION_PROMPT = `你是一名具有深厚专业知识的推理数据生成专家。请基于下方来源内容,生成包含完整思维链、可用于训练推理大语言模型(如 O1、DeepSeek-R1的问答数据。
export const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_4 = `你是一名具有深厚专业知识的推理数据生成专家。请基于下方来源内容,生成包含完整思维链、可用于训练推理大语言模型(如 O1、DeepSeek-R1的问答数据。
来源内容:
{{ content }}
@@ -73,7 +73,7 @@ export const DEFAULT_REASONING_GENERATION_PROMPT = `你是一名具有深厚专
3. 信息不足时如实处理:遇到来源信息缺失或相互矛盾时,需在推理中明确指出,并基于来源做出合理假设,但不得编造与来源无关的内容。
4. 最终答案须能从前面的推理过程中自然得出,语言简洁、结构清晰。`
export const DEFAULT_DPO_GENERATION_PROMPT = `你是一名偏好数据构造专家。请基于下方来源内容生成可用于直接偏好优化DPO的成对问答数据。
export const PREVIOUS_DEFAULT_DPO_GENERATION_PROMPT = `你是一名偏好数据构造专家。请基于下方来源内容生成可用于直接偏好优化DPO的成对问答数据。
来源内容:
{{ content }}
@@ -84,6 +84,41 @@ export const DEFAULT_DPO_GENERATION_PROMPT = `你是一名偏好数据构造专
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
@@ -98,9 +133,12 @@ export function isBuiltInGenerationPrompt(value: string) {
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,