feat: 任务进度实时展示、接口测试、暗色主题重构及多项 bug 修复

后端
- 新增 app/task_progress.py 线程安全进度注册表
- 任务改为后台线程异步执行(_run_task_background),手动触发立即返回 task_key
- 6 个任务函数(summarizer/tagger/scorer/deduplicator/brief/taxonomy)循环内上报进度
- scheduler 定时任务同步上报进度(trigger=scheduled)
- 新增 GET /api/tasks/progress 与 POST /api/tasks/progress/reset 接口
- 新增 POST /api/test-connection 接口连通性测试(独立短超时客户端)
- 修复 ai_client/rss_client 配置在 import 时固化的 bug(改为 property 运行时读取 settings),
  导致实际任务用 .env 假 key 调 LLM 401
- 修复 ai_client 对 reasoning 模型(MiniMax-M3 等)输出 <think> 块的 JSON 解析失败
- 修复 taxonomy bootstrap:LLM 超时(改用 300s 专用 client)、MiniMax 输出审查
  (精简样本仅标题 + 约束生成中性类目名)、失败误报 success(改抛异常如实标记)
- 修复 models.py 双外键关系映射启动崩溃(显式 foreign_keys)
- 修复 main.py SPA 路由 404、ArticleOut.published_at 序列化 500
- 移除 lifespan 同步 bootstrap 阻塞启动,改由 scheduler 后台异步执行

前端
- Deep Ink 高对比度暗色主题重构,修复 Element Plus 暗色模式对比度问题
- Tasks 页面任务进度实时展示(进度条/阶段/计数/状态/触发来源)+ 1.5s 轮询
- 接口测试面板(rssKeeper / LLM 连通性 + 延迟)
- 修复 nextJobs jobId 映射 bug

部署与文档
- Dockerfile 优化(BuildKit 缓存挂载、预编译 wheel、去 gcc、阿里云镜像源)
- 新增 API.md 接口文档

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
congsh
2026-06-14 15:14:40 +08:00
parent bae47a2411
commit 778ccefb22
24 changed files with 1853 additions and 312 deletions
+21 -18
View File
@@ -5,8 +5,9 @@ from typing import List, Dict, Any
from sqlalchemy.orm import Session
from app.ai_client import ai_client
from app.ai_client import AIClient
from app.rss_client import rss_client
from app.task_progress import update_progress
from models import Taxonomy
logger = logging.getLogger(__name__)
@@ -40,19 +41,19 @@ TAXONOMY_SYSTEM_PROMPT = """你是一位专业的信息分类与内容分析专
3. heat_rules 和 importance_rules 各 10-20 条,weight 范围 0.5-2.0。
4. 所有 keywords 用中文或中英双语,便于后续关键词匹配。
5. 不要输出任何解释文字,只输出 JSON。
6. **分类与标签名称必须使用中性的主题领域词**(如科技、财经、文化、体育、生活、健康、设计、商业等),
禁止使用具体事件、人名、地名、国家名、机构名或任何政治/军事/冲突相关的敏感词作为名称或关键词,
以保证内容中立、避免触发内容审查。
"""
def _build_sample_prompt(articles: List[Dict[str, Any]]) -> str:
lines = [f"共有 {len(articles)} 篇文章样本:"]
for idx, art in enumerate(articles[:50], 1):
# 只用标题和来源,不带正文摘要——降低输入中的敏感内容,避免触发内容审查
lines = [f"共有 {len(articles)} 篇文章样本(仅展示标题用于归纳主题):"]
for idx, art in enumerate(articles[:40], 1):
title = art.get("title", "")
summary = art.get("summary", "") or art.get("content", "")[:300]
feed = art.get("feed_title", "")
cat = art.get("category", "")
lines.append(f"\n[{idx}] 标题:{title}")
lines.append(f" 来源:{feed} | 源分类:{cat}")
lines.append(f" 摘要:{summary[:400]}")
lines.append(f"[{idx}] {title} (来源:{feed}")
return "\n".join(lines)
@@ -72,22 +73,24 @@ def bootstrap_taxonomy(db: Session, force: bool = False) -> bool:
logger.info("强制重新初始化 taxonomy")
logger.info("开始从 rssKeeper 拉取样本文章并生成分类体系...")
update_progress("bootstrap_taxonomy", status="running", stage="拉取样本文章", current=0, total=0)
articles = rss_client.fetch_recent(hours=24 * 7, limit=200)
if not articles:
logger.warning("未获取到样本文章,无法生成分类体系")
return False
raise RuntimeError("未获取到样本文章,无法生成分类体系")
user_prompt = _build_sample_prompt(articles)
try:
result = ai_client.chat_completion_json(
system_prompt=TAXONOMY_SYSTEM_PROMPT,
user_prompt=user_prompt,
temperature=0.5,
)
except Exception as exc:
logger.error("生成分类体系失败: %s", exc)
return False
update_progress("bootstrap_taxonomy", status="running", stage="LLM 生成分类体系", current=0, total=0, message="正在调用 LLM 生成分类规则,可能需要 2-4 分钟")
# bootstrap 是一次性大任务(生成 categories+tags+rules),MiniMax-M3 reasoning 模式较慢,
# 用专用大 timeout client(默认 60s 不够),失败抛异常由调用方捕获并如实标记进度
bootstrap_ai = AIClient(timeout=300, max_retries=2)
result = bootstrap_ai.chat_completion_json(
system_prompt=TAXONOMY_SYSTEM_PROMPT,
user_prompt=user_prompt,
temperature=0.5,
)
update_progress("bootstrap_taxonomy", status="running", stage="保存规则", current=0, total=0)
_save_taxonomy(db, result)
logger.info("taxonomy 初始化完成,共写入 %d 条规则", db.query(Taxonomy).count())
return True