docs: 文档清理与重构 — 统一为 3 个核心文档

- 删除 5 个过时/残留文档(project_plan/agent_prompt/optimization_plan/report_db_design/deploy/README)
- 新建 docs/architecture.md(项目架构:11 包职责+数据模型+配置+产物)
- 重写 docs/user-guide.md(CLI 全量+增量/断点续跑+MCP+FAQ)
- 重写 README.md(精简入口+文档索引)
- 更新 continuation.md(追加本次记录)
- 更新 .gitignore(排除 data/* 运行产物)
This commit is contained in:
2026-08-22 17:10:39 +08:00
commit 65ead54b4f
101 changed files with 21093 additions and 0 deletions
+636
View File
@@ -0,0 +1,636 @@
# 项目架构文档
> 版本:v2.0 | 最后更新:2026-08-22
---
## 目录
1. [整体架构](#1-整体架构)
2. [数据流全景](#2-数据流全景)
3. [包职责与导出 API](#3-包职责与导出-api)
4. [核心数据模型](#4-核心数据模型)
5. [配置体系](#5-配置体系)
6. [产物目录结构](#6-产物目录结构)
7. [运行环境](#7-运行环境)
---
## 1. 整体架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 统一 CLI (a_share_cli) │
│ crawl | extract | dedup | events | embed | ingest | pipeline │
│ search | status | report | cninfo | watchlist | discover │
└──────────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────┼──────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌────────┐ ┌──────────┐ ┌──────────┐
│ crawler │──▶│extractor │──▶│ dedup │──▶│ llm │──▶│embedding │
│ (M1) │ │ (M2) │ │ (M3) │ │ (M4) │ │ (M5) │
└─────────┘ └──────────┘ └────────┘ └──────────┘ └─────┬────┘
│
▼
┌──────────┐
│vectorstore│
│ (M6) │
└─────┬────┘
│
┌──────────────────────────────────────┤
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ mcp_server│ │scheduler │
│ (M8) │ │ (M7) │
└──────────┘ └─────┬────┘
│
┌─────────────────┤
▼ ▼
┌──────────┐ ┌──────────────┐
│ reporter │ │stock_reporter │
│ (日报) │ │ (个股日报) │
└─────┬────┘ └──────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│report_db │ │report_ │ │ MySQL │
│ (M10) │ │ import │ │(myquant) │
└──────────┘ └──────────┘ └──────────┘
```
**11 个包**(按管道顺序):
| 序号 | 包 | 模块 | 概述 |
|------|-----|------|------|
| 1 | `crawler/` | M1 | 新闻抓取:httpx 静态直连 + Playwright JS 渲染 + cninfo 公告 API |
| 2 | `extractor/` | M2 | GNE 中文新闻正文提取,输出 Article |
| 3 | `dedup/` | M3 | 三层去重:URL Hash → Content Hash → SimHash |
| 4 | `llm/` | M4 | DeepSeek/Qwen 投资事件抽取,输出 ExtractedEvent |
| 5 | `embedding/` | M5 | DashScope 远程 / 本地 BGE-M3 向量化 |
| 6 | `vectorstore/` | M6 | Qdrant 本地文件模式,语义检索 |
| 7 | `scheduler/` | M7 | APScheduler 定时任务 + pipeline 编排 + 日报生成 |
| 8 | `mcp_server/` | M8 | MCP 5 工具,供 Cherry Studio/Claude Code 调用 |
| 9 | `a_share_cli/` | CLI | 统一命令行入口(argparse),所有子命令 |
| 10 | `report_db/` | M10 | 日报 MySQL 结构化入库(pymysql) |
| 11 | `report_import/` | M10 | 历史日报 HTML 解析与批量导入 |
辅助目录:
| 目录 | 用途 |
|------|------|
| `configs/` | sources.yaml(新闻源)、watchlist.yaml(关注列表)、llm_models.yaml(LLM 场景配置)、loader.py |
| `prompts/` | 4 个 LLM Prompt 模板(event_extraction/company_analysis/industry_analysis/risk_analysis) |
| `scripts/` | 独立运行脚本(每个模块一个入口) + systemd 服务文件 |
| `tests/` | pytest 测试(225+ passed) |
| `api/` | 占位(空包,预留给未来 API 服务) |
| `app/` | 空目录(预留) |
---
## 2. 数据流全景
```
财经网站 / cninfo API
│
▼
┌───────────┐
│ M1 抓取 │ → data/raw/{source}/{YYYYMMDD}/*.html + index.jsonl
└─────┬─────┘
│
▼
┌───────────┐
│ M2 提取 │ → data/processed/{source}/{YYYYMMDD}/{url_hash}.json (Article)
└─────┬─────┘
│
▼
┌───────────┐
│ M3 去重 │ → data/deduped/{YYYYMMDD}/uniques/{url_hash}.json
└─────┬─────┘ data/dedup/fingerprints.sqlite3 (指纹库)
│
▼
┌───────────┐
│ M4 LLM │ → data/events/{YYYYMMDD}/{url_hash}.json (ExtractedEvent)
└─────┬─────┘
│
▼
┌───────────┐
│ M5 向量化 │ → data/embeddings/{YYYYMMDD}/{url_hash}.json (1024 维向量)
└─────┬─────┘
│
▼
┌───────────┐
│ M6 入库 │ → data/qdrant_storage/ (Qdrant 本地文件)
└─────┬─────┘
│
┌────┴────┐
▼ ▼
┌──────┐ ┌──────┐
│ MCP │ │ 日报 │
│ 检索 │ │ 生成 │
└──────┘ └──┬───┘
│
▼
┌────────┐
│ MySQL │ → news_report / news_event (myquant 库)
└────────┘
```
**关键设计**:
- **增量处理**:M2/M4/M5 产物存在即跳过(2026-08-12 新增),`--force` 全量重建
- **断点续跑**:`pipeline --once --resume` 从上次失败步骤继续,状态文件 `data/pipeline/state.json`
- **去重多源记录**:M3 指纹库 `source_ids` 列记录同一篇新闻的多个来源
---
## 3. 包职责与导出 API
### 3.1 `crawler/` — M1 新闻抓取
| 文件 | 职责 |
|------|------|
| `engine.py` | Crawl4AI 异步引擎:httpx 静态直连(js_render=false) / Playwright(js_render=true) |
| `config.py` | 加载 `configs/sources.yaml` |
| `models.py` | SourceConfig / CrawlerConfig / CrawlResult / ArticleLink / CninfoItem / CrawlStage |
| `storage.py` | 保存 raw HTML + index.jsonl,URL 去重 |
| `cninfo.py` | cninfo 公告/调研/互动易 API 抓取 |
**公开 API**:
```python
from crawler import (
crawl_all, crawl_source, extract_article_links,
load_crawler_config,
SourceConfig, CrawlerConfig, CrawlResult, CrawlStage, ArticleLink, CninfoItem,
)
```
**抓取分流规则**:
| 条件 | 引擎 | 特点 |
|------|------|------|
| `js_render=False` 且无 `wait_for` | httpx 直连 | 快,不触发反爬 |
| `js_render=True` 或有 `wait_for` | Playwright | 支持 JS 渲染 |
**新闻源**:14 个(13 Web + 1 API:新闻联播),见 `configs/sources.yaml`
### 3.2 `extractor/` — M2 正文提取
| 文件 | 职责 |
|------|------|
| `parser.py` | GNE 中文正文提取,模板文本过滤 |
| `models.py` | Article / ExtractError |
**公开 API**:
```python
from extractor import (
extract_article, Article, ExtractError, MIN_CONTENT_LENGTH, SOURCE_NAME_MAP,
)
```
**Article 模型**(M2 输出,M3+ 输入的唯一格式):
| 字段 | 类型 | 说明 |
|------|------|------|
| `source_id` | str | M1 源 ID |
| `url` | str | 文章 URL |
| `url_hash` | str | SHA1 前 16 位,主键 |
| `title` | str | 标题 |
| `content` | str | 清理后纯文本正文 |
| `author` | str\|None | 作者 |
| `publish_time` | datetime\|None | 标准化发布时间 |
| `word_count` | int | 中文字符数 |
| `item_type` | str\|None | cninfo 类型:announcement/research/irm |
### 3.3 `dedup/` — M3 三层去重
| 文件 | 职责 |
|------|------|
| `deduper.py` | Deduper 主类:check / ingest / stats |
| `hasher.py` | SimHash 64 位 + 汉明距离 + content_hash |
| `store.py` | FingerprintStore,SQLite 持久化 |
| `models.py` | DedupLayer / DedupResult / DedupStats / Fingerprint |
**公开 API**:
```python
from dedup import (
Deduper, article_to_fingerprint, FingerprintStore,
DedupLayer, DedupResult, DedupStats, Fingerprint,
simhash64, hamming, content_hash, normalize_content,
DEFAULT_HAMMING_THRESHOLD, DEFAULT_TIME_WINDOW_DAYS,
)
```
**三层去重逻辑**:
| 层 | 算法 | 命中条件 |
|----|------|----------|
| L1 URL | URL Hash (SHA1) | 完全相同 URL |
| L2 Content | 标准化正文 SHA1 | 正文完全一致 |
| L3 SimHash | 64 位 SimHash + 汉明距离 | 距离 ≤ 3 (默认) |
### 3.4 `llm/` — M4 投资事件抽取
| 文件 | 职责 |
|------|------|
| `client.py` | OpenAI 兼容客户端,指数退避重试,LLMConfig 加载 |
| `extractor.py` | Prompt 模板加载,JSON 解析,extract_event / extract_event_async |
| `models.py` | EventExtraction / ExtractedEvent / Sentiment / LLMCallError / EVENT_TYPES |
**公开 API**:
```python
from llm import (
extract_event, extract_event_async, parse_event_json,
PromptTemplate, load_llm_config, LLMConfig,
make_sync_client, make_async_client,
EventExtraction, ExtractedEvent, Sentiment, LLMCallError,
EVENT_TYPES, MAX_CONTENT_CHARS, MAX_IMPORTANCE, MIN_IMPORTANCE,
SCENE_EVENT_EXTRACTION, SCENE_DAILY_REPORT, SCENE_STOCK_REPORT,
)
```
**ExtractedEvent 模型**(M4 落盘格式):
| 字段 | 类型 | 说明 |
|------|------|------|
| `source_id` | str | 主源 |
| `url` | str | 原文 URL |
| `url_hash` | str | SHA1 前 16 位 |
| `title` | str | 标题 |
| `publish_time` | datetime\|None | 发布时间 |
| `sources` | list[str] | 全部来源(去重合并) |
| `event` | EventExtraction | LLM 抽取结果 |
| `provider` | str | deepseek / qwen |
| `model` | str | 模型名 |
| `attempts` | int | LLM 实际调用次数(含重试) |
**EventExtraction**(LLM 输出 JSON 结构):
| 字段 | 类型 | 说明 |
|------|------|------|
| `stock_codes` | list[str] | 6 位代码,可带 .SH/.SZ/.BJ |
| `company_names` | list[str] | 公司中文简称 |
| `industries` | list[str] | 行业(申万二级) |
| `sentiment` | Sentiment | positive/neutral/negative |
| `importance` | int | 1-5 重要程度 |
| `event_type` | str | 23 种事件类型之一 |
| `summary` | str | 一句话摘要(≤200 字) |
**23 种事件类型**:业绩预告/业绩快报/财报披露/合作签约/投资并购/重大合同/产品发布/技术突破/监管处罚/诉讼仲裁/股东减持/股东增持/回购/分红/高管变动/资产重组/停牌复牌/ST警示/退市风险/宏观政策/行业政策/国际局势/其他
**LLM 场景配置**(`configs/llm_models.yaml`):
| 场景 | 用途 | 调用方 |
|------|------|--------|
| `event_extraction` | 投资事件抽取(M4) | llm/extractor.py |
| `daily_report` | 日报 AI 摘要 | scheduler/reporter.py |
| `stock_report` | 个股 AI 要点 | scheduler/stock_reporter.py |
| `embedding` | 文本向量化(M5) | embedding/factory.py |
### 3.5 `embedding/` — M5 向量化
| 文件 | 职责 |
|------|------|
| `base.py` | EmbeddingProvider / AsyncEmbeddingProvider ABC + compose_text |
| `remote.py` | DashScope 远程 Embedding(OpenAI 兼容) |
| `local.py` | 本地 BGE-M3(可选,需 `uv sync --extra local-embedding`) |
| `factory.py` | resolve_provider_type / make_sync_provider / make_async_provider |
| `models.py` | EmbeddingProviderType / EmbeddingResult / EmbeddingError |
**公开 API**:
```python
from embedding import (
make_sync_provider, make_async_provider, resolve_provider_type,
compose_text, EmbeddingProvider, AsyncEmbeddingProvider,
EmbeddingResult, EmbeddingError, EmbeddingProviderType,
DashScopeEmbeddingProvider, DashScopeAsyncEmbeddingProvider,
DASHSCOPE_DEFAULT_DIM, DASHSCOPE_DEFAULT_MODEL, DASHSCOPE_BATCH_LIMIT,
MAX_TEXT_CHARS,
)
```
**向量维度**:1024(DashScope `text-embedding-v3` / 本地 BGE-M3)
**文本组装**:`compose_text()` 统一策略——标题 + 正文 + 事件摘要,最大 8000 字符
### 3.6 `vectorstore/` — M6 Qdrant 知识库
| 文件 | 职责 |
|------|------|
| `client.py` | VectorStore 封装(init/upsert/query/count/info) + make_qdrant_client 工厂 |
| `models.py` | SearchFilter / SearchResult / CollectionInfo |
**公开 API**:
```python
from vectorstore import (
VectorStore, make_qdrant_client,
SearchFilter, SearchResult, CollectionInfo,
DEFAULT_COLLECTION, DEFAULT_VECTOR_DIM,
)
```
**Qdrant 模式**:默认本地文件模式(`data/qdrant_storage/`),零依赖,ARM64 兼容
**SearchFilter 支持**:source_id / stock_codes / company_names / industries / sentiment / importance_min / event_types / publish_date_from / publish_date_to
### 3.7 `scheduler/` — M7 定时任务与 Pipeline
| 文件 | 职责 |
|------|------|
| `pipeline.py` | run_pipeline / run_step,编排 M1→M6,断点续跑 |
| `reporter.py` | 日报生成(收集→AI 摘要→MySQL 入库) |
| `stock_reporter.py` | 个股日报生成(关注列表) |
**公开 API**:
```python
from scheduler import (
run_pipeline, run_step, PipelineResult, StepResult,
STEP_COMMANDS, STEP_TIMEOUTS,
)
```
**Pipeline 步骤**:
| 步骤名 | 中文 | 默认超时 | 说明 |
|--------|------|----------|------|
| `crawler` | M1 新闻抓取 | 900s | 13 源,含 Playwright |
| `xwlb` | M1 新闻联播 | 60s | 纯 HTTP API |
| `extractor` | M2 正文提取 | 300s | GNE 提取 |
| `dedup` | M3 新闻去重 | 120s | 三层去重 |
| `llm` | M4 LLM 抽取 | 900s | API 调用 |
| `embedding` | M5 向量化 | 300s | DashScope |
| `qdrant` | M6 Qdrant 入库 | 300s | 本地文件写入 |
| `report` | 日报生成 | 30s | 仅 07:00 执行 |
| `cninfo_crawl` | cninfo 公告抓取 | 900s | 06:30 执行 |
| `cninfo_extract` | cninfo 正文提取 | 300s | — |
| `cninfo_pdf` | cninfo PDF 补充 | — | — |
**超时优先级**:`TIMEOUT_{NAME}` 环境变量 > `PIPELINE_STEP_TIMEOUT` > 硬编码默认值 > 1800s
**调度时间表**:
| 时间 | 步骤 |
|------|------|
| 06:30 | cninfo 公告管道 |
| 07:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant→**日报** |
| 07:30 | 个股日报 |
| 12:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant |
| 18:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant |
| 22:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant |
### 3.8 `mcp_server/` — M8 MCP 服务
| 文件 | 职责 |
|------|------|
| `tools.py` | FastMCP 服务器,5 个 MCP 工具 |
**5 个 MCP 工具**:
| 工具 | 参数 | 说明 |
|------|------|------|
| `search_news` | query, top_k | 通用语义检索 |
| `search_company_news` | query, company, top_k | 按公司过滤 |
| `search_industry_news` | query, industry, top_k | 按行业过滤 |
| `search_stock_events` | query, stock_code, top_k | 按股票代码过滤 |
| `search_sentiment_trend` | query, sentiment, top_k | 情绪趋势 + 统计 |
**启动方式**:stdio 模式(Cherry Studio/Claude Code 自动管理进程)或 SSE 模式(调试:`--sse 8765`)
### 3.9 `a_share_cli/` — 统一 CLI
| 文件 | 职责 |
|------|------|
| `main.py` | argparse 子命令路由,所有命令行操作 |
**全部子命令**:
| 命令 | 功能 | 关键参数 |
|------|------|----------|
| `crawl` | M1 抓取 | `--source`, `--no-save` |
| `extract` | M2 提取 | `--date`, `--source` |
| `dedup` | M3 去重 | `--date`, `--reset` |
| `events` | M4 LLM 抽取 | `--date`, `--provider`, `--model`, `--limit`, `--concurrency` |
| `embed` | M5 向量化 | `--date`, `--provider`, `--model` |
| `ingest` | M6 入库 | `--date`, `--recreate` |
| `pipeline` | 全链路/守护 | `--once`, `--resume`, `--steps`, `--report`, `--cninfo-once` |
| `search` | 检索知识库 | `query`, `--top`, `--source`, `--sentiment`, `--stock`, `--industry`, `--min-importance` |
| `status` | 数据总览 | 无参数 |
| `report` | 生成日报 | `--date`, `--no-upload` |
| `report-import` | 历史日报导入 | `--dir`, `--date`, `--type`, `--force` |
| `cninfo` | 公告抓取 | `--no-save`, `--enrich-pdf`, `--pdf-limit` |
| `stock-report` | 个股日报 | `--no-upload` |
| `watchlist` | 关注列表管理 | `add`/`remove`/`list` |
| `discover` | 站点分析 | `url`, `--name`, `--extra`, `--add` |
| `add-entry` | 追加入口 | `source`, `urls...` |
### 3.10 `report_db/` — M10 日报 DB 层
| 文件 | 职责 |
|------|------|
| `db.py` | 连接/事务/建表/保存/查询 |
| `models.py` | EventRow / ReportData (Pydantic) |
| `schema.py` | DDL(CREATE TABLE) |
**公开 API**:
```python
from report_db import (
connect, init_schema, save_report, load_db_config,
exists_report, fetch_report,
EventRow, ReportData,
)
```
### 3.11 `report_import/` — M10 历史日报导入
| 文件 | 职责 |
|------|------|
| `parser.py` | BeautifulSoup 解析 finance/intl 历史 HTML |
| `importer.py` | 批量导入,幂等,ImportStats 统计 |
**公开 API**:
```python
from report_import import (
import_history, ImportStats,
parse_report, parse_finance_report, parse_intl_report, ReportParseError,
)
```
---
## 4. 核心数据模型
### 4.1 管道数据模型关系
```
CrawlResult (M1) ──→ Article (M2) ──→ DedupResult (M3)
│
▼
ExtractedEvent (M4)
│
▼
EmbeddingResult (M5)
│
▼
SearchResult (M6)
```
### 4.2 日报数据模型
```
ReportData (Pydantic)
├── report_date: date
├── report_type: str (finance | intl)
├── file_name: str
├── generated_at: datetime
├── ai_summary: str | None
├── stats: dict (JSON)
└── events: list[EventRow]
├── section: str (xwlb | news | cninfo | intl)
├── rank: int
├── importance: int | None
├── event_type: str | None
├── title: str
├── summary: str | None
├── sentiment: str | None
└── source: str | None
```
### 4.3 全部 Pydantic 模型清单
| 包 | 模型 | 用途 |
|----|------|------|
| crawler | SourceConfig | 单个新闻源配置 |
| crawler | CrawlerConfig | 全局抓取配置 |
| crawler | CrawlerSettings | 抓取设置(并发/重试/UA) |
| crawler | CrawlResult | 单次抓取结果 |
| crawler | ArticleLink | 列表页发现的链接 |
| crawler | CninfoItem | 公告/调研/互动易条目 |
| extractor | Article | 标准化文章(M2 输出) |
| dedup | Fingerprint | 指纹记录(SQLite) |
| dedup | DedupResult | 判重结果 |
| dedup | DedupStats | 指纹库统计 |
| llm | EventExtraction | LLM 输出 JSON 结构 |
| llm | ExtractedEvent | M4 落盘格式(文章+事件) |
| embedding | EmbeddingResult | 向量化结果 |
| vectorstore | SearchFilter | 检索过滤条件 |
| vectorstore | SearchResult | 单条检索结果 |
| vectorstore | CollectionInfo | Qdrant Collection 信息 |
| scheduler | StepResult | 单步执行结果 |
| scheduler | PipelineResult | 全链路执行结果 |
| report_db | EventRow | 日报事件行 |
| report_db | ReportData | 完整日报数据 |
---
## 5. 配置体系
### 5.1 配置文件
| 文件 | 格式 | 用途 | 热更新 |
|------|------|------|--------|
| `configs/sources.yaml` | YAML | 14 个新闻源配置 | 每次抓取重读 |
| `configs/llm_models.yaml` | YAML | 4 个 LLM 场景配置 | 每次调用重读 |
| `configs/watchlist.yaml` | YAML | cninfo 公告关注列表 | 每次操作重读 |
| `.env` | dotenv | API Key + 调度/超时/DB 配置 | 重启服务生效 |
### 5.2 配置优先级(LLM 场景)
```
CLI 显式参数 (--provider / --model)
↓
configs/llm_models.yaml scenes.<场景>.xxx
↓
.env 环境变量 (LLM_PROVIDER / DEEPSEEK_MODEL 等)
↓
代码内置默认值 (仅超时/温度等参数)
```
> **模型名**无内置兜底:缺失即报错,绝不静默使用错误模型。
### 5.3 关键环境变量
| 变量 | 用途 | 生产值示例 |
|------|------|-----------|
| `DASHSCOPE_API_KEY` | 百炼 LLM + Embedding | sk-xxx |
| `DEEPSEEK_API_KEY` | DeepSeek LLM | sk-xxx |
| `SCHEDULE_TIMES` | 调度时间 | `07:00,12:00,18:00,22:00` |
| `PIPELINE_STEP_TIMEOUT` | 全局超时 | `1800` |
| `NEWS_DB_HOST` | 日报 DB 主机 | `192.168.1.10`(pi5) / `127.0.0.1`(Mac) |
| `NEWS_DB_PORT` | 日报 DB 端口 | `13306` |
---
## 6. 产物目录结构
```
data/
├── raw/ # M1 原始抓取
│ ├── cls/{YYYYMMDD}/ # 按源分目录
│ │ ├── index.jsonl # 文章列表(CrawlResult 摘要)
│ │ └── *.html # 原始 HTML
│ ├── eastmoney/{YYYYMMDD}/
│ ├── ...
│ └── cninfo/{YYYYMMDD}/ # 公告独立目录
│
├── processed/ # M2 正文提取
│ ├── cls/{YYYYMMDD}/
│ │ └── {url_hash}.json # Article
│ └── ...
│
├── dedup/ # M3 指纹库(跨日)
│ └── fingerprints.sqlite3
│
├── deduped/ # M3 去重产物
│ └── {YYYYMMDD}/
│ ├── uniques/
│ │ └── {url_hash}.json # 唯一文章
│ ├── duplicates.jsonl # 重复记录
│ └── sources.json # 多源映射
│
├── events/ # M4 事件抽取
│ └── {YYYYMMDD}/
│ └── {url_hash}.json # ExtractedEvent
│
├── embeddings/ # M5 向量化
│ └── {YYYYMMDD}/
│ └── {url_hash}.json # 1024 维向量 + 事件
│
├── qdrant_storage/ # M6 Qdrant 本地文件
│
├── pipeline/ # 断点续跑状态
│ └── state.json
│
└── reports_history/ # 历史日报 HTML(可选)
```
---
## 7. 运行环境
| 项目 | 值 |
|------|-----|
| Python | 3.11 |
| 包管理器 | uv |
| 虚拟环境 | `.venv/` |
| 生产服务器 | 树莓派 5 (ARM64), `pi@192.168.1.160` |
| 数据库 | MySQL/MariaDB (myquant 库), 通过 `pi@192.168.1.10` autossh 隧道 |
| 系统服务 | systemd `a-share-research` + `a-share-db-tunnel` |
| 测试 | pytest (asyncio_mode=auto, integration 标记默认跳过) |
| 静态检查 | ruff + mypy |
| LLM 模型 | `deepseek-v4-flash`(锁定,不可修改) |
---
—— 架构文档结束 ——