BettaFish 项目结构分析与优化方案
版本:v1.0
状态:Draft
更新时间:2026-04-01
1. 结论摘要
当前仓库的核心问题不是单一的“目录命名不统一”,而是以下几类问题同时存在:
- 根目录承担了过多职责,源码、启动入口、脚本、运行时数据、构建产物和临时文件混在一起。
- 新架构和旧架构并存,
app.py + templates、backend/、frontend/、SingleEngineApp/、各*Engine/目录同时存在,但边界没有完全收口。 - 一级目录没有按职责分层,而是按历史演进结果堆叠,导致前端、后端、爬虫、引擎、模型、脚本、输出物难以快速区分。
- 配置、依赖、构建和运行入口分散,给后续维护、迁移和自动化带来了明显成本。
这意味着优化方案不能只做“改文件夹名字”,而需要同时完成以下四件事:
- 重建一级目录边界。
- 收敛启动装配层。
- 统一配置与工具入口。
- 将运行时数据和构建产物从源码区彻底隔离。
建议的总方向是:保持“单仓库、单部署单元、模块化单体”的架构,不急于拆成微服务,但要把仓库从“历史堆叠型目录”重构成“按职责分层的工程化目录”。
2. 当前仓库现状
2.1 顶层目录的真实职责
当前顶层目录大致可以分成六类,但它们没有被清晰区分:
| 类别 | 当前目录/文件 | 现状说明 |
|---|---|---|
| Web 装配层 |
app.py、backend/、templates/、static/frontend/、frontend/
|
既有旧 Flask 模板入口,也有新的 Vue 前端和新的后端 API 模块 |
| 分析引擎 |
InsightEngine/、MediaEngine/、QueryEngine/、ReportEngine/、ForumEngine/
|
这些才是核心业务能力,但仍直接暴露在仓库根目录 |
| 爬虫体系 |
MindSpider/、MindSpider/DeepSentimentCrawling/MediaCrawler/、crawler_web.py、crawler_browser_data/
|
本体、适配层、浏览器数据和兼容入口混在一起 |
| 模型与研究资产 | SentimentAnalysisModel/ |
更像研究资产或训练资产,而不是当前主应用的一层 |
| 工具与脚本 |
report_engine_only.py、regenerate_latest_*、export_pdf.py、scripts/
|
一部分脚本仍在根目录,一部分已经进入 scripts/
|
| 运行时产物 |
logs/、final_reports/、*_streamlit_reports/、output/、db_data/、.codex-tmp-*
|
与源码处于同一级目录,持续污染仓库根目录 |
2.2 体量分布暴露出的结构问题
按当前工作区统计,几个最显著的目录如下:
-
crawler_browser_data/:约337 MB -
SentimentAnalysisModel/:约177 MB -
frontend/:约116 MB -
db_data/:约48 MB -
static/:约35 MB -
ReportEngine/:约32 MB -
MindSpider/:约28 MB -
output/:约19 MB
其中尤其值得注意的是:
-
frontend/的体量几乎全部来自本地node_modules/,约116 MB。 -
static/frontend/是构建产物,当前约24 MB。 -
crawler_browser_data/、db_data/、output/都属于运行时数据,不应该继续与源码同级放置。
这说明当前仓库的“视觉复杂度”有很大一部分不是业务源码造成的,而是运行时目录、构建产物和本地依赖目录没有被隔离。
3. 主要结构性问题
3.1 一级目录不是按职责划分,而是按历史演进堆叠
当前一级目录同时存在:
- 业务引擎目录:
QueryEngine/、MediaEngine/、InsightEngine/、ReportEngine/、ForumEngine/ - 新增平台目录:
backend/、frontend/、docs/、scripts/ - 旧单页/单引擎入口:
SingleEngineApp/ - 爬虫侧目录:
MindSpider/ - 模型目录:
SentimentAnalysisModel/ - 运行目录:
logs/、output/、final_reports/、db_data/
这会带来两个直接问题:
- 新人无法在 30 秒内判断“主应用代码到底在哪里”。
- 任何新功能都很容易继续往根目录堆。
3.2 命名规范混用,降低可读性和迁移一致性
当前顶层目录命名风格同时包含:
- PascalCase:
QueryEngine、MediaEngine、MindSpider、SingleEngineApp - lowercase:
backend、frontend、scripts、tests - snake_case:
crawler_browser_data、query_engine_streamlit_reports
这会导致:
- 路径扫描时很难快速形成稳定心智模型。
- 跨平台时更容易出现大小写、导入和路径映射问题。
- 未来继续迁移时,目录命名没有统一规则可依。
3.3 根目录承担了超级入口角色
app.py 当前不只是 Web 入口,它同时承担:
- Flask 应用创建
- Blueprint 注册
- Streamlit 子进程生命周期管理
- ForumEngine 生命周期管理
- 系统启动/关闭装配
- 健康检查
- 路由定义
- 前端静态入口兜底
这意味着当前根目录其实在扮演“平台装配层”,但它没有被明确建模为一个独立应用层。
3.4 新旧前端/后端结构并存,但边界未完全闭合
当前平台已经明显在向“Vue 前端 + Flask API + 引擎装配层”演进:
-
frontend/已经是完整的 Vue 3 + Vite + TypeScript 工程。 -
backend/已经开始承接配置、研究任务、爬虫 API。 -
templates/index.html仍保留旧兜底页面。 -
static/frontend/仍作为前端构建产物目录。 -
crawler_web.py已退化为兼容层。
这说明项目方向是对的,但收口还没做完:
- 旧入口仍在根目录。
- 新应用层还没有成为唯一事实来源。
- 构建产物目录仍暴露在源码树中。
3.5 配置层分裂
当前配置体系至少有四层:
- 根配置:
config.py - 引擎内配置:
InsightEngine/utils/config.py、MediaEngine/utils/config.py、QueryEngine/utils/config.py、ReportEngine/utils/config.py - 爬虫配置:
MindSpider/config.py - 外部子项目配置:
MindSpider/DeepSentimentCrawling/MediaCrawler/config/*
更复杂的是,代码对这些配置的使用并不统一:
- 有的模块直接依赖根
config.py - 有的模块依赖本引擎的
utils/config.py - 新增
backend/config_admin.py又直接负责.env写入
最终问题是:
- 配置来源不单一。
- 配置责任边界不清晰。
- 修改配置时,开发者无法立即判断影响范围。
3.6 依赖管理分裂
当前依赖管理至少存在以下几套:
- 根
requirements.txt MindSpider/requirements.txtMindSpider/DeepSentimentCrawling/MediaCrawler/requirements.txtMindSpider/DeepSentimentCrawling/MediaCrawler/pyproject.tomlfrontend/package.json
其中根 requirements.txt 还同时混入了:
- Web 服务依赖
- 爬虫依赖
- ML 推理依赖
- PDF 依赖
- 开发依赖
这会带来:
- 安装链路重。
- Docker 镜像构建时间长。
- 很难区分生产依赖和开发依赖。
- 很难判断某个子模块是否真的属于主运行链路。
3.7 外部子项目与主仓库耦合过深
MindSpider/DeepSentimentCrawling/MediaCrawler/ 目前是一个 Git 子模块,并且它自己就是一个完整项目,拥有:
- 自己的
.git - 自己的配置
- 自己的测试
- 自己的依赖
- 自己的运行入口
但当前主仓库又直接依赖它的 web_runtime.py 和目录结构。
这会让主仓库结构长期处于一种“外部项目已被内嵌使用,但又没有被真正封装”的状态。
3.8 脚本入口散落,缺少统一工具层
当前脚本入口散落在两层:
- 根目录脚本:
report_engine_only.py、regenerate_latest_html.py、regenerate_latest_md.py、regenerate_latest_pdf.py、export_pdf.py scripts/reports/export_pdf.py
其中 export_pdf.py 已经只是一个 thin wrapper,这说明项目实际上已经开始往 scripts/ 迁移,但没有彻底完成。
3.9 运行时目录与源码混放
当前仓库根目录直接暴露了大量运行期目录:
logs/final_reports/insight_engine_streamlit_reports/media_engine_streamlit_reports/query_engine_streamlit_reports/output/crawler_browser_data/db_data/.codex-tmp-mediacrawler*
这些目录会持续造成:
- 根目录噪音极大
- Docker volume 与源码路径纠缠
- Git ignore、docker ignore、备份和清理策略难统一
3.10 自动化和测试边界偏弱
当前自动化现状更偏“镜像发布”,而不是“代码质量守门”:
-
.github/workflows/docker_ci.yml主要负责打镜像 - 测试主要集中在
tests/下的 ForumEngine 日志解析与部分 ReportEngine 结构校验 - 缺少统一 lint、type-check、前端测试、后端 API 测试、端到端测试流程
这会直接提高后续大规模目录调整的风险。
4. 优化目标
本次结构优化建议遵循以下目标:
4.1 保持单仓库、单部署单元
当前项目虽然模块多,但仍更适合“模块化单体”而不是微服务。
原因:
- 引擎之间耦合非常紧。
- Docker 部署链路已经围绕单应用构建。
- 配置、任务、日志、报告都集中在一个平台里。
因此不建议在本轮优化中拆成多个服务仓库。
4.2 一级目录必须只表达职责,不表达历史来源
一级目录应只回答一个问题:
“这块内容属于哪一类职责?”
建议只保留少数顶层职责:
apps/services/tools/infra/docs/tests/-
var/或.local/ -
vendor/或research/
4.3 源码、构建产物、运行时数据彻底分离
这是本次优化最重要的约束之一。
必须做到:
-
frontend/node_modules/不进入源码结构讨论。 -
static/frontend/视为构建产物,而不是应用源码目录。 -
crawler_browser_data/、db_data/、logs/等统一下沉到运行目录。
4.4 新 Web 平台成为主装配层,旧入口逐步兼容退出
当前方向已经很明确,应将:
frontend/backend/app.py
收敛成“平台应用层”。
旧的 templates/index.html、crawler_web.py、根目录 wrapper 脚本应改为兼容层或迁移后删除。
4.5 配置必须单一事实来源
目标不是删除所有局部配置,而是建立清晰规则:
- 全局环境配置:一处统一定义
- 引擎局部配置:仅保留运行参数,不重复环境变量模型
- 外部子项目配置:通过适配层注入,不直接向上扩散
5. 建议的目标目录结构
建议按“应用层 / 业务服务层 / 工具层 / 基础设施 / 运行数据”重组:
BettaFish/
├── apps/
│ ├── web_api/
│ │ ├── app/
│ │ │ ├── api/
│ │ │ ├── orchestration/
│ │ │ ├── sockets/
│ │ │ └── templates/
│ │ └── main.py
│ ├── web_ui/
│ │ ├── src/
│ │ ├── package.json
│ │ └── vite.config.ts
│ └── engine_console/
│ ├── insight_streamlit_app.py
│ ├── media_streamlit_app.py
│ └── query_streamlit_app.py
├── services/
│ ├── engines/
│ │ ├── insight_engine/
│ │ ├── media_engine/
│ │ ├── query_engine/
│ │ ├── report_engine/
│ │ └── forum_engine/
│ ├── crawler/
│ │ ├── mindspider/
│ │ └── mediacrawler_adapter/
│ └── shared/
│ ├── config/
│ ├── llm/
│ ├── logging/
│ ├── storage/
│ └── utils/
├── tools/
│ ├── reports/
│ ├── dev/
│ └── migrations/
├── infra/
│ └── docker/
│ ├── Dockerfile
│ ├── Dockerfile.runtime-hotfix
│ ├── docker-compose.yml
│ └── docker-compose.override.yml
├── docs/
├── tests/
│ ├── unit/
│ ├── integration/
│ └── e2e/
├── vendor/
│ └── mediacrawler/
├── research/
│ └── sentiment_models/
└── var/
├── logs/
├── reports/
├── crawler/
├── db/
└── output/
6. 当前目录到目标目录的映射建议
6.1 应用层
| 当前路径 | 建议目标 | 说明 |
|---|---|---|
app.py |
apps/web_api/main.py |
保留为平台主入口,但职责缩减为装配和启动 |
backend/ |
apps/web_api/app/api/ |
现有 Blueprint 直接归入 Web API 应用层 |
templates/index.html |
apps/web_api/app/templates/frontend_fallback.html |
只保留兜底,不再放根模板目录 |
frontend/ |
apps/web_ui/ |
成为正式前端应用目录 |
SingleEngineApp/ |
apps/engine_console/ |
标注为 legacy/辅助入口,而非主产品层 |
6.2 业务服务层
| 当前路径 | 建议目标 | 说明 |
|---|---|---|
InsightEngine/ |
services/engines/insight_engine/ |
统一转为 snake_case |
MediaEngine/ |
services/engines/media_engine/ |
同上 |
QueryEngine/ |
services/engines/query_engine/ |
同上 |
ReportEngine/ |
services/engines/report_engine/ |
同上 |
ForumEngine/ |
services/engines/forum_engine/ |
同上 |
utils/ |
services/shared/utils/ |
公共工具不再悬挂根目录 |
根 config.py
|
services/shared/config/settings.py |
作为全局配置唯一事实来源 |
6.3 爬虫层
| 当前路径 | 建议目标 | 说明 |
|---|---|---|
MindSpider/ |
services/crawler/mindspider/ |
主仓库维护的爬虫服务代码 |
MindSpider/DeepSentimentCrawling/MediaCrawler/ |
vendor/mediacrawler/ 或 services/crawler/vendor/mediacrawler/
|
明确它是外部依赖,不与主仓库内部层级混淆 |
crawler_web.py |
删除或并入 apps/web_api/app/api/crawler.py
|
当前已是兼容层,应尽快收口 |
6.4 工具与脚本层
| 当前路径 | 建议目标 | 说明 |
|---|---|---|
report_engine_only.py |
tools/reports/report_engine_only.py |
明确为运维/调试工具 |
regenerate_latest_html.py |
tools/reports/regenerate_latest_html.py |
同上 |
regenerate_latest_md.py |
tools/reports/regenerate_latest_md.py |
同上 |
regenerate_latest_pdf.py |
tools/reports/regenerate_latest_pdf.py |
同上 |
export_pdf.py |
保留 wrapper 或删除 | 如果保留,只做过渡兼容 |
scripts/ |
tools/ |
正式成为工具层,而非零散脚本层 |
6.5 基础设施与运行数据
| 当前路径 | 建议目标 | 说明 |
|---|---|---|
Dockerfile*、docker-compose*.yml
|
infra/docker/ |
所有部署文件集中 |
logs/ |
var/logs/ |
运行日志目录 |
final_reports/ |
var/reports/final/ |
报告产物目录 |
*_streamlit_reports/ |
var/reports/engines/* |
引擎中间产物目录 |
crawler_browser_data/ |
var/crawler/browser_data/ |
浏览器会话和缓存目录 |
db_data/ |
var/db/postgres/ |
数据卷目录 |
output/ |
var/output/ |
临时输出目录 |
static/frontend/ |
前端构建目录,不入库 | 由构建流程生成并复制到镜像中 |
frontend/node_modules/ |
本地依赖,不入库 | 不应成为结构讨论的一部分 |
6.6 研究资产
| 当前路径 | 建议目标 | 说明 |
|---|---|---|
SentimentAnalysisModel/ |
research/sentiment_models/ |
如果长期保留,建议视作研究资产而不是主应用目录 |
7. 配置与依赖的重构建议
7.1 配置重构
建议分三层:
全局环境配置
统一放在services/shared/config/settings.py模块运行参数
各引擎只保留本模块运行参数,如超时、默认 limit、输出目录等,不再重复声明所有环境变量外部子项目配置适配
MediaCrawler 等外部项目的配置由适配层注入,主仓库不直接依赖其内部配置结构
建议规则:
-
.env的读写只允许经过统一配置层。 -
backend/config_admin.py后续改为调用共享配置服务,而不是直接写散落的环境模型。 - 引擎内
utils/config.py最终只保留兼容 wrapper,逐步废弃。
7.2 依赖重构
建议中期统一为:
- Python 主依赖:根
pyproject.toml或分层requirements/ - 前端依赖:
apps/web_ui/package.json - 外部子模块依赖:保持独立,但通过 adapter 隔离
如果不希望一次性切到 pyproject.toml,也至少应拆分:
requirements/base.txtrequirements/web.txtrequirements/crawler.txtrequirements/ml.txtrequirements/dev.txt
这样可以减少:
- 本地安装成本
- Docker 层缓存失效率
- 不必要的生产镜像体积
8. 命名规范建议
建议统一为以下规则:
8.1 一级目录
- 使用
lowercase_snake_case或lowercase-kebab-case - 本项目更推荐
lowercase_snake_case,因为与 Python 包命名更一致
8.2 Python 包目录
- 全部使用
snake_case - 避免
PascalCase目录名
8.3 前端目录
- 应用级目录使用
snake_case - Vue 组件文件继续保持
PascalCase.vue可以接受
8.4 运行时目录
- 统一放在
var/ - 不再出现
*_reports、db_data、crawler_browser_data这种散落根目录的命名
9. 分阶段实施方案
Phase 0:先清场,不改业务逻辑
目标:先让仓库根目录恢复可控状态。
建议动作:
- 扩充
.gitignore和.dockerignore - 明确
var/目录方案 - 停止将
frontend/node_modules/、static/frontend/、.codex-tmp-*视为源码内容 - 新增一份目录规范文档
交付结果:
- 根目录只保留真正的源码和工程文件
- 运行数据和构建产物开始分流
优先级:P0
Phase 1:建立新的一级目录骨架
目标:把“前端、后端、引擎、工具、基础设施”先分层放好。
建议动作:
- 建立
apps/、services/、tools/、infra/、var/ - 将
frontend/迁移到apps/web_ui/ - 将
backend/和根app.py装配层迁移到apps/web_api/ - 将
SingleEngineApp/迁移到apps/engine_console/ - 将根目录脚本统一迁移到
tools/reports/
交付结果:
- 一级目录职责变清晰
- 根目录不再是“超级杂物层”
优先级:P0
Phase 2:收敛核心服务层
目标:把引擎、共享能力和配置层放进统一服务结构。
建议动作:
- 将所有
*Engine迁移到services/engines/* - 抽取
services/shared/config/ - 抽取
services/shared/llm/ - 抽取
services/shared/utils/ - 对旧导入路径增加临时兼容 shim
交付结果:
- 核心服务目录统一
- 配置与共享逻辑不再散落
优先级:P0
Phase 3:重构爬虫边界
目标:把主仓库爬虫代码和外部子项目边界拉清。
建议动作:
-
MindSpider/迁入services/crawler/mindspider/ -
MediaCrawler子模块迁入vendor/mediacrawler/ - 新建
services/crawler/mediacrawler_adapter/ - 平台其余模块只允许通过 adapter 调用爬虫能力
交付结果:
- 外部项目不再污染主目录结构
- 后续升级 MediaCrawler 风险更低
优先级:P1
Phase 4:统一工具链和自动化
目标:让仓库具备结构调整后的工程化保障。
建议动作:
- 建立统一测试入口
- 增加前端 type-check
- 增加 Python lint / format / test
- 增加 API 集成测试
- 将 GitHub Actions 从“只打镜像”升级为“先校验,再打镜像”
交付结果:
- 结构迁移可持续推进
- 重构风险可被自动化守住
优先级:P1
Phase 5:研究资产与主应用解耦
目标:降低主应用维护噪音。
建议动作:
- 评估
SentimentAnalysisModel/是否仍属于主运行链路 - 如果主要用于研究和训练,迁入
research/ - 如长期独立演进,可考虑单独仓库或子模块化
交付结果:
- 主应用和研究资产边界更清晰
优先级:P2
10. 推荐的实施顺序
建议不要一次性“大搬家”,而采用以下顺序:
- 先做运行目录隔离和 ignore 规则收口。
- 再建立
apps/、services/、tools/、infra/、var/的新骨架。 - 先迁移脚本和前后端目录,再迁移引擎目录。
- 配置收敛与 import shim 在同一阶段完成,避免大面积导入失效。
- 最后处理爬虫子模块和研究资产。
这样可以把风险控制在“每一步都可运行、可回滚、可验证”的范围内。
11. 本轮优化中不建议做的事
为了降低风险,本轮不建议同时做以下动作:
- 不建议一边改目录,一边重写全部业务逻辑。
- 不建议现在就拆成多个部署服务。
- 不建议现在就把 Flask 完全替换成别的后端框架。
- 不建议立即把 MediaCrawler 代码完全吸收到主仓库内部。
- 不建议在没有自动化保护前,直接全量 rename 所有目录和 import。
12. 建议优先落地的 10 个动作
P0
- 建立
var/目录,并把logs/、final_reports/、output/、crawler_browser_data/、db_data/的挂载路径统一过去。 - 扩充
.gitignore与.dockerignore,明确忽略frontend/node_modules/、static/frontend/、.codex-tmp-*等目录。 - 创建
apps/web_api/,把当前app.py的装配逻辑迁入。 - 创建
apps/web_ui/,将frontend/定位为正式前端应用目录。 - 把根目录脚本迁移到
tools/reports/,根目录只保留过渡 wrapper 或彻底删除。
P1
- 建立
services/engines/并逐步迁移*Engine/目录。 - 建立
services/shared/config/,统一配置模型与.env读写。 - 让
backend/的配置管理只调用共享配置服务,不直接分散控制。 - 将
SingleEngineApp/改名并迁入apps/engine_console/。 - 将 Docker 相关文件迁入
infra/docker/,统一部署入口。
13. 最终收益
完成本方案后,仓库会获得以下收益:
- 新人能快速分辨“产品应用、业务服务、爬虫、工具、运行数据”。
- 目录命名统一后,代码搜索、导航和迁移成本明显下降。
- 前后端、引擎、脚本和运行产物之间的边界更清晰。
- Docker、配置、脚本和自动化入口更容易标准化。
- 后续继续推进“场馆运营研究平台”时,不会再被旧目录结构持续拖累。
14. 推荐决策
如果只做一件事,最值得先做的是:
先完成一级目录重组方案和运行时目录下沉,再开始下一轮业务开发。
原因很简单:
- 现在业务还在快速演进期。
- 如果继续在当前结构上叠功能,后续迁移成本只会更高。
- 当前已经具备足够清晰的新平台方向,正是适合做结构收口的时间点。