first commit

This commit is contained in:
2026-04-28 11:29:33 +08:00
commit c2a398930d
44 changed files with 5723 additions and 0 deletions

2
tests/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
# tests/__init__.py
"""测试模块"""

184
tests/test_embedding.py Normal file
View File

@@ -0,0 +1,184 @@
# tests/test_embedding.py
"""嵌入和分块测试"""
import pytest
from loguru import logger
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from src.services.embedding.text_chunker import RegulationChunker, TextChunk, ChunkMetadata
from src.services.embedding.bge_m3_embedder import BGEM3Embedder, EmbeddingResult
class TestRegulationChunker:
"""法规分块器测试"""
@pytest.fixture
def chunker(self):
"""创建分块器实例"""
return RegulationChunker(chunk_size=512)
@pytest.fixture
def sample_regulation(self):
"""示例法规文档"""
return """
# GB 7258-2017 机动车运行安全技术条件
第一章 范围
第一条 本标准规定了机动车运行安全技术条件。
第二条 本标准适用于在我国道路上行驶的所有机动车。
第二章 术语和定义
第三条 下列术语和定义适用于本标准。
(一)机动车:以动力装置驱动或者牵引,上道路行驶的供人员乘用或者用于运送物品以及进行工程专项作业的轮式车辆。
(二)整车:完整的机动车,包括所有必要的部件和系统。
第三章 技术要求
第四条 机动车应满足以下基本要求:
1. 车辆应具有唯一的产品标识;
2. 车辆结构应安全可靠;
3. 车辆应配备必要的安全装置。
"""
def test_chunk_document(self, chunker, sample_regulation):
"""测试文档分块"""
chunks = chunker.chunk_document(
sample_regulation,
doc_id="gb7258",
doc_name="GB 7258-2017",
regulation_type="车辆安全"
)
# 应该有多个分块
assert len(chunks) > 3
# 每个分块应该有内容
for chunk in chunks:
assert len(chunk.content) > 0
assert chunk.metadata.doc_id == "gb7258"
def test_section_detection(self, chunker, sample_regulation):
"""测试章节检测"""
chunks = chunker.chunk_document(
sample_regulation,
doc_id="test",
doc_name="测试"
)
# 应该检测到章节
section_numbers = [c.metadata.section_number for c in chunks]
assert any(s for s in section_numbers) # 至少有一个章节编号
def test_clause_detection(self, chunker, sample_regulation):
"""测试条款检测"""
chunks = chunker.chunk_document(
sample_regulation,
doc_id="test",
doc_name="测试"
)
# 应该检测到条款
clause_numbers = [c.metadata.clause_number for c in chunks]
assert any(c for c in clause_numbers) # 至少有一个条款编号
def test_long_clause_split(self, chunker):
"""测试长条款分割"""
long_clause = """
第一条 本条款内容很长,需要进行分割处理。
本条款包含以下多项内容:
1. 第一项内容,这是一个非常长的子项,包含了大量的文字描述,需要进行适当的处理。
2. 第二项内容,这也是一个较长的子项,包含了相关的技术要求和规范说明。
3. 第三项内容,继续描述相关要求和注意事项,确保文档的完整性和规范性。
4. 第四项内容,补充说明其他相关事项,保证内容的全面性。
"""
chunks = chunker.chunk_document(
long_clause,
doc_id="test",
doc_name="测试"
)
# 长条款应该被分割成多个chunk
assert len(chunks) >= 1
class TestBGEM3Embedder:
"""BGE-M3嵌入模型测试"""
@pytest.fixture
def embedder(self):
"""创建嵌入模型实例"""
try:
return BGEM3Embedder()
except Exception as e:
pytest.skip(f"嵌入模型加载失败: {e}")
def test_embed_single(self, embedder):
"""测试单文本嵌入"""
text = "这是一条测试文本"
result = embedder.embed_single(text)
# 应该包含dense和sparse向量
assert 'dense' in result
assert 'sparse' in result
# dense向量维度应该是1024
assert len(result['dense']) == 1024
def test_embed_batch(self, embedder):
"""测试批量嵌入"""
texts = [
"第一条 本标准规定了机动车安全要求",
"第二条 机动车应符合技术条件",
"第三条 生产企业应建立管理体系"
]
result = embedder.embed(texts)
# 应该返回正确数量的向量
assert len(result.dense_embeddings) == 3
# 维度应该是1024
assert result.dense_embeddings.shape[1] == 1024
def test_embed_empty_list(self, embedder):
"""测试空列表嵌入"""
result = embedder.embed([])
# 应该返回空结果
assert len(result.dense_embeddings) == 0
def test_similarity(self, embedder):
"""测试相似度计算"""
import numpy as np
texts = [
"机动车安全标准要求",
"汽车安全技术规范",
"食品安全管理规定" # 不相关文本
]
result = embedder.embed(texts)
# 计算第一个文本与其他文本的相似度
query = result.dense_embeddings[0]
docs = result.dense_embeddings[1:]
similarities = embedder.compute_similarity(query, docs)
# 相关文档的相似度应该更高
assert similarities[0] > similarities[1] # 车辆安全 > 食品安全
if __name__ == "__main__":
pytest.main([__file__, "-v"])

136
tests/test_milvus.py Normal file
View File

@@ -0,0 +1,136 @@
# tests/test_milvus.py
"""Milvus集成测试"""
import pytest
from loguru import logger
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from src.services.storage.milvus_client import MilvusClient, SearchResult
from src.services.embedding.bge_m3_embedder import BGEM3Embedder
from src.config.settings import settings
class TestMilvusConnection:
"""Milvus连接测试"""
def test_connection(self):
"""测试Milvus连接"""
client = MilvusClient()
result = client.connect()
assert result == True
client.disconnect()
def test_create_collection(self):
"""测试创建Collection"""
client = MilvusClient()
client.connect()
result = client.create_collection(recreate=True)
assert result == True
# 检查Collection是否存在
stats = client.get_collection_stats()
assert stats["name"] == settings.milvus_collection
client.disconnect()
class TestMilvusOperations:
"""Milvus操作测试"""
@pytest.fixture
def client(self):
"""创建测试客户端"""
client = MilvusClient()
client.connect()
client.create_collection(recreate=True)
client.load_collection()
yield client
client.disconnect()
def test_insert_and_search(self, client):
"""测试插入和检索"""
from src.services.embedding.text_chunker import TextChunk, ChunkMetadata
# 创建测试数据
chunks = [
TextChunk(
content="第一条 为保障机动车安全技术性能,预防和减少机动车交通事故,保护人身安全,制定本标准。",
metadata=ChunkMetadata(
doc_id="test_doc",
doc_name="测试文档",
chunk_id="test_chunk_1",
clause_number="第一条",
regulation_type="车辆安全"
)
),
TextChunk(
content="第二条 本标准适用于在我国道路上行驶的所有机动车。",
metadata=ChunkMetadata(
doc_id="test_doc",
doc_name="测试文档",
chunk_id="test_chunk_2",
clause_number="第二条",
regulation_type="车辆安全"
)
)
]
# 生成嵌入
embedder = BGEM3Embedder()
embeddings = embedder.embed([c.content for c in chunks])
# 插入数据
inserted_ids = client.insert_chunks(chunks, embeddings)
assert len(inserted_ids) == 2
# 执行检索
query = "机动车安全标准"
query_embedding = embedder.embed_single(query)
results = client.hybrid_search(
query_dense=query_embedding['dense'].tolist(),
query_sparse=query_embedding['sparse'],
top_k=2
)
assert len(results) > 0
assert "机动车" in results[0].content or "安全" in results[0].content
class TestEmbedding:
"""嵌入模型测试"""
def test_embed_single_text(self):
"""测试单文本嵌入"""
embedder = BGEM3Embedder()
result = embedder.embed_single("这是一条测试文本")
assert 'dense' in result
assert 'sparse' in result
assert len(result['dense']) == 1024 # BGE-M3默认维度
def test_embed_batch(self):
"""测试批量嵌入"""
embedder = BGEM3Embedder()
texts = [
"第一条 本标准规定了机动车安全要求",
"第二条 机动车应符合以下技术条件",
"第三条 生产企业应建立质量管理体系"
]
result = embedder.embed(texts)
assert len(result.dense_embeddings) == 3
assert result.dense_embeddings.shape[1] == 1024
if __name__ == "__main__":
pytest.main([__file__, "-v"])

118
tests/test_parser.py Normal file
View File

@@ -0,0 +1,118 @@
# tests/test_parser.py
"""文档解析测试"""
import pytest
from loguru import logger
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from src.services.parser.pdf_parser import PDFParser, parse_pdf_to_markdown
from src.services.parser.docx_parser import DocxParser, parse_docx_to_markdown
from src.services.parser.mineru_parser import MinerUParser, ParserOrchestrator
class TestPDFParser:
"""PDF解析测试"""
def test_parser_initialization(self):
"""测试PDF解析器初始化"""
parser = PDFParser()
assert parser is not None
def test_parse_sample_pdf(self):
"""测试解析示例PDF如果有"""
# 如果有示例PDF文件可以在此测试
sample_pdf = os.path.join(os.path.dirname(__file__), "sample.pdf")
if os.path.exists(sample_pdf):
parser = PDFParser()
result = parser.parse(sample_pdf)
assert result.total_pages > 0
assert len(result.pages) > 0
assert len(result.markdown_text) > 0
class TestDocxParser:
"""Word文档解析测试"""
def test_parser_initialization(self):
"""测试Word解析器初始化"""
parser = DocxParser()
assert parser is not None
def test_parse_sample_docx(self):
"""测试解析示例DOCX"""
sample_docx = os.path.join(os.path.dirname(__file__), "sample.docx")
if os.path.exists(sample_docx):
parser = DocxParser()
result = parser.parse(sample_docx)
assert len(result.paragraphs) > 0
assert len(result.markdown_text) > 0
class TestChunker:
"""分块器测试"""
def test_chunker_initialization(self):
"""测试分块器初始化"""
from src.services.embedding.text_chunker import RegulationChunker
chunker = RegulationChunker(chunk_size=512)
assert chunker is not None
def test_chunk_sample_text(self):
"""测试分块示例文本"""
from src.services.embedding.text_chunker import RegulationChunker
sample_text = """
# 测试法规文档
第一章 总则
第一条 为规范某项行为,制定本规定。
第二条 本规定适用于相关主体。
第二章 具体要求
第三条 相关主体应当遵守以下要求:
(一)建立管理制度;
(二)配备专业人员;
(三)定期进行检查。
"""
chunker = RegulationChunker(chunk_size=256)
chunks = chunker.chunk_document(
sample_text,
doc_id="test",
doc_name="测试法规"
)
assert len(chunks) > 0
# 验证分块包含章节信息
has_section = any(c.metadata.section_number for c in chunks)
assert has_section
class TestFullPipeline:
"""完整流程测试"""
def test_pipeline_without_files(self):
"""测试流程初始化(无文件)"""
from src.services.document_processor import DocumentProcessor
processor = DocumentProcessor()
assert processor is not None
processor.close()
if __name__ == "__main__":
pytest.main([__file__, "-v"])

222
tests/verify_mvp.py Normal file
View File

@@ -0,0 +1,222 @@
"""
MVP功能验证脚本
用于验证完整的文档处理流程:
1. PDF/DOCX解析
2. 智能分块
3. 向量嵌入
4. Milvus入库
5. 混合检索
使用方法:
1. 首先启动Milvus: docker-compose up -d
2. 运行此脚本: python verify_mvp.py
"""
import os
import sys
import time
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
from loguru import logger
from src.config.logging import setup_logging
from src.services.document_processor import DocumentProcessor, ProcessingResult
from src.services.storage.milvus_client import MilvusClient
from src.config.settings import settings
# 设置日志
setup_logging(level="INFO")
def verify_milvus_connection():
"""验证Milvus连接"""
logger.info("=" * 50)
logger.info("Step 1: 验证Milvus连接")
logger.info("=" * 50)
client = MilvusClient()
try:
result = client.connect()
if result:
logger.success("Milvus连接成功")
# 创建Collection
client.create_collection(recreate=True)
stats = client.get_collection_stats()
logger.info(f"Collection信息: {stats}")
client.disconnect()
return True
else:
logger.error("Milvus连接失败请检查docker-compose是否启动")
return False
except Exception as e:
logger.error(f"Milvus连接异常: {e}")
logger.info("请先启动Milvus: cd docker && docker-compose up -d")
return False
def verify_embedding_model():
"""验证嵌入模型"""
logger.info("=" * 50)
logger.info("Step 2: 验证BGE-M3嵌入模型")
logger.info("=" * 50)
try:
from src.services.embedding.bge_m3_embedder import BGEM3Embedder
embedder = BGEM3Embedder()
logger.success("嵌入模型加载成功")
# 测试嵌入
test_text = "这是一条测试文本,用于验证嵌入模型功能"
result = embedder.embed_single(test_text)
logger.info(f"Dense向量维度: {len(result['dense'])}")
logger.info(f"Sparse向量词数: {len(result['sparse'])}")
return True
except Exception as e:
logger.error(f"嵌入模型验证失败: {e}")
logger.info("请确保已安装FlagEmbedding: pip install FlagEmbedding")
return False
def verify_sample_document():
"""验证示例文档处理"""
logger.info("=" * 50)
logger.info("Step 3: 验证文档处理流程")
logger.info("=" * 50)
# 使用内置的示例文本(无需外部文件)
sample_text = """
# GB 7258-2017 机动车运行安全技术条件
第一章 范围
第一条 本标准规定了机动车运行安全技术条件,适用于在我国道路上行驶的所有机动车。
第二条 本标准包括整车、发动机、传动系、行驶系、制动系、照明与信号装置等技术要求。
第二章 术语和定义
第三条 下列术语和定义适用于本标准:
(一)机动车:以动力装置驱动或者牵引,上道路行驶的供人员乘用或者用于运送物品的轮式车辆。
(二)整车产品:完整的机动车产品,包括所有必要的部件和系统。
第三章 整车技术要求
第四条 机动车整车应满足以下基本技术要求:
1. 车辆外廓尺寸应符合规定限值;
2. 车辆应具有唯一的产品标识;
3. 车辆结构应安全可靠,各部件连接牢固。
第五条 车辆应配备必要的安全装置,包括:
- 制动系统
- 照明与信号装置
- 安全带
- 灭火器
"""
try:
from src.services.embedding.text_chunker import RegulationChunker
from src.services.embedding.bge_m3_embedder import BGEM3Embedder
from src.services.storage.milvus_client import MilvusClient
# 1. 分块
logger.info("测试分块...")
chunker = RegulationChunker(chunk_size=256)
chunks = chunker.chunk_document(
sample_text,
doc_id="gb7258_test",
doc_name="GB 7258-2017 测试",
regulation_type="车辆安全"
)
logger.success(f"分块完成,共{len(chunks)}个chunk")
# 2. 嵌入
logger.info("测试嵌入...")
embedder = BGEM3Embedder()
embeddings = embedder.embed([c.content for c in chunks])
logger.success(f"嵌入完成,向量数: {len(embeddings.dense_embeddings)}")
# 3. 入库
logger.info("测试入库...")
client = MilvusClient()
client.connect()
client.create_collection(recreate=False)
client.load_collection()
inserted_ids = client.insert_chunks(chunks, embeddings)
logger.success(f"入库完成,共{len(inserted_ids)}条记录")
# 4. 检索
logger.info("测试检索...")
query = "机动车安全技术要求"
query_emb = embedder.embed_single(query)
results = client.hybrid_search(
query_dense=query_emb['dense'].tolist(),
query_sparse=query_emb['sparse'],
top_k=3
)
logger.success(f"检索完成,返回{len(results)}条结果")
for i, r in enumerate(results):
logger.info(f"结果{i+1}: 分数={r.score:.4f}, 内容={r.content[:50]}...")
client.disconnect()
return True
except Exception as e:
logger.error(f"文档处理验证失败: {e}")
return False
def main():
"""主验证流程"""
logger.info("\n" + "=" * 60)
logger.info("AI+合规智能中枢 MVP功能验证")
logger.info("=" * 60)
results = []
# 1. Milvus连接验证
results.append(("Milvus连接", verify_milvus_connection()))
# 2. 嵌入模型验证
results.append(("嵌入模型", verify_embedding_model()))
# 3. 文档处理验证
results.append(("文档处理", verify_sample_document()))
# 输出结果汇总
logger.info("\n" + "=" * 60)
logger.info("验证结果汇总")
logger.info("=" * 60)
all_passed = True
for name, passed in results:
status = "✅ 通过" if passed else "❌ 失败"
logger.info(f"{name}: {status}")
if not passed:
all_passed = False
if all_passed:
logger.success("\n🎉 所有验证通过MVP功能正常")
else:
logger.warning("\n⚠️ 部分验证失败,请检查配置和环境")
return all_passed
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)