Stabilize product data export flow

This commit is contained in:
wuyang6
2026-05-11 11:46:09 +08:00
parent 1752c81913
commit 0e2a00990f
19 changed files with 659 additions and 31 deletions
+89 -2
View File
@@ -7,9 +7,11 @@ Skill 应该先让模型生成紧凑、便于人工 review 的 dataset draft tex
再由这里的逻辑解析并补齐为 canonical records,供后续校验和导出使用。
"""
import csv
import json
import re
import time
from io import StringIO
from pathlib import Path
from typing import Any
@@ -461,6 +463,7 @@ def export_dataset_records(
output_format: str = 'jsonl',
require_validation_ok: bool = True,
overwrite: bool = True,
export_table: bool = True,
) -> dict[str, Any]:
"""校验并导出 canonical records。
@@ -480,8 +483,11 @@ def export_dataset_records(
)
path = _resolve_output_path(root, output_path)
table_path = path.with_name('records.csv')
if path.exists() and not overwrite:
raise DataRecordError(f'output_path already exists: {output_path}')
if export_table and table_path.exists() and not overwrite:
raise DataRecordError('table output_path already exists: records.csv')
path.parent.mkdir(parents=True, exist_ok=True)
if output_format == 'jsonl':
@@ -494,13 +500,83 @@ def export_dataset_records(
path.write_text(content, encoding='utf-8')
rel_path = str(path.relative_to(Path(root).resolve()))
return {
payload = {
'output_path': rel_path,
'output_format': output_format,
'record_count': len(records),
'bytes_written': len(content.encode('utf-8')),
'validation': validation,
}
if export_table:
table_content = render_dataset_records_table_csv(records)
table_path.write_text(table_content, encoding='utf-8-sig')
payload.update(
{
'table_output_path': str(table_path.relative_to(Path(root).resolve())),
'table_output_format': 'csv',
'table_bytes_written': len(table_content.encode('utf-8-sig')),
}
)
return payload
def render_dataset_records_table_csv(records: list[dict[str, Any]]) -> str:
"""把 canonical records 转成同事流转用的表格 CSV。"""
output = StringIO()
writer = csv.DictWriter(output, fieldnames=_dataset_table_columns(), lineterminator='\n')
writer.writeheader()
for record in records:
writer.writerow(_dataset_record_table_row(record))
return output.getvalue()
def _dataset_table_columns() -> list[str]:
return [
'request_id',
'timestamp',
'query',
'prev_session',
'context',
'label',
'是否迁移Function',
'function',
]
def _dataset_record_table_row(record: dict[str, Any]) -> dict[str, str]:
source = record.get('source') if isinstance(record.get('source'), dict) else {}
turn = record.get('turn') if isinstance(record.get('turn'), dict) else {}
label = record.get('label') if isinstance(record.get('label'), dict) else {}
context = record.get('context') if isinstance(record.get('context'), dict) else {}
prev_session = record.get('prev_session')
if not isinstance(prev_session, list):
prev_session = []
return {
'request_id': str(source.get('request_id') or ''),
'timestamp': str(source.get('timestamp') or turn.get('timestamp') or ''),
'query': str(turn.get('query') or ''),
'prev_session': json.dumps(_table_prev_session(prev_session), ensure_ascii=False, separators=(',', ':')),
'context': json.dumps(context, ensure_ascii=False, separators=(',', ':')),
'label': str(label.get('dataset_label') or ''),
'是否迁移Function': '',
'function': str(label.get('target') or ''),
}
def _table_prev_session(prev_session: list[Any]) -> list[dict[str, str]]:
rows: list[dict[str, str]] = []
for item in prev_session[-10:]:
if not isinstance(item, dict):
continue
rows.append(
{
'query': str(item.get('query') or ''),
'tts': str(item.get('tts') or ''),
'timestamp': str(item.get('timestamp') or ''),
}
)
return rows
def _parse_draft_text(draft_text: str) -> dict[str, Any]:
@@ -797,7 +873,7 @@ def _case_to_record(
if not current_query:
raise DataRecordError(f'case {index} current 用户 line must be non-empty')
target = str(case.get('target') or '').strip()
target = _canonical_target(str(case.get('target') or '').strip())
if not target:
raise DataRecordError(f'case {index} target is required')
@@ -866,6 +942,17 @@ def _target_type(target: str) -> str:
return 'unknown'
def _canonical_target(target: str) -> str:
"""规范常见 Agent 标签写法,允许草稿里用单引号降低 JSON 转义风险。"""
match = re.fullmatch(r'Agent\s*\(\s*tag\s*=\s*([\'"])(.+?)\1\s*\)', target)
if not match:
return target
tag = match.group(2).strip()
escaped = tag.replace('\\', '\\\\').replace('"', '\\"')
return f'Agent(tag="{escaped}")'
def _record_id(*, source_type: str, batch_id: str, request_id: str, index: int) -> str:
if source_type == 'generated':
return f'gen_{_safe_token(batch_id)}_{index:06d}'