7. 质量与测试
7.1 提示词单元测试
测试框架
医疗AI系统的提示词测试是确保诊断准确性的关键环节:
测试框架选择
LangChain Evaluators:
- LangChain提供的评估框架
- 支持医疗场景的评估指标
- 易于集成到现有系统
自定义医疗测试框架:
- 基于医疗领域特点定制
- 支持医疗专业评估标准
- 完全可控
测试框架实现
医疗诊断测试框架:
import unittest
from typing import List, Dict
import json
class MedicalPromptTestCase(unittest.TestCase):
"""医疗Prompt测试用例"""
def setUp(self):
self.test_cases = [
{
"id": "test_001",
"category": "image_diagnosis",
"input": {
"image_path": "test_data/ct_lung_nodule.dcm",
"modality": "CT",
"clinical_history": "患者,男,55岁,体检发现肺部结节"
},
"expected_output": {
"findings": ["右肺上叶结节"],
"diagnosis": "右肺上叶结节,建议进一步检查",
"confidence": 0.9
},
"min_accuracy": 0.85
},
{
"id": "test_002",
"category": "record_analysis",
"input": {
"record_text": "患者,女,35岁,主诉发热、咳嗽3天..."
},
"expected_output": {
"diagnosis_suggestions": ["肺炎", "支气管炎"],
"key_symptoms": ["发热", "咳嗽"]
},
"min_accuracy": 0.80
}
]
def test_image_diagnosis_accuracy(self):
"""测试影像诊断准确率"""
for case in self.test_cases:
if case["category"] != "image_diagnosis":
continue
# 执行诊断
result = self.diagnose_image(case["input"])
# 评估结果
accuracy = self.evaluate_diagnosis(
result,
case["expected_output"]
)
self.assertGreaterEqual(
accuracy,
case["min_accuracy"],
f"Test {case['id']} failed: accuracy {accuracy} < {case['min_accuracy']}"
)
def evaluate_diagnosis(self, result: Dict, expected: Dict) -> float:
"""评估诊断结果"""
# 计算诊断准确率
# 1. 检查关键发现是否匹配
findings_match = self._match_findings(
result.get("findings", []),
expected.get("findings", [])
)
# 2. 检查诊断是否匹配
diagnosis_match = self._match_diagnosis(
result.get("diagnosis", ""),
expected.get("diagnosis", "")
)
# 3. 综合准确率
accuracy = (findings_match + diagnosis_match) / 2
return accuracy
def _match_findings(self, actual: List[str], expected: List[str]) -> float:
"""匹配发现"""
if not expected:
return 1.0
matched = sum(1 for f in expected if f in actual)
return matched / len(expected)
def _match_diagnosis(self, actual: str, expected: str) -> float:
"""匹配诊断"""
# 使用语义相似度计算
similarity = self.calculate_semantic_similarity(actual, expected)
return similarity
测试用例设计
测试用例类型
1. 功能测试用例
- 目标:验证Prompt能否正确进行医疗诊断
- 示例:
- 输入:CT影像(肺部结节)
- 期望:识别结节位置,给出诊断建议
- 验证:诊断准确率≥85%
2. 边界测试用例
- 目标:验证Prompt在边界情况下的表现
- 示例:
- 输入:低质量影像、罕见疾病、复杂病例
- 期望:正确处理,给出合理建议
- 验证:不崩溃,返回合理结果
3. 安全性测试用例
- 目标:验证Prompt的安全性
- 示例:
- 输入:恶意输入、异常数据
- 期望:拒绝处理或返回错误提示
- 验证:不返回不当内容,保护患者隐私
4. 多模态测试用例
- 目标:验证多模态输入的处理能力
- 示例:
- 输入:影像+病历文本+检验结果
- 期望:综合多模态信息给出诊断
- 验证:诊断准确率提升
测试用例管理
测试用例格式:
{
"id": "test_medical_001",
"category": "image_diagnosis",
"modality": "CT",
"disease_type": "lung_nodule",
"input": {
"image_path": "test_data/ct_nodule.dcm",
"patient_info": {
"age": 55,
"gender": "male"
},
"clinical_history": "体检发现"
},
"expected_output": {
"findings": ["右肺上叶结节,大小约1.5cm"],
"diagnosis": "右肺上叶结节,建议进一步检查",
"confidence": 0.9,
"recommendations": ["CT增强扫描", "3个月后复查"]
},
"evaluation_criteria": {
"min_accuracy": 0.85,
"min_confidence": 0.8,
"required_keywords": ["结节", "右肺"]
},
"tags": ["CT", "lung", "nodule"]
}
自动化测试
自动化测试流程
自动化测试实现
import pytest
from typing import List, Dict
import asyncio
class AutomatedMedicalTest:
"""自动化医疗测试"""
def __init__(self, test_cases: List[Dict]):
self.test_cases = test_cases
self.results = []
async def run_all_tests(self):
"""运行所有测试"""
tasks = [self.run_test(case) for case in self.test_cases]
results = await asyncio.gather(*tasks)
# 生成测试报告
self.generate_report(results)
return results
async def run_test(self, test_case: Dict):
"""运行单个测试"""
try:
# 执行测试
result = await self.execute_test(test_case)
# 评估结果
evaluation = self.evaluate_result(result, test_case)
return {
'test_id': test_case['id'],
'status': 'passed' if evaluation['passed'] else 'failed',
'accuracy': evaluation['accuracy'],
'details': evaluation['details']
}
except Exception as e:
return {
'test_id': test_case['id'],
'status': 'error',
'error': str(e)
}
def evaluate_result(self, result: Dict, test_case: Dict) -> Dict:
"""评估测试结果"""
expected = test_case['expected_output']
criteria = test_case['evaluation_criteria']
# 计算准确率
accuracy = self.calculate_accuracy(result, expected)
# 检查置信度
confidence_ok = result.get('confidence', 0) >= criteria.get('min_confidence', 0.8)
# 检查关键词
keywords_ok = self.check_keywords(result, criteria.get('required_keywords', []))
passed = (
accuracy >= criteria.get('min_accuracy', 0.8) and
confidence_ok and
keywords_ok
)
return {
'passed': passed,
'accuracy': accuracy,
'confidence_ok': confidence_ok,
'keywords_ok': keywords_ok,
'details': {
'expected': expected,
'actual': result
}
}
7.2 端到端自动化
E2E测试流程
完整诊断流程测试
测试场景:
- 患者挂号
- 影像上传
- AI诊断
- 医生审核
- 报告生成
- 报告发布
实现示例:
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
class MedicalE2ETest:
"""医疗系统E2E测试"""
def setup_method(self):
"""测试准备"""
self.driver = webdriver.Chrome()
self.driver.get("https://medical-ai.com")
def teardown_method(self):
"""测试清理"""
self.driver.quit()
def test_complete_diagnosis_flow(self):
"""完整诊断流程测试"""
# 1. 登录
self.login("doctor@hospital.com", "password")
# 2. 上传影像
self.upload_image("test_data/ct_image.dcm")
# 3. 等待AI诊断
diagnosis_result = self.wait_for_diagnosis()
# 4. 验证诊断结果
assert diagnosis_result['status'] == 'completed'
assert diagnosis_result['confidence'] >= 0.8
# 5. 医生审核
self.review_diagnosis(diagnosis_result['diagnosis_id'])
# 6. 生成报告
report = self.generate_report(diagnosis_result['diagnosis_id'])
# 7. 验证报告
assert report['status'] == 'generated'
assert 'findings' in report
# 8. 发布报告
self.publish_report(report['report_id'])
# 9. 验证报告已发布
assert self.is_report_published(report['report_id'])
测试覆盖
测试覆盖范围
功能覆盖:
- 影像诊断功能:100%
- 病历分析功能:100%
- 知识检索功能:100%
- 报告生成功能:100%
场景覆盖:
- 常见疾病诊断:≥80%
- 罕见疾病诊断:≥60%
- 紧急情况处理:100%
- 异常情况处理:100%
数据覆盖:
- 不同影像类型:CT、MRI、X光、超声
- 不同疾病类型:常见病、罕见病
- 不同患者群体:不同年龄、性别
持续集成
CI/CD流程
GitHub Actions配置:
name: Medical AI Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-asyncio
- name: Run unit tests
run: pytest tests/unit/
- name: Run integration tests
run: pytest tests/integration/
- name: Run E2E tests
run: pytest tests/e2e/
env:
TEST_ENV: staging
7.3 红蓝对抗
对抗测试方法
对抗样本测试
目标:测试AI模型对对抗样本的鲁棒性
方法:
- 生成对抗样本(添加噪声、旋转、缩放等)
- 测试模型在对抗样本下的表现
- 评估模型鲁棒性
实现示例:
import numpy as np
from PIL import Image
import torch
class AdversarialTest:
"""对抗测试"""
def __init__(self, model):
self.model = model
def generate_adversarial_samples(self, image: np.ndarray,
attack_type: str = "noise"):
"""生成对抗样本"""
if attack_type == "noise":
# 添加高斯噪声
noise = np.random.normal(0, 0.1, image.shape)
adversarial = image + noise
elif attack_type == "rotation":
# 旋转
pil_image = Image.fromarray(image)
adversarial = np.array(pil_image.rotate(5))
elif attack_type == "brightness":
# 亮度调整
adversarial = np.clip(image * 1.2, 0, 255)
return adversarial
def test_robustness(self, test_images: List[np.ndarray]):
"""测试鲁棒性"""
results = []
for image in test_images:
# 原始预测
original_pred = self.model.predict(image)
# 对抗样本预测
adversarial_samples = [
self.generate_adversarial_samples(image, attack_type)
for attack_type in ["noise", "rotation", "brightness"]
]
adversarial_preds = [
self.model.predict(adv_sample)
for adv_sample in adversarial_samples
]
# 计算鲁棒性
robustness = self.calculate_robustness(
original_pred,
adversarial_preds
)
results.append({
'original_pred': original_pred,
'adversarial_preds': adversarial_preds,
'robustness': robustness
})
return results
def calculate_robustness(self, original_pred, adversarial_preds):
"""计算鲁棒性"""
# 计算预测一致性
consistent = sum(
1 for pred in adversarial_preds
if pred == original_pred
)
robustness = consistent / len(adversarial_preds)
return robustness
安全测试
数据安全测试
测试内容:
- 数据加密测试
- 数据访问控制测试
- 数据泄露测试
- 隐私保护测试
实现示例:
class SecurityTest:
"""安全测试"""
def test_data_encryption(self):
"""测试数据加密"""
# 测试传输加密
assert self.check_tls_encryption()
# 测试存储加密
assert self.check_storage_encryption()
def test_access_control(self):
"""测试访问控制"""
# 测试未授权访问
response = self.unauthorized_request()
assert response.status_code == 401
# 测试权限控制
response = self.insufficient_permission_request()
assert response.status_code == 403
def test_data_leakage(self):
"""测试数据泄露"""
# 测试日志中是否包含敏感信息
logs = self.get_recent_logs()
sensitive_patterns = ['patient_id', '身份证号', '手机号']
for pattern in sensitive_patterns:
assert pattern not in logs
性能测试
负载测试
测试场景:
- 正常负载:100并发请求
- 高负载:500并发请求
- 峰值负载:1000并发请求
实现示例:
import asyncio
import aiohttp
import time
class PerformanceTest:
"""性能测试"""
async def load_test(self, url: str, concurrent: int = 100):
"""负载测试"""
async def make_request(session):
start_time = time.time()
async with session.post(url, json={"test": "data"}) as response:
await response.json()
return time.time() - start_time
async with aiohttp.ClientSession() as session:
tasks = [make_request(session) for _ in range(concurrent)]
response_times = await asyncio.gather(*tasks)
return {
'avg_response_time': sum(response_times) / len(response_times),
'p95_response_time': sorted(response_times)[int(len(response_times) * 0.95)],
'p99_response_time': sorted(response_times)[int(len(response_times) * 0.99)],
'max_response_time': max(response_times),
'min_response_time': min(response_times)
}