使用pytest
来编写智能客服的测试框架:
pytest
断言来验证测试结果。首先安装pytest
和requests
库:
pip install pytest requests
创建一个CSV文件test_cases.csv
,它包含示例测试用例:
#问题,预期回复
"已付款,啥时候发货?","你好,系统在24小时内发顺丰快递的"
"麻烦尽快发货!","你好,已为您加急发货~"
"最晚几号发货?","你好,系统最晚在24小时内发顺丰快递的"
"用什么快递发货?","你好,默认是发顺丰快递"
...
然后编写测试脚本test_ai_responses.py
import csv
import requests
import pytest
# 假设的AI服务的URL
AI_SERVICE_URL = "http://192.168.1.100:8888/query"
# 函数用于发送查询到AI服务
def send_query_to_ai(query):
response = requests.post(AI_SERVICE_URL, json={"query": query})
if response.status_code == 200:
return response.json().get('response', '')
else:
# 在真实测试中,这里应该处理错误情况
return None
# 评分函数,这里使用简单的字符串相等进行评分
def score_response(ai_response, expected_response):
return ai_response.strip().lower() == expected_response.strip().lower()
# 读取CSV文件并构建测试用例
def read_test_cases(csv_file):
test_cases = []
with open(csv_file, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
test_cases.append((row['query'], row['expected_response']))
return test_cases
# 参数化测试用例
test_data = read_test_cases('test_cases.csv')
@pytest.mark.parametrize("query,expected_response", test_data)
def test_ai_response(query, expected_response):
# 发送查询并获取AI系统的响应
ai_response = send_query_to_ai(query)
# 断言AI的响应是否与预期相符
assert score_response(ai_response, expected_response), f"Query: {query}, Expected: {expected_response}, Got: {ai_response}"
代码解析:
上面定义了一个send_query_to_ai
函数来发送查询到AI客服系统,并获取响应。
我们还定义了一个score_response
函数来评分响应。
read_test_cases
函数从CSV文件中读取测试用例,并以适合pytest
参数化测试的格式返回它们。
最后用pytest.mark.parametrize
装饰器来参数化test_ai_response
函数,这样pytest
就会为CSV文件中的每个测试用例运行一个测试。
pytest
命令。pytest test_ai_responses.py
下面,来点硬货,实现更复杂的、更科学有效的评分机制:
余弦相似度
。BLEU(双语评估底线)
,这个常用在机器翻译领域。其实还有一个方案(偷懒~)是直接调用语言模型(如GPT-3或BERT)来进行语义相似度评分。
- 余弦相似度是一种计算两个非零向量夹角余弦值的度量,它可以用来评估文本向量的相似性。
- BLEU(BiLingual Evaluation Understudy)分数则通过比较机器翻译的输出和一组参考翻译来评估质量,计算n-gram的重叠度。BLEU主要关注准确性,它计算了几个不同大小的n-gram(通常是1到4)的精确匹配,并通过考虑最长的匹配序列来惩罚过短的生成句子。
- ROUGE(Recall-Oriented Understudy for Gisting Evaluation)分数和BLEU都是常用于评估自然语言生成系统的指标,尤其在机器翻译和文本摘要领域。ROUGE评估自动文本摘要时更关注召回率,即参考摘要中的n-gram有多少被生成摘要所覆盖。ROUGE有多个变体,如ROUGE-N(考虑n-gram重叠)、ROUGE-L(考虑最长公共子序列)等。
我们需要安装一些NLP库,如transformers
和sentence-transformers
,以及scikit-learn
来计算余弦相似度。
pip install transformers sentence-transformers scikit-learn
我们将使用Hugging Face的transformers
库来获取预训练的BERT模型的句子嵌入,然后使用scikit-learn
来计算余弦相似度。
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
# 加载预训练的句子嵌入模型
model = SentenceTransformer('all-MiniLM-L6-v2')
def calculate_cosine_similarity(response1, response2):
# 将文本转换为向量
embeddings = model.encode([response1, response2])
# 计算向量之间的余弦相似度
cosine_sim = cosine_similarity([embeddings[0]], [embeddings[1]])[0][0]
return cosine_sim
def score_response(ai_response, expected_response):
# 计算余弦相似度
similarity_score = calculate_cosine_similarity(ai_response, expected_response)
# 可以设置阈值来确定是否接受响应
return similarity_score
安装nltk和rouge-score库:
pip install nltk rouge-score
然后,更新测试脚本以包括BLEU和ROUGE评分:
import nltk
from rouge_score import rouge_scorer
from nltk.translate.bleu_score import sentence_bleu
# nltk下载器需要的数据
nltk.download('punkt')
def calculate_bleu_score(candidate, reference):
# 分词
candidate_tokens = nltk.word_tokenize(candidate)
reference_tokens = nltk.word_tokenize(reference)
# 计算BLEU分数
score = sentence_bleu([reference_tokens], candidate_tokens)
return score
def calculate_rouge_score(candidate, reference):
# 初始化ROUGE评分器
scorer = rouge_scorer.RougeScorer(['rouge1', 'rougeL'], use_stemmer=True)
# 计算ROUGE分数
scores = scorer.score(reference, candidate)
return scores
# ...其他测试代码保持不变...
@pytest.mark.parametrize("query,expected_response", test_data)
def test_ai_response(query, expected_response):
# 发送查询并获取AI系统的响应
ai_response = send_query_to_ai(query)
# 计算BLEU分数
bleu_score = calculate_bleu_score(ai_response, expected_response)
# 计算ROUGE分数
rouge_scores = calculate_rouge_score(ai_response, expected_response)
# 断言BLEU分数和ROUGE分数是否满足预期
assert bleu_score > 0.5, f"Query: {query}, Expected: {expected_response}, Got: {ai_response}, BLEU: {bleu_score}"
assert rouge_scores['rouge1'].fmeasure > 0.5, f"Query: {query}, Expected: {expected_response}, Got: {ai_response}, ROUGE-1: {rouge_scores['rouge1'].fmeasure}"
assert rouge_scores['rougeL'].fmeasure > 0.5, f"Query: {query}, Expected: {expected_response}, Got: {ai_response}, ROUGE-L: {rouge_scores['rougeL'].fmeasure}"
前面定义了两个函数calculate_bleu_score和calculate_rouge_score来计算BLEU和ROUGE分数。然后,在测试函数test_ai_response中,我们计算这些分数并使用assert语句来检查它们是否满足预设的阈值。请注意,BLEU和ROUGE分数的阈值(在这里假设为0.5)应该根据实际情况进行调整。这些阈值可以通过对历史数据的分析来确定,以确保它们反映出对系统性能的实际期望。此外,BLEU和ROUGE分数对于某些类型的响应可能不够灵敏,因此应该结合其他评估方法使用。
更新上面的test_ai_responses.py
脚本,修改为最新评分逻辑。
# ...其他代码保持不变...
@pytest.mark.parametrize("query,expected_response", test_data)
def test_ai_response(query, expected_response):
# 发送查询并获取AI系统的响应
ai_response = send_query_to_ai(query)
# 计算余弦相似度
similarity_score = calculate_cosine_similarity(ai_response, expected_response)
# 断言相似度得分是否高于设定的阈值
# 余弦相似度是一种常用的度量文本相似度的方法,但它可能不足以捕捉所有语义差异,因此我们设置了一个阈值来判断响应是否足够接近预期。
assert similarity_score > 0.7, f"Query: {query}, Expected: {expected_response}, Got: {ai_response}, Similarity: {similarity_score}"
使用pytest
运行测试,对比结果
pytest test_ai_responses.py