跳转至

整体架构

xgb_wiki 是一个单机数据处理 + 本地 Web 展示系统:从内网 MySQL 抽取选股宝脱水研报,整理为题材本体(SQLite),再经 Flask 渲染为可浏览的 Wiki,并导出 Ontology-Playground 兼容的本体 schema。 设计动机与模型取舍见 设计方案;字段级定义见 数据模型

1. 系统上下文

flowchart LR
    subgraph 外部依赖
        MYSQL[("内网 MySQL\nxuangubao\n(只读)")]
        LLM["Ark API\n(链映射/观点提取,可选)"]
        LAS["LAS 图片解析\n(限流 1 QPM,计费)"]
        IMG["image.xuangubao.cn\n研报配图"]
    end

    subgraph xgb_wiki
        ETL["数据同步\nscripts/sync_data.py\n(src/sync.py 编排 + src/etl.py 搬运)"]
        DB[("SQLite\ndata/xgb_wiki.db\n本体实例数据")]
        WEB["Flask Web\napp.py :5010"]
        RDF["RDF 导出\nsrc/rdf_export.py"]
    end

    subgraph 展示层
        BROWSER["浏览器\n11 个 Wiki 页面"]
        PG["Ontology-Playground\n本地镜像 / fork Pages"]
        SITE["Cloudflare Pages 静态研报站\nxgb-wiki-site.pages.dev\n(GitHub Actions CI 构建)"]
    end

    MYSQL -->|"SELECT 脱水研报\n(增量按水位)"| ETL
    LLM -.->|"可选:链映射"| ETL
    ETL --> DB
    DB --> WEB
    LAS -.->|"按需解析"| WEB
    IMG -.->|"缩略图直链"| WEB
    WEB --> BROWSER
    DB --> RDF
    RDF --> PG
    DB -->|"build_static_site.py\npush master 触发 CI"| SITE

关键边界

  • MySQL 只读且仅内网可达——所有写入一律落 SQLite;同步默认增量(水位驱动,秒级), 失败自动回退,全量重建(--full)幂等可随时执行
  • LLM 与 LAS 均为可选旁路:观点提取有规则引擎降级(无 key 可用),图片不解析不影响主流程
  • 外网图片域名直链展示;若后续公网部署需考虑图片代理/本地化

2. 数据流水线

flowchart TB
    subgraph 抽取层 extractors.py
        A1["大涨题材段\nN、个股:X(1)大涨题材:Y"]
        A2["研报来源块\n券商,分析师,S编号,标题,日期"]
        A3["正文图片\n<img src> + 前后100字上下文"]
        A4["plates 字段\nID,名称;…(仅 live)"]
    end

    subgraph 编排层 sync.py + etl.py
        B0["环境检查 → 备份 → 临时库\n校验 → 原子替换/回退 → sync_log"]
        B1["建库/重建 schema"]
        B2["逐篇处理 live+digest\n(全量/增量共用,水位驱动)"]
        B3["聚合统计\n出现次数/衰减热度/时间窗\n(cnt_5d..30d + theme_daily)"]
        B4["载入 chain_map.json\n(19链/173环节)"]
    end

    subgraph 观点层 viewpoint.py
        C1["信号句定位 evidenceQuote"]
        C2["催化类型分类 reasonType\n(8类关键词字典)"]
    end

    A1 --> B2
    A4 --> B2
    A2 --> B2
    A3 --> B2
    B0 --> B1 --> B2 --> B3 --> B4
    B2 --> C1 --> C2

日常增量同步(python scripts/sync_data.py,默认)约 5~20 秒:按水位拉新增研报、 剔除源端删除行、聚合全量重算,全程经环境检查/数据校验/失败回退(详见 数据同步指南)。全量重建加 --full 约 1 分钟。两种模式均在 etl_meta 表记录 built_at / ref_date(数据基准时间)与同步水位 wm_live_id / wm_digest_id

3. 模块划分

src/                     # 核心库(scripts 与 app.py 共用)
├── config.py            # 唯一配置入口:DB/LLM/LAS、衰减参数、栏目字典、路径
│                        #   (load_dotenv(override=True):.env 变更后重载即生效)
├── ontology.py          # 本体 schema 单一事实源:build_schema() 供 /ontology 页、RDF 导出
│                        #   与 Playground 镜像 catalogue 三方共用;实体/关系显示名为中文
├── etl.py               # MySQL→SQLite 搬运:全量/增量共用逐行提取与聚合(编排见 sync.py)
├── sync.py              # 同步编排:环境检查/备份/临时库/数据校验/回退/sync_log(详见 docs/sync.md)
├── trends.py            # 时间窗统计与查询辅助:cnt_5d..30d / theme_daily / 活跃天数
│                        #   (active_themes/chain_active)/ 活跃个股(active_stocks,题材∩窗口活跃)
│                        #   / 题材↔个股关联图(stock_theme_graph)/ 推荐概述(reco_overview)
├── nlq.py               # 规则式自然语言查询(无 LLM):时间窗/活跃天数/链/题材/日期意图解析
├── extractors.py        # 纯函数正则提取器(大涨题材段/研报来源块/图片),可单测
├── viewpoint.py         # 推荐观点规则引擎(信号句+关键词字典);LLM 版见 scripts
├── llm_chain.py         # LLM 客户端(Ark API / ark-code-latest):链映射与问答共用
├── qa.py                # NL2Data 问答(LLM 版):链内/全局上下文构建 → LLM → 实体链接
├── image_parser.py      # LAS 解析:单张异步 + 批量队列(65s/张节流)+ 结果缓存 + 题材回填
└── rdf_export.py        # Playground 兼容 RDF/XML + metadata.json 导出

scripts/                 # 命令行入口(thin wrapper)
├── sync_data.py         # 数据同步入口(默认增量;--full/--check/--log)
├── build_wiki.py        # 全量重建别名(走 sync.py 完整防护流程)
├── refresh_trends.py    # 只重算时间窗/活跃天数/theme_daily(纯 SQLite,无需内网 MySQL)
├── gen_chain_map.py     # 链映射生成/补全(--min-count --batch)
├── parse_queue.py       # 图片批量解析队列(默认 400 张)
├── export_ontology.py   # 本体导出 → data/output/xgb-a-chain/
├── extract_viewpoints_llm.py  # LLM 升级版观点提取(核心段落 reason/reasonType 全量覆盖)
├── build_playground_catalogue.py  # 重建镜像 catalogue.json(schema 变更后)
├── localize_playground.py  # Playground 镜像汉化 + AI 问答补丁(幂等,重镜像后重跑)
├── build_static_site.py # Cloudflare Pages 静态研报站生成 → data/output/site/(不入库,CI 重建)
├── deploy_site.py       # 本地手动发布到 Cloudflare Pages(线上默认走 GitHub Actions)
├── mcp_server.py        # 对外查询:MCP server(stdio,4 工具)+ --cli 自然语言命令行
├── install_skill.py     # 安装 skills/xgb-wiki → ~/.zcode/skills/(绝对路径版)
└── run_tests.py         # 全功能测试套件(205 项:路由/内容/API/RDF/数据/时间窗/NL/MCP/静态站/CI/数据同步/每日简报)

app.py                   # Flask 入口:12 页面路由(含 /daily 每日简报) + Playground 镜像 + /api/ask + /api/nlq + 图片解析 API
templates/ static/       # Jinja2 模板(Bootstrap 5 + Cytoscape 本地 vendor)+ 自定义样式
skills/xgb-wiki/         # 对外 skill 定义(查询语法/口径/红线,install_skill.py 装到全局)
.github/workflows/       # deploy-site.yml:push master → 测试+构建+Cloudflare Pages 部署
data/                    # SQLite 库 + 三个开放字典 + chain_map + 导出产物(playground/;site/ 为构建产物不入库)

依赖方向scripts → srcapp.py → srcsrc 内部 → configextractors.py 不依赖数据库(纯函数),ontology.py 只读字典 JSON——保证 schema 可脱离数据库渲染。

4. Web 路由一览

路由 模板 职责
/ index.html 产业链总览矩阵(19链 + 未定位题材)
/daily daily.html 每日简报:当日观点单元卡流(聚合篇分组+默认展开),?day= 选日;过滤(类型/题材/仅核心)与关注列表/已读标记由 static/js/daily.js 前端执行(URL 持久化;静态站同款 JS 渲染)
/timeline timeline.html 每日题材:页面级时间周期(1/3/5/10/20日,未选隐藏活跃统计)+ 推荐概述 + 日期热力条 + 单日明细(活跃天数列)+ 题材↔个股关联图(Cytoscape,题材节点唯一)+ 活跃题材/产业链侧栏;?day= 选日、?sw= 选周期
/chain/<id> chain.html 图形视图(Cytoscape:链→环节→题材→核心个股四层,热度着色,PNG导出)+ 卡片视图 + 链内 AI 问答框
/theme/<pid> theme.html 题材核心页:链位置 + 时间窗徽标(5/10/20/30日 + 近5日活跃)+ 个股热度表 + 推荐时间线 + 图片证据(LAS 解析)
/stock/<code> stock.html 个股:题材 + 推荐时间线 + 评级事件
/ratings ratings.html 评级事件列表(eventType 筛选)
/brokers/broker/<bid> brokers/broker.html 券商被引用与评级事件(导航已移除,保留深链)
/report/<live\|digest>/<id> report.html 原文渲染 + 实体标注 + 配图(可触发 AI 解析)
/ontology ontology.html schema 展示 + embed widget(RDF 走 /ontology.rdf 同源加载)
/Ontology-Playground/* (静态镜像) 本地镜像完整应用(已汉化),注入深链引导 + NL 查询接 AI 后端
GET /api/nlq 规则式自然语言查询(nlq.py,无 LLM):时间窗/活跃天数/链/题材/日期,静态站前端与 MCP 同口径
POST /api/ask AI 问答(qa.py,ark-code-latest):链内(chain 参数)/全局,Playground NL 与链页问答框共用
POST /image/<id>/parse 按需图片解析(异步提交,前端轮询)

5. 技术选型与理由

选型 理由
SQLite 单文件 实例数据随 git 仓库管理、零部署;内网 MySQL 不可达时 Wiki 仍完整可用
Flask + 服务端渲染 页面少、交互轻,无需前后端分离;Bootstrap 5 走 CDN 免构建
规则引擎优先(观点/评级) 正则可解释、零成本、可审计;LLM 仅用于批量归纳型任务(链映射)
开放字典(JSON 注册表) 类型扩展零迁移;_pending 待审区保证脏数据不丢失
Playground 本地镜像 内网无外网依赖;汉化后嵌入自家 Web,与 fork Pages 双形态并存
全量登记 + 选择性解析(图片) LAS 1 QPM + 计费硬约束下的必然取舍

6. 部署形态

双形态:本机 Flask(实例数据全功能)+ 公网静态研报站(CI 自动发布)

  • 本机python app.py 监听 127.0.0.1:5010,数据与代码同仓库;LLM 问答、图片 AI 解析等需后端的能力仅此形态可用
  • 公网静态站:https://xgb-wiki-site.pages.dev/ (Cloudflare Pages,大陆直连)。push master 触发 .github/workflows/deploy-site.yml(全功能测试 → build_static_site → wrangler 部署),data/output/site/ 不入库由构建生成;本地立即发布用 scripts/deploy_site.py。含近90天研报正文,对外分享应 --no-content 构建
  • 对外查询能力scripts/mcp_server.py(MCP stdio 4 工具 + --cli)与 skills/xgb-wikiinstall_skill.py 装到 ~/.zcode/skills/)供其他智能体/本机 agent 查询题材数据,无需 LLM key

约束与演进

  • 静态站为快照(构建时的 SQLite 数据);全量 ETL 仍需内网 MySQL,手动重跑(幂等);时间窗可脱离内网用 refresh_trends.py 重算
  • data/xgb_wiki.db 随仓库增长(当前 7797 篇/12746 图规模尚可),显著增大时再评估 Git LFS 或发布产物化