refactor: 清理历史 AI Agent 文档残留 + 重构 docs/ + Pipeline 健壮性修复

- docs: 删除 CLAUDE.md / continuation.md / english-news-plan.md 及旧版 intlnews_usage.*,
        统一迁移到 docs/{README,architecture,quickstart,usage,pipeline,configuration,deployment,development,faq}.md
- README: 精简为仓库入口,指向 docs/
- configs/sources.yaml: 更新注释指向新文档
- .env.example: 修正 DashScope Embedding 端点说明

Pipeline 修复:
- dedup/llm/embedding/vectorstore/reporter: 过滤 M2 no_content / 空正文,避免污染下游与 Qdrant
- dedup/pipeline: 改为先写唯一文件再写指纹,避免崩溃导致文章永久丢失
- crawler/orchestrator: sources_crawled 改为“尝试数”,成功数 = crawled - failed
- crawler/storage: write_index_jsonl 从文章路径推断日期,修复跨天/测试路径问题
- scheduler/pipeline: STEP_TIMEOUTS 实际生效(SIGALRM)
- scheduler/reporter: emb_count 排除 index.json;日报跳过无原文事件
- vectorstore/pipeline: payload 增加 source_ids;--recreate --all 时空日期也重建 collection
- app/cli: extract/dedup/translate/embed/index/pipeline 支持 --date;embed/index 支持 --all;crawl 全源失败返回非零
- scripts: domestic_full/crawl_8g/crawl_2g/pipeline 安全加载 .env;M1 全失败不标记且最终退出码=1
This commit is contained in:
2026-08-22 20:47:53 +08:00
parent 951276a313
commit 3b44f64f66
30 changed files with 1496 additions and 2804 deletions
+30 -243
View File
@@ -1,267 +1,54 @@
# English Financial News
国际财经新闻私有化 Deep Research 平台。
国际财经新闻私有化 Deep Research 平台:抓取、去重、翻译、事件抽取、向量知识库、日报生成。
> 完整文档已整理到 [docs/](docs/README.md)。本文件仅作为仓库入口。
## 核心能力
- **M1** 英文财经新闻抓取(Crawl4AI,headful Playwright + HTTP 代理,13 个源)
- **M2** 英文正文提取(trafilatura)
- **M3** 三层去重(URL Hash / 内容 Hash / SimHash 模糊)
- **M4** 全文英译中 + 投资事件抽取(LLM DeepSeek v4-flash)
- **M5** 向量生成(DashScope text-embedding-v3,1024 维)
- **M6** 双语向量知识库(Qdrant)
- **M7** 定时调度(06/12/18/22) + 每日 AI 摘要日报(M9 起结构化写入 MySQL,不再产出 HTML)
- **M8** MCP 服务(Cherry Studio / Claude Code Agent 深度研究)
## 部署架构
```
国内服务器(Pi 4, 8GB)
┌──────────────────────────────────────┐
│ M1 抓取(headful Playwright) │
│ HTTP 代理 → Privoxy → SOCKS5 │
│ ↓ │
│ M2 正文提取 → M3 去重 │
│ ↓ │
│ M4 翻译+事件抽取(DeepSeek) │
│ ↓ │
│ M5 向量生成 → M6 Qdrant 入库 │
│ ↓ │
│ 日报生成 → 写入 MySQL(news_report) │
│ ↓ │
│ M8 MCP 服务(研究 Agent) │
└──────────────────────────────────────┘
```
> 海外服务器已于 2026-07-14 停用,全链路在 Pi 独立运行。
---
## 新闻源状态(2026-07-23)
| 源 | ID | 策略 | 日产量 | 状态 |
|----|----|------|--------|------|
| Reuters | `reuters` | Google News RSS | ~30 篇摘要 | ⚠️ DataDome |
| CNBC | `cnbc` | CNBC RSS | ~25 篇 | ✅ |
| MarketWatch | `marketwatch` | RSS + stealth | ~25 篇 | ✅ |
| Financial Times | `ft` | FT RSS | ~20 篇摘要 | ⚠️ 付费墙 |
| Yahoo Finance | `yahoo_finance` | Yahoo RSS | ~30 篇 | ✅ |
| **Investing.com** | `investing` | **Google News RSS** + stealth | **25 篇摘要** | **✅ 2026-07-23 修复** |
| Seeking Alpha | `seekingalpha` | RSS + stealth | ~20 篇 | ✅ |
| Barron's | `barrons` | Dow Jones RSS + stealth | ~20 篇 | ✅ |
| WSJ | `wsj` | Dow Jones RSS + stealth | ~20 篇 | ✅ |
| Economist | `economist` | RSS + stealth | ~15 篇 | ✅ |
| **InvestingLive** | `investinglive` | **RSS(全文)** | **25 篇** | **✅ 2026-07-23 新增** |
| ZeroHedge | `zerohedge` | FeedBurner RSS | ~15 篇全文 | ✅ |
| ~~ForexLive~~ | ~~forexlive~~ | ~~已迁移~~ | ~~0~~ | ~~❌ 301→investinglive~~ |
> **注:** RSS 摘要源的全文通过 headful Playwright + HTTP 代理回退抓取。当前国内服务器使用 Shadowsocks + Privoxy 代理访问海外。
---
- M1 英文财经新闻抓取(RSS 优先 + Crawl4AI/Playwright Web 回退)
- M2 正文提取(trafilatura)
- M3 三层去重(URL / 内容 / SimHash)
- M4 全文英译中 + 投资事件抽取(DeepSeek)
- M5 向量生成(DashScope text-embedding-v3)
- M6 Qdrant 语义知识库
- M7 全链路调度与每日 AI 摘要日报
- M8 MCP 服务(Cherry Studio / Claude Code 接入)
- M9 日报结构化写入 MySQL
## 快速开始
### 环境要求
- Python 3.11
- uv
- Xvfb(Pi 服务器 headful 模式需要)
- Shadowsocks 客户端 + Privoxy(国内服务器海外代理)
### 安装
```bash
# 创建虚拟环境并安装依赖
# 安装
uv sync
cp .env.example .env # 编辑 API Key/数据库凭据
# 配置环境变量
cp .env.example .env
# 编辑 .env 填入真实 API Key(DeepSeek / DashScope / Qdrant)
```
### Pi 服务器前置条件
```bash
# 1. Xvfb 虚拟显示器(headful Playwright 需要)
sudo apt install xvfb
Xvfb :99 -screen 0 1280x1024x24 -ac +extension RANDR &
# 2. Shadowsocks + Privoxy(HTTP 代理访问海外)
sudo apt install shadowsocks-libev privoxy
# 配置 /etc/shadowsocks-libev/config.json(服务端信息)
# 配置 /etc/privoxy/config(forward-socks5t 127.0.0.1:1088 .)
sudo systemctl enable --now shadowsocks-libev-local
sudo systemctl enable --now privoxy
```
---
## 操作命令
### 全流程自动化
```bash
# 全流程:M1 抓取 → M2→M6 管道 → 日报
# 每天 06:00 / 12:00 / 18:00 / 22:00 自动执行
# 全流程
bash scripts/domestic_full.sh
# 中断恢复:跳过当天已完成步骤(状态存 data/run_state/{date}.state)
# 支持步骤级断点:M1_crawl / M2_extract / M3_dedup / M4_translate / M5_embed / M6_index / report
# 断点续跑
bash scripts/domestic_full.sh --resume
# 仅 M2→M6 管道(同样支持 --resume)
bash scripts/pipeline.sh --resume
```
### 单步执行
```bash
# M1 抓取全部源(headful + HTTP 代理)
bash scripts/domestic_crawl_8g.sh
# M1 单源测试
bash scripts/domestic_crawl_8g.sh investing
bash scripts/domestic_crawl_8g.sh investinglive
# M2→M6 管道(需先有 raw 数据)
# M2→M6+日报管道
bash scripts/pipeline.sh
# 仅生成日报(写入 MySQL news_report/news_event,report_type="intl")
uv run en-news report
# CLI 单步
uv run en-news --help
```
### 调度查看
## 文档导航
```bash
# 查看最近日志
tail -50 logs/full.log
# 查看日报
ls -lt data/reports/
```
---
## 配置说明
| 文件 | 用途 |
| 文档 | 说明 |
|------|------|
| `configs/sources.yaml` | 英文财经新闻源定义(13 个源) |
| `configs/system.yaml` | 模型按场景(`llm_scenes`)/重试/阈值等系统配置 |
| `configs/profiles/8g_headful.yaml` | Pi 服务器 headful 抓取配置(代理/超时) |
| `configs/profiles/8g_headful.yaml` | Pi 服务器 headful 抓取配置(代理/超时) |
| `.env` | 密钥 / 服务地址(不入 Git) |
| `prompts/` | LLM Prompt 模板(翻译/日报/搜索 Agent) |
### AI 模型按场景配置(llm_scenes)
大模型按场景独立配置,见 `configs/system.yaml` 的 `llm_scenes` 段:
| 场景 | 用途 | 模型(当前) | 参数 |
|------|------|-------------|------|
| `translation` | M4 全文英译中 + 投资事件抽取 | deepseek-v4-flash | temperature=0.1, max_tokens=8192 |
| `daily_report` | M7 日报 AI 摘要(分批生成) | deepseek-v4-flash | temperature=0.3, max_tokens=1500 |
场景未声明的字段回退 `llm` 默认段;Embedding 为单一场景(`en_finance_news` 库入库/检索向量必须同模型,不支持拆分)。
### 去重多来源(M3)
去重时跨源重复的新闻,会把所有来源记录到保留的唯一篇 `source_ids` 字段(首个来源为 `source_id`),经翻译透传后:
- `news_event.source`:拼接展示名(如 "Barron's, CNBC, Reuters",最多前 3 个,向后兼容)
- `news_event.sources`(TEXT,JSON 数组):**全部来源展示名,主源居首**,如 `["Barron's","CNBC","Reuters","Financial Times"]`,前端直接渲染完整来源列表
表结构变更(`news_event` 新增 `sources` 列)由 `report_db.init_schema()` 幂等迁移(`ALTER TABLE ... ADD COLUMN IF NOT EXISTS`,MariaDB 10.0.2+)。
### 中断恢复(--resume)
全流程脚本支持断点续跑:
- `scripts/domestic_full.sh --resume` / `scripts/pipeline.sh --resume`:跳过当天已完成步骤(步骤状态存 `data/run_state/{YYYYMMDD}.state`,按天换新,跨天自动失效)
- 步骤粒度:`M1_crawl` / `M2_extract` / `M3_dedup` / `M4_translate` / `M5_embed` / `M6_index` / `report`;某步失败不标记,`--resume` 从失败处重试
- **文件级增量(各步骤内自动跳过已处理文件)**:M2 按 `data/processed/.../{url_hash}.json` 存在性、M3 按指纹库判重、M4 按 `data/events/.../{url_hash}.json` 存在性、M5 按 `data/embeddings/.../index.json`、M6 按 Qdrant upsert 幂等(点 id=url_hash)、日报按 MySQL 唯一键覆盖
### 终端进度与日志
执行全流程时,终端**实时显示完整进度**,且**显性标注当前阶段与 AI 模型**:
- 每阶段标题:`━━━ M2 正文提取 ━━━`、`━━━ M4 翻译+事件抽取(AI 大模型: deepseek / deepseek-v4-flash)━━━`(AI 阶段自动从 `system.yaml` 读取供应商/模型)
- 每步进度:`▶ 步骤 开始` → 步骤内逐源/逐篇输出 → `✔ 完成(耗时 Ns)`;失败显示 `✗` 与退出码
- AI 调用点在 Python 层同样显性打印(`AI 大模型(场景 translation/daily_report): provider=... model=...`、`初始化 Embedding 客户端: provider=dashscope model=...`)
- 日志:`logs/domestic_full_{ts}.log`(全流程)、`logs/pipeline_{ts}.log`(管道),已入 `.gitignore`
### 日报入库(M9)
日报内容结构化写入与 [news 项目](https://github.com/) 共用的 MySQL `myquant` 库(表 `news_report` / `news_event`,`report_type="intl"`,同一天重复生成幂等覆盖)。表结构与数据契约见 news 项目 `docs/db_schema.md`。
`.env` 需配置(与 news 项目 `.env` 的 `NEWS_DB_PASSWORD` 相同):
```env
NEWS_DB_HOST=192.168.1.10 # pi5 经内网直连 pi 上的 autossh 隧道(0.0.0.0:13306 → doorcome.cn:3306)
NEWS_DB_PORT=13306
NEWS_DB_USER=myquant
NEWS_DB_PASSWORD=xxx
NEWS_DB_NAME=myquant
```
---
## 数据存储
```
data/
├── raw/{source_id}/{yyyymmdd}/ M1 原始抓取(HTML + Markdown)
├── processed/{source_id}/ M2 正文提取结果
├── dedup/ M3 去重指纹库(SQLite)
├── translations/{yyyymmdd}/ M4 翻译+事件抽取结果(JSONL)
├── embeddings/{yyyymmdd}/ M5 向量文件(JSONL)
├── reports/ M7 日报(M9 起不再产出 HTML,改存 MySQL)
└── vectorstore/ M6 Qdrant 数据
```
Qdrant collection 名称:`en_finance_news`
---
## MCP 服务
M8 模块提供 MCP(Model Context Protocol)服务,供 Cherry Studio / Claude Code 等客户端接入进行深度研究。
```bash
# 启动 MCP 服务
uv run en-news mcp-server
```
支持的工具:
| Tool | 功能 |
|------|------|
| `en_news_search` | 语义搜索知识库 |
| `en_news_filter` | 按源/日期/情绪筛选 |
| `en_news_trending` | 获取热点事件 |
| `en_news_report` | 获取最新日报 |
| `en_news_daily_brief` | 一键生成简报 |
---
## 常见问题
### 为什么有些源只拿到摘要?
部分网站有强反爬(Cloudflare/DataDome/付费墙),RSS 只返回摘要。全文可尝试 headful 浏览器回退,但成功率取决于代理线路质量。
### 日报存在哪里?
M9 起日报结构化写入 MySQL `myquant` 库:主表 `news_report`(`report_type="intl"`)+ 明细表 `news_event`(`section="intl"`),数据契约与 [news 项目](https://github.com/) 一致(见其 `docs/db_schema.md`)。历史 HTML 日报(2026-06-16 ~ 2026-08-03)已由 news 项目解析导入。API / 前端读取同一批表。
### 如何添加新源?
编辑 `configs/sources.yaml`,添加源配置(id/name/homepage/rss_url/article_url_pattern 等)。参考现有源格式。
---
## 项目计划
详见 [english-news-plan.md](english-news-plan.md)
| [docs/README.md](docs/README.md) | 文档中心 |
| [docs/architecture.md](docs/architecture.md) | 项目架构与数据流 |
| [docs/quickstart.md](docs/quickstart.md) | 快速开始 |
| [docs/usage.md](docs/usage.md) | 使用手册 |
| [docs/pipeline.md](docs/pipeline.md) | 流水线详解 |
| [docs/configuration.md](docs/configuration.md) | 配置说明 |
| [docs/deployment.md](docs/deployment.md) | 部署与运维 |
| [docs/development.md](docs/development.md) | 开发指南 |
| [docs/faq.md](docs/faq.md) | FAQ |
## 许可证