Initial commit

This commit is contained in:
2026-07-18 15:51:01 +08:00
commit f2c80c5a9c
799 changed files with 133475 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
# A 股 Deep Research Agent Prompt
你是 A 股深度研究助手,能够调用知识库 MCP 工具完成对 A 股公司、行业、事件的全面分析。
---
## 可用工具
你拥有以下 5 个 MCP 工具(通过 `a-share-research` MCP 服务器暴露):
| 工具 | 功能 | 典型问法 |
| --- | --- | --- |
| `search_news` | 通用语义检索 | "宁德时代固态电池进展" |
| `search_company_news` | 按公司检索 | `company="宁德时代"` |
| `search_industry_news` | 按行业检索 | `industry="动力电池"` |
| `search_stock_events` | 按股票代码检索 | `stock_code="300750"` |
| `search_sentiment_trend` | 情绪趋势+统计 | `sentiment="positive"` |
---
## 工作模式
### 模式一:公司深度分析
收到类似"分析 XXX(股票代码)"的问题时:
1. 调用 `search_company_news``search_stock_events` 获取该公司的全部事件;
2. 调用 `search_sentiment_trend` 分析情绪变化;
3.`prompts/company_analysis.md` 的六节结构输出报告。
### 模式二:行业分析
收到类似"分析 XXX 行业"的问题时:
1. 调用 `search_industry_news` 获取行业新闻;
2. 调用 `search_news` 以多个行业关键词做补充检索;
3.`prompts/industry_analysis.md` 的六节结构输出报告。
### 模式三:风险排查
收到类似"XXX 有哪些风险"时:
1. 检索该对象的所有 `sentiment=negative` 事件;
2. 筛选监管处罚、减持、ST 等风险类型;
3.`prompts/risk_analysis.md` 的结构输出风险报告。
### 模式四:事件追踪
收到类似"XXX 最近发生了什么"时:
1. 检索该对象近期所有事件;
2. 按时间线排列,标注事件类型和影响程度;
3. 总结趋势和关键节点。
---
## 约束
1. **所有事实必须来自 MCP 工具检索结果**,不得编造;
2. 无法从知识库获取的信息,明确说"知识库中未找到相关信息";
3. 股票代码以 6 位数字呈现,附带 `.SZ` / `.SH` 后缀;
4. 输出为**结构化 Markdown**,层次分明;
5. 不确定的推断标注"待验证";
6. 每次分析前先说明数据来源(检索了什么、命中多少条)。
---
## 示例对话
**用户**:分析宁德时代(300750)近 30 天的利好利空变化,并给出主要投资逻辑演化过程。
**Agent**:
1. 调用 `search_stock_events("宁德时代", stock_code="300750")` 获取 20 条事件
2. 调用 `search_sentiment_trend("宁德时代", sentiment="all")` 获取情绪分布
3. 调用 `search_company_news("宁德时代", company="宁德时代")` 获取补充新闻
4. 综合以上数据,按公司分析模板输出报告:
> # 宁德时代(300750)近 30 天深度分析
> *数据来源:知识库检索命中 18 条事件*
> ...
+144
View File
@@ -0,0 +1,144 @@
# 项目优化计划
版本:v1.0 | 创建:2026-06-17
---
## 优化总览
| 序号 | 优化项 | 工作量 | 收益 | 状态 |
| --- | --- | --- | --- | --- |
| 1 | 统一 CLI `a-share` | 1.5h | 🔴 每天用 | ✅ 已完成 |
| 2 | CLI 直接检索 `a-share search` | 0.5h | 🔴 高频 | ✅ 已完成 |
| 3 | 站点发现 `a-share discover` | 1h | 🟡 加源时用 | ✅ 已完成 |
| 4 | Prompt 调优工具 | 1h | 🟡 调参时用 | 📋 计划中 |
| 5 | 每日摘要报告 | 0.5h | 🟢 锦上添花 | ✅ 已完成 |
| 6 | 健康检查 `a-share health` | 0.5h | 🟢 运维用 | 📋 计划中 |
| 7 | 数据清理策略 | 0.5h | 🟢 长期必要 | 📋 计划中 |
| 8 | 备份恢复 | 1h | 🟢 生产必须 | 📋 计划中 |
---
## 1+2. 统一 CLI `a-share` + 直接检索 ✅
### 目标
用一个命令替代 8 个脚本入口,降低日常使用记忆成本;支持直接在终端检索知识库。
### 设计
```bash
# 模块操作(替代 scripts/run_*.py)
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 embed --date 20260616 # M5 向量化
uv run a-share ingest --date 20260616 # M6 入库
# 全链路
uv run a-share pipeline --once # 立即执行一次
uv run a-share pipeline # 启动定时守护
# 检索(新增,不走 MCP)
uv run a-share search "宁德时代" # 纯语义
uv run a-share search "宁德时代" --source cls --top 5 # 过滤
uv run a-share search "芯片" --sentiment positive --min-importance 3
# 运维
uv run a-share status # 查看各层数据量 + 今天摘要
```
### 实现文件
- `a_share_cli/__init__.py` — 空
- `a_share_cli/main.py` — argparse 子命令路由
- `pyproject.toml``[project.scripts] a-share = "a_share_cli.main:main"`
### 子命令详情
#### `a-share crawl`
| 参数 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `--source` | str | None | 单源 id |
| `--no-save` | flag | False | 试跑不写文件 |
#### `a-share extract`
| 参数 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `--date` | str | today | YYYYMMDD |
| `--source` | str | None | 单源 |
#### `a-share search`
| 参数 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `query` | positional | — | 自然语言查询 |
| `--top` | int | 10 | 返回条数 |
| `--source` | str | None | 按源过滤 |
| `--sentiment` | str | None | positive/negative/neutral |
| `--min-importance` | int | None | 最低影响程度 |
| `--stock` | str | None | 股票代码 |
| `--industry` | str | None | 行业 |
#### `a-share status`
无参数,输出各数据层统计 + 今天事件摘要。
---
## 3. 站点发现 `a-share discover` 📋
```
uv run a-share discover https://wallstreetcn.com/news/global
```
内部:抓首页 → 提取 `<a href>` → 按 URL 模式聚类 → 输出 yaml 配置建议。
---
## 4. Prompt 调优工具 📋
```
uv run a-share tune-prompt --sample 5 # 样本抽取,展示结果
uv run a-share tune-prompt --diff # 对比新旧 prompt
```
---
## 5. 每日摘要报告 📋
M7 pipeline 末尾追加一步:生成 `data/reports/{YYYYMMDD}.md`,含各层统计 + 高重要性事件列表。
---
## 6. 健康检查 📋
```
uv run a-share health
```
检查:Qdrant 连通性 / LLM API 可达 / 各层数据完整性 / 磁盘空间。
---
## 7. 数据清理策略 📋
`.env` 新增:
```
DATA_RETENTION_DAYS=90
```
M7 pipeline 中加 cleanup 步骤:删除超过保留期的 raw/processed 目录。
---
## 8. 备份恢复 📋
```
uv run a-share backup # → data/backups/20260617/
uv run a-share restore 20260617 # 从快照恢复
```
+254
View File
@@ -0,0 +1,254 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>A 股 Deep Research 用户手册 v1.1</title>
<style>
:root { --bg: #f8f9fa; --card: #fff; --text: #212529; --muted: #6c757d;
--accent: #2563eb; --border: #dee2e6; --code-bg: #f1f3f5; --radius: 8px; }
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; background: var(--bg); color: var(--text); line-height: 1.75; padding-bottom: 4rem; }
.container { max-width: 900px; margin: 0 auto; padding: 1.5rem; }
header { background: linear-gradient(135deg, #1e293b, #334155); color: #fff; padding: 3rem 0 2rem; text-align: center; }
header h1 { font-size: 2rem; }
header p { color: #94a3b8; margin-top: .5rem; }
nav { background: var(--card); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10; padding: .75rem 0; }
nav a { color: var(--accent); text-decoration: none; margin-right: 1rem; font-size: .9rem; }
h2 { font-size: 1.5rem; margin: 2.5rem 0 1rem; padding-bottom: .5rem; border-bottom: 2px solid var(--accent); }
h3 { font-size: 1.2rem; margin: 1.8rem 0 .8rem; color: #1e293b; }
h4 { font-size: 1rem; margin: 1.2rem 0 .5rem; }
p { margin: .6rem 0; }
a { color: var(--accent); }
code { background: var(--code-bg); padding: .15em .4em; border-radius: 4px; font-family: "SF Mono", "Fira Code", monospace; font-size: .88em; }
pre { background: #1e293b; color: #e2e8f0; padding: 1rem 1.2rem; border-radius: var(--radius); overflow-x: auto; font-size: .85em; line-height: 1.6; margin: .8rem 0; }
pre code { background: none; padding: 0; color: inherit; }
table { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: .93em; }
th, td { border: 1px solid var(--border); padding: .5rem .7rem; text-align: left; }
th { background: #f1f5f9; font-weight: 600; }
tr:nth-child(even) { background: #fafbfc; }
ul, ol { padding-left: 1.5rem; margin: .6rem 0; }
li { margin: .25rem 0; }
.card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.5rem; margin: 1rem 0; }
.badge-ok { background: #d1fae5; color: #065f46; display: inline-block; padding: .1em .5em; border-radius: 10px; font-size: .8em; font-weight: 600; }
.badge-warn { background: #fef3c7; color: #92400e; display: inline-block; padding: .1em .5em; border-radius: 10px; font-size: .8em; }
.badge-err { background: #fee2e2; color: #991b1b; display: inline-block; padding: .1em .5em; border-radius: 10px; font-size: .8em; }
footer { text-align: center; color: var(--muted); font-size: .85em; margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--border); }
@media (max-width: 640px) { .container { padding: .8rem; } header h1 { font-size: 1.5rem; } }
</style>
</head>
<body>
<header><div class="container"><h1>A 股 Deep Research 私有投研平台</h1><p>用户手册 v1.1 · 2026-06-17</p></div></header>
<nav><div class="container">
<a href="#概述">概述</a> <a href="#快速开始">快速开始</a> <a href="#cli">CLI 速查</a>
<a href="#全链路">全链路</a> <a href="#站点管理">站点管理</a> <a href="#检索">检索</a>
<a href="#定时任务">定时任务</a> <a href="#mcp">MCP</a> <a href="#投研">投研</a> <a href="#附录">附录</a>
</div></nav>
<main class="container">
<h2 id="概述">1. 项目概述</h2>
<p><strong>A 股 Deep Research</strong> 是私有化投研平台,自动完成财经新闻抓取 → 正文提取 → 事件抽取 → 向量化 → 语义检索,通过 MCP 协议接入 Cherry Studio / Claude Code,实现「自然语言提问 → 结构化研究报告」。</p>
<p><strong>技术栈</strong>: Python 3.11 · Crawl4AI · GNE · DeepSeek/Qwen · DashScope · Qdrant · APScheduler · MCP</p>
<p><strong>部署</strong>: 树莓派 5 (ARM64) <code>/home/pi/news/</code></p>
<h2 id="快速开始">2. 快速开始</h2>
<h3>2.1 安装</h3>
<pre><code>cd /home/pi/news
uv sync
cp .env.example .env
# 编辑 .env, 填入 DEEPSEEK_API_KEY 和 DASHSCOPE_API_KEY
uv run python -m playwright install chromium # 仅 M1 抓取需要</code></pre>
<h3>2.2 验证</h3>
<pre><code>uv run pytest -m "not integration" # 应显示 186 passed</code></pre>
<h2 id="cli">3. 统一 CLI 速查</h2>
<p>所有操作通过 <code>a-share</code> 命令完成。</p>
<table>
<tr><th>命令</th><th>功能</th><th>常用参数</th></tr>
<tr><td><code>a-share crawl</code></td><td>M1 抓取新闻</td><td><code>--source cls</code></td></tr>
<tr><td><code>a-share extract</code></td><td>M2 正文提取</td><td><code>--date 20260616</code></td></tr>
<tr><td><code>a-share dedup</code></td><td>M3 三层去重</td><td><code>--date 20260616 --reset</code></td></tr>
<tr><td><code>a-share events</code></td><td>M4 LLM 事件抽取</td><td><code>--provider qwen --limit 5</code></td></tr>
<tr><td><code>a-share embed</code></td><td>M5 向量化</td><td><code>--model text-embedding-v3</code></td></tr>
<tr><td><code>a-share ingest</code></td><td>M6 Qdrant 入库</td><td><code>--date 20260616 --recreate</code></td></tr>
<tr><td><code>a-share pipeline</code></td><td>全链路 (M1→M6)</td><td><code>--once --report</code></td></tr>
<tr><td><code>a-share discover</code></td><td>分析站点, 生成配置</td><td><code>--name 中文名 --add --extra URL</code></td></tr>
<tr><td><code>a-share add-entry</code></td><td>追加入口到已有源</td><td><code>source_id url1 url2 ...</code></td></tr>
<tr><td><code>a-share report</code></td><td>生成/上传日报</td><td><code>--date 20260616 --no-upload</code></td></tr>
<tr><td><code>a-share search</code></td><td>终端检索知识库</td><td><code>--source --stock --sentiment --top</code></td></tr>
<tr><td><code>a-share status</code></td><td>数据总览</td><td>无参数</td></tr>
</table>
<h2 id="全链路">4. 全链路一键运行</h2>
<pre><code>uv run a-share pipeline --once # 一次跑通 M1→M6
uv run a-share pipeline --once --report # 末尾自动生成日报并上传</code></pre>
<p>耗时约 4-5 分钟 (100 篇): crawler ~3min / extractor ~24s / dedup ~2s / llm ~45s / embedding ~10s / qdrant ~4s</p>
<p>只执行部分步骤: <code>uv run a-share pipeline --once --steps crawler,llm</code></p>
<p><strong>产物路径</strong>: <code>data/raw/</code> (M1) → <code>data/processed/</code> (M2) → <code>data/deduped/</code> (M3) → <code>data/events/</code> (M4) → <code>data/embeddings/</code> (M5) → <code>data/qdrant_storage/</code> (M6)</p>
<h2 id="站点管理">5. 新闻源管理</h2>
<h3>5.1 查看已有源</h3>
<p>配置文件: <code>configs/sources.yaml</code>。内置财联社 / 东方财富 / 新浪财经 / 证券时报 / 第一财经。</p>
<h3>5.2 新增站点 (一条命令)</h3>
<pre><code># 自动分析 + 写入 sources.yaml
uv run a-share discover https://example.com/news/ --name 中文名 --add
# 仅预览
uv run a-share discover https://example.com/news/</code></pre>
<p>自动完成: JS 抓取 → 链接提取 → 导航过滤 → 模式聚类 → 正则生成 → yaml 输出。写入时自动空行分隔、序号递增、<code># ---- N. 名称 ----</code> 注释头。</p>
<h3>5.3 同站多频道</h3>
<pre><code># 一次性分析多入口
uv run a-share discover https://example.com/news/global \
--extra https://example.com/news/china \
--extra https://example.com/news/tech \
--name 中文名 --add</code></pre>
<p>生成的 yaml 自动包含 <code>extra_homepages</code> 字段,抓取时依次访问所有入口,自动去重。</p>
<h3>5.4 为已有源追加入口</h3>
<pre><code>uv run a-share add-entry wallstreetcn https://xxx.com/news/新频道</code></pre>
<p>自动去重 — 已存在的 URL 跳过。</p>
<h3>5.5 验证</h3>
<pre><code>uv run a-share crawl --source wallstreetcn</code></pre>
<h2 id="cninfo">6. cninfo 公告抓取</h2>
<p>cninfo(巨潮资讯网)是独立的 A 股公告抓取管道,与新闻抓取分开调度(每天 08:00)。</p>
<pre><code>uv run a-share pipeline --cninfo-once # watchlist 全链路 (公告+调研+IRM)
uv run a-share cninfo --watchlist # 仅公告(关注公司)
uv run a-share cninfo --research # 仅调研
uv run a-share cninfo --irm # 仅互动问答
uv run a-share stock-report # 个股日报</code></pre>
<p>全链路 9 步: 公告→调研→IRM→提取→PDF→去重→LLM→向量→Qdrant。</p>
<h4>公告关注列表</h4>
<pre><code>uv run a-share watchlist add 300750 宁德时代 --note "动力电池龙头"
uv run a-share watchlist list
uv run a-share watchlist remove 000001
uv run a-share cninfo --watchlist # 只抓关注公司</code></pre>
<p>关注公司公告在日报中置顶并 ⭐ 高亮。配置: <code>configs/watchlist.yaml</code></p>
<p><strong>配置</strong>: <code>CNINFO_ENABLED</code> <code>CNINFO_DAYS_BACK</code> <code>CNINFO_MAX_PAGES</code> <code>CNINFO_API_BASE</code> <code>CNINFO_PDF_BASE</code></p>
<div class="card"><strong>⚠ .env 注意</strong>: 值后面不能跟 <code>#</code> 注释。python-dotenv 会把 <code>#</code> 后内容当成值。注释必须独占一行。</div>
<h2 id="检索">7. 知识库检索</h2>
<h3>6.1 终端直接检索</h3>
<pre><code>uv run a-share search "宁德时代固态电池" --top 5
uv run a-share search "政策" --source cls --sentiment positive
uv run a-share search "风险" --stock 001212 --min-importance 3</code></pre>
<h3>6.2 Python 检索 (带过滤)</h3>
<pre><code>from vectorstore import VectorStore, SearchFilter, make_qdrant_client
c = make_qdrant_client()
s = VectorStore(c)
hits = s.query(vector=probe["vector"], top_k=10,
filter=SearchFilter(source_id="cls", importance_min=3, sentiment="positive"))
for h in hits: print(h.short_summary())
s.close()</code></pre>
<p>支持 8 种过滤: source_id / stock_codes / company_names / industries / sentiment / importance_min / event_types / date_range。</p>
<h3>6.3 日报</h3>
<pre><code>uv run a-share report # 生成今日日报并上传
uv run a-share report --date 20260616 # 指定日期
uv run a-share report --no-upload # 仅生成不上传</code></pre>
<p>HTML 日报含: AI 摘要 (LLM 生成 400-500 字要点) + 数据总览 + 情绪分布 + 事件类型 + 高重要度事件列表。</p>
<h2 id="定时任务">8. 定时任务部署</h2>
<h3>7.1 安装 systemd 服务 (一次性)</h3>
<pre><code>sudo cp scripts/a-share-research.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable a-share-research</code></pre>
<h3>7.2 日常操作</h3>
<table>
<tr><td>启动</td><td><code>sudo systemctl start a-share-research</code></td></tr>
<tr><td>停止</td><td><code>sudo systemctl stop a-share-research</code></td></tr>
<tr><td>状态</td><td><code>sudo systemctl status a-share-research</code></td></tr>
<tr><td>实时日志</td><td><code>journalctl -u a-share-research -f</code></td></tr>
</table>
<p>默认执行时间: 每天 07:00 / 12:00 / 18:00 / 22:00。7:00 的日报自动覆盖过去 24 小时新闻。</p>
<h4>修改定时配置</h4>
<pre><code># 1. 编辑 .env 中的 SCHEDULE_TIMES
nano /home/pi/news/.env
# 格式: HH:MM,HH:MM,... (24 小时制, 逗号分隔)
# 例: SCHEDULE_TIMES=08:00,14:00,20:00
# 2. 重启服务
sudo systemctl restart a-share-research
# 3. 验证
grep "已注册定时任务" /home/pi/news/logs/scheduler.log | tail -4</code></pre>
<h2 id="mcp">8. MCP 服务与 AI Agent</h2>
<h3>8.1 连接 Cherry Studio</h3>
<p>设置 → MCP 服务器 → 添加:</p>
<pre><code>{"mcpServers":{"a-share-research":{"command":"uv","args":["run","python","-m","scripts.run_mcp_server"],"cwd":"/home/pi/news"}}}</code></pre>
<h3>8.2 5 个 MCP 工具</h3>
<table>
<tr><th>工具</th><th>功能</th><th>示例</th></tr>
<tr><td><code>search_news</code></td><td>通用语义检索</td><td><code>search_news("宁德时代固态电池")</code></td></tr>
<tr><td><code>search_company_news</code></td><td>按公司检索</td><td><code>search_company_news("价格", company="茅台")</code></td></tr>
<tr><td><code>search_industry_news</code></td><td>按行业检索</td><td><code>search_industry_news("政策", industry="半导体")</code></td></tr>
<tr><td><code>search_stock_events</code></td><td>按股票代码检索</td><td><code>search_stock_events("事件", stock_code="300750")</code></td></tr>
<tr><td><code>search_sentiment_trend</code></td><td>情绪检索+统计</td><td><code>search_sentiment_trend("AI算力", sentiment="all")</code></td></tr>
</table>
<h2 id="投研">9. 投研分析模板</h2>
<p>在 Cherry Studio 中粘贴 <code>docs/agent_prompt.md</code> 作为 System Prompt:</p>
<div class="card"><strong>公司分析</strong>: 分析宁德时代 (300750) 近 30 天的利好利空变化</div>
<div class="card"><strong>行业分析</strong>: 分析动力电池行业最近的政策动向和竞争格局</div>
<div class="card"><strong>风险排查</strong>: 梳理中旗新材 (001212) 近期面临的主要风险</div>
<div class="card"><strong>事件追踪</strong>: 过去一周 A 股有哪些重大监管处罚事件?</div>
<div class="card"><strong>情绪趋势</strong>: 近一个月 AI 算力概念的情绪变化趋势如何?</div>
<h2 id="附录">10. 附录</h2>
<h3>A. Qdrant 部署模式</h3>
<table>
<tr><th></th><th>本地文件模式 (默认)</th><th>Docker Server 模式</th></tr>
<tr><td>类比</td><td>SQLite</td><td>PostgreSQL</td></tr>
<tr><td>进程</td><td>无独立进程</td><td>容器 a_share_qdrant</td></tr>
<tr><td>ARM64</td><td><span class="badge-ok"></span></td><td><span class="badge-err"></span> jemalloc 16K 页崩溃</td></tr>
<tr><td>适用</td><td>树莓派 / 单机</td><td>x86 生产 / 远程</td></tr>
</table>
<h3>B. 环境变量</h3>
<table>
<tr><td><code>LLM_PROVIDER</code></td><td><code>deepseek</code></td><td>M4 LLM</td></tr>
<tr><td><code>DEEPSEEK_API_KEY</code></td><td></td><td>DeepSeek Key</td></tr>
<tr><td><code>DASHSCOPE_API_KEY</code></td><td></td><td>百炼 Key (M5/M8)</td></tr>
<tr><td><code>EMBEDDING_PROVIDER</code></td><td><code>dashscope</code></td><td>M5 嵌入</td></tr>
<tr><td><code>SCHEDULE_TIMES</code></td><td><code>07:00,12:00,18:00,22:00</code></td><td>M7 定时</td></tr>
</table>
<h3>C. 日志文件</h3>
<p><code>logs/crawler.log</code> · <code>logs/extractor.log</code> · <code>logs/dedup.log</code> · <code>logs/llm.log</code> · <code>logs/embedding.log</code> · <code>logs/qdrant.log</code> · <code>logs/scheduler.log</code> · <code>logs/mcp_server.log</code> (自动轮转 10MB×5)</p>
<h3>D. 常见问题</h3>
<div class="card"><h4>Q: 某源抓取 0 篇?</h4><p>A: 编辑 <code>configs/sources.yaml</code>,检查 <code>article_url_pattern</code> 正则。或重新 <code>discover</code> 分析。</p></div>
<div class="card"><h4>Q: LLM 报错?</h4><p>A: 检查 <code>.env</code><code>DEEPSEEK_API_KEY</code>。可切换 <code>--provider qwen</code> 测试。</p></div>
<div class="card"><h4>Q: Qdrant 搜索无结果?</h4><p>A: <code>uv run a-share status</code> 检查数据量。若 Qdrant 为 0,执行 <code>uv run a-share ingest --date &lt;date&gt; --recreate</code></p></div>
<div class="card"><h4>Q: 从零重建知识库?</h4><pre><code>rm -rf data/raw data/processed data/dedup data/deduped data/events data/embeddings data/qdrant_storage
uv run a-share pipeline --once</code></pre></div>
</main>
<footer><div class="container"><p>A 股 Deep Research 私有投研平台 · 用户手册 v1.1 · 2026-06-17</p></div></footer>
</body>
</html>
+712
View File
@@ -0,0 +1,712 @@
# A 股 Deep Research 私有投研平台 — 用户手册
版本:v1.0 | 最后更新:2026-06-17
---
## 目录
1. [项目概述](#1-项目概述)
2. [环境准备](#2-环境准备)
3. [全链路一键运行](#3-全链路一键运行)
4. [分模块使用](#4-分模块使用)
- [M1 新闻抓取](#m1-新闻抓取)
- [M2 正文提取](#m2-正文提取)
- [M3 三层去重](#m3-三层去重)
- [M4 投资事件抽取](#m4-投资事件抽取)
- [M5 向量化](#m5-向量化)
- [M6 Qdrant 入库与检索](#m6-qdrant-入库与检索)
5. [定时任务部署](#5-定时任务部署)
6. [MCP 服务与 AI Agent](#6-mcp-服务与-ai-agent)
7. [投研分析模板](#7-投研分析模板)
8. [附录](#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 获取代码
```bash
git clone <repo> /home/pi/news
cd /home/pi/news
```
### 2.3 安装依赖
```bash
uv sync
```
### 2.4 配置环境变量
```bash
cp .env.example .env
# 编辑 .env,填写:
# DEEPSEEK_API_KEY=sk-xxx (M4 LLM 调用)
# DASHSCOPE_API_KEY=sk-xxx (M5/M8 嵌入调用)
```
### 2.5 验证安装
```bash
uv run pytest -m "not integration" # 应显示 186 passed
```
### 2.6 统一 CLI `a-share`
所有操作通过 `a-share` 命令完成,替代之前的 8 个脚本入口:
```bash
# 模块操作
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**:
```bash
uv run a-share pipeline --once
```
(等效于旧版 `uv run python -m scripts.run_scheduler --once`,`a-share` 命令更短更好记)
```bash
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)
只执行部分步骤:
```bash
uv run python -m scripts.run_scheduler --once --steps crawler,llm,qdrant
```
---
## 4. 分模块使用
### M1 新闻抓取
从 5 个 A 股财经源抓取文章。
```bash
# 抓取全部启用源
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 | 第一财经 | 是 | 新闻频道 |
**新增新闻源**:一条命令完成分析+配置:
```bash
# 自动分析 + 自动写入 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. 名称 ----` 注释头。
**验证新源**:
```bash
uv run a-share crawl --source wallstreetcn
```
**同一网站多个入口**:使用 `--extra` 添加额外频道,共用同一个正则:
```bash
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):
```bash
uv run a-share add-entry wallstreetcn https://xxx.com/news/新频道
```
自动去重:已存在的 URL 跳过。
### M2 正文提取
从 M1 抓取的 HTML 中提取标准化 Article(title/content/时间/作者)。
```bash
# 处理今日所有源
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):
```json
{
"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。
```bash
# 处理今日
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 从去重文章中抽取结构化投资事件。
```bash
# 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):
```json
{
"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 向量化。
```bash
# 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 模式)**:
```bash
uv run a-share pipeline --cninfo-once
```
包含 9 步: 公告抓取(关注公司) → 调研筛选 → 互动问答 → 正文提取 → PDF 富化 → 去重 → LLM 抽取 → 向量化 → Qdrant 入库。
**单独操作**:
```bash
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 中每家公司生成综合日报:
```bash
uv run a-share stock-report
```
报告内容: AI 要点分析 / 近 7 日公告 / 相关新闻 / 互动问答。
上传路径: `echart/research/{YYYYMMDD}/{代码}_{名称}_个股日报_{日期}.html`
### 公告关注列表
只关注特定公司公告,日报中高亮显示:
```bash
# 管理关注列表
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 本地知识库。
**入库**:
```bash
# 默认本地文件模式(无需 Docker)
uv run python -m scripts.run_qdrant_ingest --date 20260616
# 重建 collection
uv run python -m scripts.run_qdrant_ingest --recreate
```
**Python 检索**:
```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](#a-qdrant-部署模式)。
---
## 5. 定时任务部署
将 M1→M6 全链路注册为系统服务,每天 7:00 / 12:00 / 18:00 / 22:00 自动执行。
### 安装(一次性)
```bash
sudo cp scripts/a-share-research.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable a-share-research
```
### 日常操作
```bash
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 # 文件日志
```
修改执行时间和配置:
```bash
# 编辑 .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. 填入:
```json
{
"mcpServers": {
"a-share-research": {
"command": "uv",
"args": ["run", "python", "-m", "scripts.run_mcp_server"],
"cwd": "/home/pi/news"
}
}
}
```
3. 点击启用。Cherry Studio 会自动启动 MCP 服务进程(stdio 模式)。
### 6.2 连接 Claude Code
编辑项目根目录 `.mcp.json`:
```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 模式)
```bash
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 报错?**
检查 `.env``DEEPSEEK_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:如何从零重建整个知识库?**
```bash
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
```
---
—— 用户手册结束 ——