DeepTutor v0.3.0 发布解析:统一 PromptManager 架构、CI/CD 自动化与本地优先部署
DeepTutor v0.3.0 是一次以开发者体验为核心的版本:它将分散在 10 多个 agent 模块中的提示词加载逻辑收敛到统一的 PromptManager 单例,引入 GitHub Actions 自动化测试与 Docker 镜像发布流水线,并将部署路径简化为一条 docker run 命令、把 LLM 配置聚焦到 Ollama 与 LM Studio 等本地推理端。读完本文,你可以理解 PromptManager 的缓存与多语言回退机制是如何在源码中落地的,并能直接复现 v0.3.0 的容器化部署与升级流程。
版本概览
v0.3.0(发布日期 2026-01-06)围绕三条主线展开,完整记录见 v0.3.0 发布说明:
- 统一 PromptManager 架构:集中式提示词管理,替代各 agent 自行维护的
_load_prompts方法; - GitHub Actions 与 CI/CD:依赖自动更新、自动化测试、Docker 镜像自动发布;
- 预构建 Docker 镜像:从 GitHub Container Registry 拉取镜像,约 30 秒完成部署。
此外还有 Local-First LLM 配置:设置页只保留本地推理端 Ollama(默认)与 LM Studio,移除 OpenAI、Gemini、Azure 等云端 provider,对齐项目"隐私优先、自托管 AI 辅导"的定位。
统一 PromptManager 架构
从分散加载到单例收敛
v0.3.0 最重要的重构是:把所有 agent 模块的提示词加载收敛到一个集中式的 PromptManager 单例。发布说明列出了四项核心改进:
- 全局缓存(global caching):提示词加载一次后驻留内存,并支持模块级失效(module-level invalidation),可在不动全局缓存的情况下只刷新某个模块;
- 语言回退链(language fallback chain):
zh → en、en → zh,保证任何语言配置下都能取到可用提示词; - ISO 639-1 合规:所有
cn/提示词目录统一重命名为zh/,消除cn这一非标准语言代码; - agent 代码瘦身:删除各模块中重复的
_load_prompts方法与类级缓存。
发布说明中该实现位于 deeptutor/core/prompt_manager.py,并从 deeptutor/core/__init__.py 导出 get_prompt_manager()。从当前仓库的源码结构看,这一模块后来被重组到了 deeptutor/services/prompt/ 包中,核心实现见 PromptManager,对外导出见 prompt 包入口:
from deeptutor.services.prompt import get_prompt_manager, PromptManager
# 获取全局单例
pm = get_prompt_manager()
# 为某个 agent 加载提示词
prompts = pm.load_prompts("solve", "solve_agent", language="en")
# 从已加载的配置中安全取值
system_prompt = pm.get_prompt(prompts, "system", "base")
源码中的关键机制
对照 manager.py,发布说明中的每一项特性都能在源码中找到对应实现:
单例模式:PromptManager.__new__ 保证全局只有一个实例(manager.py#L49-L52),模块级的 get_prompt_manager() 函数则维护了一个惰性初始化的全局引用(manager.py#L238-L247)。
全局缓存与键设计:类属性 _cache 以 {module}_{agent}_{lang}[_{subdir}] 为键缓存整个提示词字典(_build_cache_key,manager.py#L83-L92)。因此同模块同 agent 不同语言、或不同子目录(如 solve 模块的 solve_loop)的提示词互不干扰。缓存失效则分两级:clear_cache() 清空全部,clear_cache("research") 只删除以 research_ 开头的键(manager.py#L207-L219)——这正是发布说明里"module-level invalidation"的落地方式。若外部直接改写了 YAML 文件,还有 reload_prompts() 强制绕过缓存重新加载(manager.py#L221-L235)。
语言回退链:LANGUAGE_FALLBACKS 定义了 zh: ["zh", "cn", "en"] 与 en": ["en", "zh", "cn"](manager.py#L23-L26)。查找时按"主语言 → 区域变体归并 → 兜底英文"的顺序逐目录尝试,任何一个目录命中即停止。值得注意的是回退链中保留了 cn 这一历史代码——这与"所有 cn/ 目录已重命名为 zh/"的迁移历史相吻合:新目录用标准代码,查找链兼容旧命名,避免破坏存量提示词文件。
容错取值:get_prompt(prompts, section, field, fallback) 对"section 不存在 / 层级不是字符串 / 嵌套字段缺失"三种情况统一返回 fallback 而非抛异常(manager.py#L174-L205),使得 agent 侧的提示词读取完全无样板代码。
十个 agent 模块的迁移
发布说明列出了本版本迁移到 PromptManager 的 10 个文件,覆盖了当时仓库中主要的 agent 工作流:
| 模块 | 迁移文件(v0.3.0 时的路径) |
|---|---|
| research | research/agents/base_agent.py |
| solve | solve/base_agent.py |
| guide | guide/agents/base_guide_agent.py |
| question | question/agents/generation_agent.py、question/agents/validation_agent.py、question/validation_workflow.py |
| ideagen | ideagen/idea_generation_workflow.py、ideagen/material_organizer_agent.py |
| co_writer | co_writer/edit_agent.py、co_writer/narrator_agent.py |
从当前仓库结构看,co_writer/edit_agent.py 等文件已重组至 deeptutor/co_writer/ 与 deeptutor/agents/ 之下(如 deeptutor/agents/question 目录),提示词资产则集中在各模块的 prompts/{zh,en}/ 目录中——例如 question 模块的 pipeline.yaml。
遗留代码清理
与新增同步进行的是删除:
- 删除
deeptutor/agents/solve/utils/prompt_loader.py(独立提示词加载器); - 移除
BaseAgent上的use_prompt_loader参数; - 清理所有模块中的
_PROMPT_CACHE类级缓存; - 移除
solve/__init__.py中的PromptLoader导出。
这意味着提示词加载从此只有一个入口,"双轨加载"(旧 PromptLoader 与新 PromptManager 并存)被彻底关闭,后续维护只需关注一处。
数据驱动的前端配置端点
发布说明还提到新增了 /api/v1/config/agents 端点,用于"数据驱动的前端配置"(data-driven frontend configuration)。当前仓库中对应的路由是 agent_config.py,提供 GET /agents 与 GET /agents/{agent_type} 两个端点——前端无需硬编码 agent 列表与元数据,而是从后端动态获取,这与此前 agent 模块统一注册到运行时注册表的重构方向一致。
GitHub Actions 与 CI/CD 自动化
v0.3.0 引入了三套自动化工作流:
| 工作流 | 说明 |
|---|---|
dependabot.yml |
依赖自动更新(pip、npm、GitHub Actions、Docker 四类生态) |
tests.yml |
push / PR 触发的自动化测试 |
docker-publish.yml |
自动将 Docker 镜像发布到 GHCR |
当前仓库的 .github/workflows 目录下保留了 tests.yml 与镜像发布流水线(现名 docker-release.yml),可以据此还原这套 CI 的完整构成:
tests.yml 在 push 到 main/dev 分支或对应 PR 且路径命中 deeptutor/**、tests/**、requirements/**、web/** 等范围时触发,串行编排了四个检查:
- Lint and Format:Python 3.11 环境下安装
ruff==0.16.0,依次执行ruff check .与ruff format --check .; - Web Node Tests:Node.js 22 +
npm ci --legacy-peer-deps(依赖锁定文件为 web/package-lock.json),执行npm run test:node; - Import Check:在 Python 3.11/3.12/3.13 矩阵(外加 3.14 实验性、失败不阻塞)上安装 requirements/server.txt 后,逐个 import 关键模块做冒烟校验,其中明确包含
from deeptutor.services.prompt.manager import PromptManager这一项——PromptManager 作为核心基础设施被纳入导入健康检查; - Python Tests:与 import-check 相同的版本矩阵,追加安装 requirements/partners.txt 与
pytest pytest-asyncio,并在data/user/settings/main.yaml中写入最小运行配置(system.language: en、logging.level: WARNING)后执行pytest -q tests deeptutor/learning/tests。
四个 job 的结果最终汇入 test-summary,以 Markdown 表格写入 PR 摘要(GITHUB_STEP_SUMMARY),任一失败即整体失败。这套流水线正是 PromptManager 重构能在 10 个 agent 模块上安全落地的质量保障基础。
预构建 Docker 镜像:约 30 秒完成部署
v0.3.0 提供了 GitHub Container Registry 上的预构建镜像,发布说明给出的部署命令是:
docker run -d --name deeptutor \
-p 8001:8001 -p 3782:3782 \
--env-file .env \
-v $(pwd)/data:/app/data \
ghcr.io/hkuds/deeptutor:latest
参数含义:
-p 8001:8001:后端 API 端口;-p 3782:3782:前端 Web 服务端口;--env-file .env:从本地.env注入环境变量(如密钥类配置);-v $(pwd)/data:/app/data:把宿主机当前目录下的data/挂载到容器内/app/data,知识库、记忆与用户数据(如data/user/settings/main.yaml)持久化在容器外,升级镜像不丢数据。
发布说明称使用该预构建镜像可在约 30 秒内完成部署,省去本地构建 Dockerfile 的耗时。仓库根目录同时提供 docker-compose.yml、compose.yaml 等编排文件,适合需要多服务协同的场景。
Local-First LLM 配置
v0.3.0 简化了设置页,使其只面向本地 LLM provider:
- ✅ Ollama(默认)
- ✅ LM Studio
- ❌ 移除云端 provider(OpenAI、Gemini 等)
发布说明将这一取舍定位为对齐 DeepTutor "privacy-first、self-hosted AI tutoring" 的产品愿景:提示词、知识库与对话全部在用户自己的硬件上完成推理,数据不出本机。
值得说明的是同一份发布说明的 "What's Changed" 部分收录了 PR #34(Dynamic LLM Provider Support, Local & Cloud),表明动态 provider 能力在本版本开发周期内也在演进——即"设置页聚焦本地端"与"底层支持动态 provider 注册"是不同层面的两件事,前者是产品策略,后者是基础设施能力。
测试佐证:PromptManager 的行为契约
发布说明中的每一项 PromptManager 特性,都有对应单元测试固化,见 test_prompt_manager.py:
- 单例契约:
PromptManager()两次实例化为同一对象,get_prompt_manager()返回的也是该对象(test_singleton_pattern); - 缓存契约:同一
(module, agent, language)两次load_prompts返回同一字典对象(prompts1 is prompts2); - 模块级失效:
clear_cache("research")之后solve的缓存保留、research的缓存被清(test_clear_cache_module_specific); - 多语言加载:对
research/pipeline与solve/solve_agent(含solve_loop子目录)分别验证en语言加载; - 容错取值:
get_prompt对嵌套字典与缺失字段的行为(test_get_prompt_helper)。
语言解析相关的测试见 test_parse_language.py,与 parse_language(PromptManager 内部的语言代码归一化入口)配套。
升级方式
发布说明给出的升级路径:
git pull origin main
docker pull ghcr.io/hkuds/deeptutor:latest
源码部署执行第一条命令后重启服务;容器部署执行第二条命令后用前文的 docker run 参数重新拉起容器即可,由于数据目录挂载在容器外,升级对用户数据无破坏性。
小结
v0.3.0 表面是一次发布说明,实质是 DeepTutor 从"功能开发期"走向"工程化运营期"的转折点:
- 架构上,
PromptManager单例 + 全局缓存 + 语言回退链消除了 10 个 agent 模块的重复加载逻辑,且用模块级缓存失效保留了灵活性; - 流程上,ruff 格式化、多 Python 版本矩阵的导入检查与 pytest 流水线、GHCR 镜像自动发布,让每次合并都有质量门禁;
- 部署上,一条
docker run加数据目录挂载即可完成本地化部署,配合 Ollama/LM Studio 的本地推理配置,形成完整的自托管闭环。
对后续版本的读者,理解 v0.3.0 的这一层架构,是理解当前仓库中 deeptutor/services/prompt 包、.github/workflows 流水线以及 Dockerfile 部署形态的起点。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00