- core/rag-kb:领域 RAG 知识库按模板配置(PRD 5.4/7.3,EPIC #6 子任务) - templating.py:知识源三类分类 + 模板命名推导 + 零依赖轻量 YAML 配置加载 - documents.py:文档段落抽取分块,chunk 携带 文档/章节/段落 溯源信息 - store.py:from_template_config 按模板自动构建 + 中英混合词频检索 + 类别过滤 - config/kb.template.yaml:ti-cl4 模板 RAG 库资产示例(工艺规范/SOP/国标) - tests:29 用例全绿(模板化/分块溯源/检索排序/类别过滤/配置校验)
127 lines
4.5 KiB
Python
127 lines
4.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""模板化(templating)单元测试:知识源分类 / 命名推导 / YAML 配置加载。"""
|
||
import os
|
||
import sys
|
||
import unittest
|
||
|
||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
import _bootstrap # noqa: F401
|
||
|
||
from rag_kb.templating import ( # noqa: E402
|
||
KbTemplateConfig,
|
||
KbTemplateNaming,
|
||
KnowledgeSourceKind,
|
||
SOURCE_KINDS,
|
||
load_kb_config,
|
||
sanitize,
|
||
)
|
||
|
||
CONFIG_PATH = os.path.join(
|
||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||
"config", "kb.template.yaml",
|
||
)
|
||
|
||
|
||
class SanitizeTest(unittest.TestCase):
|
||
def test_lower_and_replace_unsafe(self):
|
||
# 小写 + 空格等非安全字符替换为 `_`;RAG 命名**保留中文**(文档对象键)
|
||
self.assertEqual(sanitize("Ti-Cl4 模板"), "ti-cl4_模板")
|
||
self.assertEqual(sanitize("A.B-c_d"), "a.b-c_d")
|
||
|
||
def test_chinese_kept(self):
|
||
self.assertEqual(sanitize("沸腾氯化工艺规范"), "沸腾氯化工艺规范")
|
||
# `/` 属非安全字符,替换为 `_`
|
||
self.assertEqual(sanitize("GB/T 氯气安全使用标准"), "gb_t_氯气安全使用标准")
|
||
|
||
def test_empty_fallback(self):
|
||
self.assertEqual(sanitize(""), "kb")
|
||
self.assertEqual(sanitize("..."), "kb")
|
||
|
||
|
||
class SourceKindTest(unittest.TestCase):
|
||
def test_kinds_registered(self):
|
||
# 三类知识源固定注册(PRD 7.3:工艺规范 / SOP / 国标)
|
||
self.assertEqual(
|
||
set(SOURCE_KINDS),
|
||
{"process", "sop", "standard"},
|
||
)
|
||
|
||
def test_labels(self):
|
||
self.assertEqual(KnowledgeSourceKind.PROCESS.label, "工艺规范")
|
||
self.assertEqual(KnowledgeSourceKind.SOP.label, "SOP/操作手册")
|
||
self.assertEqual(KnowledgeSourceKind.STANDARD.label, "国标/标准")
|
||
|
||
|
||
class NamingTest(unittest.TestCase):
|
||
def setUp(self):
|
||
self.naming = KbTemplateNaming(template="ti-cl4")
|
||
|
||
def test_collection(self):
|
||
# 向量库 collection:下划线形态(对齐 data-bus `tpl_{tpl}` schema)
|
||
self.assertEqual(self.naming.collection(), "ti_cl4_kb")
|
||
|
||
def test_index_and_namespace(self):
|
||
self.assertEqual(self.naming.index_name(), "ti_cl4_kb_idx")
|
||
self.assertEqual(self.naming.namespace(), "tpl-ti-cl4-kb")
|
||
|
||
def test_doc_object_key(self):
|
||
self.assertEqual(
|
||
self.naming.doc_object_key("沸腾氯化工艺规范"),
|
||
"kb/沸腾氯化工艺规范.md",
|
||
)
|
||
|
||
def test_default_template(self):
|
||
self.assertEqual(KbTemplateNaming("").collection(), "kb_kb")
|
||
|
||
|
||
class YamlLoadTest(unittest.TestCase):
|
||
def test_load_example_config(self):
|
||
config = load_kb_config(CONFIG_PATH)
|
||
self.assertEqual(config.template, "ti-cl4")
|
||
self.assertEqual(config.version, "1.0.0")
|
||
# 三类知识源各至少声明一份文档
|
||
self.assertIn(KnowledgeSourceKind.PROCESS, {s.kind for s in config.sources})
|
||
self.assertIn(KnowledgeSourceKind.SOP, {s.kind for s in config.sources})
|
||
self.assertIn(KnowledgeSourceKind.STANDARD, {s.kind for s in config.sources})
|
||
self.assertGreaterEqual(len(config.all_documents()), 3)
|
||
|
||
def test_documents_for_kind(self):
|
||
config = load_kb_config(CONFIG_PATH)
|
||
docs = config.documents_for(KnowledgeSourceKind.PROCESS)
|
||
self.assertIn("沸腾氯化工艺规范", docs)
|
||
self.assertEqual(
|
||
config.documents_for(KnowledgeSourceKind.STANDARD),
|
||
["GB/T 氯气安全使用标准", "GB/T 钛及钛合金加工标准"],
|
||
)
|
||
|
||
def test_unknown_kind_rejected(self):
|
||
# 未知知识源类别必须报错(杜绝配置拼写漂移)
|
||
import tempfile
|
||
|
||
with tempfile.NamedTemporaryFile("w", suffix=".yaml", delete=False,
|
||
encoding="utf-8") as fh:
|
||
fh.write("template: ti-cl4\nsources:\n - kind: hmm\n documents: [a]\n")
|
||
path = fh.name
|
||
try:
|
||
with self.assertRaises(ValueError):
|
||
load_kb_config(path)
|
||
finally:
|
||
os.unlink(path)
|
||
|
||
def test_missing_template_rejected(self):
|
||
import tempfile
|
||
|
||
with tempfile.NamedTemporaryFile("w", suffix=".yaml", delete=False,
|
||
encoding="utf-8") as fh:
|
||
fh.write("version: 1.0.0\nsources: []\n")
|
||
path = fh.name
|
||
try:
|
||
with self.assertRaises(ValueError):
|
||
load_kb_config(path)
|
||
finally:
|
||
os.unlink(path)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|