feat: 新增多格式文件上传入库与认证体系

- 新增 JWT 认证模块,支持登录/注册/用户管理
- 新增文件上传接口,支持 .txt/.md/.html/.pdf/.docx 等格式解析入库
- 新增检索结果 AI 总结功能
- 新增文本去重缓存机制
- 新增全局认证夹具简化测试
- 新增配置项与环境变量支持
- 完善文档与测试覆盖
This commit is contained in:
2026-07-30 10:30:15 +08:00
parent 51dc8dc4f6
commit dce9e31bde
31 changed files with 3021 additions and 45 deletions
+66
View File
@@ -0,0 +1,66 @@
"""检索结果 AI 总结
对检索返回的 chunk 命中结果,调用 Ollama 本地模型生成一段针对用户 query 的总结回答。
仅基于检索结果内容,不编造未提及的信息。
"""
import structlog
from app.config import settings
from app.models.search import SearchHit
from app.services.ollama import OllamaClient
logger = structlog.get_logger()
class ResultSummarizer:
"""检索结果总结器"""
def __init__(self, ollama: OllamaClient | None = None) -> None:
self.ollama = ollama or OllamaClient()
async def summarize(self, query: str, hits: list[SearchHit]) -> str:
"""对检索结果生成针对 query 的总结
取前 settings.result_summary_max_hits 条命中拼接为上下文,
无命中时返回空字符串(不调 LLM)。
"""
if not hits:
return ""
max_hits = settings.result_summary_max_hits
selected = hits[:max_hits]
context = self._build_context(selected)
prompt = self._build_prompt(query, context)
try:
summary = await self.ollama.generate(prompt)
except Exception:
logger.warning("检索结果总结生成失败,返回空字符串", exc_info=True)
return ""
logger.info("检索结果总结完成", query=query, hits_count=len(selected), summary_len=len(summary))
return summary.strip()
@staticmethod
def _build_context(hits: list[SearchHit]) -> str:
"""拼接命中结果为带编号的上下文"""
blocks: list[str] = []
for i, hit in enumerate(hits, start=1):
header_parts = [f"[{i}]"]
if hit.title:
header_parts.append(hit.title)
if hit.section_path:
header_parts.append(hit.section_path)
header = " / ".join(header_parts)
blocks.append(f"{header}\n{hit.text}")
return "\n\n---\n\n".join(blocks)
@staticmethod
def _build_prompt(query: str, context: str) -> str:
return (
"请根据以下检索结果,针对用户问题生成一段简洁的总结回答。\n"
"要求:\n"
"- 综合多条结果信息,不要简单逐条罗列;\n"
"- 只基于检索结果内容,不编造未提及的信息;\n"
"- 用中文回答,简洁明了。\n\n"
f"用户问题:{query}\n\n"
f"检索结果:\n{context}"
)