Initial: integrated 2025 LawGraph (graphrag_pipeline) + 2026 kg_project

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 10:13:39 +08:00
commit 6c1a69af0d
83 changed files with 14295 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
# 测试文件说明
本目录包含所有测试脚本。
## 测试文件列表
### 1. `test_basic.py`
基础功能测试,包括:
- 模块导入测试
- 本体模型测试
- Prompt模板测试
运行方式:
```bash
uv run python tests/test_basic.py
```
### 2. `test_siliconflow.py`
测试硅基流动API配置,验证API密钥和连接是否正常。
运行方式:
```bash
uv run python tests/test_siliconflow.py
```
### 3. `test_documents.py`
测试文档解析功能,包括:
- 文档解析(.docx文件)
- TextUnit切分
- 不调用LLM,仅测试解析逻辑
运行方式:
```bash
uv run python tests/test_documents.py
```
### 4. `test_model.py`
测试硅基流动平台上的多个模型,找出可用模型。
运行方式:
```bash
uv run python tests/test_model.py
```
### 5. `test_my_models.py`
测试项目配置使用的两个指定模型:
- `deepseek-ai/DeepSeek-R1-Distill-Qwen-7B`
- `Qwen/Qwen2.5-7B-Instruct`
运行方式:
```bash
uv run python tests/test_my_models.py
```
### 6. `test_quick.py`
快速测试完整流程,处理少量TextUnit(1个文档的前2个),验证:
- 文档解析
- 实体识别(NER
- 关系抽取(RE
- 知识图谱构建
- 社区检测
运行方式:
```bash
uv run python tests/test_quick.py
```
## 注意事项
- 测试脚本需要使用配置的API密钥(`.env`文件)
- 部分测试会调用LLM API,可能需要一些时间
- 确保已安装所有依赖包:`uv sync`
@@ -0,0 +1,72 @@
"""
基础功能测试
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
def test_imports():
"""测试模块导入"""
try:
from src.preprocessing.text_processor import TextProcessor
from src.preprocessing.document_parser import DocumentParser
from src.ontology.schema import OntologySchema
from src.prompts.ner_prompts import NERPromptBuilder
from src.extraction.ner import NERExtractor
from src.kg_builder.graph import KnowledgeGraph
print("✅ 所有模块导入成功")
return True
except ImportError as e:
print(f"❌ 模块导入失败: {e}")
return False
def test_ontology():
"""测试本体模型"""
try:
from src.ontology.schema import OntologySchema
schema = OntologySchema()
summary = schema.get_schema_summary()
assert summary["entity_types"] == 9
assert summary["relation_types"] == 8
print("✅ 本体模型测试通过")
return True
except Exception as e:
print(f"❌ 本体模型测试失败: {e}")
return False
def test_prompt_template():
"""测试Prompt模板"""
try:
from src.prompts.template import PromptTemplate
template = PromptTemplate(
task_description={"system": "test", "user": "test"},
candidate_targets={"entity": "definition"},
task_examples=[{"input": "test", "output": "test"}],
task_emphasis="test",
)
assert template.task_description is not None
print("✅ Prompt模板测试通过")
return True
except Exception as e:
print(f"❌ Prompt模板测试失败: {e}")
return False
if __name__ == "__main__":
print("开始运行基础测试...")
test_imports()
test_ontology()
test_prompt_template()
print("测试完成!")
@@ -0,0 +1,100 @@
#!/usr/bin/env python
"""
测试文档解析(仅测试3个文件,不调用LLM)
"""
import sys
import json
import logging
from pathlib import Path
# 修复:移除conda环境的路径,确保使用虚拟环境的包
sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()]
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.preprocessing.document_parser import DocumentParser
from src.utils.config import load_config
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def main():
"""测试文档解析"""
logger.info("=" * 60)
logger.info("测试文档解析(3个文件)")
logger.info("=" * 60)
# 配置
config = load_config()
data_dir = Path("../data/1法律")
output_dir = Path("./output")
output_dir.mkdir(parents=True, exist_ok=True)
# 解析文档
logger.info(f"解析目录: {data_dir}")
parser = DocumentParser()
docs = parser.parse_directory(str(data_dir))
# 只处理前3个
docs = docs[:3]
logger.info(f"处理 {len(docs)} 个文档")
# 切分为TextUnit
logger.info("切分文档为TextUnit...")
all_textunits = []
for i, doc in enumerate(docs, 1):
logger.info(f"处理文档 {i}/{len(docs)}: {Path(doc.get('file_path', '')).name}")
textunits = parser.split_into_textunits(
doc,
max_length=config.MAX_TEXTUNIT_LENGTH
)
for tu in textunits:
tu["doc_id"] = doc.get("file_path", "")
tu["id"] = f"{doc.get('file_path', '')}_{tu.get('paragraph_index', 0)}"
all_textunits.extend(textunits)
logger.info(f" - 生成 {len(textunits)} 个TextUnit")
logger.info(f"共生成 {len(all_textunits)} 个TextUnit")
# 保存结果
output_file = output_dir / "test_textunits.json"
result = {
"num_docs": len(docs),
"num_textunits": len(all_textunits),
"docs": [
{
"file_path": doc.get("file_path", ""),
"title": doc.get("title", ""),
"text_length": len(doc.get("text", "")),
}
for doc in docs
],
"sample_textunits": [
{
"id": tu.get("id", ""),
"text": tu.get("text", "")[:200] + "..." if len(tu.get("text", "")) > 200 else tu.get("text", ""),
"paragraph_index": tu.get("paragraph_index", 0),
}
for tu in all_textunits[:10] # 只保存前10个作为示例
]
}
with open(output_file, "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
logger.info("=" * 60)
logger.info("测试完成!")
logger.info(f"结果已保存到: {output_file}")
logger.info(f"统计信息:")
logger.info(f" - 文档数: {result['num_docs']}")
logger.info(f" - TextUnit数: {result['num_textunits']}")
logger.info("=" * 60)
if __name__ == "__main__":
main()
@@ -0,0 +1,73 @@
#!/usr/bin/env python
"""
测试硅基流动API并找到可用的模型
"""
import sys
import os
from pathlib import Path
sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()]
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.utils.config import load_config
from src.utils.llm_client import LLMClient, LLMProvider
# 常见的硅基流动模型名称
TEST_MODELS = [
"deepseek-ai/DeepSeek-V2.5",
"deepseek-ai/DeepSeek-V2",
"Qwen/Qwen2.5-72B-Instruct",
"Qwen/Qwen2.5-72B-Chat",
"meta-llama/Llama-3.1-70B-Instruct",
"01-ai/Yi-1.5-34B-Chat",
"mistralai/Mistral-7B-Instruct-v0.2",
]
def test_model(provider, model_name):
"""测试单个模型"""
try:
config = load_config()
client = LLMClient(provider=provider, model=model_name, config=config)
messages = [{"role": "user", "content": "你好"}]
response = client.chat(messages)
print(f"✅ 模型 {model_name} 可用")
print(f" 响应: {response[:50]}...")
return True
except Exception as e:
print(f"❌ 模型 {model_name} 不可用: {str(e)[:100]}")
return False
if __name__ == "__main__":
print("=" * 60)
print("测试硅基流动模型")
print("=" * 60)
print()
config = load_config()
if not config.SILICONFLOW_API_KEY:
print("❌ SILICONFLOW_API_KEY 未配置")
sys.exit(1)
print(f"API密钥: {config.SILICONFLOW_API_KEY[:20]}...")
print(f"API Base: {config.SILICONFLOW_API_BASE}")
print()
# 测试所有模型
available_models = []
for model in TEST_MODELS:
if test_model(LLMProvider.SILICONFLOW, model):
available_models.append(model)
print()
print("=" * 60)
if available_models:
print(f"✅ 找到 {len(available_models)} 个可用模型:")
for model in available_models:
print(f" - {model}")
print(f"\n建议使用的模型: {available_models[0]}")
else:
print("❌ 没有找到可用的模型,请检查API密钥或模型名称")
print("=" * 60)
@@ -0,0 +1,61 @@
#!/usr/bin/env python
"""
测试用户指定的两个模型
"""
import sys
from pathlib import Path
sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()]
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.utils.llm_client import LLMClient, LLMProvider
from src.utils.config import load_config
def test_model(provider, model_name):
"""测试单个模型"""
try:
config = load_config()
client = LLMClient(provider=provider, model=model_name, config=config)
messages = [{"role": "user", "content": "你好"}]
response = client.chat(messages)
print(f"✅ 模型 {model_name} 可用")
print(f" 响应: {response[:50]}...")
return True
except Exception as e:
print(f"❌ 模型 {model_name} 不可用: {str(e)[:100]}")
return False
if __name__ == "__main__":
print("=" * 60)
print("测试指定的两个模型")
print("=" * 60)
print()
config = load_config()
if not config.SILICONFLOW_API_KEY:
print("❌ SILICONFLOW_API_KEY 未配置")
sys.exit(1)
# 测试用户指定的两个模型
models_to_test = [
"deepseek-ai/DeepSeek-R1-Distill-Qwen-7B",
"Qwen/Qwen2.5-7B-Instruct",
]
available = []
for model in models_to_test:
if test_model(LLMProvider.SILICONFLOW, model):
available.append(model)
print()
print("=" * 60)
if len(available) == len(models_to_test):
print(f"✅ 所有模型都可用!")
elif available:
print(f"⚠️ 部分模型可用: {len(available)}/{len(models_to_test)}")
else:
print("❌ 没有可用模型")
print("=" * 60)
@@ -0,0 +1,57 @@
#!/usr/bin/env python
"""
快速测试:只处理1个文档的前2个TextUnit
"""
import sys
import logging
from pathlib import Path
sys.path = [p for p in sys.path if 'anaconda3' not in p.lower() and 'conda' not in p.lower()]
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.preprocessing.document_parser import DocumentParser
from src.kg_builder.indexer import GraphIndexer
from src.utils.config import load_config
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def main():
logger.info("=" * 60)
logger.info("快速测试:处理1个文档的前2个TextUnit")
logger.info("=" * 60)
config = load_config()
# 解析1个文档
parser = DocumentParser()
doc_path = "../data/1法律/10-中华人民共和国乡村振兴促进法.docx"
doc = parser.parse_docx(doc_path)
# 切分为TextUnit,只取前2个
textunits = parser.split_into_textunits(doc, max_length=config.MAX_TEXTUNIT_LENGTH)[:2]
for tu in textunits:
tu["doc_id"] = doc.get("file_path", "")
tu["id"] = f"{doc.get('file_path', '')}_{tu.get('paragraph_index', 0)}"
logger.info(f"准备处理 {len(textunits)} 个TextUnit")
# 构建知识图谱(只处理这2个)
logger.info("开始构建知识图谱...")
indexer = GraphIndexer(config=config)
kg_graph = indexer.index(textunits, use_verification=True)
logger.info("=" * 60)
logger.info("快速测试完成!")
logger.info(f"节点数: {kg_graph.number_of_nodes()}")
logger.info(f"边数: {kg_graph.number_of_edges()}")
logger.info("=" * 60)
if __name__ == "__main__":
main()
@@ -0,0 +1,81 @@
#!/usr/bin/env python
"""
测试硅基流动API配置
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from src.utils.llm_client import LLMClient, LLMProvider
from src.utils.config import load_config
def test_siliconflow():
"""测试硅基流动API"""
print("=" * 60)
print("测试硅基流动API配置")
print("=" * 60)
# 加载配置
config = load_config()
# 检查API密钥
if not config.SILICONFLOW_API_KEY:
print("❌ 错误: SILICONFLOW_API_KEY 未配置")
print("请在 .env 文件中设置 SILICONFLOW_API_KEY")
return False
print(f"✅ API密钥已配置: {config.SILICONFLOW_API_KEY[:20]}...")
print(f"✅ API Base URL: {config.SILICONFLOW_API_BASE}")
# 创建客户端(使用一个常见的模型,用户需要根据实际可用模型调整)
print("\n尝试创建客户端...")
try:
# 注意:这里使用的模型名称需要根据硅基流动平台实际可用模型调整
# 常用的模型包括:Qwen/Qwen2.5-72B-Instruct, meta-llama/Llama-3.1-70B-Instruct 等
client = LLMClient(
provider=LLMProvider.SILICONFLOW,
model="Qwen/Qwen2.5-72B-Instruct", # 请根据实际情况修改模型名称
config=config
)
print("✅ 客户端创建成功")
except Exception as e:
print(f"❌ 客户端创建失败: {e}")
return False
# 测试简单对话
print("\n测试API调用...")
messages = [
{"role": "user", "content": "请用一句话介绍知识图谱。"}
]
try:
response = client.chat(messages)
print(f"✅ API调用成功")
print(f"\n响应内容:\n{response}")
return True
except Exception as e:
print(f"❌ API调用失败: {e}")
print("\n提示:")
print("1. 请检查API密钥是否正确")
print("2. 请检查网络连接")
print("3. 请确认模型名称是否在硅基流动平台可用")
print("4. 可以访问 https://siliconflow.cn/ 查看可用模型列表")
return False
if __name__ == "__main__":
success = test_siliconflow()
if success:
print("\n" + "=" * 60)
print("✅ 硅基流动API配置测试通过!")
print("=" * 60)
else:
print("\n" + "=" * 60)
print("❌ 硅基流动API配置测试失败,请检查配置")
print("=" * 60)
sys.exit(1)