Initial commit: QMDSearch 分层信息检索服务
- FastAPI + Qdrant + Redis + Ollama 技术栈 - L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合) - 文档三级总结与 2.5 级回退 - query 解析路由与分类 - /admin 管理页面
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""Summarizer 标题树接入的单元测试"""
|
||||
|
||||
from app.core.summarizer import Summarizer
|
||||
from app.models.document import SummaryLevel
|
||||
|
||||
|
||||
class FakeOllama:
|
||||
"""记录调用次数的 OllamaClient 替身"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.prompts: list[str] = []
|
||||
|
||||
async def generate(self, prompt: str) -> str:
|
||||
self.prompts.append(prompt)
|
||||
return "LLM 生成结果"
|
||||
|
||||
|
||||
def _structured_text() -> str:
|
||||
"""构造带标题结构且长度 ≥ 500 的文档(不触发 2.5 回退)"""
|
||||
paragraph = "这是章节正文内容,包含足够多的信息量。" * 10
|
||||
return f"# 安装指南\n{paragraph}\n## 环境准备\n{paragraph}\n## 安装步骤\n{paragraph}"
|
||||
|
||||
|
||||
class TestStructuredDocument:
|
||||
"""结构化文档:L2 直接使用标题树,不调 LLM"""
|
||||
|
||||
async def test_l2_from_headings_skips_llm(self):
|
||||
ollama = FakeOllama()
|
||||
summarizer = Summarizer(ollama=ollama) # type: ignore[arg-type]
|
||||
|
||||
summary = await summarizer.summarize(_structured_text(), title="安装文档")
|
||||
|
||||
assert summary.level == SummaryLevel.L3
|
||||
# L1 + L3 两次调用,跳过了 L2 的 LLM 调用
|
||||
assert len(ollama.prompts) == 2
|
||||
# 大纲由标题树渲染,包含标题文本与层级缩进
|
||||
assert summary.l2_outline is not None
|
||||
assert "- 安装指南" in summary.l2_outline
|
||||
assert " - 环境准备" in summary.l2_outline
|
||||
assert " - 安装步骤" in summary.l2_outline
|
||||
assert summary.l2_outline != "LLM 生成结果"
|
||||
# L3 的 prompt 中注入了标题树大纲
|
||||
assert "安装指南" in ollama.prompts[1]
|
||||
|
||||
|
||||
class TestPlainDocument:
|
||||
"""无结构文档:L2 回退 LLM 生成"""
|
||||
|
||||
async def test_l2_falls_back_to_llm(self):
|
||||
ollama = FakeOllama()
|
||||
summarizer = Summarizer(ollama=ollama) # type: ignore[arg-type]
|
||||
text = "这是一段没有标题结构的正文内容," * 40 # 640 字符,不触发 2.5 回退
|
||||
|
||||
summary = await summarizer.summarize(text, title="")
|
||||
|
||||
assert summary.level == SummaryLevel.L3
|
||||
# L1 + L2 + L3 三次调用
|
||||
assert len(ollama.prompts) == 3
|
||||
assert summary.l2_outline == "LLM 生成结果"
|
||||
|
||||
|
||||
class TestFallbackUnaffected:
|
||||
"""2.5 级回退逻辑不受标题树改造影响"""
|
||||
|
||||
async def test_short_structured_text_still_fallback(self):
|
||||
ollama = FakeOllama()
|
||||
summarizer = Summarizer(ollama=ollama) # type: ignore[arg-type]
|
||||
text = "# 标题一\n简短内容。\n# 标题二\n简短内容。"
|
||||
|
||||
summary = await summarizer.summarize(text, title="")
|
||||
|
||||
assert summary.level == SummaryLevel.L2_HALF
|
||||
assert summary.l2_outline is None
|
||||
# L1 + L2.5 两次调用
|
||||
assert len(ollama.prompts) == 2
|
||||
Reference in New Issue
Block a user