自动监控语雀 PRD 文档变更,基于 Git 实现版本管理,提供可视化 Diff、AI 变更摘要和多渠道通知。
- 自动轮询 — 可配置间隔定时拉取语雀文档,检测变更
- Git 版本追踪 — 每次变更自动 commit,保留完整修改历史
- 可视化 Diff — 基于 diff2html 的逐行对比,红绿标注增删改
- AI 变更摘要 — 自动标注改动所属模块,一句话概括变更点(可选)
- 多渠道通知 — 钉钉 Webhook / 邮件,变更即时推送(可选)
- 暂停/恢复 — 按任务控制是否追踪,手动执行不受暂停影响
- 多模式拉取 — 支持 doc-mcp / 语雀 Token / 自定义 MCP 三种模式
- 下载报告 — 支持下载 Diff HTML 报告和历史版本 .md 文件
基于 Streamlit 构建的 Web 界面:
- 首页:任务列表 + 全局状态 + 操作按钮
- 详情页:提交历史 + Diff 查看 + 下载
- 设置页:所有配置项可在线修改
- Python 3.9+
- Node.js(doc-mcp 模式需要)
- Git
git clone https://github.com/CloudyNow/PRD_Track.git
cd PRD_Track
pip install -r requirements.txt
cp .env.example .env编辑 .env 文件,根据你的拉取模式配置:
模式 1:doc-mcp(企业内部)
fetch_mode=doc_mcp
ACCESS_KEY=你的公司AI平台密钥模式 2:语雀 Token(个人/其他公司)
fetch_mode=yuque_token
yuque_token=你的语雀个人Token
yuque_base_url=https://www.yuque.com模式 3:自定义 MCP
fetch_mode=custom_mcp
custom_mcp_command=npx -y @your/mcp-server --stdio语雀个人 Token 获取:语雀开发者设置
./start.sh
# 或: streamlit run app.py浏览器打开 http://localhost:8501
- 添加追踪 — 左侧边栏粘贴语雀 PRD 链接,设置轮询频率
- 查看变更 — 点击"查看历史" → "查看 Diff"
- 配置通知 — 点击"⚙️ 配置"开启钉钉/邮件通知
- 下载报告 — Diff 页面支持下载 HTML 报告和 .md 原文
配置通义千问 API Key 后,自动生成变更摘要并标注所属模块:
llm_api_key=sk-你的DashScope密钥
default_ai_summary_enabled=truedingtalk_webhook_url=https://oapi.dingtalk.com/robot/send?access_token=xxx钉钉机器人安全设置需添加关键词
PRD
├── app.py # Streamlit 入口
├── config.py # 配置管理
├── start.sh # 启动脚本
├── src/
│ ├── watcher.py # 业务编排器
│ ├── mcp_client.py # 文档拉取(多模式)
│ ├── markdown_converter.py
│ ├── repo_manager.py # Git 仓库管理
│ ├── scheduler.py # 定时调度
│ ├── state_store.py # 状态持久化
│ ├── diff_renderer.py
│ ├── llm_summarizer.py
│ ├── notifier.py
│ ├── providers/ # LLM Provider(可扩展)
│ └── channels/ # 通知渠道(可扩展)
├── templates/ # Jinja2 模板
├── static/ # diff2html 资源
├── docs/ # 文档
└── tests/ # 测试
# src/providers/your_provider.py
from src.providers import register
@register("your_provider")
class YourProvider:
name = "your_provider"
def summarize(self, diff_text, model, prompt_system, **kwargs) -> str:
...# src/channels/feishu.py
from src.channels import register_channel
@register_channel("feishu")
class FeishuChannel:
name = "feishu"
def send(self, subject, body):
...MIT