Files
news/docs/user_guide.md
T
2026-07-18 15:51:01 +08:00

20 KiB
Raw Blame History

A 股 Deep Research 私有投研平台 — 用户手册

版本:v1.0 | 最后更新:2026-06-17


目录

  1. 项目概述
  2. 环境准备
  3. 全链路一键运行
  4. 分模块使用
  5. 定时任务部署
  6. MCP 服务与 AI Agent
  7. 投研分析模板
  8. 附录

1. 项目概述

A 股 Deep Research 是一个私有化投研平台,自动完成财经新闻抓取 → 正文提取 → 事件抽取 → 向量化 → 语义检索的完整链路,最终通过 MCP 协议接入 Cherry Studio / Claude Code 等 AI 客户端,实现"用自然语言提问,获得结构化研究报告"。

核心数据流:

财经网站 → 抓取(HTML+Markdown) → 正文提取 → 去重
    → LLM 抽取(股票/公司/行业/情绪/事件类型)
    → Embedding 向量化(1024维)
    → Qdrant 知识库(语义检索,2ms 级)
    → MCP 服务 → AI Agent 深度研究

技术栈:Python 3.11 / Crawl4AI / GNE / DeepSeek / DashScope / Qdrant / APScheduler / MCP

部署位置:树莓派 5(ARM64),/home/pi/news/


2. 环境准备

2.1 前置条件

  • Python 3.11
  • uv(包管理器):brew install uv(macOS) 或 curl -LsSf https://astral.sh/uv/install.sh | sh(Linux)
  • Playwright 浏览器(仅 M1 抓取需要):uv run python -m playwright install chromium

2.2 获取代码

git clone <repo> /home/pi/news
cd /home/pi/news

2.3 安装依赖

uv sync

2.4 配置环境变量

cp .env.example .env
# 编辑 .env,填写:
#   DEEPSEEK_API_KEY=sk-xxx        (M4 LLM 调用)
#   DASHSCOPE_API_KEY=sk-xxx       (M5/M8 嵌入调用)

2.5 验证安装

uv run pytest -m "not integration"   # 应显示 186 passed

2.6 统一 CLI a-share

所有操作通过 a-share 命令完成,替代之前的 8 个脚本入口:

# 模块操作
uv run a-share crawl                     # M1 抓取全部源
uv run a-share crawl --source cls        # 单源
uv run a-share extract --date 20260616   # M2 提取
uv run a-share dedup --date 20260616     # M3 去重
uv run a-share events --date 20260616    # M4 LLM 抽取
uv run a-share events --provider qwen    # 切换 LLM
uv run a-share embed --date 20260616     # M5 向量化
uv run a-share ingest --date 20260616    # M6 入库

# 全链路
uv run a-share pipeline --once           # 立即执行一次全链路

# 检索(直接查 Qdrant,无需写代码或配 Cherry Studio)
uv run a-share search "宁德时代固态电池"
uv run a-share search "政策" --source cls --sentiment positive
uv run a-share search "风险" --stock 001212 --min-importance 3

# 状态总览
uv run a-share status

子命令速查:

命令 功能 常用参数
a-share crawl M1 抓取 --source cls
a-share extract M2 提取 --date 20260616
a-share dedup M3 去重 --date 20260616 --reset
a-share events M4 LLM 抽取 --provider qwen --limit 5
a-share embed M5 向量化 --model text-embedding-v3
a-share ingest M6 入库 --date 20260616 --recreate
a-share pipeline 全链路 --once --report
a-share discover 站点分析 --name 中文名 --add --extra URL
a-share add-entry 追加入口 source_id url1 url2 ...
a-share report 生成日报 --date 20260616 --no-upload
a-share search 检索知识库 --source --stock --sentiment
a-share status 数据总览 无参数

3. 全链路一键运行

一条命令跑通 M1→M6:

uv run a-share pipeline --once

(等效于旧版 uv run python -m scripts.run_scheduler --once,a-share 命令更短更好记)

uv run python -m scripts.run_scheduler --once

全链路耗时约 4-5 分钟(100 篇文章):

步骤 耗时 说明
crawler ~3 分钟 含浏览器渲染,最耗时
extractor ~24 秒 GNE 正文提取
dedup ~2 秒 三层去重
llm ~45 秒 DeepSeek 事件抽取
embedding ~10 秒 DashScope 向量化
qdrant ~4 秒 写入知识库

产物路径:

  • data/raw/ — 原始 HTML + Markdown(M1)
  • data/processed/ — 提取后的 Article(M2)
  • data/deduped/ — 去重后唯一文章(M3)
  • data/events/ — 结构化投资事件(M4)
  • data/embeddings/ — 1024 维向量(M5)
  • data/qdrant_storage/ — Qdrant 知识库(M6)

只执行部分步骤:

uv run python -m scripts.run_scheduler --once --steps crawler,llm,qdrant

4. 分模块使用

M1 新闻抓取

从 5 个 A 股财经源抓取文章。

# 抓取全部启用源
uv run python -m scripts.run_crawler

# 抓取单个源
uv run python -m scripts.run_crawler --source cls

# 试跑不写文件
uv run python -m scripts.run_crawler --no-save

配置文件:configs/sources.yaml

当前支持的源:

ID 名称 JS 渲染 说明
cls 财联社 深度频道
eastmoney 东方财富 首页
sina 新浪财经 静态页面,速度快
stcn 证券时报 要闻列表
yicai 第一财经 新闻频道

新增新闻源:一条命令完成分析+配置:

# 自动分析 + 自动写入 sources.yaml(推荐)
uv run a-share discover https://wallstreetcn.com/news/global --name 华尔街见闻 --add

# 仅预览,不写入
uv run a-share discover https://example.com/news/

discover 自动完成 7 步:

  1. Crawl4AI 抓取首页(自动判断 JS 渲染)
  2. 提取所有站内 <a href> 链接
  3. 过滤导航/功能页(about/download/member 等)
  4. 按 URL 路径模式聚类
  5. 数字 ID 模式优先排序(文章特征)
  6. 自动生成正则
  7. 输出 yaml 配置 + --add 自动追加到 sources.yaml

--add 写入时自动:空行分隔、序号递增、# ---- N. 名称 ---- 注释头。

验证新源:

uv run a-share crawl --source wallstreetcn

同一网站多个入口:使用 --extra 添加额外频道,共用同一个正则:

uv run a-share discover https://wallstreetcn.com/news/global \
  --extra https://wallstreetcn.com/news/china \
  --extra https://wallstreetcn.com/news/tech \
  --name 华尔街见闻 --add

生成的 yaml 自动包含 extra_homepages 字段。抓取时依次访问所有入口,自动去重。

为已有源追加额外入口(无需重新 discover):

uv run a-share add-entry wallstreetcn https://xxx.com/news/新频道

自动去重:已存在的 URL 跳过。

M2 正文提取

从 M1 抓取的 HTML 中提取标准化 Article(title/content/时间/作者)。

# 处理今日所有源
uv run python -m scripts.run_extractor

# 指定日期
uv run python -m scripts.run_extractor --date 20260616

# 单源
uv run python -m scripts.run_extractor --source sina --date 20260616

输入:data/raw/{source}/{YYYYMMDD}/*.html 输出:data/processed/{source}/{YYYYMMDD}/{url_hash}.json

输出格式(Article):

{
  "source_id": "sina",
  "url": "https://finance.sina.com.cn/...",
  "url_hash": "abc123...",
  "title": "宁德时代发布新一代麒麟电池",
  "content": "财联社6月15日电...",
  "publish_time": "2026-06-15T14:30:00",
  "author": "记者 张三",
  "word_count": 1234
}

M3 三层去重

对 M2 输出做 URL Hash → 内容 Hash → SimHash 三层去重,指纹持久化到 SQLite。

# 处理今日
uv run python -m scripts.run_dedup

# 指定日期
uv run python -m scripts.run_dedup --date 20260616

# 重建指纹库
uv run python -m scripts.run_dedup --reset

# 调 SimHash 阈值(默认 3)
uv run python -m scripts.run_dedup --simhash-threshold 5

输入:data/processed/{source}/{YYYYMMDD}/*.json 输出:

  • data/deduped/{YYYYMMDD}/uniques/{url_hash}.json — 唯一文章
  • data/deduped/{YYYYMMDD}/duplicates.jsonl — 重复记录
  • data/dedup/fingerprints.sqlite3 — 指纹库(跨日累积)

M4 投资事件抽取

调用 DeepSeek/Qwen 从去重文章中抽取结构化投资事件。

# DeepSeek(默认)
uv run python -m scripts.run_event_extraction --date 20260616

# Qwen(百炼)
uv run python -m scripts.run_event_extraction --provider qwen --model qwen-plus

# 联调小批量
uv run python -m scripts.run_event_extraction --limit 5

# 调整并发(默认 3)
uv run python -m scripts.run_event_extraction --concurrency 5

输入:data/deduped/{YYYYMMDD}/uniques/*.json 输出:data/events/{YYYYMMDD}/{url_hash}.json

输出格式(ExtractedEvent):

{
  "source_id": "cls",
  "url": "https://...",
  "title": "宁德时代签订100GWh供货协议",
  "event": {
    "stock_codes": ["300750.SZ"],
    "company_names": ["宁德时代"],
    "industries": ["动力电池"],
    "sentiment": "positive",
    "importance": 5,
    "event_type": "重大合同",
    "summary": "宁德时代签5年100GWh协议,金额超1500亿"
  },
  "provider": "deepseek",
  "model": "deepseek-chat"
}

23 种事件类型:业绩预告/业绩快报/财报披露/合作签约/投资并购/重大合同/产品发布/技术突破/监管处罚/诉讼仲裁/股东减持/股东增持/回购/分红/高管变动/资产重组/停牌复牌/ST警示/退市风险/宏观政策/行业政策/国际局势/其他

M5 向量化

将 M4 事件文本用 DashScope text-embedding-v3(1024 维)或本地 BGE-M3 向量化。

# DashScope(默认)
uv run python -m scripts.run_embedding --date 20260616

# 强制覆盖模型
uv run python -m scripts.run_embedding --provider dashscope --model text-embedding-v3

# 本地 BGE-M3(需先 uv sync --extra local-embedding)
uv run python -m scripts.run_embedding --provider local-bge

输入:data/events/{YYYYMMDD}/*.json 输出:data/embeddings/{YYYYMMDD}/{url_hash}.json(含 1024 维向量)

cninfo 公告抓取

cninfo(巨潮资讯网)是独立的 A 股公告抓取管道,与新闻抓取分开调度(每天 08:00)。

全链路(一条命令, watchlist 模式):

uv run a-share pipeline --cninfo-once

包含 9 步: 公告抓取(关注公司) → 调研筛选 → 互动问答 → 正文提取 → PDF 富化 → 去重 → LLM 抽取 → 向量化 → Qdrant 入库。

单独操作:

uv run a-share cninfo --watchlist     # 仅公告(关注公司)
uv run a-share cninfo --research      # 仅调研(关注公司)
uv run a-share cninfo --irm           # 仅互动问答(关注公司)
uv run a-share cninfo --enrich-pdf    # PDF→Markdown

**分步操作**:

```bash
# 抓取公告
uv run a-share cninfo --days 1
uv run a-share cninfo --start 2026-06-17 --end 2026-06-19 --max-pages 50

# PDF 正文 (MarkItDown → Markdown)
uv run a-share cninfo --enrich-pdf --pdf-limit 50

个股日报(关注列表)

为 watchlist 中每家公司生成综合日报:

uv run a-share stock-report

报告内容: AI 要点分析 / 近 7 日公告 / 相关新闻 / 互动问答。 上传路径: echart/research/{YYYYMMDD}/{代码}_{名称}_个股日报_{日期}.html

公告关注列表

只关注特定公司公告,日报中高亮显示:

# 管理关注列表
uv run a-share watchlist add 300750 宁德时代 --note "动力电池龙头"
uv run a-share watchlist add 000001 平安银行
uv run a-share watchlist list                      # 查看关注列表
uv run a-share watchlist remove 000001             # 移除

# 只抓关注公司公告
uv run a-share cninfo --watchlist

# 日报效果: 关注公司事件置顶 + ⭐ 标记

关注列表存储在 configs/watchlist.yaml

.env 配置:

变量 默认 说明
CNINFO_ENABLED true 是否启用
CNINFO_DAYS_BACK 1 每次抓最近 N 天
CNINFO_MAX_PAGES 20 最大页数(×30=600 条/次)
CNINFO_API_BASE http://www.cninfo.com.cn/new API 地址
CNINFO_PDF_BASE http://static.cninfo.com.cn PDF 地址

.env 注意: 值后面不能跟 # 注释。python-dotenv 会错误地把 # 后内容当成值。注释必须独占一行。

M6 Qdrant 入库与检索

将 M5 向量 + M4 事件标签写入 Qdrant 本地知识库。

入库:

# 默认本地文件模式(无需 Docker)
uv run python -m scripts.run_qdrant_ingest --date 20260616

# 重建 collection
uv run python -m scripts.run_qdrant_ingest --recreate

Python 检索:

from vectorstore import VectorStore, SearchFilter, make_qdrant_client

c = make_qdrant_client()
store = VectorStore(c)

# 基础语义检索
hits = store.query(query_vector=probe["vector"], top_k=10)
for h in hits:
    print(h.short_summary())

# 结构化过滤
hits = store.query(
    query_vector=probe["vector"],
    top_k=10,
    filter=SearchFilter(
        source_id="cls",         # 按源
        importance_min=3,        # 影响程度 >= 3
        sentiment="positive",    # 利好
        stock_codes=["300750"],  # 按股票代码
        industries=["动力电池"], # 按行业
        publish_date_from="2026-06-01",  # 时间范围
    ),
)

store.close()

Qdrant 部署说明见附录 A


5. 定时任务部署

将 M1→M6 全链路注册为系统服务,每天 7:00 / 12:00 / 18:00 / 22:00 自动执行。

安装(一次性)

sudo cp scripts/a-share-research.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable a-share-research

日常操作

sudo systemctl start a-share-research     # 启动
sudo systemctl stop a-share-research      # 停止
sudo systemctl restart a-share-research   # 重启
sudo systemctl status a-share-research    # 查看状态(含 PID)

# 查看日志
journalctl -u a-share-research -f         # 实时系统日志
tail -f logs/scheduler.log               # 文件日志

修改执行时间和配置:

# 编辑 .env 中的 SCHEDULE_TIMES
nano /home/pi/news/.env
# 格式: HH:MM,HH:MM,... (24 小时制,逗号分隔)
# 例: SCHEDULE_TIMES=08:00,14:00,20:00

# 重启服务使新配置生效
sudo systemctl restart a-share-research

# 验证新配置
grep "已注册定时任务" /home/pi/news/logs/scheduler.log | tail -4

6. MCP 服务与 AI Agent

6.1 连接 Cherry Studio

  1. 打开 Cherry Studio → 设置 → MCP 服务器 → 添加
  2. 填入:
{
  "mcpServers": {
    "a-share-research": {
      "command": "uv",
      "args": ["run", "python", "-m", "scripts.run_mcp_server"],
      "cwd": "/home/pi/news"
    }
  }
}
  1. 点击启用。Cherry Studio 会自动启动 MCP 服务进程(stdio 模式)。

6.2 连接 Claude Code

编辑项目根目录 .mcp.json:

{
  "mcpServers": {
    "a-share-research": {
      "command": "uv",
      "args": ["run", "python", "-m", "scripts.run_mcp_server"],
      "cwd": "/home/pi/news"
    }
  }
}

6.3 5 个 MCP 工具

工具 使用示例
search_news search_news("宁德时代固态电池进展")
search_company_news search_company_news("价格调整", company="贵州茅台")
search_industry_news search_industry_news("最新政策", industry="半导体")
search_stock_events search_stock_events("重大事件", stock_code="300750")
search_sentiment_trend search_sentiment_trend("AI算力", sentiment="all")

6.4 调试(HTTP SSE 模式)

uv run python -m scripts.run_mcp_server --sse 8765
# 浏览器访问 http://<host>:8765/sse

6.5 工作原理

用户输入自然语言查询
    → DashScope 嵌入(1024 维)
    → Qdrant 余弦相似度检索(2.3ms)
    → 按过滤条件筛选(公司/行业/代码/情绪)
    → Markdown 格式化返回

7. 投研分析模板

在 Cherry Studio 中粘贴 docs/agent_prompt.md 的内容作为 System Prompt,然后可以使用以下提问模式:

公司深度分析

分析宁德时代(300750)近 30 天的利好利空变化,并给出主要投资逻辑演化过程。

行业分析

分析动力电池行业最近的政策动向和竞争格局变化。

风险排查

帮我梳理中旗新材(001212)近期面临的主要风险点。

事件追踪

过去一周 A 股市场有哪些重大监管处罚事件?

情绪趋势

近一个月 AI 算力概念的情绪变化趋势如何?


8. 附录

A. Qdrant 部署模式

本项目支持两种 Qdrant 运行模式:

本地文件模式(默认) Docker Server 模式
类比 SQLite PostgreSQL
进程 无独立进程 容器 a_share_qdrant
端口 不监听 6333/6334
并发 单进程 多客户端
数据位置 data/qdrant_storage/ Docker volume
适用场景 树莓派/单机 x86 生产/Cherry Studio 远程
ARM64 兼容 (jemalloc 16K 页崩溃)

B. 环境变量完整列表

变量 默认值 说明
LLM_PROVIDER deepseek M4 LLM 提供商
DEEPSEEK_API_KEY - DeepSeek API Key
DASHSCOPE_API_KEY - 百炼 API Key(M5/M8)
EMBEDDING_PROVIDER dashscope M5 嵌入提供商
SCHEDULE_TIMES 07:00,12:00,18:00,22:00 M7 定时时间
QDRANT_COLLECTION a_share_news M6 Collection 名
LOG_LEVEL INFO 日志级别

C. 日志文件

文件 对应模块
logs/crawler.log M1 抓取
logs/extractor.log M2 提取
logs/dedup.log M3 去重
logs/llm.log M4 LLM
logs/embedding.log M5 向量化
logs/qdrant.log M6 入库
logs/scheduler.log M7 调度
logs/mcp_server.log M8 MCP

日志自动轮转(10MB/文件,保留 5 份)。

D. 项目目录结构

news/
├── crawler/           M1 新闻抓取
├── extractor/         M2 正文提取
├── dedup/             M3 三层去重
├── llm/               M4 事件抽取
├── embedding/         M5 向量化
├── vectorstore/       M6 Qdrant 客户端
├── scheduler/         M7 定时任务
├── mcp_server/        M8 MCP 服务
├── configs/           配置文件(sources.yaml)
├── prompts/           LLM Prompt 模板
├── docs/              文档与 Agent Prompt
├── scripts/           运行脚本(每个模块一个入口)
├── tests/             pytest 测试
├── data/              数据产物(各层按日期组织)
├── logs/              运行日志
├── pyproject.toml     依赖定义
└── .env.example       环境变量模板

E. 常见问题

Q:M1 某源抓取 0 篇文章?

编辑 configs/sources.yaml,检查该源的 article_url_pattern 正则是否匹配真实链接格式。可先用 uv run python -m scripts.run_crawler --source <id> 单源调试。

Q:M2 提取的正文不是新闻内容(而是"郑重声明"等模板文本)?

这是已知的 M2.2 模板兜底问题,提取器已内置关键词黑名单自动过滤。若遇到新模板,可在 extractor/parser.py_BOILERPLATE_PATTERNS 追加。

Q:M4 调用 LLM 报错?

检查 .envDEEPSEEK_API_KEY 是否填写。可用 --provider qwen 切换到百炼测试。

Q:Qdrant 搜索不到结果?

检查是否已入库:uv run python -c "from vectorstore import VectorStore, make_qdrant_client; c=make_qdrant_client(); s=VectorStore(c); print(s.count()); s.close()"。若 count=0,运行 uv run python -m scripts.run_qdrant_ingest --date <date> 入库。

Q:如何从零重建整个知识库?

rm -rf data/raw data/processed data/dedup data/deduped data/events data/embeddings data/qdrant_storage
uv run python -m scripts.run_scheduler --once

—— 用户手册结束 ——