zhoujie 4d83c0f4a9
Some checks failed
CI / Lint (ruff) (push) Has been cancelled
CI / Import Check (push) Has been cancelled
feat(scheduler): 新增热点自动采集功能并优化发布路径
- 新增热点自动采集后台线程,支持定时搜索关键词并执行 AI 分析,结果缓存至结构化状态
- 新增热点分析状态管理接口,提供线程安全的 `get_last_analysis` 和 `set_last_analysis` 方法
- 新增热点数据桥接函数 `feed_hotspot_to_engine`,将分析结果注入 TopicEngine 实现热点加权推荐
- 新增热点选题下拉组件,分析完成后自动填充推荐选题,选中后自动写入选题输入框
- 优化 `generate_from_hotspot` 函数,自动获取结构化分析摘要并增强生成上下文
- 新增热点自动采集配置节点,支持通过 `config.json` 管理关键词和采集间隔

♻️ refactor(queue): 实现智能排期引擎并统一发布路径

- 新增智能排期引擎,基于 `AnalyticsService` 的 `time_weights` 自动计算最优发布时段
- 新增 `PublishQueue.suggest_schedule_time` 和 `auto_schedule_item` 方法,支持时段冲突检测和内容分布控制
- 修改 `generate_to_queue` 函数,新增 `auto_schedule` 和 `auto_approve` 参数,支持自动排期和自动审核
- 重构 `_scheduler_loop` 的自动发布分支,改为调用 `generate_to_queue` 通过队列发布,统一发布路径
- 重构 `auto_publish_once` 函数,移除直接发布逻辑,改为生成内容入队并返回队列信息
- 新增队列时段使用情况查询方法 `get_slot_usage`,支持 UI 热力图展示

📝 docs(openspec): 新增内容排期优化和热点探测优化规范文档

- 新增 `smart-schedule-engine` 规范,定义智能排期引擎的功能需求和场景
- 新增 `unified-publish-path` 规范,定义统一发布路径的改造方案
- 新增 `hotspot-analysis-state` 规范,定义热点分析状态存储的线程安全接口
- 新增 `hotspot-auto-collector` 规范,定义定时热点自动采集的任务流程
- 新增 `hotspot-engine-bridge` 规范,定义热点数据注入 TopicEngine 的桥接机制
- 新增 `hotspot-topic-selector` 规范,定义热点选题下拉组件的交互行为
- 更新 `services-queue`、`services-scheduler` 和 `services-hotspot` 规范,反映功能修改和新增参数

🔧 chore(config): 新增热点自动采集默认配置

- 在 `DEFAULT_CONFIG` 中新增 `hotspot_auto_collect` 配置节点,包含 `enabled`、`keywords` 和 `interval_hours` 字段
- 提供默认关键词列表 `["穿搭", "美妆", "好物"]` 和默认采集间隔 4 小时

🐛 fix(llm): 增强 JSON 解析容错能力

- 新增 `_try_fix_truncated_json` 方法,尝试修复被 token 限制截断的 JSON 输出
- 支持多种截断场景的自动补全,包括字符串值、数组和嵌套对象的截断修复
- 提高 LLM 分析热点等返回 JSON 的函数的稳定性

💄 style(ui): 优化队列管理和热点探测界面

- 在队列生成区域新增自动排期复选框,勾选后隐藏手动排期输入框
- 在日历视图旁新增推荐时段 Markdown 面板,展示各时段权重和建议热力图
- 在热点探测 Tab 新增推荐选题下拉组件,分析完成后动态填充选项
- 在热点探测 Tab 新增热点自动采集控制区域,支持启动、停止和配置采集参数
2026-02-28 22:22:27 +08:00

84 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Context
当前系统有两条独立的发布路径:
1. `scheduler.py``auto_publish_once()` → 直接生成 + 直接发布到小红书(绕过队列)
2. `queue_ops.py``generate_to_queue()``PublishQueue` SQLite 持久化 → `QueuePublisher._loop()` → 按排期/审核状态发布
`AnalyticsService` 已在 `content_weights.json` 中保存 `time_weights`3小时段权重`"18-21时": {"weight": 85, "count": 12}`),但这些数据没有被排期逻辑使用。用户排期必须手动输入时间字符串。
## Goals / Non-Goals
**Goals:**
- G1: 基于 `time_weights` 自动计算最优发布时段,为入队内容分配 `scheduled_time`
- G2: 消除调度器绕过队列的直接发布路径,统一为队列驱动
- G3: 支持内容间距控制(同一时段不超过 N 篇),分散到多天
- G4: UI 增加自动排期开关和排期建议展示
**Non-Goals:**
- 不改动 `PublishQueue` 的 SQLite 表结构(不新增列)
- 不增加 A/B 测试或基于实时反馈的动态调整
- 不修改 `QueuePublisher` 的发布执行逻辑(仅改其上游输入)
- 不改变评论/点赞/收藏等非发布类自动化任务
## Decisions
### D1: 智能排期引擎放在 `publish_queue.py` 中作为 `PublishQueue` 的方法
**决定**: 在 `PublishQueue` 类上新增 `suggest_schedule_time()``auto_schedule_item()` 方法。
**理由**: 排期引擎需要查询现有队列排期(避免时段冲突),`PublishQueue` 已持有 SQLite 连接和查询方法,放在此处可避免跨模块传递 db 连接。
**备选方案**: 独立模块 `schedule_engine.py` — 但会增加新文件,且仍需注入 `PublishQueue` 实例来查询已排期项。
### D2: `time_weights` 通过 `AnalyticsService.get_time_weights()` 新方法获取
**决定**: 在 `AnalyticsService` 上新增 `get_time_weights() -> dict` 方法,返回 `time_weights` 字典。无数据时返回默认值。
**理由**: 封装内部 `_weights` 结构,提供干净的 API 供排期引擎调用。
**默认时段**: 无分析数据时 fallback 到: `{"08-11时": 70, "12-14时": 60, "18-21时": 85, "21-24时": 75}`(小红书高流量经验值)。
### D3: 统一发布路径 — 调度器 publish 分支改为 generate_to_queue + auto_approve
**决定**: `_scheduler_loop` 的自动发布分支改为调用 `generate_to_queue(auto_schedule=True, auto_approve=True)`,不再调用 `auto_publish_once` 内的发布逻辑。`auto_publish_once` 保留但重构为仅生成内容入队。
**理由**:
- 统一发布路径,所有内容都走队列审核流程
- `QueuePublisher` 已有重试、错误处理、日志记录能力
- 调度器生成的内容也会出现在队列表格和日历中,可追溯
**行为变化**: 调度器发布从「立即生成并发布」变为「生成入队 → QueuePublisher 下一轮 check最多 60s发布」。延迟可接受。
### D4: 时段冲突检测基于 SQLite 查询
**决定**: `suggest_schedule_time()` 查询 `queue` 表中 `scheduled_time` 列,统计每个时段已排队的数量,优先选择高权重且低负载的时段。
**规则**:
- 每个3小时段最多 `max_per_slot`(默认 2
- 同一天内最多 `max_per_day`(默认 5
- 优先当天还有空余的高权重时段,当天满则顺延到次日
- 最远排到7天后
### D5: `generate_to_queue` 新增 `auto_schedule` 和 `auto_approve` 参数
**决定**: `generate_to_queue()` 签名增加 `auto_schedule: bool = False``auto_approve: bool = False`
- `auto_schedule=True` → 调用 `PublishQueue.suggest_schedule_time()` 为每篇内容分配时间
- `auto_approve=True` → 入队后自动调用 `approve()`,状态从 draft 直接变为 scheduled/approved
**理由**: 最小改动对现有手动流程无影响调度器场景两者都开启UI 场景用户可选。
### D6: UI 增加自动排期复选框和排期建议面板
**决定**:
- 在「批量生成到队列」区域增加 `auto_schedule` 复选框,勾选后隐藏手动排期输入框
- 在日历视图旁增加「📊 推荐时段」Markdown 面板,展示 `time_weights` top 时段
**理由**: 最小 UI 变更,不重构现有布局。
## Risks / Trade-offs
- **[排期延迟]** 调度器发布不再即时,会有最多 60s 延迟QueuePublisher check 间隔)→ 对社交媒体发布场景可接受,且可通过缩短 `check_interval` 缓解
- **[无数据 fallback]** 首次使用时 `time_weights` 为空,排期基于经验默认值 → 运行一段时间后数据学习会逐步优化
- **[时段冲突查询性能]** 每次入队都需查询队列排期 → 队列规模通常 < 100 SQLite WAL 模式下查询性能无忧
- **[auto_approve 安全性]** 调度器自动审核跳过人工审核 仅在调度器自动模式下启用用户手动入队仍需审核