首页
/ DeepTutor v1.0.0-beta.2 发布解读:设置热重载、MinerU 嵌套目录修复与 Python 3.11 基线

DeepTutor v1.0.0-beta.2 发布解读:设置热重载、MinerU 嵌套目录修复与 Python 3.11 基线

2026-09-05 22:33:59作者:柯茵沙

DeepTutor v1.0.0-beta.2(发布于 2026.04.07)是一次以运行时体验为核心的版本更新:模型设置(API Key、模型选择、Endpoint)在保存后立即生效而无需重启服务,问题提取器能够发现 MinerU 嵌套输出目录(如 hybrid_auto/)中成功解析出的 Markdown,同时修复了 /mimic WebSocket 端点的 NameError 崩溃,并将 Python 最低版本要求提升至 3.11。读完本文,你将掌握这四项变更在 DeepTutor 源码中的具体落点:设置缓存失效的调用链、MinerU 目录发现算法的实现细节,以及对应的回归测试位置,便于在自部署环境中验证与排查同类问题。

版本定位与变更总览

v1.0.0-beta.2 的发布说明位于 assets/releases/past_releases/ver1-0-0-beta-2.md,它承接 beta.1 进入 1.0 稳定化阶段,变更可归纳为四类:

类别 变更 影响的代码位置
功能增强 Hot Settings Reload(设置热重载) deeptutor/api/routers/settings.py
功能增强 MinerU 嵌套输出目录支持 deeptutor/tools/question/question_extractor.py
缺陷修复 /mimic WebSocket NameError 崩溃 deeptutor/api/routers/question.py
基线调整 最低 Python 版本提升至 3.11 pyproject.toml

以下逐一展开,并结合当前仓库源码印证每项变更的实际实现。

Hot Settings Reload:保存即生效的设置热重载

这是本版本最重要的体验改进。在此之前的行为是:修改 API Key、切换模型或更换 Endpoint 后,运行中的 LLM 客户端、嵌入(Embedding)客户端以及配置缓存仍持有旧值,必须重启服务才能生效。v1.0.0-beta.2 之后,通过 Settings 页面或引导向导(onboarding tour)保存设置时,运行时缓存会被自动失效,下一次调用即加载最新配置。

失效机制的源码实现

核心实现是 deeptutor/api/routers/settings.py 中的 _invalidate_runtime_caches() 函数:

def _invalidate_runtime_caches() -> None:
    """Force runtime clients/config to pick up the latest saved catalog.

    The LLM and embedding clients are process-wide singletons, so resetting
    them here will affect any user turn that is mid-flight on another worker.
    Admins issuing Apply during active sessions accept that trade-off; we log
    a WARNING so the cause is visible in the audit trail.
    """
    logger.warning(
        "Admin applied catalog; resetting global LLM/embedding clients. "
        "In-flight user turns may flip backend client mid-call."
    )
    clear_llm_config_cache()
    reset_llm_client()
    reset_embedding_client()

从源码结构看,热重载由三个步骤组成:

  1. clear_llm_config_cache() —— 清空 LLM 配置缓存。DeepTutor 中 LLM 客户端与嵌入客户端是进程级单例,配置一旦解析就会驻留在内存中;不清缓存的话,即使落盘了新配置,下一次 get_llm_config() 仍会返回旧值。
  2. reset_llm_client() —— 重置 LLM 客户端实例,使下一次请求按新的 API Key / Endpoint / 模型重新构造连接。
  3. reset_embedding_client() —— 对嵌入客户端做同样处理,保证向量化服务(如知识库构建、RAG 检索)也切换到新配置。

值得注意的是函数体中的 WARNING 日志:由于客户端是进程级单例,若管理员在有用户会话进行中时执行 Apply,正在飞行的请求(mid-flight turn)可能会在调用中途切换后端客户端。DeepTutor 没有为避免这一竞态而引入复杂的事务机制,而是接受这一权衡,通过日志保证"审计可见"——这是从单例缓存失效方案中推断出的设计取舍。

该函数在路由中至少被三处触发(对应 Settings 页的保存、目录 Apply 以及 onboarding tour 完成等入口):

# deeptutor/api/routers/settings.py
...
_invalidate_runtime_caches()   # L1016
...
_invalidate_runtime_caches()   # L1025
...
_invalidate_runtime_caches()   # L1242

回归测试保障

发布说明中提到"为设置缓存失效新增了回归测试",对应实现位于 tests/api/test_settings_router.py,其中包含三个针对性用例:

  • test_update_catalog_invalidates_runtime_caches(约 L528)——验证"更新模型目录"接口会触发缓存失效;
  • test_apply_catalog_invalidates_runtime_caches(约 L573)——验证"Apply"操作路径同样失效缓存;
  • test_complete_tour_invalidates_runtime_caches(约 L641)——验证 onboarding tour 完成时也会失效运行时缓存。

这些测试通过对 llm_config_module.clear_llm_config_cache()llm_client_module.reset_llm_client() 打桩(monkeypatch)来断言失效逻辑确实被执行,与源码中"保存 → 失效 → 下次请求重建"的调用链一一对应。

MinerU 嵌套输出目录支持

问题背景

DeepTutor 的"仿真题生成"(mimic exam)等工作流依赖文档解析引擎(MinerU)先把 PDF 解析为 Markdown,再由问题提取器(question extractor)读取这些 Markdown 来出题。MinerU 的输出目录结构并不固定:不同版本/模式下,解析产物可能位于输出目录顶层,也可能嵌套在 auto/hybrid_auto/ 等子目录中。

v1.0.0-beta.2 之前的问题是:MinerU 明明成功解析了文档,但解析产物落在嵌套子目录里,提取器只按旧规则查找,找不到 Markdown,最终导致"解析成功但出题失败"的假性故障。

目录发现算法

修复后的核心逻辑是 deeptutor/tools/question/question_extractor.py 中的 _find_parsed_content_dir()

def _find_parsed_content_dir(paper_dir: Path) -> Path:
    """Locate the MinerU output directory that contains parsed markdown artifacts."""
    candidate_dirs: list[Path] = []

    for preferred_name in ("auto", "hybrid_auto"):
        preferred_dir = paper_dir / preferred_name
        if preferred_dir.is_dir():
            candidate_dirs.append(preferred_dir)

    for child in sorted(paper_dir.iterdir()):
        if child.is_dir() and child not in candidate_dirs:
            candidate_dirs.append(child)

    nested_artifact_dirs = {
        artifact.parent
        for pattern in ("*.md", "*_content_list.json")
        for artifact in paper_dir.rglob(pattern)
    }
    for artifact_dir in sorted(nested_artifact_dirs):
        if artifact_dir not in candidate_dirs:
            candidate_dirs.append(artifact_dir)

    for candidate_dir in candidate_dirs:
        if list(candidate_dir.glob("*.md")):
            return candidate_dir

    return candidate_dirs[0] if candidate_dirs else paper_dir

可以将其理解为三层候选收集 + 一次 Markdown 验证的策略:

  1. 优先目录:先检查 auto/hybrid_auto/ 两个已知嵌套模式目录,存在即纳入候选(这正是本版本修复的核心场景);
  2. 直接子目录:把 paper_dir 下其余所有子目录按排序加入候选,保持确定性;
  3. 递归产物定位:用 rglob 递归查找 *.md*_content_list.json(MinerU 的解析产物清单文件),把所有产物所在父目录收集为候选——无论嵌套多深都能命中;

最后遍历候选目录,返回第一个确实包含 *.md 文件的目录;若所有候选都没有 Markdown,则退化为返回第一个候选或原始 paper_dir,由上层加载逻辑继续判定。

同样的嵌套目录意识也出现在解析结果缓存层:deeptutor/services/parsing/cache.py 中对 auto/ / hybrid_auto/ 的查找逻辑保持一致("Engines (MinerU especially) may nest output under auto/ / hybrid_auto/"),说明这是对整个 MinerU 输出约定的系统性适配,而非单点修补。

该能力有专门测试覆盖(见 tests/tools/test_question_extractor.pytests/tools/test_mineru.py),发布说明中的"question extractor regression tests"即包含此项。

Mimic WebSocket 崩溃修复

/mimic 是仿真题生成的 WebSocket 端点,定义于 deeptutor/api/routers/question.py

@router.websocket("/mimic")
async def websocket_mimic_generate(websocket: WebSocket):
    """
    WebSocket endpoint for mimic exam paper question generation.

    Supports two modes:
    1. Upload PDF directly via WebSocket (base64 encoded)
    2. Use a pre-parsed paper directory path
    """

本版本修复了该端点因缺少 sysPath 导入而在运行时抛出 NameError 的崩溃——即模块内代码引用了 sys/Path 但头部没有对应 import,导致相关分支一执行就中断连接。当前仓库中该文件头部已包含这两处导入:

# deeptutor/api/routers/question.py
from pathlib import Path   # L5
...
import sys                 # L7

从该端点的实现还可以看到本次热重载之外的工程细节:出题产物输出目录 _mimic_output_dir() 采用每次调用时动态解析get_path_service().get_question_dir() / "mimic_papers",见 question.py L36-L40),以确保多用户场景下按认证后的调用者工作区路由,而不是使用 import 时刻固化的管理员目录。对应的路由回归测试位于 tests/api/test_question_router.py

Python 3.11+ 最低版本基线

v1.0.0-beta.2 移除了对 Python 3.10 的支持,最低要求为 Python 3.11,CI 矩阵、pyproject.toml 与全部文档同步更新。当前仓库中的声明为:

# pyproject.toml
requires-python = ">=3.11,<3.14"

即安装 DeepTutor 时 Python 解释器须满足 3.11 及以上(且上限为 3.13)。这一约束直接影响自部署:在 3.10 环境的 Docker 镜像或 venv 中,pip install -e . / pip install -r requirements/server.txt(见 requirements/server.txt)会因解释器版本不满足元数据而直接失败,属于安装前置条件而非可选建议。

配合的 CI 维护变更包括:

  • 精简 CI 测试矩阵为 Python 3.11 / 3.12 两档;
  • 移除 Dependabot 自动依赖更新 PR;
  • 为问题提取器、mimic WebSocket 路由、设置缓存失效新增回归测试(前文各节已分别给出对应测试文件)。

社区贡献与版本脉络

本版本由社区贡献者完成主要修复:

  • @2023Anita — MinerU 嵌套输出修复与 Python 3.11 依赖标记(PR #250、#251);
  • @YizukiAme — Mimic WebSocket 导入修复与设置缓存失效(PR #253、#254)。

在仓库的发布文档序列中,ver1-0-0-beta-1 之前为 0.x 系列(如 ver0-5-0),beta.2 之后依次有 beta-3beta-4 直至正式版 v1.0.1。如果你正在维护较旧的 beta.1 部署,本文列出的三项修复(热重载、MinerU 嵌套目录、/mimic 崩溃)正是升级到 beta.2 及以上的直接收益;若部署环境仍停留在 Python 3.10,需要先升级解释器再应用该版本。

小结

v1.0.0-beta.2 虽然是一个 beta 里程碑版本,但四项变更分别命中了自部署场景的三个高频痛点:改配置要重启(_invalidate_runtime_caches 的三函数失效链)、MinerU 解析成功却出题失败(_find_parsed_content_dir 的三层候选目录算法)、以及 /mimic 端点因导入缺失而崩溃(补上 sys/Path)。所有关键行为均有 tests/ 下的回归测试对应,且 Python 3.11 基线写入了 pyproject.tomlrequires-python 元数据,作为安装时机的硬性校验。

登录后查看全文
热门项目推荐
相关项目推荐