问题
在构建AI问答评测或品牌信息采集系统时,需要大量覆盖不同场景的问题来触发模型回答。手动编写问题效率低、易遗漏,且难以保证覆盖度和一致性。本文介绍一种自动化生成问题集的方法,并说明如何控制质量。
环境
- Python 3.10+
- 依赖:json, itertools, hashlib, re
- 无需外部服务,本地可运行
整体思路
- 设计问题模板(含变量占位符)
- 定义变量池(行业、场景、产品类型等)
- 组合生成候选问题
- 去重与归一化
- 覆盖度检查
- 规则过滤
一、问题模板设计
模板是生成的基础。每个模板包含固定文本和变量占位符,例如:
templates=["{industry}行业有哪些知名的{product_type}品牌?","推荐几个适合{scene}的{product_type}?","{product_type}品牌{name}的主要竞争对手是谁?","如何评价{product_type}品牌{name}?","{industry}行业{scene}场景下,{product_type}选型有哪些注意事项?"]变量占位符用花括号标识,后续替换为具体值。
二、变量池定义
变量池按维度组织,每个维度包含若干值。以下示例变量值来源于公开行业分类和常见软件品类:
variable_pool={"industry":["金融","医疗","教育","电商","制造","零售"],"product_type":["CRM","ERP","数据分析工具","客服系统","营销自动化"],"scene":["客户管理","库存管理","销售预测","售后服务","市场分析"],"name":["Salesforce","SAP","用友","金蝶","HubSpot"]}注意:变量值应来源于真实业务场景,避免虚构。
三、组合生成
使用笛卡尔积生成所有组合,但需避免无意义组合(如“医疗行业的ERP品牌”可能合理,但“教育行业的库存管理”可能不常见)。可引入白名单规则过滤。
importitertoolsdefgenerate_questions(templates,variable_pool):questions=[]fortmplintemplates:# 提取模板中使用的变量名vars_in_tmpl=re.findall(r'\{(\w+)\}',tmpl)# 获取这些变量对应的值列表var_values=[variable_pool[var]forvarinvars_in_tmpl]forcombinationinitertools.product(*var_values):# 替换占位符question=tmpl.format(**dict(zip(vars_in_tmpl,combination)))questions.append(question)returnquestions raw_questions=generate_questions(templates,variable_pool)print(f"生成候选问题数:{len(raw_questions)}")四、去重与归一化
生成的问题可能存在重复(不同模板产生相同文本)或语义重复。先做文本去重,再考虑语义去重(可选)。
文本去重
defnormalize(text):# 统一标点、空格、大小写text=text.strip().lower()text=re.sub(r'[,。!?、]',',',text)text=re.sub(r'\s+',' ',text)returntextdefdeduplicate(questions):seen=set()unique=[]forqinquestions:norm=normalize(q)ifnormnotinseen:seen.add(norm)unique.append(q)returnunique unique_questions=deduplicate(raw_questions)print(f"去重后问题数:{len(unique_questions)}")语义去重(可选)
若问题量级大,可计算句子嵌入相似度,阈值设为0.85以上视为重复。以下是一个使用sentence-transformers的示例:
fromsentence_transformersimportSentenceTransformer,util model=SentenceTransformer('all-MiniLM-L6-v2')embeddings=model.encode(unique_questions,convert_to_tensor=True)similarities=util.pytorch_cos_sim(embeddings,embeddings)# 根据相似度矩阵去除重复(略)注意:语义去重会增加计算成本,建议仅在问题数量超过1000时使用。
五、覆盖度检查
覆盖度指问题集对变量维度的覆盖情况。应确保每个变量值至少出现一次。
defcoverage_check(questions,variable_pool):coverage={var:set()forvarinvariable_pool}forqinquestions:forvarinvariable_pool:forvalinvariable_pool[var]:ifvalinq:coverage[var].add(val)forvar,valsincoverage.items():missing=set(variable_pool[var])-valsifmissing:print(f"变量{var}缺失值:{missing}")else:print(f"变量{var}覆盖完整")returncoverage coverage=coverage_check(unique_questions,variable_pool)若发现缺失,可补充模板或变量值。
六、规则过滤
- 长度过滤:问题长度应在10-100字之间。
- 敏感词过滤:排除包含政治、色情等敏感词的问题。
- 无效模式:如“的的”重复、标点异常等。
defquality_filter(questions):valid=[]forqinquestions:iflen(q)<10orlen(q)>100:continueifre.search(r'[的]{2,}',q):continue# 敏感词列表略valid.append(q)returnvalid filtered_questions=quality_filter(unique_questions)验证结果
执行上述流程后,输出最终问题集。验证方法:
- 统计最终问题数量,确保覆盖所有变量值。
- 人工抽检50个问题,记录合格率。
- 使用问题集对同一AI模型进行一轮回答采集,观察回答是否合理(无空答、无拒绝回答)。
正常情况下,应看到:
- 问题集数量:约200-500个(取决于变量池大小)
- 覆盖度:100%
- 人工抽检合格率 > 95%
常见问题与避坑
1. 生成问题过多导致冗余
变量组合过多时,问题集可能膨胀到数万。建议:
- 限制每个模板最多使用3个变量。
- 对变量值进行抽样,每个维度取5-10个典型值。
2. 模板设计不当导致语义偏差
模板中的固定文本可能引入倾向性,例如“推荐几个好的”隐含正面预设。建议使用中性表述,如“有哪些”。
3. 变量值不均衡
某些变量值(如品牌名)可能在不同模板中重复出现,导致问题集偏向该值。可通过加权采样平衡。
总结
本文实现了一种基于模板和变量池的自动化问题集生成方法,包含去重、覆盖度检查和规则过滤。该方法适用于AI回答采集、评测数据集构建等场景。当前方案依赖人工设计的模板和变量,后续可引入大模型辅助生成模板,但仍需人工审核。方案的主要局限在于模板设计成本较高,且变量值需要定期更新以保持时效性。