整体架构
xgb_wiki 是一个单机数据处理 + 本地 Web 展示系统:从内网 MySQL 抽取选股宝脱水研报,整理为题材本体(SQLite),再经 Flask 渲染为可浏览的 Wiki,并导出 Ontology-Playground 兼容的本体 schema。 设计动机与模型取舍见 设计方案;字段级定义见 数据模型。
1. 系统上下文
🖼 交互版大图:xgb_wiki 系统上下文(architecture)——可搜索、聚焦链路(主数据链 / 可选旁路 / 发布链)、明暗主题。规格源
diagrams/xgb-context.architecture.json。
flowchart LR
subgraph 外部依赖
MYSQL[("内网 MySQL\nxuangubao\n(只读)")]
LLM["Ark API\n(链映射/观点提取/问答,可选\n配置页 /settings/llm 可调)"]
LAS["LAS 图片解析\n(限流 1 QPM,计费)"]
IMG["image.xuangubao.cn\n研报配图"]
WEKNORA["WeKnora v0.8.2\n后台核心知识库\n(已实施:文档推送+检索)"]
TOS[("火山 TOS 上海桶\n三库备份 xgb-wiki/backup/*\nlatest.json 指向最新")]
end
subgraph xgb_wiki
ETL["数据同步\nscripts/sync/sync_data.py\n(sync.py 编排 + etl.py 搬运\n含 report_tag 栏目重建)"]
DB[("SQLite\ndata/xgb_wiki.db\n本体实例数据 + report_tag")]
WEB["Flask Web\napp.py → src/web :5010\n(含 /reports 栏目页\n与 /settings/llm)"]
RDF["RDF 导出\nsrc/kg/rdf_export.py"]
end
subgraph 展示层
BROWSER["浏览器\n14+ Wiki 页面\n(栏目/简报/配置页)"]
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
DB -->|"push_to_weknora.py\n篇级 MD+月份标签\n(推送进度标记幂等)"| WEKNORA
DB -.->|"backup_tos.py(工作机侧)\n三库快照+.env 滚动6份"| TOS
TOS -.->|"tos_preflight.py(多机预检)\n云端新则恢复三库"| DB
关键边界:
- MySQL 只读且仅内网可达——所有写入一律落 SQLite;同步默认增量(同步进度标记驱动,秒级),
失败自动回退,全量重建(
--full)幂等可随时执行 - LLM 与 LAS 均为可选旁路:观点提取有规则引擎降级(无 key 可用),图片不解析不影响主流程
- 外网图片域名直链展示;若后续公网部署需考虑图片代理/本地化
- WeKnora 语义层(双核演进中):结构化事实口径(热度/时间窗/证据)永远以本库为准;语义侧(全文检索/RAG(检索增强生成)问答/图谱浏览)按 融合方案 v0.2 逐步移交 WeKnora——文档推送已实施(4844 篇,数据至 2026-09-30),问答迁离(S1)与图谱接管(S2)执行前,本页路由表仍为现状
- 数据分发双通道:git(代码 + db 快照随仓库走)与火山 TOS(
backup_tos.py三库备份 /tos_preflight.py按需恢复,多机协作主通道)并行;MySQL/xgb-wiki/TOS/WeKnora 四系统角色与全部流程见 四系统总览
2. 数据流水线
🖼 交互版大图:增量同步流水线(workflow)——主链 / 校验与回退 / 合并新增三个视角。规格源
diagrams/xgb-sync-pipeline.workflow.json。
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(全量/增量共用,进度标记驱动)"]
B2T["栏目 tag 重建\nreport_tag 全量映射\n(一篇可多栏目,/reports 依据)"]
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 --> B2T --> B3 --> B4
B2 --> C1 --> C2
日常增量同步(python scripts/sync/sync_data.py,默认)约 5~20 秒:按同步进度标记拉新增研报、
剔除源端删除行、全量重建 report_tag 栏目映射、聚合全量重算,全程经环境检查/
数据校验/失败回退(详见 数据同步指南)。全量重建加 --full 约 1 分钟。
两种模式均在 etl_meta 表记录 built_at / ref_date(数据基准时间)与同步进度标记
wm_live_id / wm_digest_id。
3. 模块划分
2026-09 分层重构后的结构(重构过程与决策见 refactoring.md;放置规则见仓库根
AGENTS.md末节)。
src/ # 核心库(一律绝对导入 from src.<pkg>.<module>,无 path hack)
├── config.py # 唯一配置入口:DB/LLM/LAS、衰减参数、栏目字典(LIVE/DIGEST_TAG_COLUMNS + REPORT_NAV)、路径
│ # (load_dotenv(override=True);llm_settings() 调用时叠加 data/llm_config.json 覆盖)
├── web/ # Flask 应用层(app.py 仅 7 行入口)
│ ├── __init__.py # create_app 工厂 + get_db/close_db + 5 过滤器与 10 蓝图注册;ROOT 锚定仓库根
│ ├── helpers.py # 模板过滤器 days_ago/heat/seg_label/event_badge/dt_short(注册名不变)
│ ├── api.py # /api/nlq、/api/ask 蓝图
│ └── views/ # 9 页面蓝图:chains(/ /chain) themes(/theme /timeline /mindmap) stocks(/stock)
│ # daily(/daily) reports(/reports /report /ratings) brokers ontology(含 Playground
│ # 镜像与 /ontology.rdf) settings(/settings/llm 图片解析 LLM测试) graph(/graph + 5 API)
├── kg/ # 知识图谱与本体域
│ ├── ontology.py # 本体 schema 单一事实源:build_schema() 供 /ontology 页、RDF 导出
│ │ # 与 Playground 镜像 catalogue 三方共用;实体/关系显示名为中文
│ ├── rdf_export.py # Playground 兼容 RDF/XML + metadata.json 导出
│ ├── kg_schema.py # 图谱存储层(graph.db 三元组+mention+90天半衰期;移植 2_xuangubao v0.5.0)
│ ├── kg_query.py # 图谱查询层(主题群/行业子图/公司反查/搜索;时间窗权重)
│ ├── kg_cluster.py # Louvain 主题群聚类(networkx;cluster 表缓存)
│ └── kg_extractor.py # 图谱 LLM 抽取(三元组+防幻觉校验;LLM 不可用规则降级)
├── llm/ # LLM 与问答域
│ ├── llm_chain.py # LLM 客户端:call_llm 调用时读 llm_settings()(.env < data/llm_config.json 覆盖),链映射/问答/观点升级共用
│ ├── qa.py # NL2Data 问答(LLM 版):链内/全局上下文构建 → LLM → 实体链接
│ ├── nlq.py # 规则式自然语言查询(无 LLM):时间窗/活跃天数/链/题材/日期意图解析
│ └── viewpoint.py # 推荐观点规则引擎(信号句+关键词字典);LLM 版见 scripts/kg
├── pipeline/ # 数据管道域(MySQL→SQLite)
│ ├── sync.py # 同步编排:环境检查/备份/临时库/数据校验/回退/sync_log(详见 docs/sync.md)
│ ├── etl.py # MySQL→SQLite 搬运:全量/增量共用逐行提取与聚合(编排见 sync.py)
│ ├── extractors.py # 纯函数正则提取器(大涨题材段/研报来源块/图片),可单测
│ └── image_parser.py # LAS 解析:单张异步 + 批量队列(65s/张节流)+ 结果缓存 + 题材回填
└── content/ # 内容生成域(纯 SQLite)
├── trends.py # 时间窗统计与查询辅助:cnt_5d..30d / theme_daily / 活跃天数
│ # (active_themes/chain_active)/ 活跃个股(active_stocks,题材∩窗口活跃)
│ # / 题材↔个股关联图(stock_theme_graph)/ 推荐概述(reco_overview)
└── daily.py # 每日简报与当日脑图载荷(观点单元卡流数据源)
scripts/ # 命令行入口(thin wrapper 五分类;新环境先 pip install -e .)
├── sync/ # sync_data(增量/全量入口,--check/--log)· build_wiki(全量别名)
│ # · refresh_trends(时间窗重算,无需 MySQL)· refresh_daily(观点单元回填)
│ # · parse_queue(图片批量解析队列,默认 400 张)
├── build/ # build_static_site(静态站 → data/output/site/,含栏目 hub + 15 子页)
│ # · build_playground_catalogue(镜像 catalogue 重建)· localize_playground(汉化+问答补丁)
├── kg/ # build_graph(图谱构建 --stats/--since/--force)· kg_recluster(重算主题群)
│ # · gen_chain_map(链映射 --min-count/--batch)· export_ontology · extract_viewpoints_llm
├── deploy/ # deploy_site(本地手动发布 Cloudflare Pages;线上默认走 GitHub Actions)
└── ops/ # run_tests(242 项全功能测试)· mcp_server(MCP stdio 4 工具 + --cli)
# · install_skill(skills → ~/.zcode/skills 绝对路径版)· git_slim_db(db 入库瘦身 filter)
app.py # Flask 入口(7 行):from src.web import create_app → 127.0.0.1:5010
templates/ static/ # Jinja2 模板(Bootstrap 5 + Cytoscape 本地 vendor)+ 自定义样式
skills/ # 对外 skill 定义(xgb-wiki / xgb-sync,查询语法/口径/红线)
.github/workflows/ # deploy-site.yml:push master → 测试+构建+Cloudflare Pages 部署
data/ # SQLite 库 + 三个开放字典 + chain_map + 导出产物(playground/;site/ 为构建产物不入库)
依赖方向:scripts → src,app.py → src.web;包间 web → {kg,llm,content} → {pipeline, config};config 不依赖任何模块。extractors.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 筛选) |
/graph |
graph.html | 知识图谱(移植 2_xuangubao v0.5.0):主题群→行业→环节群→公司三级钻取(ECharts),data/graph.db 独立库 |
/graph/api/* |
— | clusters/cluster/industry/company/search(页面内部接口;静态站 KG_EMBED 内嵌路由) |
/reports |
reports.html | 研报栏目(2026-09 吸收自 2_xuangubao):3 组 15 栏目导航,tag 经 report_tag 全量映射 |
/reports/<live\|digest>/<tag_id> |
report_list.html | 栏目列表:日期范围筛选 + 已读 localStorage + 只看未读 |
/brokers、/broker/<bid> |
brokers/broker.html | 券商被引用与评级事件(导航已移除,保留深链) |
/report/<live\|digest>/<id> |
report.html | 原文渲染 + 实体标注 + 配图(可触发 AI 解析) |
/settings/llm |
settings_llm.html | LLM 配置页(本地 Flask 专用):生效配置展示/覆盖保存(data/llm_config.json)/连通测试 |
/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,模型/端点经 /settings/llm 配置):链内(chain 参数)/全局,Playground NL 与链页问答框共用 |
POST /api/llm/test |
— | LLM 连通性测试(最小请求,返回延迟/回复/错误) |
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/deploy_site.py。含近90天研报正文,对外分享应--no-content构建 - 对外查询能力:
scripts/ops/mcp_server.py(MCP stdio 4 工具 +--cli)与skills/xgb-wiki(install_skill.py装到~/.zcode/skills/)供其他智能体/本机 agent 查询题材数据,无需 LLM key - WeKnora 后台核心知识库(
7.weknora部署实例,API 18080 / UI 18081):scripts/sync/push_to_weknora.py已将 2025-01-01 以来 4844 篇研报(篇级 Markdown + 观点内嵌 + 月份标签,推送进度标记幂等,数据至 2026-09-30)推入其混合检索库;后续按 融合方案 v0.2 的 S0–S4 路线将 RAG 问答(现/api/ask)与图谱浏览(现/graph)迁往 WeKnora,本站收敛为精确结构化查询
约束与演进:
- 仓库 private / 站点公开:GitHub 仓库私有(含付费正文),CI 经 CF token 部署不受影响;公网只有 pages.dev 产物(见 publishing.md §0 可见性矩阵与收紧路径)
- 静态站为快照(构建时的 SQLite 数据);全量 ETL 仍需内网 MySQL,手动重跑(幂等);时间窗可脱离内网用
refresh_trends.py重算 data/xgb_wiki.db随仓库增长(当前 7797 篇/12746 图规模尚可),显著增大时再评估 Git LFS 或发布产物化