Files

38 KiB
Raw Permalink Blame History

DevelopmentPlan.md:小红书 / 抖音热榜评论抓取 + AI 分析报告工具

1. 文档信息

  • 文档阶段:DevelopmentPlan(技术方案与开发计划)
  • 需求来源:docs/RequirementsDoc.mddocs/PRD.mddocs/FeatureSummary.md
  • API Spike 依据:docs/API-Spike-Xiaohongshu.mddocs/API-Spike-Douyin.md
  • 项目类型:学习型小工具 / 全栈流程演示项目
  • MVP 周期:4 天单人开发
  • 当前版本目标:锁定 MVP 技术选型、系统架构、数据模型、接口契约、任务流程、AI 方案、开发排期与验收方式

2. 核心技术决策

2.1 总体技术栈

MVP 采用轻量单体架构,优先保证 4 天内跑通完整链路。

层级 技术选型 决策理由
后端框架 Python 3.12 + FastAPI 上手快,适合 API、模板页面、后台任务与导出接口统一实现
数据库 SQLite 单人 MVP、Docker Compose 本机部署足够;无须额外数据库服务
ORM / 数据访问 SQLAlchemy 2.x 明确数据模型,便于后续迁移 PostgreSQL
数据校验 Pydantic 与 FastAPI 生态一致,适合请求、配置、AI 输出 schema 校验
HTTP 客户端 httpx MVP 统一使用同步 httpx.Client,支持超时、重试封装和后续异步演进
前端 FastAPI Jinja2 模板 + 原生 JavaScript 页面数量有限,避免引入 React/Vite 构建复杂度
样式 简单 CSS MVP 以可用和清晰为主,不做复杂视觉系统
后台任务 进程内 ThreadPoolExecutor 满足手动任务与低并发演示;不引入 Redis / Celery
AI 服务 OpenAI-compatible 结构化输出接口 通过 JSON Schema 降低解析不稳定性,并保留替换模型供应商空间
部署 Docker Compose 符合 PRD 要求,一键启动 Web 服务和持久化 SQLite 数据

并发与数据库约束

  • 任务执行线程池固定为 ThreadPoolExecutor(max_workers=1),确保同一时间只有一个任务在执行,从根本上避免 SQLite 写入冲突。
  • SQLAlchemy 引擎初始化时须启用以下配置:
from sqlalchemy import create_engine, event

engine = create_engine(
    "sqlite:///data/app.db",
    connect_args={"check_same_thread": False, "timeout": 10},
)

@event.listens_for(engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("PRAGMA journal_mode=WAL")
    cursor.close()
  • 若未来需要支持多任务并行执行,应将其作为迁移 PostgreSQL 的触发条件。

httpx 使用模式(MVP

  • 后台任务线程(ThreadPoolExecutor)中的所有外部 API 调用(TikHub、AI 服务)使用 httpx.Client(同步模式)。
  • FastAPI 路由层统一使用 def(同步端点),由 FastAPI 自动将其分配到线程池执行,避免阻塞 ASGI 事件循环。
  • MVP 阶段不使用 async def 路由 + httpx.AsyncClient,降低并发模型复杂度。
  • 后续如需异步演进,将路由改为 async def 并替换为 httpx.AsyncClient 即可,httpx API 兼容。

可选:HTMX 辅助局部刷新

  • 可通过 CDN 引入 HTMX<script src="https://unpkg.com/htmx.org@1.9.12"></script>),用声明式属性替代手写 fetch + DOM 操作。
  • 示例:任务列表轮询只需 <div hx-get="/api/tasks" hx-trigger="every 5s" hx-swap="innerHTML">
  • 此项为可选优化,不引入不影响功能完整性,但可在 Day 4 节省约 1-2 小时前端开发时间。

数据库初始化

  • MVP 使用 SQLAlchemy Base.metadata.create_all(engine) 在应用首次启动时自动创建表结构。
  • 不引入 Alembic 迁移工具。
  • 开发阶段如需变更数据模型,直接删除 SQLite 文件后重启应用即可重建。

2.2 不采用的技术

  • 不使用 Celery / Redis:MVP 不做复杂任务队列、分布式调度、任务恢复。
  • 不使用 PostgreSQL:当前数据量小,SQLite 更省部署成本。
  • 不使用 WebSocket:任务状态通过手动刷新按钮兜底,P1 可增加简单轮询。
  • 不使用前后端分离 SPA:页面复杂度不高,模板页面更利于 4 天交付。
  • 不做登录鉴权:MVP 默认内部环境使用,不面向公网。

2.3 AI 服务选型

AI 层采用 OpenAI-compatible 接口,首选支持 Structured Outputs / JSON Schema 的模型服务。

技术要求:

  • 支持通过环境变量配置:
    • AI_BASE_URL
    • AI_API_KEY
    • AI_MODEL
    • AI_PROVIDER
  • 评论分析请求必须要求模型输出严格 JSON Array。
  • 后端必须使用 Pydantic / JSON Schema 做二次校验。
  • 单批评论建议 20 条。
  • AI 请求并发数上限为 2,硬上限不超过 3。
  • 单次 AI 请求超时时间为 30s。
  • AI 输出解析失败时最多重试 3 次。

参考依据:

  • OpenAI Structured Outputs 官方文档说明,结构化输出可通过 JSON Schema 约束模型响应,并比普通 JSON mode 更强调 schema adherence。
  • 文档链接:https://developers.openai.com/api/docs/guides/structured-outputs

3. MVP 范围

3.1 P0 必须实现

  1. Docker Compose 启动系统,并能通过浏览器访问。
  2. 首页 / 任务列表支持选择平台并手动创建任务。
  3. 任务创建页面支持抓取规模配置:
    • 热点关键词数量上限:默认 5,范围 1–10;
    • 每热点内容条目数上限:默认 5,范围 1–10;
    • 每内容条目评论数上限:默认 50,范围 10–100。
  4. 小红书链路:
    • 热榜;
    • 热榜标题搜索笔记;
    • 笔记一级评论。
  5. 抖音链路:
    • 创作者热点榜单;
    • 热点标题搜索视频;
    • 视频一级评论。
  6. 评论分页抓取,默认最多 50 条,配置最多 100 条,最大翻页轮次 5。
  7. 数据入库并保留原始 API 响应。
  8. AI 评论级结构化分析。
  9. 预生成内容条目级报告和热点级报告。
  10. 页面查看任务、热点、内容条目、报告和评论明细。
  11. 导出:
    • CSV 评论明细;
    • Markdown 热点级报告;
    • Markdown 内容条目级报告。
  12. 基础容错:
    • 单条内容条目失败不阻断整批任务;
    • HTTP 429 指数退避;
    • AI 输出解析失败重试。

3.2 P1 建议实现

  • 任务列表自动轮询。
  • 基础进度展示:
    • processed_items_count / total_items_count
    • successful_items_count / total_items_count
  • 内容条目详情页开发调试 JSON 入口。

3.3 P2 明确不做

  • 定时任务。
  • 多用户 / 登录 / 权限。
  • 分布式任务队列。
  • 复杂任务恢复、自动补跑、单条重试按钮。
  • 二级评论抓取。
  • Top 50 以上热点或单内容 200 条以上评论。
  • 平台级日报、跨热点深度洞察。
  • Excel 导出、正式 JSON 导出。
  • 任务取消 / 中断:用户主动终止正在运行的任务。
  • 历史数据自动清理:基于时间策略自动删除过期任务数据。
  • 搜索笔记分页:搜索接口翻页获取更多候选内容条目。
  • 热点级 CSV 导出的高级格式定制。

4. 系统架构

4.1 架构形态

Browser
  ↓
FastAPI Web App
  ├─ HTML Templates / Static Assets
  ├─ REST Endpoints
  ├─ Task Service
  ├─ Platform Crawlers
  │   ├─ Xiaohongshu Client
  │   └─ Douyin Client
  ├─ AI Analysis Service
  ├─ Report Service
  ├─ Export Service
  └─ SQLite Database

4.2 推荐目录结构

app/
  main.py
  config.py
  db.py
  models.py
  schemas.py
  services/
    task_service.py
    crawl_service.py
    ai_service.py
    report_service.py
    export_service.py
  platforms/
    base.py
    xiaohongshu.py
    douyin.py
  templates/
    tasks.html
    task_detail.html
    hotspot_report.html
    item_detail.html
  static/
    app.css
    app.js
tests/
  test_config.py
  test_models.py
  test_platform_mapping.py
  test_ai_schema.py
  test_report_stats.py
  test_export.py
Dockerfile
docker-compose.yml
.env.example

4.3 模块边界

  • platforms/*:只负责调用外部平台 API、字段映射、分页、保留 raw_data。
  • task_service.py:负责任务生命周期、进度字段、错误聚合和后台执行。
  • ai_service.py:负责 prompt、批处理、JSON Schema 校验、重试、AI 成功率统计。
  • report_service.py:负责结构化统计、典型评论选取、AI 简短总结、报告预生成。
  • export_service.py:负责 Markdown / CSV 文件内容与响应头。
  • models.py:统一定义任务、热点、内容条目、评论、报告表。
  • templates/*:只做展示,不承载业务计算。

僵尸任务恢复

  • 在 FastAPI lifespan 启动事件中,执行以下逻辑:
# main.py lifespan 启动阶段
UPDATE tasks SET status = 'failed',
    error_stage = 'system',
    error_type = 'unexpected_restart',
    error_message = '系统重启,任务被中断'
WHERE status = 'running'
  • 目的:Docker 容器重启或进程 Crash 后,避免任务永久停留在 running 状态(僵尸任务)。
  • 该逻辑在应用启动时自动执行一次,无需用户干预。

5. 数据模型设计

5.1 tasks

字段 类型 说明
id string UUID
platform string xiaohongshu / douyin
status string running / success / failed
analysis_status string normal / insufficient
analysis_success_rate float AI 结构化成功率
hot_limit integer 本任务热点数量配置
item_limit_per_hot integer 每热点内容条目配置
comment_limit_per_item integer 每内容条目评论配置
total_items_count integer 总内容条目数
processed_items_count integer 已处理内容条目数
successful_items_count integer 成功内容条目数
failed_items_count integer 失败内容条目数
error_stage string nullable 失败阶段
error_type string nullable 错误类型
error_message text nullable 简要错误原因
created_at datetime 创建时间
started_at datetime nullable 开始时间
finished_at datetime nullable 完成时间

状态规则:

  • 没有任何内容条目成功抓取并完成分析:status = failed
  • 至少 1 条内容条目成功抓取并完成分析:status = success
  • AI 成功率低于 80%status 不变,analysis_status = insufficient

5.2 hotspots

字段 类型 说明
id string UUID
task_id string 所属任务
platform string 平台
source_hot_id string nullable 平台原始热点 ID
rank integer nullable 排名
title text 热点标题
heat_value string nullable 热度值或榜单指标
raw_data text 原始 JSON
created_at datetime 创建时间

5.3 content_items

字段 类型 说明
id string UUID
task_id string 所属任务
hotspot_id string 所属热点
platform string 平台
source_item_id string 平台原始内容 ID
item_type string note / video
title text nullable 标题
summary text nullable 摘要
url text nullable 内容 URL
status string pending / success / failed
error_stage string nullable 失败阶段
error_type string nullable 错误类型
error_message text nullable 错误说明
raw_data text 原始 JSON
created_at datetime 创建时间

唯一性建议:

  • MVP 保留跨热点重复内容。
  • 使用 task_id + hotspot_id + source_item_id 作为业务去重依据。

5.4 comments

字段 类型 说明
id string UUID
task_id string 所属任务
hotspot_id string 所属热点
content_item_id string 所属内容条目
platform string 平台
source_comment_id string 平台原始评论 ID
content text 评论内容
author_name text nullable 作者昵称
author_id text nullable 作者 ID
like_count integer nullable 点赞数
comment_time datetime nullable 评论时间
sentiment string nullable positive / negative / neutral / unknown
labels text nullable JSON Array 字符串
reason text nullable 简短理由
ai_analysis_status string pending / success / failed
ai_raw_data text nullable AI 原始响应
raw_data text 评论原始 JSON
created_at datetime 创建时间

唯一性建议:

  • 同一任务、同一内容条目、同一评论 ID 不重复:task_id + content_item_id + source_comment_id

labels 字段格式与转换规则

  • 入库格式:统一为 JSON Array 字符串,如 ["价格吐槽", "物流慢"]
  • 导出格式:由 export_service.py 负责将 JSON Array 转换为中文逗号拼接字符串,如 价格吐槽,物流慢
  • 页面展示:由 Jinja2 模板层解析 JSON Array 后逐个渲染为标签元素。
  • 三个消费场景的格式转换各自负责,入库层只保证 JSON Array 格式正确。

5.5 reports

字段 类型 说明
id string UUID
task_id string 所属任务
hotspot_id string nullable 热点级报告使用
content_item_id string nullable 内容条目级报告使用
report_type string hotspot / item
metrics_json text 情绪、标签、样本数等结构化统计
typical_comments_json text 典型评论
summary text AI 简短总结
markdown_content text 预生成 Markdown
created_at datetime 创建时间
updated_at datetime 更新时间

报告生成策略:

  • MVP 采用预生成型。
  • 任务完成后先生成内容条目级报告,再生成热点级报告。
  • 报告数据不做自动重算;如任务重新执行,生成新任务和新报告。

reports 表业务约束

  • report_type = 'hotspot' 时:hotspot_id 不为空,content_item_id 为空。
  • report_type = 'item' 时:hotspot_id 不为空,content_item_id 不为空。
  • 该约束在应用层(report_service.py 创建报告时)保证,SQLite 不设数据库级约束。

5.6 建议索引(按需添加)

以下索引在 MVP 数据量下非必需,当性能出现瓶颈时可添加:

CREATE INDEX idx_comments_task_item ON comments(task_id, content_item_id);
CREATE INDEX idx_comments_dedup ON comments(task_id, content_item_id, source_comment_id);
CREATE INDEX idx_content_items_task_hotspot ON content_items(task_id, hotspot_id);
CREATE INDEX idx_reports_task_type ON reports(task_id, report_type);

已知技术债:数据保留策略

  • MVP 不实现自动数据清理机制。
  • 所有任务及关联数据(热点、内容条目、评论、报告)永久保留在 SQLite 中。
  • 后续版本可基于 tasks.created_at 索引实现过期数据自动清理(如保留最近 30 天)。
  • 在此之前,用户可通过删除 SQLite 文件并重启应用来手动清理全部数据。

6. 外部 API 设计

6.1 通用调用策略

  • 所有外部 API 通过 httpx 调用。
  • 每次请求设置 20s 超时。
  • 对 HTTP 429 使用指数退避:1s → 2s → 4s。
  • 429 超过最大重试次数后,当前条目标记失败,任务继续。
  • 非 429 网络错误最多重试 2 次。
  • 所有成功响应和关键失败响应尽量保留 raw_data 或错误摘要。

6.2 小红书链路

fetch_hot_list
→ data.data.items[].title
→ search_notes(keyword = hot.title)
→ note.id
→ get_note_comments(note_id)
→ comments

字段映射:

层级 来源字段 入库字段
热点 id source_hot_id
热点 title title
热点 score heat_value
笔记 note.id source_item_id
笔记 note.title title
笔记 note.desc summary
笔记 note.comments_count 用于优先筛选
评论 source_comment_id ← data.get("comment_id") or data.get("id") 优先 comment_id
评论 content / text content
评论 like_count like_count
评论 create_time comment_time

筛选策略:

  • 优先选择 comments_count > 0 的笔记。
  • 不足目标数量时补充 comments_count = 0 的笔记。
  • 平台返回笔记总数不足目标数量时,以实际数量为准,不视为任务失败。

搜索笔记分页策略(MVP

  • MVP 阶段搜索笔记接口不做分页,仅使用首页返回结果。
  • 若首页结果中 comments_count > 0 的笔记不足目标数量,执行 FeatureSummary 中定义的降级策略(补充 comments_count = 0 的笔记,或以实际可用数量为准)。
  • 搜索分页作为后续优化项,不在 MVP 范围内。

6.3 抖音链路

fetch_creator_hot_spot_billboard
→ hot.title
→ fetch_video_search_v2(keyword = hot.title)
→ aweme_info.aweme_id
→ fetch_video_comments(aweme_id)
→ comments

字段映射:

层级 来源字段 入库字段
热点 query_id source_hot_id
热点 title title
热点 rank rank
热点 hot_score heat_value
视频 aweme_info.aweme_id source_item_id
视频 aweme_info.desc title / summary
视频 aweme_info.author raw_data 中保留
评论 source_comment_id ← data.get("comment_id") or data.get("cid") 优先 comment_id
评论 text content
评论 digg_count like_count
评论 create_time comment_time

6.4 评论分页策略

统一终止条件:

  1. 已抓取评论数达到 comment_limit_per_item
  2. API 返回评论列表为空。
  3. 达到最大翻页轮次 5。
  4. 连续请求失败且超过重试次数。

分页实现要求:

  • 小红书使用 cursor / index 字段推进。
  • 抖音使用 cursor 字段推进,单次 count=20
  • 若 API 未返回明确下一页游标,则停止翻页。

分页请求间隔

  • 每次评论分页请求之间须等待 1-2 秒(建议默认 1.5 秒),降低触发平台限流的概率。
  • 该间隔独立于 §6.1 的 429 指数退避策略;收到 429 响应后切换为退避策略,退避结束后恢复基础间隔。

7. AI 分析方案

7.1 评论级输出 Schema

AI 评论分析必须返回 JSON Array,每一项对应一条输入评论。

[
  {
    "comment_id": "string",
    "sentiment": "positive | negative | neutral | unknown",
    "labels": ["string"],
    "reason": "string"
  }
]

校验规则:

  • comment_id 必须能匹配输入评论。
  • sentiment 必须属于枚举值。
  • labels 必须是数组,最多 3 个标签。
  • reason 可为空字符串。
  • 任意一条结果不合法时,该条评论标记为 ai_analysis_status = failed
  • 批无法解析时,整批重试,最多 3 次。

7.2 Prompt 约束

Prompt 输入格式

传递给 LLM 的评论数据须采用以下 JSON Array 格式:

[
  { "comment_id": "abc123", "content": "这个产品太好了,强烈推荐" },
  { "comment_id": "def456", "content": "物流太慢了,等了一周才到" }
]

要求

  • Prompt 模板中须明确指示 AI:"请原样回填输入中的 comment_id,不得修改或生成新 ID"。
  • 传递前对单条评论内容做截断处理:保留前 150 个字符,超出部分丢弃。目的是控制 Token 消耗并提高输出格式稳定性(情绪和标签通常在评论开头即可判断)。

System Prompt 要求:

  • 只返回 JSON Array。
  • 不输出 Markdown。
  • 不输出解释性自然语言。
  • 标签使用简短中文短语。
  • 每条评论输出 1–3 个标签;确实无法判断时可返回空数组并将 sentiment 标记为 unknown

7.3 批量与并发

  • 默认每批 20 条评论。
  • AI 并发数默认为 2。
  • 最大并发数不超过 3。
  • 单次请求超时 30s。
  • 任务内按内容条目逐步分析,便于进度统计和失败隔离。

AI 请求并发控制

  • 并发粒度AI 并发数 2 指同一任务内最多 2 个 AI 批量请求同时进行,粒度为跨内容条目级别。即同一时间可以并行分析 2 个不同内容条目的评论批次,但同一内容条目的多批评论串行处理。
  • 默认并发数2(通过环境变量 AI_CONCURRENCY 配置)。

降级重试策略

  • 单批 AI 请求失败后按 §7.1 规则重试,最多 AI_MAX_RETRIES 次(默认 3)。
  • 降级拆分:若连续 2 次重试均因 JSON 解析失败(ai_parse_failed),则第 3 次重试时自动将当前 batch_size 减半(如 20 → 10)后重新请求。
  • 若减半重试仍失败,将该批次所有评论的 ai_analysis_status 标记为 failed,继续处理下一批次。

7.4 AI 质量状态

任务完成后计算:

analysis_success_rate = analysis_success_comments / total_comments

判定:

  • analysis_success_rate >= 0.8analysis_status = normal
  • analysis_success_rate < 0.8analysis_status = insufficient

注意:

  • analysis_status 不改变任务 status
  • 任务生命周期状态仍只有 running / success / failed
  • 页面须展示 AI 分析成功率或“分析不足”提示。

8. 报告生成方案

8.1 内容条目级报告

输入:

  • 内容条目基础信息;
  • 该内容条目下所有已分析评论;
  • 情绪和标签结构化结果。

生成步骤:

  1. 统计评论样本数。
  2. 统计正向、负向、中性、未知数量和占比。
  3. 按标签字面值统计 Top 5。
  4. 按情绪分组选取典型评论:
    • 优先按点赞数降序;
    • 点赞数缺失时按抓取顺序。
  5. 调用 AI 生成内容条目总结。
  6. 生成 Markdown 内容并保存到 reports

报告总结 AI 调用规格

  • 调用方式:独立 AI 请求,不复用评论分析的批量调用。
  • 输入内容
    • 统计数据摘要:情绪分布(正面/中性/负面各占比)、标签 Top 5 及其出现次数。
    • 典型评论文本:每个情绪类别取 2-3 条代表性评论原文(截断前 150 字符)。
  • 输出要求:纯文本,不使用 JSON Schema,控制在 200 字以内
  • Prompt 模板:独立模板文件,存放于 Prompt 模板目录(如 prompts/report_summary.txt)。
  • 超时与失败策略:复用 §7.3 的 AI_TIMEOUT_SECONDS 配置;总结生成失败不阻断报告创建,报告中该字段显示为默认文本"总结生成失败,请查看详细数据"。

8.2 热点级报告

输入:

  • 热点基础信息;
  • 热点下所有内容条目;
  • 热点下所有已分析评论;
  • 内容条目级统计结果。

生成步骤:

  1. 聚合内容条目数量。
  2. 聚合评论样本数。
  3. 聚合情绪分布。
  4. 聚合 Top 5 标签。
  5. 选取典型评论。
  6. 调用 AI 生成热点总结。
  7. 生成 Markdown 内容并保存到 reports

报告总结 AI 调用规格

  • 调用方式:独立 AI 请求,不复用评论分析的批量调用。
  • 输入内容
    • 统计数据摘要:情绪分布(正面/中性/负面各占比)、标签 Top 5 及其出现次数。
    • 典型评论文本:每个情绪类别取 2-3 条代表性评论原文(截断前 150 字符)。
  • 输出要求:纯文本,不使用 JSON Schema,控制在 200 字以内
  • Prompt 模板:独立模板文件,存放于 Prompt 模板目录(如 prompts/report_summary.txt)。
  • 超时与失败策略:复用 §7.3 的 AI_TIMEOUT_SECONDS 配置;总结生成失败不阻断报告创建,报告中该字段显示为默认文本"总结生成失败,请查看详细数据"。

8.3 统计一致性原则

  • 情绪数量、标签数量、样本数必须由评论结构化结果计算。
  • AI 总结只能基于统计结果和典型评论生成,不反向覆盖结构化统计。
  • 页面展示和导出均读取同一份报告数据。

9. 后端接口契约

9.1 页面路由

方法 路径 说明
GET / 任务列表 / 创建任务页
GET /tasks/{task_id} 热点与内容条目列表页
GET /hotspots/{hotspot_id}/report 热点级报告页
GET /items/{item_id} 内容条目详情页

9.2 API 路由

方法 路径 说明
POST /api/tasks 创建抓取任务
GET /api/tasks 查询任务列表
GET /api/tasks/{task_id} 查询任务详情
GET /api/tasks/{task_id}/hotspots 查询任务热点与内容条目
GET /api/items/{item_id}/comments 查询评论明细
GET /api/export/items/{item_id}/comments.csv 导出 CSV
GET /api/export/hotspots/{hotspot_id}.md 导出热点 Markdown
GET /api/export/items/{item_id}.md 导出内容条目 Markdown
GET /health 健康检查

补充路由

  • GET /api/export/hotspots/{hotspot_id}/comments.csv — 导出指定热点下所有内容条目的评论汇总 CSV。

接口参数预留

  • GET /api/items/{item_id}/comments 预留可选分页参数:?page=1&page_size=50。MVP 默认返回全部评论(不分页),但接口签名须支持这两个参数以备后续启用。

9.3 创建任务请求

{
  "platform": "xiaohongshu",
  "hot_limit": 5,
  "item_limit_per_hot": 5,
  "comment_limit_per_item": 50
}

校验:

  • platform 必须为 xiaohongshudouyin
  • hot_limit 范围 110。
  • item_limit_per_hot 范围 110。
  • comment_limit_per_item 范围 10100。

10. 前端页面计划

10.1 任务列表 / 首页

展示:

  • 平台选择;
  • 抓取规模配置;
  • 开始抓取按钮;
  • 刷新任务列表按钮;
  • 任务列表:
    • 创建时间;
    • 平台;
    • 状态;
    • AI 分析状态 / 成功率;
    • 成功 X / 共 Y 条内容条目;
    • 错误阶段与错误类型;
    • 进入任务结果入口。

交互:

  • 提交前做前端范围校验。
  • 后端仍必须做同样校验。
  • P0 使用手动刷新。
  • P1 可每 5 秒轮询运行中任务。

10.2 热点与内容条目列表页

展示:

  • 任务基础信息;
  • 任务状态和 AI 分析状态;
  • 热点列表;
  • 每个热点下内容条目列表;
  • 进入热点报告和内容条目详情入口。

10.3 热点级报告页

展示:

  • 热点基础信息;
  • 内容条目数量;
  • 评论样本数;
  • 情绪分布;
  • Top 5 标签;
  • 典型评论;
  • 热点总结;
  • Markdown 导出入口。

10.4 内容条目详情页

展示:

  • 热点与内容条目基础信息;
  • 内容条目级报告;
  • 评论明细;
  • Markdown 导出入口;
  • CSV 导出入口;
  • P1 可展示原始 JSON 调试入口。

11. 导出方案

11.1 CSV 评论明细

  • 编码:UTF-8-SIG
  • 文件名:{platform}_{task_id}_{hotspot_keyword}.csv
  • hotspot_keyword 超过 20 字符时截断并附加省略号。
  • 标签字段使用中文逗号拼接或 JSON 字符串,MVP 建议中文逗号拼接,便于 Excel 查看。

文件名安全处理

  • hotspot_keyword 超过 20 字符时截断。
  • 文件名中的非法字符(包括但不限于 /\:*?"<>|)统一替换为下划线 _
  • 连续多个下划线合并为单个下划线。

字段:

  1. 平台;
  2. 任务 ID
  3. 热点 ID
  4. 热点标题;
  5. 内容条目 ID
  6. 内容条目标题;
  7. 评论 ID
  8. 评论内容;
  9. 情绪倾向;
  10. 方向标签;
  11. 点赞数;
  12. 评论时间。

11.2 Markdown 报告

  • 直接读取 reports.markdown_content
  • 响应头设置下载文件名。
  • 页面展示与 Markdown 导出必须来自同一份报告数据。

12. 配置与部署

12.1 环境变量

APP_ENV=development
APP_HOST=0.0.0.0
APP_PORT=8000
DATABASE_URL=sqlite:///./data/app.db

TIKHUB_API_KEY=
TIKHUB_BASE_URL=https://api.tikhub.io

AI_PROVIDER=openai-compatible
AI_BASE_URL=
AI_API_KEY=
AI_MODEL=
AI_BATCH_SIZE=20
AI_CONCURRENCY=2
AI_MAX_RETRIES=3
AI_TIMEOUT_SECONDS=30

HTTP_TIMEOUT_SECONDS=20
HTTP_MAX_RETRIES=3
变量名 默认值 说明
AI_MAX_RETRIES 3 AI 单批请求最大重试次数
AI_CONCURRENCY 2 AI 请求最大并发数(跨内容条目级别)

12.2 Docker Compose

MVP 只需要一个 app 服务和一个数据卷:

services:
  app:
    build: .
    ports:
      - "8000:8000"
    env_file:
      - .env
    volumes:
      - ./data:/app/data

验收:

  • docker compose up --build 可启动;
  • 浏览器访问 http://localhost:8000
  • /health 返回 200
  • SQLite 数据写入 ./data/app.db

13. 错误处理与日志

13.1 错误类型

error_stage error_type 示例
crawl_hotspots api_error 热点接口失败
crawl_items api_response_invalid 搜索结果字段缺失
crawl_comments rate_limited HTTP 429
ai_analysis ai_timeout AI 请求超时
ai_analysis ai_parse_failed JSON 解析失败
report_generation report_failed 报告生成失败
database db_error 入库失败

13.2 容错规则

  • 热点列表获取失败:任务失败。
  • 单个热点搜索内容失败:记录错误,继续下一个热点。
  • 单个内容条目评论抓取失败:该内容条目标记失败,继续下一个内容条目。
  • 单条评论 AI 分析失败:该评论标记分析失败,继续其他评论。
  • 没有任何内容条目成功:任务失败。
  • 至少 1 条内容条目成功:任务成功,并展示失败数量和错误摘要。

13.3 日志

  • 使用 Python 标准 logging。
  • 每个任务日志必须带 task_id
  • 外部 API 错误日志记录:
    • 平台;
    • 接口名称;
    • HTTP 状态码;
    • 错误摘要;
    • 不记录完整 API Key。

14. 测试计划

14.1 单元测试

必须覆盖:

  • 配置范围校验;
  • 小红书字段映射;
  • 抖音字段映射;
  • 评论分页终止条件;
  • 评论去重;
  • AI JSON Schema 校验;
  • analysis_success_rateanalysis_status 计算;
  • 情绪和标签统计;
  • Markdown 生成;
  • CSV UTF-8-SIG 导出。

14.2 集成测试

建议覆盖:

  • 使用 mock 外部 API 创建一条小红书任务并完成全流程;
  • 使用 mock 外部 API 创建一条抖音任务并完成全流程;
  • 单个内容条目失败但任务成功;
  • AI 解析失败重试后成功;
  • AI 成功率低于 80% 时任务成功但 analysis_status = insufficient

14.3 手工验收

  1. docker compose up --build 启动。
  2. 打开首页。
  3. 创建小红书默认规模任务。
  4. 刷新任务列表直到任务完成。
  5. 查看热点列表、热点报告、内容条目详情、评论明细。
  6. 导出 CSV 和 Markdown。
  7. 创建抖音默认规模任务并重复验收。
  8. 手动填入非法配置值,确认前后端均阻止提交。

15. 开发周期安排

若从 2026-07-01 开始,建议排期如下。

Day 1:项目骨架、数据模型、任务框架

目标:

  • FastAPI 项目可启动;
  • SQLite 表结构完成;
  • 任务创建和状态流转可用;
  • 页面能创建任务并看到任务列表。

任务:

  1. 初始化项目结构。
  2. 编写配置管理和 .env.example
  3. 定义 SQLAlchemy 数据模型。
  4. 实现数据库初始化。
  5. 实现任务创建 API。
  6. 实现任务列表页面。
  7. 实现 /health
  8. 编写基础单元测试。

验收:

  • 本地启动后可创建一条空任务;
  • 任务列表展示平台、创建时间、状态;
  • Docker Compose 能启动 app。

Day 2:平台抓取链路

⚠️ 排期风险备注:Day 2 优先完成小红书完整链路(搜索 + 笔记详情 + 评论含分页 + 字段映射 + raw_data 保存)。若进度受阻,抖音链路可延至 Day 3 上午。判断标准:如果到 Day 2 下午 4 点小红书链路尚未跑通端到端测试,立即停止并将抖音推迟。

目标:

  • 小红书和抖音最小链路工程化;
  • 热点、内容条目、评论可入库;
  • 分页、限流和字段兼容策略落地。

任务:

  1. 实现 TikHub HTTP client。
  2. 实现小红书热点、笔记、评论抓取。
  3. 实现抖音热点、视频、评论抓取。
  4. 实现评论分页与 429 退避。
  5. 实现 raw_data 保存。
  6. 实现任务进度统计字段。
  7. 编写字段映射和分页测试。

验收:

  • 默认规模可抓取至少一个平台的真实数据;
  • 小红书 / 抖音链路均可在 mock 测试中通过;
  • 字段缺失不导致整批任务崩溃。

Day 3AI 分析与报告生成

⚠️ 排期调整说明:若抖音链路从 Day 2 延入,Day 3 上午优先完成抖音链路,下午实现 AI 分析 + 报告生成。报告的 Markdown 排版以信息可读为标准,不追求视觉效果,必要时直接使用字符串拼接。

目标:

  • 评论级结构化分析可用;
  • AI 输出校验、重试和质量状态可用;
  • 内容条目级和热点级报告预生成。

任务:

  1. 编写评论分析 JSON Schema。
  2. 实现 AI client。
  3. 实现批量评论分析。
  4. 实现 AI 解析失败重试。
  5. 实现 analysis_success_rateanalysis_status
  6. 实现情绪 / 标签统计。
  7. 实现典型评论选取。
  8. 实现报告总结和 Markdown 生成。
  9. 编写 AI schema、统计和报告测试。

验收:

  • 任务完成后评论有情绪和标签;
  • 报告统计与评论明细一致;
  • AI 成功率低于 80% 时页面可见分析不足提示。

Day 4:页面、导出、Docker 验收

目标:

  • 所有页面可用;
  • CSV / Markdown 导出可用;
  • Docker Compose 端到端验收通过;
  • 文档与环境示例补齐。

任务:

  1. 完成热点与内容条目列表页。
  2. 完成热点级报告页。
  3. 完成内容条目详情页和评论明细。
  4. 完成 CSV 导出。
  5. 完成 Markdown 导出。
  6. 完成前端手动刷新和配置校验。
  7. 完成 Dockerfile 和 docker-compose.yml。
  8. 执行端到端手工验收。
  9. 修复高优先级问题。

验收:

  • 两个平台至少各跑通一次默认任务;
  • 页面与导出内容一致;
  • Docker Compose 一键启动;
  • MVP 关键验收清单全部通过或记录明确缺口。

16. 风险与降级方案

风险 影响 降级方案
TikHub 接口字段变化 抓取失败或字段为空 保留 raw_data,字段映射使用多候选字段
平台 API 限流 任务变慢或部分内容失败 429 指数退避,超过重试后跳过当前条目
AI 输出不稳定 评论无法结构化 JSON Schema 校验 + 3 次重试 + 单条失败隔离
AI 成本或耗时过高 任务执行时间变长 降低默认抓取规模或 AI batch size
SQLite 写入冲突 任务失败 ThreadPoolExecutor(max_workers=1) + WAL 模式 + timeout=10(见 §2.1
AI 批量 JSON 解析失败 评论无法结构化 3 次重试 + 第 3 次自动 batch_size 减半(见 §7.3
容器重启导致任务僵尸 任务永久停留在运行中 lifespan 启动时自动将 running 任务标记为 failed(见 §4.3
页面轮询未实现 用户不知道任务进度 P0 保留手动刷新按钮和创建时间
报告生成失败 结果不可查看 内容条目标记失败;已生成评论明细仍可展示

17. 关键验收清单

MVP 完成时必须满足:

  1. 系统可通过 Docker Compose 启动,并能在浏览器访问。
  2. 用户可从页面选择小红书或抖音并手动触发任务。
  3. 用户可配置热点数、每热点内容条目数、每内容评论数,且非法值无法提交。
  4. 系统可获取默认 Top 5 热点。
  5. 系统可为每个热点拆分默认最多 5 条内容条目。
  6. 系统可为每条内容条目抓取默认最多 50 条一级评论。
  7. 任务内至少 80% 的评论成功生成情绪分类和方向标签;低于 80% 时 analysis_status 标记为分析不足。
  8. 系统可生成热点级汇总报告。
  9. 系统可生成内容条目级分析报告。
  10. 页面可查看任务列表、热点列表、热点级报告、内容条目详情和评论明细。
  11. 用户可导出 UTF-8-SIG 编码的 CSV 评论明细。
  12. 用户可导出 Markdown 热点级汇总报告。
  13. 用户可导出 Markdown 内容条目级报告。
  14. 任务失败时,页面可展示失败状态,错误原因至少包含失败阶段和错误类型。
  15. 报告统计数据与评论结构化结果一致。

18. 后续文档衔接

DevelopmentPlan 完成后,建议继续产出:

  1. UIDesign.md
    • 页面信息结构;
    • 表单布局;
    • 任务状态展示;
    • 报告页展示结构。
  2. TDD.md
    • 单元测试、集成测试、端到端验收用例;
    • mock API 响应样例;
    • AI 输出解析失败用例。
  3. Tasks.md
    • 按 Day 1–Day 4 拆成可执行开发任务;
    • 标明每个任务的输入、输出、验收和依赖关系。

变更日志

日期 版本 变更内容
2025-07-10 v1.0 初始版本
2025-07-10 v1.1 基于双审阅报告合并修订:锁定 ThreadPoolExecutor(max_workers=1) + SQLite WAL 模式;明确 httpx 同步使用策略;补充 AI Prompt 输入格式与 comment_id 回填要求;补充报告总结 AI 调用独立规格;新增僵尸任务恢复机制;AI 批量重试增加降级拆分策略;明确 AI 并发粒度为跨内容条目级别;补充评论分页请求间隔;明确搜索笔记不分页;补充 reports 表业务约束与建议索引;补充 labels 格式转换归属;字段映射候选字段标注优先级;移除冗余 refresh 路由;补充热点级 CSV 导出路由与评论接口分页预留;文件名安全处理规则;环境变量补充 AI_MAX_RETRIES 和 AI_CONCURRENCY;数据保留策略记录为技术债;P2 列表扩充;Day 2/3 排期风险备注;风险表引用更新。共 23 条修订指令。