基于文档自动生成 QA 对(文档 - 问题 - 答案三元组)是构建高质量测试集、知识库评估或模型微调数据的核心方法,其核心流程围绕 “文档采样→内容解析→LLM 生成→质量过滤→格式输出” 展开。
一、核心目标与前置准备
在启动前需明确目标,避免后续流程偏离需求:
- 核心目标:生成 “高质量” QA 对 —— 问题需覆盖文档关键信息(如事实、逻辑、细节、总结),答案需精准对应文档内容(无编造、无遗漏),且三元组能有效用于 LLM / 知识库的测试(如问答准确性、信息召回率评估)。
- 前置条件:
- 知识库准备:整理结构化(如 PDF、Word、Excel、Markdown)或非结构化(如扫描件 OCR 后文本)文档,建议统一转为纯文本格式(便于后续解析),并建立文档元数据(如 ID、标题、章节、关键词)。
- LLM 选型:根据需求选择合适的 LLM,优先推荐闭源模型(生成质量高)或大参数量开源模型(隐私性好)。
二、完整实施流程(6 个步骤)
步骤 1:知识库文档随机采样
需保证采样的 “随机性” 和 “代表性”,避免测试集偏倚:
- 采样策略:
- 分层随机采样(推荐):按文档类型(如产品手册、技术文档、法规文件)、长度(短文档 <1000 字,中文档 1000-5000 字,长文档> 5000 字)、重要性(核心知识 / 边缘知识)分层,每层内随机抽取一定比例(如 10%-20%)文档,确保覆盖知识库全貌。
- 简单随机采样:直接对所有文档 ID 打乱排序,抽取固定数量(如抽取 100 篇),适用于知识库文档类型单一的场景。
步骤 2:文档内容预处理与解析
长文档直接输入 LLM 易导致 “信息过载”,需拆分并聚焦关键信息:
- 预处理操作:
- 文本清洗:去除文档中的特殊字符(如
\n
、\t
、乱码)、页眉页脚、重复内容,统一编码为 UTF-8。 - 分段拆分:将长文档(如 > 3000 字)按 “章节→段落→语义块” 拆分,每个语义块控制在 500-1500 字(匹配 LLM 上下文窗口,避免信息丢失),示例:用
nltk
按句子分割,再按语义合并为块。 - 关键信息标注:用关键词提取工具(如
jieba
、TF-IDF
、TextRank
)标注每个语义块的核心词(如 “产品参数”“操作步骤”“故障排查”),为后续 LLM 生成定向问题提供引导。
- 文本清洗:去除文档中的特殊字符(如
步骤 3:设计 LLM 提示词(Prompt)生成 QA 对
Prompt 设计是决定 QA 质量的核心,需明确 “生成规则”“问题类型”“格式要求”,避免 LLM 生成模糊或偏离文档的内容:
适用于大多数文档类型,需包含 “角色定义→文档内容→生成要求→格式输出” 四部分:
角色:你是一名专业的QA生成专家,擅长基于给定文档内容生成精准、全面的问题和答案。文档内容:{doc_content} # 此处替换为步骤2拆分后的语义块内容生成要求: 1. 问题需基于文档内容,不得编造信息;答案需完全来自文档,确保100%准确,不添加外部知识。 2. 覆盖以下5类问题类型,每类至少生成2个QA对:- 事实类:询问文档中的具体数据、名称、时间、地点(如“产品X的额定电压是多少?”);- 细节类:询问步骤、条件、限制(如“执行操作A前需满足哪些前提条件?”);- 逻辑类:询问因果、对比、关联(如“为什么采用方案B而非方案C?”);- 总结类:询问段落/文档的核心观点(如“本节主要介绍了什么内容?”);- 推断类:基于文档信息合理推断(如“根据文档,若出现故障D,下一步应执行什么操作?”)。 3. 问题需简洁清晰,避免歧义;答案需精炼,长度控制在1-3句话(复杂内容可适当延长)。 4. 若文档内容无某类问题对应的信息,可跳过该类,但需说明原因。输出格式: 以JSON数组形式输出,每个元素包含"doc_id":"{doc_id}","semantic_block_id":"{block_id}","question":"","answer":"","question_type":""}
步骤 4:调用 LLM 生成 QA 对
根据 LLM 类型选择调用方式,需处理 “上下文窗口限制” 和 “批量生成效率”:
from openai import OpenAI import jsonclient = OpenAI(api_key="your_api_key")def generate_qa(doc_id, block_id, doc_content):prompt = 基础Prompt模板.format(doc_id=doc_id, block_id=block_id, doc_content=doc_content)try:response = client.chat.completions.create(model="gpt-4o",messages=[{"role": "user", "content": prompt}],temperature=0.3, # 低温度(0.2-0.4)保证生成稳定、少幻觉max_tokens=2000)# 解析JSON输出qa_list = json.loads(response.choices[0].message.content)return qa_listexcept Exception as e:print(f"生成失败(doc_id={doc_id}, block_id={block_id}):{e}")return []# 批量生成 all_qa = [] for doc in sampled_docs:doc_id = doc["doc_id"]# 假设已拆分出语义块列表semantic_blocks = split_doc_into_blocks(doc["content"]) # 步骤2实现的拆分函数for i, block in enumerate(semantic_blocks):qa = generate_qa(doc_id, f"B{i:03d}", block)all_qa.extend(qa)
步骤 5:QA 对质量过滤与清洗
LLM 生成的 QA 对可能存在 “答案错误”“问题冗余”“格式异常” 等问题,需通过 “自动过滤 + 人工抽检” 保证质量:
- 自动过滤规则(用 Python 实现):
- 答案准确性校验:检查答案是否为文档内容的子集(用字符串匹配或余弦相似度,如
simbert
计算答案与文档的相似度,阈值设为 0.7 以上); - 问题有效性校验:过滤长度 <5 字或> 50 字的问题,排除歧义句(如 “这是什么?”);
- 格式合规校验:检查 JSON 字段是否完整(doc_id、block_id、question、answer、question_type),排除格式错误的条目;
- 冗余去重:对相同 doc_id 和 block_id 下的 QA 对,用 “问题 + 答案” 的哈希值去重,避免重复生成。
- 答案准确性校验:检查答案是否为文档内容的子集(用字符串匹配或余弦相似度,如
- 人工抽检:随机抽取 10%-15% 的 QA 对,由领域专家评估 “问题覆盖度”“答案准确性”“与测试目标匹配度”,若合格率 < 80%,需返回步骤 3 优化 Prompt 或步骤 4 调整 LLM 参数。
步骤 6:生成最终测试集(文档 - 问题 - 答案三元组)
将清洗后的 QA 对与原始文档关联,按需求输出格式:
- 输出格式:
- CSV/Excel:便于人工查看和编辑,字段包括
doc_id
、doc_title
、question
、answer
、question_type
、semantic_block_id
; - JSONL:便于模型测试(如 LLM 问答评估),每行一个三元组
- 数据库存储:存入 MySQL/PostgreSQL,建立
docs
(文档元数据)、qa_pairs
(QA 对)表,用doc_id
关联,便于后续查询和更新。
- CSV/Excel:便于人工查看和编辑,字段包括