project-structure-optimization-memory.md 119 KB

BettaFish 项目结构优化记忆文件

版本:v6.0
状态:Active
更新时间:2026-04-09

关联文档:

1. 用途

本文件用于记录“项目结构优化”这条长期任务线上的稳定事实、关键决策、已完成工作和下一步建议。
后续继续推进结构优化时,默认先读取本文件,再开始执行下一批任务。

2. 稳定目标

当前结构优化的长期目标已经明确:

  • 保持单仓库、单部署单元、模块化单体,不做微服务拆分。
  • 重建一级目录边界,明确区分应用层、服务层、爬虫层、工具层、基础设施层和运行时目录。
  • 将运行时数据和构建产物从源码树中剥离。
  • 将当前“根目录超级入口”收敛为清晰的应用装配层。
  • 为未来持续重构保留兼容层,避免一次性破坏当前运行链路。

3. 当前已确认的关键事实

3.1 仓库结构事实

  • 当前仓库顶层已以 apps/backend/docs/infra/research/scripts/services/static/templates/tests/tools/utils/var/vendor/ 为主,一级目录边界已经明显清晰。
  • 根目录 .codex-tmp-mediacrawler/.codex-tmp-mediacrawler2/.codex-tmp-mediacrawler.zip 已在 T-105 中实际删除,不再属于当前残留集合。
  • 五大核心业务能力的真实代码已经迁移到 services/engines/
    • services/engines/insight/
    • services/engines/media/
    • services/engines/query/
    • services/engines/report/
    • services/engines/forum/
  • T-517 已完成目录级历史兼容壳清理,以下根目录兼容包 / 兼容 CLI 目录已从仓库物理移除:
    • InsightEngine/
    • MediaEngine/
    • QueryEngine/
    • ReportEngine/
    • ForumEngine/
    • MindSpider/
    • frontend/
    • SingleEngineApp/
    • SentimentAnalysisModel/
  • apps/backend/docs/infra/research/scripts/ 已成为当前真实工作流的一级入口,而不是过渡骨架。
  • 根目录当前仅保留少量文件级兼容入口:start_local.bat;原根目录 app.py 已在 T-525 删除。
  • infra/docker/ 已成为 Docker / compose 的唯一 canonical 基础设施目录;根目录 Docker 相关兼容副本已在 T-521 删除。
  • services/crawler/mindspider/ 已成为 MindSpider 主爬虫实现的 canonical 目录。
  • vendor/mediacrawler/ 已成为外部 MediaCrawler 子模块的 canonical 目录。

3.2 当前架构演进方向

  • apps/web_ui/ 已经是完整的 Vue 3 + Vite + TypeScript 工程,本地开发默认通过 9527 上的 Vite dev server 提供热更新。
  • apps/web_api/ 已开始承接真实 Flask 装配入口。
  • apps/web_api/app.py 当前在开发态通过 BETTAFISH_FRONTEND_DEV_URL/ 重定向到 Vite dev server;在无开发服务器时仍会优先托管 static/frontend/index.html,因此它目前仍承担单服务部署的前端交付职责。
  • apps/engine_console/ 已开始承接真实 Streamlit 引擎控制台入口。
  • services/engines/ 已开始承接真实业务服务代码,五大引擎真实实现已经迁入。
  • services/shared/ 骨架已建立,但真实共享代码尚未迁入。
  • services/shared/config/ 已开始承接真实共享配置基础设施。
  • services/crawler/ 已建立,services/crawler/mindspider/ 已开始承接真实主爬虫实现。
  • tools/ 已建立,tools/reports/ 已开始承接真实报告类工具脚本。
  • infra/docker/ 已开始承接真实 Docker / compose 入口,CI 与说明文档已优先切到 canonical 路径。
  • 根目录目录级兼容壳已在 T-517 中移除;当前兼容面只剩少量文件级入口与说明文件。
  • 根目录当前仅保留 start_local.bat 这一个文件级入口,用于 Windows 一键启动;原根目录 app.py 已删除。
  • vendor/mediacrawler/ 当前承接真实 MediaCrawler 代码与 Docker / compose 运行路径。
  • services/crawler/adapters/mediacrawler_adapter.py 当前承接主仓库访问外部 MediaCrawler 的统一适配入口。
  • backend/ 已经承接了一部分 Web API 职责;爬虫 Web 控制的 canonical 导出固定为 backend/crawler/,根目录 crawler_web.py 已在 T-520 删除。
  • templates/index.html 仅剩兜底页面价值。
  • apps/web_api/apps/engine_console/ 已开始优先导入 services.engines.* canonical 路径。
  • research/sentiment_models/ 已成为情感分析研究模型的 canonical 目录,根目录 SentimentAnalysisModel/ 已在 T-517 中删除。
  • research/sentiment_models/WeiboMultilingualSentiment/ 已在 T-605 后成为多语言情感分析研究示例的 canonical 目录。
  • services/engines/insight/tools/sentiment_analyzer.pyWeiboMultilingualSentiment/ 的运行时路径依赖已在 T-603 中完成配置化收口。
  • INSIGHT_SENTIMENT_MODEL_DIR 当前默认值为 var/models/insight_weibo_multilingual,同时仍允许通过配置覆盖到 research/ 或任意绝对路径目录。
  • tests/ 已在 T-503 后收口为统一测试入口,主仓库测试分为 tests/unit/tests/integration/tests/e2e/,共享静态数据位于 tests/fixtures/
  • .github/workflows/quality_ci.yml 已在 T-504 后建立,当前主仓库最小自动化守门已覆盖 Python compile/lint/test 与前端 vue-tsc 类型检查。
  • .github/workflows/mediacrawler_ci.ymltools/ci/run_mediacrawler_quality.py 已在 T-505 后建立,vendor/mediacrawler 当前通过独立 Python 3.11 workflow 运行隔离 smoke tests。
  • services/engines/report/services/engines/report/utils/services/engines/report/nodes/ 与根目录 ReportEngine/ 当前都已改为惰性导出,避免包级初始化把 PDF / 图表等重型依赖链路提前拉起。
  • 当前本地验证环境已补齐 pytestflake8loguruopenai 与前端依赖,能够完成 T-504 的最小守门验证;但 pymysql 等完整业务运行依赖仍未作为本轮自动化前提。
  • 源码启动链路已在 T-506 后支持 .env.local 优先于 .env,并支持通过 BETTAFISH_ENV_FILE 显式指定 env 文件。
  • scripts/dev/start_local.py 已成为当前推荐的纯本地启动入口;它负责本地 env 选择、数据库占位值校验,并支持 --frontend-mode dev|build|skip 三种模式,其中默认 dev 模式会同时启动 Vite HMR + Flask API
  • scripts/dev/start_local.py 已在 T-527 中移除未被后端消费的 BETTAFISH_FRONTEND_MODE 环境变量写入;当前真正驱动开发态跳转的只有 BETTAFISH_FRONTEND_DEV_URL
  • infra/python/requirements-local.txt 已在 T-508 中建立,成为纯本地主站启动的 runtime 依赖基线;它与根 requirements.txt 的“全仓库/全能力”依赖角色正式分离。
  • scripts/dev/bootstrap_local.py 已在 T-508 中建立,负责统一准备本地 Python 依赖、Playwright Chromium 与前端依赖,作为 start_local.py 之前的 bootstrap 入口。
  • 2026-04-08 已在本机 Conda Python 3.13 环境下完成纯本地主站启动验证:补齐最小依赖、修复 MindSpider 的裸 config 导入污染,并确认 http://127.0.0.1:5000 返回 200
  • 2026-04-08 已进一步确认 python -m pip install --dry-run -r infra/python/requirements-local.txt 可在本机 Python 3.13 环境下完成解析,这意味着纯本地启动链路已经从“当前环境碰巧能跑”收口到“仓库内有可复现 bootstrap 基线”。
  • .env.local.example 已成为纯本地默认模板;根目录 .env.example 继续作为通用基础模板,兼容 Docker 使用场景。
  • 主 README 与英文 README 当前都已把“纯本地启动”提升为默认推荐方式,Docker 已降级为可选兼容启动路径。
  • scripts/dev/start_local_stack.ps1 已在 T-509 / T-522 / T-523 后成为 Windows 平台的一键启动 canonical 脚本;它负责 .env.local 预检、缺依赖时自动 bootstrap、5000/9527 端口复用检测、可选 -FrontendPort 端口覆盖与 PostgreSQL 自修复衔接。
  • apps/web_ui/vite.config.ts 已在 T-522 中切换为“开发走 /,构建走 /static/frontend/”的双模式配置,并为 /api/socket.io 提供到 http://127.0.0.1:5000 的开发代理。
  • --frontend-mode build / --build-frontend 的当前口径已在 T-527 中统一为 Flask / Docker 单服务部署兼容模式,而不是本地开发主入口。
  • T-528 已进一步确认:当前前端具备独立静态托管基础,但要真正实现 API-only 后端,还必须先收口前端 URL 配置、报告预览 / 下载链接与引擎 iframe 的端口耦合。
  • 根目录 start_local.bat 已在 T-509 中建立,作为面向 Windows 用户的极薄便捷入口;真实启动逻辑继续集中在 scripts/dev/
  • scripts/dev/prepare_local_postgres.py 已在 T-510 中建立,成为纯本地 PostgreSQL 的 canonical 诊断/建库/schema 初始化入口;它统一处理 env 解析、端口检查、数据库存在性校验与对 services/crawler/mindspider/schema/init_database.py 的调用。
  • services/shared/config/app_settings.py 已在 T-510 中将默认 DB_PORT3306 修正为 5432,从而与默认 DB_DIALECT=postgresql 保持一致。
  • scripts/dev/repair_local_postgres.py 已在 T-515 建立,成为纯本地 PostgreSQL 的项目自管修复入口;当系统 5432 实例不可用或凭据不匹配时,它会在 var/db/postgres-local/ 下拉起项目自管实例,并把 .env.local 切到 127.0.0.1:55432
  • 当前 .env.local 已切换到 DB_HOST=127.0.0.1DB_PORT=55432DB_DIALECT=postgresql,纯本地数据库链路现在优先依赖仓库内的项目自管 PostgreSQL,而不再依赖系统实例管理员密码。
  • var/logs/postgres-local.log 当前承接项目自管 PostgreSQL 的日志;var/logs/t515-start-local-stdout.logvar/logs/t515-start-local-stderr.log 记录了修复后的首次一键启动验证。
  • services/crawler/mindspider/schema/init_database.py 已在 T-515 修复 Windows psycopg 异步驱动与 ProactorEventLoop 的兼容问题;scripts/dev/prepare_local_postgres.py 也已补齐本地化“数据库不存在”场景下的维护库回退建库逻辑与 schema 初始化时的 PYTHONPATH 修正。
  • Dockerfile.runtime-hotfixMindSpider/config.py.exampleMindSpider/requirements.txt 已在 T-112 中删除,不再属于当前仓库结构的保留兼容面。
  • docs/runtime-retention-assessment.md 已在 T-113 中建立,用于记录 var/ 下冷备份、结构性空目录与 scratch 目录的保留边界。
  • var/backups/runtime-migration/20260407/t108/ 当前约 385.27 MB,仍是迁移回滚基线,不应直接删除。
  • T-114 已完成 var/output/chrome-headless/ 的受控清理;当前 var/output/2.49 MB,仅保留 screens/ 截图产物与 .gitkeep

因此,结构优化不应回退到旧结构,而应继续沿着 apps/ + backend/ 的方向收口。

3.3 当前主要结构问题

  • 根目录职责过重。
  • 一级目录命名风格混用。
  • 新旧前后端结构并存但未完全闭合。
  • 配置层分裂。
  • 依赖管理分裂。
  • 脚本入口散落。
  • 运行时目录与源码混放。
  • 外部子模块 MediaCrawler 与主仓库耦合过深。
  • 主仓库基础自动化守门已建立,但当前仍属于最小链路,尚未覆盖 vendor/mediacrawler 与完整运行依赖矩阵。

4. 已确认的目标目录方向

当前已经确认的目标目录骨架如下:

AI-Agent-Platform/
├── apps/
├── services/
├── tools/
├── infra/
├── docs/
├── tests/
├── vendor/
├── research/
└── var/

职责约定:

  • apps/:面向用户的应用装配层,例如 Web API、Web UI、引擎控制台。
  • services/:业务服务层,包括引擎、爬虫、共享基础模块。
  • tools/:运维、导出、重建、开发辅助脚本。
  • infra/:Docker、compose、部署和环境编排。
  • vendor/:外部子项目或第三方源码依赖。
  • research/:研究资产、模型资产和不属于主应用层的实验内容。
  • var/:日志、报告、浏览器数据、数据库卷、临时输出等运行时目录。

5. 已确认的重要决策

决策 D-001

本轮结构优化不拆微服务,只做单仓库内的工程化分层。

原因:

  • 当前系统内部模块高度耦合。
  • Docker 和运行链路已经以单应用方式组织。
  • 先把仓库结构收口,比先拆服务更有价值。

决策 D-002

当前最优先做的不是目录大搬迁,而是先隔离运行目录和构建产物。

原因:

  • 这是对根目录可读性提升最大的低风险动作。
  • 也是后续迁移 apps/services/ 的前置条件。

决策 D-003

apps/backend/ 代表的是正确演进方向,应升级为正式应用层,而不是回退到旧单文件结构。

决策 D-004

MediaCrawler 应保持“外部依赖 + 适配层”策略,不在本轮直接深度内化。

决策 D-005

后续每完成一个结构优化任务,都必须同时更新:

  • docs/project-structure-optimization-tasks.md
  • docs/project-structure-optimization-memory.md

决策 D-006

运行时目录统一以后,优先统一主机侧目录到 var/,但在容器内暂时保留旧路径兼容挂载。

原因:

  • 本轮目标是先完成“目录归属统一”,而不是一次性清空所有旧路径引用。
  • MediaCrawler 与部分旧模块仍存在历史路径依赖,直接删除兼容路径风险过高。
  • 兼容挂载可以把风险压缩到最小,同时为后续 apps/services/ 迁移争取缓冲区。

决策 D-007

根目录清理遵循“临时产物先删、旧运行目录先迁后删、兼容入口按任务阶段收口”的顺序,不在 T-104 一次性强删所有历史文件。

原因:

  • 根目录残留并不都属于同一风险等级,必须分类处理。
  • crawler_browser_data/db_data/ 等目录包含高风险运行态数据。
  • 报告类 root wrapper、低价值基础 shim 与根目录 Docker / Compose 兼容副本已在 T-519T-520T-521 完成回收;后续文件级兼容入口评估重点收敛为 app.pystart_local.bat

决策 D-008

T-201 只建立 apps/ 目录骨架和职责说明,不在同一步骤直接搬迁代码。

原因:

  • 先建立一级目录边界,可以让后续迁移有明确承接点。
  • 将“建骨架”和“搬代码”拆开,有助于控制风险、简化验证。
  • 当前仓库仍存在多个兼容入口与历史导入路径,直接混合迁移容易放大回归面。

决策 D-009

前端源码迁移到 apps/web_ui/ 后,构建产物暂时继续输出到 static/frontend/,并保留根目录 frontend/ 兼容壳。

原因:

  • 当前 Flask 与容器静态资源链路已经稳定依赖 static/frontend/
  • 将“源码迁移”和“静态资源部署路径调整”拆开,可以显著降低回归风险。
  • 根目录兼容壳可以让旧的前端命令继续工作,为后续 T-205 提供缓冲。

决策 D-010

Web API 装配层迁移到 apps/web_api/ 后,显式把 Flask 的 static_foldertemplate_folder 指回仓库根目录,同时保留根目录 app.py 兼容 shim。

原因:

  • 迁移后的模块位置变化会影响 Flask 默认的静态文件与模板搜索目录。
  • 显式目录配置可以避免迁移后 static/frontend/templates/index.html 失效。
  • 根目录兼容 shim 能保持 python app.py 不被立即打断,同时允许 Docker 优先使用新入口。

决策 D-011

引擎控制台迁移到 apps/engine_console/ 后,保留根目录 SingleEngineApp/ 作为兼容 wrapper 目录,同时把 apps/web_api 内部脚本路径切到新目录。

原因:

  • 真实脚本若继续由 apps/web_api 指向旧目录,会让新结构失去收口意义。
  • 根目录兼容 wrapper 可以让 README 中的旧命令和外部使用习惯继续工作。
  • 先迁真实入口、再保留薄兼容层,是当前阶段风险最低的收口方式。

决策 D-012

在真正迁移五大引擎代码前,先建立 services/engines/ 目录骨架,并用 README 固化目标映射。

原因:

  • 先固定服务层命名和边界,可以减少后续批量搬迁时的决策噪音。
  • 这一步几乎不触碰业务逻辑,适合作为从应用层迁移切换到服务层迁移的低风险过渡。
  • 后续 services/shared/、配置收口和引擎迁移都可以围绕这个骨架推进。

决策 D-013

在统一配置入口前,先建立 services/shared/ 公共层骨架,并把 configllmutilslogging 作为首批共享能力分类。

原因:

  • 当前配置与公共能力分散在根目录和多个引擎内部,必须先明确分类边界。
  • 先建立共享层骨架,有助于后续配置收口时避免目录结构再次反复。
  • 这一步仍然是低风险动作,适合在真正搬迁共享代码前先固化目标结构。

决策 D-014

配置统一优先采用“共享基类 + 兼容 shim”策略,而不是一次性删除所有引擎专属 Settings 类。

原因:

  • 各引擎当前字段集合仍有明显差异,直接硬合并会放大回归风险。
  • 先统一 .env 定位、加载优先级和共享主入口,可以先解决最分裂的基础设施问题。
  • 保留各引擎自己的 Settings 类名称与模块路径,可以把外部导入影响降到最低。

决策 D-041

纯本地启动链路采用“.env.local 优先,.env 保留给 Docker/兼容场景”的双轨策略,不直接把 Docker 配置和本地配置混在同一个 env 文件里。

原因:

  • 用户当前明确要求把默认启动方式切到纯本地,但仓库仍需保留 Docker 兼容能力。
  • 直接把 .env.example 改成 Docker / 本地共用且自动切换,会放大数据库主机名、主机绑定地址等配置串线风险。
  • 通过 .env.local 优先级和 scripts/dev/start_local.py 统一入口,可以在不破坏现有 Docker 兼容链路的前提下,把默认体验切到纯本地。

决策 D-042

对于“仓内无有效引用 + 已有 canonical 替代路径 + 不承接默认命令入口”的低风险兼容文件,允许在双重复核后直接执行物理删除,而不再长期保留只读别名文件。

原因:

  • 继续保留这类文件,会让根目录边界重新变模糊。
  • T-112 已验证这种删除试点风险较低,且能实际减少兼容噪音。
  • 把删除门槛明确下来,有助于后续继续清理真正无价值的兼容残留。

决策 D-043

运行时目录中的“空目录”必须按其所属父级语义处理,而不是统一按空目录批量删除。

原因:

  • PostgreSQL、Chromium profile、报告骨架和日志目录都可能天然包含空目录,这些目录不等于垃圾。
  • 真正值得回收空间的对象通常是 scratch/cache 子树,例如 var/output/chrome-headless/,而不是数据库卷或登录态目录。
  • T-113 已证明,仅凭“当前为空”不足以判断目录可删。

决策 D-044

纯本地启动场景下,优先修复“导入边界污染”和“驱动兼容性”问题,而不是继续把 Docker 当作默认兜底运行方式。

原因:

  • T-506 已经把默认启动方式切到纯本地,后续如果仍把 Docker 视为默认兜底,就会削弱结构优化对真实开发体验的改善效果。
  • 本轮启动失败首先暴露的是 MindSpiderimport config 污染全局模块命名空间,以及 PostgreSQL 异步驱动对 Python 3.13 的兼容性问题;这两类问题都属于结构边界问题,应优先在代码层修正。
  • 把这类问题修掉后,主站入口已经可以在本机直接启动并返回 200,说明“修边界”比“回退到 Docker”更符合当前项目目标。

决策 D-045

纯本地启动依赖不再强行复用根 requirements.txt,而是建立独立的 runtime 依赖清单与 bootstrap 入口,优先保障“主站可本地启动”的可复现性。

原因:

  • requirements.txt 当前同时承载了 Web 主站、爬虫、报告、研究/训练等多类依赖,其中包含对 Python 3.13 不友好的旧版固定约束,不适合作为纯本地启动的唯一入口。
  • 当前用户需求是把默认启动方式切到纯本地,并让项目可以直接拉起;这要求先收口一条稳定、可执行、文档可描述的 bootstrap 链路,而不是一次性解决全量研究依赖矩阵。
  • 通过 infra/python/requirements-local.txtscripts/dev/bootstrap_local.py,可以把“主站本地启动”与“完整研究/训练依赖”分层处理,既不掩盖遗留问题,也能显著改善当前开发体验。

决策 D-046

Windows 可以保留根目录 start_local.bat 作为用户向一键启动入口,但真实装配逻辑必须统一收口到 scripts/dev/start_local_stack.ps1

原因:

  • 用户明确需要“一键启动”,根目录提供极薄入口能显著降低使用门槛,尤其适合双击启动和命令行快速拉起。
  • 如果把真实逻辑继续散落在根目录 batch 文件中,会重新放大结构混乱问题,与本轮“目录边界清晰化”的目标相冲突。
  • 让 batch 只做转发、让 scripts/dev/ 承载真实逻辑,既保留易用性,也保持结构治理的一致性。

决策 D-047

当纯本地已经成为默认启动方式,且 CI、README 与结构文档都已经切到 infra/docker/ 后,可以删除根目录 Dockerfiledocker-compose*.yml 兼容副本,避免继续维护两套 Docker 入口。

原因:

  • 当前用户已经明确以纯本地为主,Docker 只需保留在 infra/docker/ 下的可选兼容链路。
  • CI、README 与结构文档都已稳定指向 infra/docker/*,根目录副本的主要兼容价值已经消失。
  • Docker / compose 文件不是像 start_local.bat 那样的薄转发入口,继续保留只会增加配置漂移和认知噪音。

决策 D-048

执行 T-114 时优先采用“先核对活跃进程实际使用的 profile 路径,再删除仓内闲置 scratch 子树”的策略,而不是因为主站仍在运行就一刀切推迟空间回收。

原因:

  • 本机 5000 端口上的 apps.web_api 主站与 scripts.dev.start_local 监控进程仍在运行,直接停服会打断当前本地工作流。
  • 通过进程命令行可以确认活跃 Chrome / Playwright 使用的是用户目录下的 ms-playwright profile,而不是仓内 var/output/chrome-headless
  • 这样可以在不影响主站可用性的前提下,安全删除已经陈旧的 scratch 目录,并保留 var/output/screens/ 等真实产物。

决策 D-015

爬虫层也采用“先建骨架、后迁真实实现”的策略,优先在 services/crawler/ 中拆出 mindspideradaptersweb 三个目标方向。

原因:

  • 当前爬虫能力同时分散在 MindSpider/backend/crawler/ 和嵌套的 MediaCrawler 中。
  • 先明确三条边界,可以避免后续迁移时把主爬虫、Web 管理层和外部适配层继续混在一起。
  • 这一步仍然不直接碰业务代码,适合作为服务层迁移中的低风险推进。

决策 D-016

引擎迁移优先采用“真实代码迁入 services/engines/* + 根目录同名包保留 __path__ 兼容壳”的策略,而不是一次性全仓替换所有旧包名导入。

原因:

  • 当前仓库内仍有测试、脚本、兼容入口和历史逻辑继续使用 InsightEngineReportEngine 等旧包名。
  • 先建立兼容包可以在不大规模改业务代码的前提下完成目录迁移,显著降低回归面。
  • 应用层可以逐步切到 services.engines.*,而外部和历史调用方保持不中断。

决策 D-017

工具层迁移优先采用“真实脚本迁入 tools/ + 根目录旧脚本保留 wrapper”的策略,而不是立即删除所有历史 CLI 文件。

原因:

  • 当前仍可能存在人工命令习惯、文档命令和外部自动化直接调用根目录脚本。
  • 先把真实实现移入 tools/,可以完成目录收口,同时把兼容风险压到最低。
  • wrapper 方案与应用层、服务层前面的兼容策略保持一致,便于后续统一回收历史入口。

决策 D-018

基础设施层迁移优先采用“真实 Docker / compose 文件迁入 infra/docker/ + 根目录同名文件保留兼容副本”的策略,而不是立即删除根目录历史入口。

原因:

  • Dockerfile 不是像 Python 模块那样可转发的轻量入口,直接删除根目录文件会立刻打断旧 build 命令。
  • 先把 CI、README 和结构文档切到 infra/docker/,可以先完成目录收口,再逐步回收历史命令入口。
  • 这一策略与 apps/services/tools/ 的兼容迁移方式保持一致,更利于控制变更风险。

决策 D-019

爬虫主实现迁移优先采用“真实 MindSpider 代码迁入 services/crawler/mindspider/ + 根目录 MindSpider/ 保留兼容壳 + MediaCrawler 旧路径暂留”的策略,而不是在同一步骤里同时完成主爬虫迁移和外部子项目 vendor 隔离。

原因:

  • MediaCrawler 仍是一个完整外部子项目,和主爬虫代码一起强拆会把 T-402T-403 风险叠加。
  • 先完成 MindSpider 主体目录收口,可以让 apps/web_apibackend/crawler 和文档优先切到 canonical 路径。
  • 通过 runtime_paths.py 暂时解析旧 MediaCrawler 路径,可以为后续 T-403 / T-404 继续隔离留出缓冲。

决策 D-020

完成 T-403 时继续采用“外部子模块迁入 vendor/ + MindSpider 保留兼容壳 + 运行时 helper 统一解析”的策略,而不是在迁移同一步骤里引入新的适配层抽象。

原因:

  • T-403 的目标是先完成目录归属与基础设施路径收口,避免把目录迁移、适配器重构和运行链路修改一次叠加。
  • runtime_paths.py 已经是主仓库内统一的 MediaCrawler 定位入口,先把它切到 vendor/mediacrawler/ 能显著降低迁移风险。
  • 先完成 vendor 隔离,再执行 T-404 提炼 mediacrawler_adapter,任务边界更清晰,也更容易验证回归。

决策 D-021

完成 T-404 时优先采用“适配层承接真实实现 + runtime_paths.py 保留兼容转发”的策略,而不是立刻删除历史 helper 或一次性改完所有旧兼容入口。

原因:

  • runtime_paths.py 已经被历史脚本和兼容层引用,直接删除会把结构优化任务和回归风险绑定在一起。
  • 先让 backend/crawler/ 与 MindSpider 主流程切到 mediacrawler_adapter,再把 runtime_paths.py 降级为 wrapper,可以同时获得清晰边界和低风险迁移。
  • 这样可以把 T-405 的重点聚焦在 crawler_web.py 等兼容入口回收,而不是反复处理路径解析逻辑。

决策 D-022

完成 T-405 时优先采用“backend/crawler/ 成为 canonical export surface + 根目录 crawler_web.py 仅保留最薄重导出 shim”的策略,而不是继续让应用层直接依赖 backend.crawler.routes,或在未确认外部引用前直接删除历史入口。

原因:

  • 先把 canonical 导出面固化在包级别,可以让 apps/web_api/、后续脚本和文档都依赖同一个稳定入口。
  • 根目录 crawler_web.py 仍可能被历史命令或外部调用引用,保留最薄 shim 比直接删除的风险更低。
  • 这样可以把兼容负担压缩到单一文件,同时避免根目录再次承载真实装配逻辑。

决策 D-023

完成 T-601 时将 SentimentAnalysisModel/ 归类为“研究/训练资产集合”,并把后续动作定义为迁入 research/,而不是继续视作主应用一级目录,或尝试把整组内容直接改造成单一 vendor 依赖。

原因:

  • 目录内主体内容是数据集、模型权重、Notebook 与训练/预测样例脚本,结构上更接近研究资产而不是应用装配层。
  • 仓内主运行链路对该目录的直接 Python 引用只有一处,而且只是 WeiboMultilingualSentiment/ 的本地缓存路径,不构成整目录留在根层的充分理由。
  • 目录由多个异构实验子项目组成,不具备单一上游仓库边界,直接按 vendor 思路处理会模糊“研究资产”和“运行依赖”的职责分界。

决策 D-024

执行 T-602 时采用“先迁走明确的研究资产子目录,暂留 WeiboMultilingualSentiment/ 作为根目录兼容/运行时缓存落点”的分步迁移策略,而不是立即把整个 SentimentAnalysisModel/ 目录彻底移出根目录。

原因:

  • 当前唯一真实运行依赖只落在 WeiboMultilingualSentiment/ 一处,分步迁移可以最大化降低对 InsightEngine 的影响。
  • 先迁走 4 组明确的研究模型目录,已经能显著降低根目录噪音并建立 research/ 承接边界。
  • 待运行时缓存路径完成配置化后,再决定是否彻底移除根目录兼容目录,会比当前一步到位更稳妥。

决策 D-025

执行 T-503 时优先采用“主仓库测试统一回收至 tests/ + vendor/mediacrawler 自带测试保持独立”的策略,而不是把第三方子模块测试一起并入主仓库默认测试入口。

原因:

  • vendor/mediacrawler 是外部子模块,测试依赖和运行前提与主仓库并不完全一致。
  • 先把主仓库自己的测试边界拉清,可以尽快为 T-504 的自动化守门建立可控的默认测试集合。
  • 第三方测试后续若要纳入 CI,应单独设计依赖安装和隔离策略,而不是在当前阶段混到同一入口里。

决策 D-026

执行 T-504 时优先采用“最小可执行 CI + 包级惰性导出削减初始化副作用 + 根级兼容 shim 补足历史导入”的策略,而不是把整份 requirements.txt、PDF 渲染依赖和模型相关重型依赖一次性全部塞进默认质量流水线。

原因:

  • 主仓库当前最稳定、最有结构优化价值的守门对象,是统一后的测试目录与前端类型系统,而不是完整业务运行栈。
  • 将最小依赖集与惰性导出结合,可以让 pytest tests/unit tests/integrationvue-tsc 先稳定落地,避免 CI 被重型依赖和无关初始化副作用拖垮。
  • 根级兼容 shim 与惰性导出策略延续了本轮结构优化一贯的“canonical 路径收口 + 薄兼容层保留”的低风险迁移原则。

决策 D-027

执行 T-603 时优先采用“先配置化、再决定最终迁移目的地”的策略,而不是立即把 WeiboMultilingualSentiment/ 从根目录彻底移走。

原因:

  • SentimentAnalysisModel/WeiboMultilingualSentiment 当时仍是默认运行链路可用的兼容缓存路径,直接搬迁会引入额外回归风险。
  • 先用 INSIGHT_SENTIMENT_MODEL_DIR 打通相对路径与绝对路径覆盖,可以在不改变默认行为的前提下解除代码硬编码。
  • 这让后续把缓存迁往 var/ 或独立模型缓存目录时,只需要调整配置与部署文档,而不必再改运行时代码。

决策 D-028

执行 T-505 时优先采用“第三方子模块独立 workflow + 固定 smoke-test 子集 + Python 3.11 版本钉住”的策略,而不是把 vendor/mediacrawler 的测试直接并回主仓库默认质量守门。

原因:

  • vendor/mediacrawler 的依赖矩阵与主仓库当前使用的 Python 3.13 本地环境并不一致,至少 Pillow==9.5.0 已在 3.13 本地安装验证中暴露兼容问题。
  • 通过独立 workflow 固定安装 vendor 自身依赖,可以保留主仓库最小守门链路的稳定性,不让第三方包版本波动反向污染主仓库 CI。
  • 先收敛一组不依赖数据库、Redis、MongoDB 与代理服务的 smoke tests,更符合当前“先建立可执行边界,再逐步扩大覆盖面”的低风险推进方式。

决策 D-029

执行 T-604 时优先采用“把默认模型缓存迁入 var/ + 保留旧路径可配置回切”的策略,而不是直接删除根目录 SentimentAnalysisModel/WeiboMultilingualSentiment

原因:

  • 运行时模型缓存本质上属于可再生的运行期数据,迁入 var/ 与本轮目录治理原则一致。
  • 旧根目录仍保留示例脚本与文档,继续允许通过 INSIGHT_SENTIMENT_MODEL_DIR 回切,能够降低对历史使用方式的破坏性。
  • 同时移除对运行时缓存目录的 sys.path 注入,可以把模型文件缓存和 Python 源码目录彻底解耦。

决策 D-030

执行 T-605 时优先采用“正式研究示例迁入 research/ + 根目录旧路径保留最薄 wrapper”的策略,而不是立刻删除根目录 SentimentAnalysisModel/WeiboMultilingualSentiment/

原因:

  • 当前仍可能存在人工命令、历史文档或环境变量把用户带到旧根目录路径,直接删除会制造不必要的硬中断。
  • 先把正式示例迁入 research/,可以明确 canonical 目录归属,同时用 wrapper 保持旧命令可继续运行。
  • 这种做法能顺手修复旧示例脚本已不可 py_compile 的问题,并把研究示例与运行时缓存职责进一步拆开。

决策 D-031

执行 T-406 时优先采用“先收口根目录 MindSpider/ 的说明、目录树与 CLI 指引,再决定是否继续删除兼容文件”的策略,而不是直接裁掉 requirements.txtconfig.py.example

原因:

  • 当前根目录 MindSpider/ 的主要问题已经从“真实代码混放”转为“用户认知上仍像正式源码目录”,先修正文档边界的收益最高、风险最低。
  • 旧命令 python MindSpider/main.py ...、旧依赖安装路径和历史复制配置文件方式仍可能被外部使用,先把 canonical 路径写清楚,再评估删除兼容文件更稳。
  • 这能把后续是否继续压缩兼容壳的决策,建立在更准确的用户入口和文档边界之上。

决策 D-032

执行 T-407 时优先采用“先把 MindSpider/config.py.exampleMindSpider/requirements.txt 变成明确可用的兼容入口,再决定是否删除”的策略,而不是直接移除这两个文件。

原因:

  • 这两个文件承接的是历史复制配置和依赖安装习惯,直接删除虽然能让根目录更干净,但会引入不可观测的外部断裂风险。
  • 当前最主要的问题不是它们存在,而是它们此前过于像占位文件、对 canonical 路径提示不清。
  • 先把兼容文件语义写清楚,可以把“是否删除”从高风险动作,降级成后续可单独评估的收尾动作。

决策 D-033

执行 T-105 时优先采用“先删除 inventory 中已明确标记为可立即删除的 .codex-tmp-* 临时残留”的策略,而不是一开始就进入 logs/db_data/ 等可能含有效运行态数据的目录。

原因:

  • 这些 .codex-tmp-* 目录和压缩包本身就是临时工作产物,删除风险最低,且能立刻降低根目录噪音。
  • 先完成一轮真实物理清理,有助于把结构治理从“文档规划”推进到“实际收口”。
  • 这样可以把高风险旧运行目录的后续处理,留到有更清晰归档策略时再推进。

决策 D-034

执行 T-106 时优先采用“迁移到 var/ + 按内容分层归档 + 删除根目录旧目录”的策略,而不是简单删除 logs/final_reports/*_streamlit_reports/output/

原因:

  • 这些轻量运行目录虽然不再应停留在根目录,但其中仍有活跃日志、任务状态、报告中间产物和示例输出,直接删除会造成无谓的信息损失。
  • 迁移到 var/ 后,既能让运行时目录边界与 utils/runtime_paths.py、Docker volume、工具脚本保持一致,又能真正降低根目录噪音。
  • 先把低风险运行目录做成“已物理收口”的真实案例,可以为后续更敏感的高风险目录迁移提供操作模板。

决策 D-035

执行 T-107 时优先采用“先复核高风险目录是否仍是 canonical 路径,再决定是否冷迁移”的策略,而不是看到 crawler_browser_data/db_data/ 仍在根目录就直接复制或删除。

原因:

  • docker-compose.ymlDockerfile 已经切到 var/crawler/browser_datavar/db/postgres,说明当前核心问题更可能是旧数据遗留,而不是代码仍依赖根目录。
  • var/ 目标目录当前仍几乎为空,意味着直接覆盖式迁移会把“旧数据是否还需要”与“新路径是否已正式接管”这两个问题混在一起,风险过高。
  • 先完成体量、挂载边界、最近修改时间与目录结构复核,能把后续动作收敛成一个明确的冷迁移/归档任务,而不是模糊清理。

决策 D-036

执行 T-108 时优先采用“删除仍绑定旧根目录路径的退出容器并按当前 compose 重建”的策略,而不是直接复用旧的 bettafishbettafish-db 容器做验证。

原因:

  • 旧容器虽然已经退出,但 docker inspect 显示它们仍绑定 crawler_browser_data/db_data/logs/final_reports/ 等已经删除的历史根目录路径。
  • 如果继续复用旧容器,会把“数据已迁移到 var/”和“容器仍引用旧挂载”这两个状态混在一起,验证结论不可靠。
  • 先删除旧容器再重建,才能真正验证当前 infra/docker/ compose 配置与 var/ 目录接管链路已经闭环。

决策 D-037

执行“清理无用文件夹和文件”时优先采用“只删除明确无用的缓存目录”的策略,而不是进一步清理 var/、报告产物或浏览器 profile 内部缓存。

原因:

  • __pycache__/.pytest_cache/ 是确定性的可再生缓存,删除风险最低,且能显著降低工作区噪音。
  • var/ 下的报告、日志、浏览器数据与备份目录虽然部分看起来像缓存,但它们仍可能承载运行态、历史调试信息或迁移回滚能力,不能和编译缓存混为一谈。
  • 先把“无争议垃圾”清掉,可以在不伤害运行数据的前提下,继续推进仓库整洁度。

决策 D-038

执行 T-110 时优先采用“冻结最薄 shim、继续评估目录级兼容壳”的策略,而不是在没有明确外部依赖证据的情况下直接删除根目录 wrapper。

原因:

  • 根目录 app.pyconfig.pycrawler_web.py、报告脚本 wrapper、frontend/package.jsonSingleEngineApp/*.py 已经足够薄,继续删除它们的收益很小,但破坏旧命令和历史自动化的风险并不低。
  • 当前真正占据根目录认知空间的,不再是这些单文件 shim,而是 MindSpider/、根目录 Docker / compose 兼容副本和研究示例兼容目录。
  • 先把“薄 shim”和“目录级兼容壳”分层,能让后续删除动作更聚焦,也避免把兼容性风险扩散到不值得动的文件上。

决策 D-039

执行 T-110 时优先采用“共享 shim helper + 惰性导出统一实现”的策略,而不是继续让每个根目录兼容包各自维护一套相似但逐渐分叉的逻辑。

原因:

  • ForumEngine/MediaEngine/QueryEngine/InsightEngine/ReportEngine/ 本质上都是旧包名兼容层,保持一致的实现方式更容易长期维护。
  • MediaEngine/QueryEngine/ForumEngine/ 在本轮之前仍是 eager import,和 InsightEngine/ReportEngine/ 的惰性行为不一致;统一后可以减少导入副作用。
  • 共享 helper 还能把“兼容层应该有多薄”固化为代码约束,降低后续兼容包再次长胖的概率。

决策 D-040

执行 T-111 时优先采用“默认冻结 repo-root 约定型入口,优先把仅剩自引用的兼容文件列为删除候选”的策略,而不是把所有目录级兼容壳一刀切地纳入删除范围。

原因:

  • docker-compose.ymldocker-compose.override.ymlDockerfile 虽然主文档已经切到 infra/docker/,但它们仍承接 repo-root 默认命令习惯,这类入口的兼容价值高于删除收益。
  • MindSpider/main.pySentimentAnalysisModel/WeiboMultilingualSentiment/predict.py 仍被 README 明确保留为兼容命令,继续保留更符合当前对外说明。
  • 相比之下,MindSpider/config.py.exampleMindSpider/requirements.txtDockerfile.runtime-hotfix 在仓内几乎只剩文件自身和清单文档引用,更适合进入删除候选。

决策 D-041

执行 T-515 时优先采用“项目自管 PostgreSQL 实例接管纯本地工作流”的策略,而不是继续把“修好系统 5432 实例管理员凭据”作为唯一前置条件。

原因:

  • 当前用户对系统 PostgreSQL 服务缺少稳定的重载 / 重启权限,继续依赖修改 pg_hba.conf 的系统级修复路径,会把纯本地体验绑定到管理员操作窗口。
  • 仓库已经具备可直接复用的 PostgreSQL 二进制、标准化 .env.local 配置与 prepare_local_postgres 诊断入口,把它们收口成项目自管实例的自动修复链路,能显著降低后续重复排障成本。
  • 这种方案不会破坏用户机器上的系统 PostgreSQL 安装,必要时也仍可手动切回 5432,因此兼顾了当前可用性和后续回退弹性。

6. 已完成工作

2026-04-01

  • 完成整仓结构审计。
  • 识别出根目录职责过重、目录命名不统一、配置和依赖分裂、运行目录混入源码区等核心问题。
  • 生成结构分析与优化基线文档:
    • docs/project-structure-optimization-plan.md

2026-04-02

  • 创建结构优化执行型任务文档:
    • docs/project-structure-optimization-tasks.md
  • 创建结构优化长期记忆文件:
    • docs/project-structure-optimization-memory.md
  • 将当前工作拆成 6 个阶段、22 个任务项。
  • 明确了后续更新协议:每次任务执行后同步更新 task 文档和 memory 文档。
  • 完成 T-101
    • 建立 var/ 统一运行时目录骨架。
    • 新增 utils/runtime_paths.py 作为运行时路径单一入口。
    • 将日志、最终报告、三引擎输出、浏览器数据、数据库卷、临时输出统一纳入 var/
  • 完成 T-102
    • 收紧 .gitignore
    • 收紧 .dockerignore
    • 明确 var/ 只跟踪目录骨架,不跟踪运行时内容。
  • 完成 T-103
    • 更新 docker-compose.yml 主机侧 volume 到 ./var/...
    • 更新 Dockerfile 预创建新旧运行路径。
    • 保留容器内旧路径兼容挂载,避免未迁移模块立即失效。
  • 完成 T-104
    • 新增 docs/project-structure-root-cleanup-inventory.md
    • 明确根目录残留的四类边界:立即删除、迁移后删除、暂时保留兼容、目标态保留目录。
    • 记录旧运行目录的规模信息与处理顺序,为后续清场和目录迁移提供依据。
    • 记录 ReportEngine/utils/config.pycrawler_web.pyexport_pdf.pyreport_engine_only.pyregenerate_latest_*.pyDockerfile.runtime-hotfix 的过渡角色。
  • 完成 T-201
    • 新建 apps/ 一级目录骨架。
    • 建立 apps/web_api/apps/web_ui/apps/engine_console/ 三个应用层承接目录。
    • 通过 README 说明当前源目录与后续迁移目标,明确本阶段只建边界、不搬代码。
  • 完成 T-202
    • 将真实前端工程迁移到 apps/web_ui/
    • 更新 Dockerfile 前端构建路径到 apps/web_ui/
    • 保持构建产物继续输出到 static/frontend/
    • 将根目录 frontend/ 降级为命令转发兼容壳。
  • 完成 T-203
    • 将真实 Flask 装配入口迁移到 apps/web_api/app.py
    • apps/web_api 新增模块启动入口。
    • 显式配置 Flask 静态目录和模板目录继续指向仓库根目录。
    • 将根目录 app.py 降级为兼容 shim。
    • 更新 Dockerfile 默认启动命令到 python -m apps.web_api
  • 完成 T-204
    • 将真实 Streamlit 引擎控制台脚本迁移到 apps/engine_console/
    • 修正迁移后脚本的项目根路径解析逻辑。
    • apps/web_api 内部控制台脚本路径切到新目录。
  • 完成 T-205
    • 为根目录 frontend/app.pySingleEngineApp/ 保留兼容壳。
    • 形成“新入口可用 + 旧入口不中断”的过渡闭环。
  • 完成 T-301
    • 建立 services/engines/ 服务层骨架。
    • 建立五大引擎目标子目录并记录迁移映射。
  • 完成 T-303
    • 建立 services/shared/ 公共层骨架。
    • 建立 configllmutilslogging 四类共享能力目标子目录。
  • 完成 T-304
    • 建立 services/shared/config/ 的真实共享配置基础设施。
    • 将根目录 config.py 降级为兼容 shim。
    • 将多个引擎配置模块统一切换到 SharedSettings 基类。
  • 完成 T-401
    • 建立 services/crawler/ 服务层骨架。
    • 建立 mindspideradaptersweb 三类爬虫目标子目录。
  • 完成 T-302
    • 将五大引擎真实代码迁移到 services/engines/insight/media/query/report/forum/
    • apps/web_api/apps/engine_console/ 的核心导入切换到 services.engines.*
    • 将服务层引擎 README 从“骨架预留”更新为“真实代码已迁入”。
  • 完成 T-305
    • 为五大根目录旧包名建立兼容导入壳。
    • 通过扩展 __path__ 将旧包导入链路转发到 services/engines/* 中的真实代码。
  • 完成 T-501
    • 建立 tools/tools/reports/ 目录。
    • 将五个报告类根脚本的真实实现迁入 tools/reports/
    • 将根目录旧脚本与 scripts/reports/export_pdf.py 降级为兼容 wrapper。
    • 将报告类工具脚本切到 services.engines.reportutils.runtime_paths.py
  • 完成 T-502
    • 建立 infra/infra/docker/ 文档说明。
    • 将真实 DockerfileDockerfile.runtime-hotfixdocker-compose.ymldocker-compose.override.yml 迁入 infra/docker/
    • 将根目录同名 Docker / compose 文件降级为兼容副本,并保留旧命令可用。
    • 将 CI 与 README 文档切换到 infra/docker/ canonical 路径。
  • 完成 T-402
    • 将 MindSpider 主爬虫真实代码迁入 services/crawler/mindspider/
    • 建立 runtime_paths.py,用统一 helper 解析 MediaCrawler 过渡路径。
    • apps/web_api/ 切到导入 services.crawler.mindspider.main
    • 将根目录 MindSpider/ 降级为兼容壳,并保留 DeepSentimentCrawling/MediaCrawler/ 旧路径等待后续 vendor 隔离。
  • 完成 T-403
    • MediaCrawler 子模块工作树迁入 vendor/mediacrawler/
    • .gitmodules、本地 submodule 配置、Docker / compose 路径与运行时路径 helper 统一切到 vendor canonical 目录。
    • 将根目录 MindSpider/ 收口为纯兼容壳,不再承载真实 MediaCrawler 源码。
    • 新增 vendor/README.md,明确 vendor/ 的一级目录职责。
  • 完成 T-404
    • 建立 services/crawler/adapters/mediacrawler_adapter.py,统一承接 MediaCrawler 路径与配置访问。
    • backend/crawler/runtime.pyservices/crawler/mindspider/main.pyplatform_crawler.py 切到适配层 API。
    • services/crawler/mindspider/runtime_paths.py 降级为兼容 wrapper,避免历史调用链立即中断。
  • 完成 T-405
    • backend/crawler/__init__.py 收口为爬虫 Web 控制的 canonical 导出面。
    • 将根目录 crawler_web.py 收口为仅重导出 backend.crawler 的最薄兼容 shim。
    • apps/web_api/app.py 中的蓝图导入切到 from backend.crawler import crawler_bp
  • 完成 T-601
    • 盘点 SentimentAnalysisModel/ 的子目录结构、体积构成与仓库引用关系。
    • 确认该目录整体应归类为研究/训练资产集合,后续目标目录应为 research/
    • 确认主运行链路当前只对 WeiboMultilingualSentiment/ 存在可选本地缓存路径引用,需要在 T-602 中单独处理。
  • 完成 T-602
    • 建立 research/README.mdresearch/sentiment_models/README.md,明确研究资产目录职责。
    • 将 4 组情感分析研究模型目录迁入 research/sentiment_models/
    • 将根目录 SentimentAnalysisModel/ 收口为仅保留 WeiboMultilingualSentiment/ 的兼容/运行时缓存目录,并补充迁移说明。
    • 更新 README.mdREADME-EN.md 中的目录树与模型命令路径。
  • 完成 T-503
    • 建立 tests/unit/tests/integration/tests/e2e/tests/fixtures/ 分层骨架。
    • 将报告引擎与论坛监控相关测试从扁平目录/业务目录回收到统一测试目录。
    • 建立 pytest.ini 默认测试入口,并保留 tests/run_tests.py 兼容入口。
    • 将测试导入优先切到 services.engines.* canonical 路径。
  • 完成 T-504
    • 建立 .github/workflows/quality_ci.yml,接入 Python compile/lint/test 与前端 type-check 最小守门链路。
    • 修复 CrawlerControlForm.vue 的隐式 any 事件参数与 useCrawlerController.ts 的泛型字段恢复赋值错误,使现有 vue-tsc 脚本可直接纳入 CI。
    • 新增根级 openai_compat.py 兼容 shim,并将 services/engines/report/services/engines/report/utils/services/engines/report/nodes/ReportEngine/ 改为惰性导出,解除测试收集阶段对 PDF / 图表重型依赖的耦合。
  • 完成 T-603
    • services/shared/config/app_settings.py 中新增 INSIGHT_SENTIMENT_MODEL_DIR 共享配置项,默认保持根目录兼容路径不变。
    • services/engines/insight/tools/sentiment_analyzer.py 的模型工作目录解析改为可配置函数,并补充 workspace_dir / cache_dir 诊断信息。
    • services/engines/insight/services/engines/insight/tools/ 与根目录 InsightEngine/ 的包级导出改为惰性导出,降低新路径单测的导入副作用。
    • 新增 tests/unit/insight/test_sentiment_analyzer_paths.py,覆盖默认值、项目相对路径覆盖和绝对路径覆盖。
    • 更新 .env.exampleREADME.mdREADME-EN.mdSentimentAnalysisModel/README.mdresearch/sentiment_models/README.md,同步新配置入口与默认兼容行为。
  • 完成 T-505
    • 新增 .github/workflows/mediacrawler_ci.yml,将 vendor/mediacrawler 的隔离守门收口为独立 GitHub Actions 工作流。
    • 新增 tools/ci/run_mediacrawler_quality.pytools/ci/README.md,固定 vendor smoke tests 入口并保留本地可复用命令。
    • 更新 tools/README.mdvendor/README.md,明确第三方测试不并入主仓库默认 pytest,而是通过独立入口执行。
    • 将 vendor 守门显式钉住 Python 3.11,以规避本地 Python 3.13 与 vendor/mediacrawler 依赖矩阵不一致的问题。
  • 完成 T-604
    • INSIGHT_SENTIMENT_MODEL_DIR 的默认值切到 var/models/insight_weibo_multilingual,让默认运行时缓存脱离根目录。
    • 移除 services/engines/insight/tools/sentiment_analyzer.py 中对运行时模型目录的 sys.path 注入,避免把缓存目录继续当作源码路径使用。
    • var/README.md.gitignorevar/models/.gitkeep 中补齐模型缓存骨架说明与跟踪规则。
    • 扩展路径单测,确保新默认值与旧根目录兼容 override 同时成立。
    • 更新 README、研究说明与根目录清理清单,明确根目录 SentimentAnalysisModel/ 已降级为示例/兼容目录。
  • 完成 T-605
    • 将多语言情感分析正式研究示例迁入 research/sentiment_models/WeiboMultilingualSentiment/,补齐研究目录 README 与新的 CLI / demo / 交互脚本。
    • 将根目录 SentimentAnalysisModel/WeiboMultilingualSentiment/predict.py 改为最薄 wrapper,继续兼容旧命令。
    • 将根目录 SentimentAnalysisModel/ 与其子目录 README 改为兼容说明,停止把根目录位置描述为正式示例目录。
    • 更新主 README、英文 README 与 research/sentiment_models/README.md,把 canonical 路径与示例命令切换到 research/
    • 为研究示例补充 model/ 缓存 ignore 规则,避免研究样例缓存重新污染仓库。
  • 完成 T-406
    • 更新根目录 MindSpider/README.md,明确其仅作为兼容壳保留,并列出 canonical / legacy 两套入口命令。
    • 更新 services/crawler/mindspider/README.md,把推荐启动命令、数据库初始化命令、Python API 导入示例与浏览器登录缓存说明切到当前 canonical 结构。
    • 更新主 README 与英文 README 的目录树,明确根目录 MindSpider/ 是 wrapper,services/crawler/mindspider/ 才是正式 crawler 实现目录。
    • 在主 README 的爬虫单独使用章节中补充 canonical 命令与旧入口兼容关系,减少继续沿旧路径操作的概率。
  • 完成 T-407
    • MindSpider/config.py.example 从占位说明改为真正的兼容示例入口,显式导出 Settingssettings,并标明 canonical 来源。
    • MindSpider/requirements.txt 补充推荐命令与 canonical 路径说明,保留旧安装命令别名。
    • 更新 MindSpider/README.md,把 requirements.txtconfig.py.example 从“模糊残留”提升为“仍保留但应优先切换”的兼容文件。
  • 完成 T-105
    • 删除根目录 .codex-tmp-mediacrawler/.codex-tmp-mediacrawler2/.codex-tmp-mediacrawler.zip 三个 codex 临时残留。
    • 将根目录清理状态从“已有可删清单”推进为“已执行一轮真实物理删除”。
    • 保留 .gitignore.codex-tmp-* 的忽略规则,避免这些临时产物再次回流。
  • 完成 T-106
    • 将根目录 logs/final_reports/insight_engine_streamlit_reports/media_engine_streamlit_reports/query_engine_streamlit_reports/output/ 的内容迁入 var/ 下对应 canonical 目录。
    • 将仍有参考价值的历史日志归档到 var/logs/archive/legacy-root/20260407/,活跃日志、任务状态与报告产物改由 var/ 接管。
    • 收口 utils/forum_reader.pyservices/engines/report/utils/config.py 默认路径,避免新运行继续写回根目录旧目录。
  • 完成 T-107
    • 复核根目录 crawler_browser_data/db_data/ 的实际体量、结构和最近修改时间,确认其仍分别保留 337.49 MB47.78 MB 的旧数据。
    • 复核 Docker / compose 与运行路径引用已全部切向 var/crawler/browser_datavar/db/postgres,确认根目录高风险目录已不再是 canonical 运行位置。
    • 将问题收敛为“待执行一次带停进程前置检查的冷迁移/归档任务”,并建立 T-108 作为下一执行步骤。
  • 完成 T-108
    • 将根目录 crawler_browser_data/db_data/ 冷备份到 var/backups/runtime-migration/20260407/t108/,再完整复制到 var/crawler/browser_data/var/db/postgres/
    • 在文件数、文件集合与总字节数全部对账一致后,删除根目录 crawler_browser_data/db_data/
    • 删除仍绑定旧根目录路径的退出容器 bettafishbettafish-db,并按当前 compose 配置重建验证环境。
  • 完成 T-109
    • 全仓删除 __pycache__/.pytest_cache/,共清理 106 个 Python / pytest 缓存目录。
    • 将“清理无用文件夹和文件”收口为明确的低风险缓存清理动作,避免运行时数据继续和编译缓存混放。
  • 完成 T-110
    • 新增 utils/compat_shims.py,统一根目录兼容包的 canonical 路径挂接与惰性导出逻辑。
    • ForumEngine/InsightEngine/MediaEngine/QueryEngine/ReportEngine/ 收口到一致的 thin-shim 实现,减少包级导入副作用。
    • 新增 docs/root-compatibility-shim-inventory.md,把根目录兼容壳拆分为“可长期冻结的最薄 shim”“目录级兼容壳”“下一轮候选”三类。
    • SingleEngineApp/ 补齐 README,并新增 tests/unit/test_root_compat_shims.py 验证兼容包行为。
  • 完成 T-111
    • 以仓内代码、README 与结构文档为依据,完成目录级兼容壳的最终冻结/删除边界评估。
    • 明确 docker-compose.yml / docker-compose.override.yml / DockerfileMindSpider/main.py 系列 wrapper、SentimentAnalysisModel/WeiboMultilingualSentiment/predict.py 进入“冻结保留”。
    • 明确 Dockerfile.runtime-hotfixMindSpider/config.py.exampleMindSpider/requirements.txt 进入“删除候选”,并把下一步切到 T-112 删除试点。
  • 已完成验证:
    • python -m py_compile 通过。
    • ensure_runtime_dirs() 已成功创建全部 var/ 子目录。
    • docker compose -f docker-compose.yml -f docker-compose.override.yml config 通过。
    • docker compose -f infra/docker/docker-compose.yml -f infra/docker/docker-compose.override.yml config 通过。
    • 已核对根目录残留目录、兼容入口和历史路径引用现状。
    • 已核对 apps/ 目录骨架与任务映射一致。
    • 已验证 apps/web_ui/ 的前端构建链路与根目录兼容壳命令链路。
    • 已确认 Docker 前端构建阶段当前仅受外部镜像拉取失败阻塞,不是 apps/web_ui/ 路径配置错误。
    • 已确认 Web API 入口迁移后的 Python 语法层级无误,且仓库内无其他模块依赖旧版根目录 app.py 的内部符号。
    • 已确认引擎控制台迁移后的 Python 语法层级无误,且 apps/web_api 已切换为使用 apps/engine_console/ 的真实脚本路径。
    • 已确认 services/engines/ 骨架与五大引擎目标子目录已实际创建。
    • 已确认 services/shared/ 骨架与四个共享能力目标子目录已实际创建。
    • 已确认共享配置入口、根配置 shim 与多个引擎配置模块的统一加载链路可静态通过。
    • 已确认 services/crawler/ 骨架与三个爬虫目标子目录已实际创建。
    • 已确认五大引擎真实代码目录已实际迁入 services/engines/*
    • 已确认 apps/web_api/apps/engine_console/ 的核心导入已切到 services.engines.*
    • python -m compileall services/engines InsightEngine MediaEngine QueryEngine ReportEngine ForumEngine apps/engine_console apps/web_api 通过。
    • python -m compileall tools export_pdf.py regenerate_latest_html.py regenerate_latest_md.py regenerate_latest_pdf.py report_engine_only.py scripts/reports/export_pdf.py 通过。
    • 已确认 tools/reports/ 已承接五个报告类工具脚本,根目录历史入口已降级为 wrapper。
    • 已确认 infra/docker/ 已承接真实 Docker / compose 文件,根目录同名文件已降级为兼容副本。
    • 已确认 .github/workflows/docker_ci.yml 与 README 文档已优先引用 infra/docker/ canonical 路径。
    • python -m compileall services/crawler/mindspider MindSpider backend/crawler/runtime.py apps/web_api/app.py 通过。
    • 已确认 MindSpiderMindSpider.BroadTopicExtractionMindSpider.DeepSentimentCrawlingMindSpider.schema 兼容命名空间已同时指向 root 兼容目录与 canonical 目录。
    • 已确认 services.crawler.mindspider.runtime_paths 当前优先解析 vendor/mediacrawler/,并可返回对应 web_runtime.py
    • 已确认 backend.crawler.runtime.RUNTIME_PATH 当前已切到 vendor/mediacrawler/web_runtime.py
    • 已确认 git submodule status 当前已显示新路径 vendor/mediacrawler
    • python -m compileall services/crawler/adapters services/crawler/mindspider backend/crawler/runtime.py apps/web_api/app.py MindSpider 通过。
    • 已确认 services.crawler.adapters.mediacrawler_adapter.get_mediacrawler_paths() 可返回 rootruntimedb_config 等完整路径集合。
    • 已确认 services.crawler.mindspider.runtime_paths 当前仅作为兼容转发层存在,真实实现已切到适配层。
    • python -m compileall backend/crawler apps/web_api crawler_web.py 通过。
    • 已确认 backend/crawler/__init__.py 当前仅负责统一重导出,crawler_web.py 当前仅负责从 backend.crawler 转发符号。
    • 已确认仓内未新增 backend.crawler.routes 直连导入或 crawler_web 反向依赖。
    • 已确认 git submodule status 仍显示 vendor/mediacrawler,前序 vendor 隔离状态保持稳定。
    • 已确认 SentimentAnalysisModel/ 当前包含 61 个文件、17 个子目录,体积主体是 .csv.pth.pkl 等研究/训练产物。
    • 已确认 SentimentAnalysisModel/ 最大文件主要是训练集与模型权重,而不是主应用运行代码。
    • 已确认仓内只有 services/engines/insight/tools/sentiment_analyzer.py 一处 Python 代码直接引用 SentimentAnalysisModel/ 路径。
    • 已确认迁移后根目录 SentimentAnalysisModel/ 仅剩 WeiboMultilingualSentiment/README.md
    • 已确认 research/sentiment_models/ 已承接 4 组研究模型目录并补充目录说明文档。
    • 已确认 README.mdREADME-EN.md 中已迁移模型的命令路径已切换到 research/sentiment_models/
    • python -m compileall tests 通过。
    • 已确认除当前迁移记录外,旧测试路径引用已从主仓库文档与代码中移除。
    • 已确认 services/engines/report/utils/ 不再混放主仓库测试文件。
    • 已确认 pytest.ini 默认只收集主仓库 tests/ 分层目录,不把 vendor/mediacrawler 自带测试混入默认入口。
    • python -m compileall tests services/engines/forum/monitor.py services/engines/report/utils/chart_validator.py services/engines/report/utils/json_parser.py services/engines/report/nodes/chapter_generation_node.py openai_compat.py 通过。
    • python -m flake8 tests --extend-ignore=E501,W293,W391,E402,E712,F401 通过。
    • python -m pytest tests/unit tests/integration 通过,结果为 75 passed
    • npm --prefix apps/web_ui run typecheck 通过。
    • python -m py_compile services/shared/config/app_settings.py services/engines/insight/tools/sentiment_analyzer.py services/engines/insight/__init__.py services/engines/insight/tools/__init__.py InsightEngine/__init__.py tests/unit/insight/test_sentiment_analyzer_paths.py 通过。
    • python -m pytest tests/unit/insight/test_sentiment_analyzer_paths.py 通过,结果为 3 passed
    • python -m flake8 tests --extend-ignore=E501,W293,W391,E402,E712,F401 通过。
    • python -m pytest tests/unit tests/integration 通过,结果为 78 passed
    • python -m py_compile tools/ci/run_mediacrawler_quality.py 通过。
    • python tools/ci/run_mediacrawler_quality.py --print-only 通过,并已输出固定 smoke-test 命令。
    • 已确认本地 Python 3.13 临时虚拟环境在安装 vendor/mediacrawler/requirements.txt 时会卡在 Pillow==9.5.0 构建阶段,这证明独立 workflow 锁定 Python 3.11 是必要约束。
    • 已确认 var/crawler/browser_data/ 与冷备份目录都包含 5013 个文件,var/db/postgres/ 与冷备份目录都包含 1462 个文件,且总字节数与迁移前完全一致。
    • 已确认重建后的 bettafish-db 挂载 var/db/postgres 且健康检查通过,重建后的 bettafish 已改为挂载 var/logsvar/reports/*var/outputvar/crawler/browser_data
    • 已确认根目录 logs/final_reports/insight_engine_streamlit_reports/media_engine_streamlit_reports/query_engine_streamlit_reports/output/crawler_browser_data/db_data/ 均未被重新创建。
    • 已确认缓存清理后,全仓不再存在 __pycache__/.pytest_cache/ 目录。
    • python -m py_compile utils/compat_shims.py ForumEngine/__init__.py InsightEngine/__init__.py MediaEngine/__init__.py QueryEngine/__init__.py ReportEngine/__init__.py 通过。
    • python -m pytest tests/unit/test_root_compat_shims.py -q 通过,结果为 4 passed
  • 已确认五大根目录兼容包导入后仍保持原有 __all__ 边界,但不再在 import 阶段 eager 拉起对应重型子模块。
  • 已通过 git grep -n 确认 MindSpider/config.py.exampleMindSpider/requirements.txt 在仓内已无除文件自身与兼容说明外的有效引用。
  • 已通过 git grep -n 确认 Dockerfile.runtime-hotfix 在仓内未发现除兼容清单外的有效引用。
  • python -m py_compile services/shared/config/app_settings.py services/engines/insight/tools/sentiment_analyzer.py tests/unit/insight/test_sentiment_analyzer_paths.py 通过。
    • python -m pytest tests/unit/insight/test_sentiment_analyzer_paths.py 通过,结果为 4 passed
    • python -m flake8 tests --extend-ignore=E501,W293,W391,E402,E712,F401 通过。
  • python -m pytest tests/unit tests/integration 通过,结果为 79 passed
  • python -m py_compile utils/forum_reader.py services/engines/report/utils/config.py 通过。
  • docker compose -f infra/docker/docker-compose.yml -f infra/docker/docker-compose.override.yml config 通过。
  • 已确认根目录轻量旧运行目录与高风险旧运行目录都已完成迁移删除,var/crawler/browser_data/var/db/postgres/ 已接管对应运行数据。
  • python -m scripts.dev.start_local --help 通过。
  • 已通过临时实验确认 pydantic-settings 对多 env 文件采用“后者覆盖前者”策略,因此当前 .env.env.local 的优先级设计成立。
  • 已确认本轮验证后新生成的 __pycache__/.pytest_cache/ 已再次清理,仓库当前未保留本轮产生的缓存目录。
  • git grep -n "Dockerfile.runtime-hotfix\\|MindSpider/config.py.example\\|MindSpider/requirements.txt" 已无匹配结果。
  • 已确认本轮空目录扫描命中的均为 var/ 下运行时数据与冷备份内部目录,而非新的源码树垃圾目录。
  • 已确认 var/backups/runtime-migration/20260407/t108/6475 个文件、385.27 MB
  • 已确认 var/output/358 个文件、18.50 MB,其中空目录 57 个。
  • 尝试执行 docker ps -a 时确认本机 Docker daemon 当前不可用,因此本轮运行时评估未基于容器在线状态。

2026-04-08 / T-507

  • 任务:T-507
  • 目标:修复纯本地启动在本机 Python 3.13 环境下的兼容问题,并完成主站 Web 入口实机验证。
  • 结果:
    • 已补齐主站纯本地启动所需的最小 Python 依赖。
    • 已修复 MindSpider 通过裸 import config 污染全局模块命名空间的问题。
    • 已将 services/crawler/mindspider/main.pyservices/crawler/mindspider/schema/init_database.pyservices/engines/insight/utils/db.py 中的 PostgreSQL 驱动切到 psycopg
    • 已确认 http://127.0.0.1:5000 返回 200/api/research/options 返回 success: true
  • 关键结论:
    • 当前“纯本地启动”已经从文档层面的推荐入口,变成了经过实机验证的可运行入口。
    • 下一步若要继续追求端到端研究链路可用,重点将从“主站能否起来”转为“本地 PostgreSQL 常驻服务是否就绪”。

2026-04-08 / T-508

  • 任务:T-508
  • 目标:将纯本地启动链路从“当前环境手工补齐依赖后可运行”收口为“仓库内可复现 bootstrap 基线”。
  • 结果:
    • 已建立 infra/python/requirements-local.txt,作为纯本地主站启动的 runtime 依赖清单。
    • 已建立 scripts/dev/bootstrap_local.py,统一执行本地 Python 依赖、Playwright Chromium 与前端依赖准备。
    • 已将 README.mdREADME-EN.md 的纯本地启动步骤切到“先 bootstrap、后 start_local”的链路。
    • 已对根 requirements.txt 做主站本地路径所需的兼容性收口:移除默认本地路径继续依赖 asyncpg,并将 Pillow 放宽到 Python 3.13 可安装范围。
    • 已确认 python -m pip install --dry-run -r infra/python/requirements-local.txt 通过,且运行中的主站 http://127.0.0.1:5000/api/research/options 持续可用。
  • 关键结论:
    • 当前“纯本地启动”已经不仅有 canonical 启动命令,也具备 canonical bootstrap 入口。
    • requirements.txt 与纯本地 runtime 依赖已经分层,后续可以分别处理“主站可用性”和“全仓库重型依赖兼容”。

2026-04-09 / T-509

  • 任务:T-509
  • 目标:补齐 Windows 下一键拉起纯本地主站的便捷入口,并把本地环境预检逻辑收口到统一脚本。
  • 结果:
    • 已重写 scripts/dev/start_local_stack.ps1,用纯 ASCII 方式统一处理 env 选择、bootstrap、前端依赖检查、端口复用与 PostgreSQL 监听预警。
    • 已重写根目录 start_local.bat,作为 Windows 用户的极薄一键启动 wrapper。
    • 已将 README.mdREADME-EN.md 补充为“Python canonical 启动 + Windows 一键入口并存”的说明。
    • 已再次通过一键入口拉起主站,并确认 http://127.0.0.1:5000/api/research/options 可访问。
  • 关键结论:
    • 当前纯本地链路已经同时具备可复现 bootstrap、canonical Python 启动命令和 Windows 一键入口。
    • 当前最大缺口已经进一步收敛为“本机 PostgreSQL 是否常驻可用”,而不再是“项目本身能否被一键拉起”。

2026-04-09 / T-510

  • 任务:T-510
  • 目标:把“本地 PostgreSQL 是否就绪”从人工排查收口为仓库内可执行脚本,并接回纯本地默认工作流。
  • 结果:
    • 已新增 scripts/dev/prepare_local_postgres.py,支持 env 解析、配置校验、TCP 探测、目标数据库检查、可选建库与 schema 初始化。
    • 已更新 scripts/dev/start_local_stack.ps1,在 PostgreSQL 未监听时直接指向新的诊断命令。
    • 已更新主 README 与英文 README,把 --check-only--ensure-db --apply-schema 纳入纯本地推荐步骤。
    • 已修正 services/shared/config/app_settings.py 的默认数据库端口,使其与默认 PostgreSQL 方言一致。
  • 关键结论:
    • 当前仓库已经不再缺少“如何准备本地 PostgreSQL”的 canonical 命令;剩余缺口主要是系统层面的 PostgreSQL 服务安装/启动。
    • 后续只要本机 127.0.0.1:5432 能监听,就可以直接通过 python -m scripts.dev.prepare_local_postgres --ensure-db --apply-schema 继续收口数据库链路。

7. 当前推荐下一步

当前结构优化主干任务、纯本地默认启动链路、项目自管 PostgreSQL、自述文档重写、T-114 受控空间回收、根级 Docker 入口收口,以及 T-522 的前后端分离热更新链路都已经完成。后续更推荐按下面三条线继续:

纯本地体验收尾

建议原因:

  • 当前主站纯本地链路已经可用,剩余体验问题主要收敛为 Conda PowerShell 编码噪声与 WeasyPrint / Scipy 等非阻塞依赖告警。
  • 这类问题不会改变结构边界,但会持续影响开发体验,适合在主干结构任务收尾后单独处理。
  • 处理这条线不会再反向引入新的 repo-root 兼容入口。

前后端彻底解耦收尾

建议原因:

  • 当前默认开发模式已经切到“前端 9527 + 后端 5000”的双进程结构,但 apps/web_api/app.py 仍保留了静态前端兼容托管逻辑,主要用于 --frontend-mode build
  • 如果后续要进一步走独立部署或纯 API 服务边界,这部分兼容逻辑可以继续降级或外移。
  • 这条线属于工程化收尾,不会再影响当前已经可用的本地热更新体验。

根级入口最终收口

建议原因:

  • T-521 完成后,剩余根级文件兼容入口已经收敛到 app.pystart_local.bat 两个文件。
  • 其中 start_local.bat 仍有明确的一键启动价值,而 app.py 是否还需要长期保留,已经成为下一步最适合单独评估的边界问题。
  • 如果这一步也完成,仓库顶层将更接近“只有正式一级目录 + 极少数明确入口”的稳定状态。

2026-04-08 / T-506

  • 任务:T-506
  • 目标:将默认启动体验从 Docker 优先切到纯本地,同时保留 Docker 兼容能力。
  • 结果:
    • 已建立 .env.local 优先于 .env 的配置读取链路。
    • 已新增 scripts/dev/start_local.py 作为纯本地 canonical 启动入口。
    • 已新增 .env.local.example,并把 README / README-EN 的默认推荐启动方式切到纯本地。
    • 已在验证收尾阶段再次清理本轮生成的 __pycache__/.pytest_cache/
  • 关键决策:
    • 不把 Docker 与本地数据库配置硬塞进同一个默认 env,而是采用 .env.local / .env 双轨策略。
  • 影响范围:
    • 共享配置解析层
    • 本地启动脚本
    • 根目录 env 模板
    • README 与 fallback 提示
  • 验证:
    • python -m scripts.dev.start_local --help 通过
    • env 优先级顺序已通过临时实验与新增单元测试校验
  • 遗留问题:
    • 本地数据库与模型 API 仍需用户自行准备
    • 根目录低风险兼容文件删除试点尚未执行
  • 下一步:
    • 执行 T-112

2026-04-08 / T-112

  • 任务:T-112
  • 目标:对已确认的低风险兼容文件执行第一次真实删除试点。
  • 结果:
    • 已删除 Dockerfile.runtime-hotfix
    • 已删除 MindSpider/config.py.example
    • 已删除 MindSpider/requirements.txt
    • 已确认删除后三类文件在仓内不再有残留引用
  • 关键决策:
    • 将“低风险兼容文件”从“删除候选”升级为“允许直接删除”的对象类型,并以这次试点作为基线
  • 影响范围:
    • 根目录 Docker 热修兼容副本
    • MindSpider/ 下两个已失效的兼容说明入口
    • 结构优化记忆与清理文档
  • 验证:
    • git grep -n "Dockerfile.runtime-hotfix\\|MindSpider/config.py.example\\|MindSpider/requirements.txt" 已无匹配
    • 空目录扫描未发现新增源码树垃圾目录
  • 遗留问题:
    • var/backups/runtime-migration/20260407/t108/ 仍占用磁盘空间
    • 运行时空目录是否保留尚未分类
  • 下一步:
    • 执行 T-113

2026-04-08 / T-113

  • 任务:T-113
  • 目标:评估运行时冷备份与空目录的保留窗口,避免把正常结构误删为“垃圾目录”。
  • 结果:
    • 已建立 docs/runtime-retention-assessment.md
    • 已确认 t108 冷备份应继续保留
    • 已确认 db/postgres/crawler/browser_data/reports/final/logs/ 下的空目录属于结构性空目录
    • 已识别 var/output/chrome-headless/ 为更适合后续回收的 scratch 候选
  • 关键决策:
    • 运行时空目录不再按“空就删”处理,而按父级语义分类处理
  • 影响范围:
    • var/ 目录保留策略
    • 根目录清理清单
    • 结构优化任务与记忆文件
  • 验证:
    • 已完成文件数 / 体积 / 空目录统计
    • docker ps -a 失败,确认当前 Docker daemon 不可用
  • 遗留问题:
    • 受控空间回收步骤尚未执行
    • t108 冷备份回滚窗口尚未定义结束条件
  • 下一步:
    • 执行 T-114

2026-04-09 / T-511

  • 任务:T-511
  • 目标:移除多余的 BettaFish/ 外层目录,把当前仓库正式扁平化到上级目录,降低路径层级和后续维护成本。
  • 结果:
    • 已将旧仓库根 d:\工作\APP\AI-Agent-Platform\BettaFish 的全部内容迁移到新的仓库根 d:\工作\APP\AI-Agent-Platform
    • 已确认 Git 根目录随之切换到上级目录,后续本地脚本、README、任务文档与排障命令都应从新根目录执行。
    • 已在清理旧目录前停止仍引用旧路径的本地启动残留进程,并保留 var/logs/t509-start-local-stdout.logvar/logs/t509-start-local-stderr.log 作为迁移前后的衔接日志。
  • 关键决策:
    • 将“移除多余包装层目录”视为结构优化主线的一部分,而不是单次手工整理;从这一轮开始,canonical workspace root 固定为 d:\工作\APP\AI-Agent-Platform
    • 历史任务记录中保留的 ...\BettaFish\... 绝对路径不做批量回写,避免篡改历史执行上下文;新增记录与后续文档统一使用新根目录。
  • 影响范围:
    • 仓库顶层目录结构
    • Git 仓库根路径
    • 本地启动与脚本执行入口
    • 结构优化任务文档与长期记忆文档
  • 验证:
    • git rev-parse --show-toplevel 返回 D:/工作/APP/AI-Agent-Platform
    • Test-Path .\BettaFish 返回 False
    • 旧本地启动日志已可在 var/logs/ 下继续访问
  • 遗留问题:
    • 当前本地服务在迁移清理过程中已经停止,下一轮联调需要从新根目录重启。
    • 结构追踪文档中的旧绝对路径仍然存在,但应视为历史快照而非当前推荐路径。
  • 下一步:
    • 从新根目录重新执行一轮纯本地启动验证。
    • 后续若继续推进结构收尾,可把仍显式写死 BettaFish 绝对路径的文档说明逐步更新为新根目录表述。

2026-04-09 / T-512

  • 任务:T-512
  • 目标:验证仓库扁平化后,纯本地启动链路仍能从新的根目录直接工作。
  • 结果:
    • 已从 d:\工作\APP\AI-Agent-Platform 重新执行 powershell -ExecutionPolicy Bypass -NoProfile -File scripts/dev/start_local_stack.ps1 -SkipPlaywright
    • 已确认 127.0.0.1:5000 在迁移后重新监听,且 http://127.0.0.1:5000 返回 200
    • 已生成新的迁移后启动日志 var/logs/t511-start-local-stdout.logvar/logs/t511-start-local-stderr.log,可用于后续排障与环境比对。
  • 关键决策:
    • 把“目录扁平化后的启动复验”单独作为一个收口任务记录,而不是附属于迁移动作本身,避免后续把“目录已改”误判为“运行也已验证”。
    • git grep "BettaFish" 的命中结果进行语义区分:项目名、GitHub 仓库名和品牌文本继续保留;后续只需要逐步修正把 BettaFish/ 当作当前本地目录层级展示的文档说明。
  • 影响范围:
    • 本地启动链路
    • var/logs/ 启动日志
    • 结构优化任务与长期记忆文档
  • 验证:
    • Get-NetTCPConnection -LocalPort 5000 -State Listen
    • Invoke-WebRequest http://127.0.0.1:5000
    • 启动日志中的 Repo root: D:\工作\APP\AI-Agent-Platform
  • 遗留问题:
    • 启动日志里仍提示 WeasyPrint / Scipy 缺失,它们当前不影响主站启动,但会影响部分报告渲染能力。
    • Conda PowerShell 激活链路仍会打印编码异常噪声,后续若继续优化本地体验,值得单独清理。
  • 下一步:
    • 继续补齐本地 PostgreSQL 并完成数据库初始化链路验证。
    • 或者继续修正文档中仍把 BettaFish/ 当作当前目录树展示根的内容。

2026-04-09 / T-513

  • 任务:T-513
  • 目标:在用户完成本机 PostgreSQL 安装后,验证“服务已装好”和“仓库配置已能连上”这两件事是否同时成立。
  • 结果:
    • 已确认 Windows 服务 postgresql-x64-15 正在运行,本机 5432 端口已监听,说明 PostgreSQL 安装与服务注册本身没有问题。
    • 已确认 .env.local 当前使用 DB_HOST=127.0.0.1DB_PORT=5432DB_USER=bettafishDB_NAME=bettafish,仓库配置已经正确指向本地实例。
    • 已通过 python -m scripts.dev.prepare_local_postgres --check-only 验证出当前真正的阻塞点是应用账号 bettafish 认证失败,而不是 PostgreSQL 未安装、端口未开或脚本不可用。
    • 已修复 scripts/dev/prepare_local_postgres.py 在 Windows GBK 终端下打印数据库错误时可能触发 UnicodeEncodeError 的问题,并把认证失败单独提示出来。
  • 关键决策:
    • 将“PostgreSQL 已安装”与“仓库配置已打通”明确拆成两个验证层次;本轮结论是前者已完成,后者仍卡在凭据对齐。
    • 优先修复脚本诊断质量,而不是在认证未打通时盲目尝试 --ensure-db --apply-schema,避免输出继续误导后续排障。
  • 影响范围:
    • 纯本地数据库准备脚本
    • PostgreSQL 诊断体验
    • 结构优化任务与长期记忆文档
  • 验证:
    • Get-Service *postgres*
    • Get-NetTCPConnection -LocalPort 5432 -State Listen
    • python -m py_compile scripts/dev/prepare_local_postgres.py
    • python -m scripts.dev.prepare_local_postgres --check-only
  • 遗留问题:
    • 本机 PostgreSQL 的 bettafish 角色密码与 .env.local 中的密码未对齐,导致当前仍无法进入建库与 schema 初始化阶段。
    • 当前环境里的 Conda PowerShell 激活仍会产生编码异常噪声,但它不影响本次 PostgreSQL 服务验证结论。
  • 下一步:
    • 对齐 bettafish 角色凭据和 .env.local 后重新执行 --check-only
    • 认证通过后执行 --ensure-db --apply-schema,把本地数据库链路从“服务就绪”推进到“仓库脚本可用”。

2026-04-09 / T-514

  • 任务:T-514
  • 目标:验证能否直接接管系统 PostgreSQL 5432 实例的认证配置,并明确这条路径是否适合作为仓库默认修复方案。
  • 结果:
    • 已定位系统 PostgreSQL 的真实数据目录与 pg_hba.conf 路径,并创建备份 var/backups/postgres/t514/pg_hba.conf.bak
    • 已尝试通过临时放宽本地回环认证来重置 bettafish 角色密码,但在当前用户权限下无法完成 pg_ctl reload / Restart-Service
    • 已确认 pg_hba.conf 恢复为 scram-sha-256,系统 5432 实例继续保持原有安全状态。
  • 关键决策:
    • 不再把“系统级认证修改成功”作为纯本地数据库链路的唯一收口手段。
  • 影响范围:
    • 系统 PostgreSQL 认证修复边界
    • var/backups/postgres/t514/
    • 结构优化任务与长期记忆文档
  • 验证:
    • 系统 PostgreSQL 服务仍在运行,5432 继续监听。
    • pg_hba.conf 当前已恢复到 scram-sha-256
  • 遗留问题:
    • 当前用户无法稳定控制系统 PostgreSQL 服务重载 / 重启。
    • 系统实例超级用户密码仍未知。
  • 下一步:
    • 转向项目自管 PostgreSQL 实例方案。

2026-04-09 / T-515

  • 任务:T-515
  • 目标:在不依赖系统 PostgreSQL 管理员密码的前提下,直接打通纯本地数据库链路与一键启动入口。
  • 结果:
    • 已新增 scripts/dev/repair_local_postgres.py,自动在 var/db/postgres-local/ 下拉起项目自管 PostgreSQL,并把 .env.local 切到 127.0.0.1:55432
    • 已加固 scripts/dev/start_local.pyscripts/dev/start_local_stack.ps1,让纯本地启动入口内建 PostgreSQL 自修复能力。
    • 已修复 scripts/dev/prepare_local_postgres.py 对本地化“数据库不存在”错误的处理,以及 schema 初始化时的 PYTHONPATH / 工作目录问题。
    • 已修复 services/crawler/mindspider/schema/init_database.py 在 Windows 下与 psycopg 异步驱动的事件循环兼容问题。
    • 已完成数据库建库、26 张表初始化,以及修复后的主站重启验证。
  • 关键决策:
    • 让仓库自身接管纯本地 PostgreSQL 生命周期,而不是继续依赖系统实例凭据对齐。
  • 影响范围:
    • 纯本地数据库修复链路
    • .env.local 当前数据库目标
    • Windows / Python 启动入口
    • README 与环境模板
  • 验证:
    • python -m scripts.dev.prepare_local_postgres --check-only 通过。
    • python -m scripts.dev.repair_local_postgres 通过。
    • psql -h 127.0.0.1 -p 55432 -U bettafish -d bettafish -c "\dt" 可列出 26 张表。
    • http://127.0.0.1:5000 返回 200/api/research/options 返回 success: true
  • 遗留问题:
    • 启动日志中仍有 WeasyPrint / Scipy 缺失告警和 Conda PowerShell 编码噪声。
    • 系统 5432 实例仍保留在机器上,但已不再是当前项目的必需前提。
  • 下一步:
    • 如需继续优化纯本地体验,可单独清理环境噪声与非阻塞依赖告警。
    • 如需继续推进结构清理,转入 T-114

2026-04-09 / T-516

  • 任务:T-516
  • 目标:重新整理 README 中的项目代码结构和功能说明,使文档与当前仓库真实结构保持一致。
  • 结果:
    • 已重新审计当前仓库一级目录和主功能边界,确认当前 canonical 结构以 apps/services/backend/scripts/tools/infra/research/vendor/tests/utils/var/ 为主。
    • 已在 README.md 新增“当前代码结构(截至 2026-04-09)”“当前功能分层说明”“推荐入口与职责边界”“兼容目录说明”四个主说明块。
    • 已将旧的目录树标题降级为“历史目录树(已过时,仅供兼容背景参考)”,显式区分当前主结构与历史兼容布局。
    • 已补充 .env.local.example 默认 5432repair_local_postgres 修复后切换到 55432 的关系说明,避免 README 与本地启动链路脱节。
  • 关键决策:
    • 在 README 中显式区分“当前 canonical 结构”和“历史兼容目录”,而不是继续维护一棵混合新旧路径的单一目录树。
    • 兼容层仍然保留,但文档主视角必须优先指向 apps/services/tools/infra/research/vendor/ 等当前正式落点。
  • 影响范围:
    • README.md
    • 结构优化任务文档
    • 结构优化长期记忆文档
  • 验证:
    • Select-String -Path README.md -Pattern "当前代码结构(截至 2026-04-09)|当前功能分层说明|推荐入口与职责边界|历史目录树(已过时,仅供兼容背景参考)"
    • 手工复核 README 新增结构说明与当前仓库目录一致
  • 遗留问题:
    • README 里历史目录树正文仍占用较大篇幅,后续若继续收紧文档,可再单独裁剪。
    • README-EN.md 尚未同步本轮中文结构重写。
  • 下一步:
    • 继续收敛 README 中残留的新旧路径混合说明,把兼容层描述压缩为更短的附注或参考段落。

2026-04-09 / T-517

  • 任务:T-517
  • 目标:清理根目录历史兼容包目录与兼容 CLI 目录,把仓库主结构正式切换到 canonical 一级目录。
  • 结果:
    • 已删除 QueryEngine/MediaEngine/InsightEngine/ReportEngine/ForumEngine/MindSpider/frontend/SingleEngineApp/SentimentAnalysisModel/
    • 已删除与这些兼容目录配套的 utils/compat_shims.pytests/unit/test_root_compat_shims.py
    • 已同步更新 README.mdREADME-EN.mdapps/*.mdservices/*/README.mdresearch/README.mddocs/root-compatibility-shim-inventory.mddocs/project-structure-root-cleanup-inventory.md.gitignore.dockerignore,把主叙述切换到当前 canonical 目录。
  • 关键决策:
    • 不再继续维持目录级兼容壳,把兼容边界收缩到少量根级文件入口。
    • 默认假设仓库内部和后续维护都已接受切换到 apps/services/tools/infra/research/vendor/ 等正式目录。
  • 影响范围:
    • 根目录历史兼容包目录
    • 兼容辅助 helper / 测试
    • README、清单和忽略规则
  • 验证:
    • git grep -n "compat_shims\\|QueryEngine\\|MediaEngine\\|InsightEngine\\|ReportEngine\\|ForumEngine\\|MindSpider\\|SingleEngineApp\\|SentimentAnalysisModel" -- '*.py' ':!README.md' ':!README-EN.md' ':!docs/**' ':!static/**' 无结果
    • python -m pytest tests/unit/shared/test_env_resolution.py tests/unit/report/test_json_parser.py tests/unit/insight/test_sentiment_analyzer_paths.py -q 通过(24 passed
    • 手工复核仓库顶层目录,确认上述历史兼容目录已不再存在
  • 遗留问题:
    • 所有依赖旧目录名的外部脚本、旧 import 路径与旧 CLI 命令将不再可用。
    • README 主文档中仍残留大段历史目录树,虽然已降级为“历史参考”,但仍会制造视觉噪音。
  • 下一步:
    • 继续收敛主 README 中的历史树说明,并补一轮缓存目录清理。

2026-04-09 / T-518

  • 任务:T-518
  • 目标:压缩主 README 中残留的历史目录树噪音,并清理当前源码树里的缓存目录,让仓库主视图更干净。
  • 结果:
    • 已将 README.mdREADME-EN.md 中的大段历史目录树替换为精简说明,改为指向 docs/root-compatibility-shim-inventory.mddocs/project-structure-root-cleanup-inventory.md 作为历史迁移参考。
    • 已更新本记忆文档顶部“稳定事实”和“当前推荐下一步”,使其与 T-517 之后的真实仓库状态保持一致。
    • 已清理仓库源码树中的 .pytest_cache/ 与多处 __pycache__/,减少顶层与代码目录噪音。
  • 关键决策:
    • 主 README 不再承担“保存整棵旧目录树”的职责,历史迁移细节统一沉淀到专门清单文档。
    • 缓存目录按“源码树优先清理、运行时数据目录谨慎保留”的原则处理,避免误删 var/ 下有语义的运行态内容。
  • 影响范围:
    • README.md
    • README-EN.md
    • docs/project-structure-optimization-memory.md
    • 源码树中的 .pytest_cache/__pycache__/
  • 验证:
    • 手工复核 README.mdREADME-EN.md,确认主文档不再内嵌已删除兼容目录的大段历史树
    • Get-ChildItem -Path . -Directory -Force | Where-Object { $_.Name -eq '.pytest_cache' } 无结果
    • Get-ChildItem apps,backend,scripts,services,tests,utils -Recurse -Directory -Force | Where-Object { $_.Name -eq '__pycache__' } 无结果
  • 遗留问题:
    • 根目录仍保留少量文件级兼容入口,后续仍可继续收口。
    • var/ 下的 scratch / 下载目录仍需按 T-114 受控回收,而不是本轮直接清空。
  • 下一步:
    • 继续评估根级文件兼容入口与 var/ 下的受控空间回收方案。

2026-04-09 / T-519

  • 任务:T-519
  • 目标:回收根目录报告类兼容 wrapper,彻底把报告工具链收口到 tools.reports.* canonical 入口。
  • 结果:
    • 已修正 services/engines/report/ 内部残留的 ReportEngine 旧导入、硬编码路径与示例文案,统一切换到 services.engines.report canonical 路径。
    • 已修正 services/engines/report/scripts/ 中报告脚本的路径引导逻辑与命令示例,确保从 canonical 包入口运行时不再依赖根目录兼容包。
    • 已修正报告模板目录默认值,不再硬编码已删除的 ReportEngine/report_template 路径。
    • 已验证 python -m tools.reports.export_pdf --helppython -m tools.reports.report_engine_only --help 可正常运行。
    • 已验证 tools.reports.regenerate_latest_htmltools.reports.regenerate_latest_mdtools.reports.regenerate_latest_pdf 可以正常导入。
    • 已删除根目录 report_engine_only.pyregenerate_latest_html.pyregenerate_latest_md.pyregenerate_latest_pdf.pyexport_pdf.py
    • 已同步更新 README.mdREADME-EN.mdtools/reports/README.md 与结构清单文档中的用户指令,统一改为 python -m tools.reports.*
    • 已清理本轮验证生成的 services/engines/report/tools/reports/__pycache__/ 目录,避免缓存重新污染源码树。
  • 关键决策:
    • 先修复 canonical 报告工具链,再删除根目录兼容脚本,避免把旧入口替换成不可用的新入口。
    • 本轮只回收低风险、已完成 canonical 化的报告类 wrapper;app.pycrawler_web.py 与 Docker / compose 根级入口暂不在本轮删除范围。
  • 影响范围:
    • services/engines/report/renderers/html_renderer.py
    • services/engines/report/renderers/markdown_renderer.py
    • services/engines/report/renderers/pdf_renderer.py
    • services/engines/report/utils/chart_repair_api.py
    • services/engines/report/utils/chart_review_service.py
    • services/engines/report/scripts/export_to_pdf.py
    • services/engines/report/scripts/generate_all_blocks_demo.py
    • services/engines/report/scripts/validate_ir.py
    • services/engines/report/nodes/template_selection_node.py
    • services/engines/report/utils/config.py
    • services/engines/report/utils/dependency_check.py
    • services/engines/report/README.md
    • README.md
    • README-EN.md
    • tools/reports/README.md
    • tools/reports/report_engine_only.py
    • docs/root-compatibility-shim-inventory.md
    • docs/project-structure-root-cleanup-inventory.md
    • docs/project-structure-optimization-memory.md
    • docs/project-structure-optimization-tasks.md
  • 验证:
    • python -m py_compile services/engines/report/renderers/html_renderer.py services/engines/report/renderers/markdown_renderer.py services/engines/report/renderers/pdf_renderer.py services/engines/report/utils/chart_repair_api.py services/engines/report/utils/chart_review_service.py services/engines/report/scripts/export_to_pdf.py services/engines/report/scripts/generate_all_blocks_demo.py services/engines/report/scripts/validate_ir.py tools/reports/export_pdf.py tools/reports/report_engine_only.py 通过。
    • python -m tools.reports.export_pdf --help 通过。
    • python -m tools.reports.report_engine_only --help 通过。
    • Select-String -Path (Get-ChildItem services/engines/report,tools/reports -Recurse -File | Select-Object -ExpandProperty FullName) -Pattern 'ReportEngine/report_template|python -m ReportEngine|ReportEngine/scripts/' 无结果。
    • 已确认根目录 report_engine_only.pyregenerate_latest_html.pyregenerate_latest_md.pyregenerate_latest_pdf.pyexport_pdf.py 不再存在。
  • 遗留问题:
    • WeasyPrintScipy 等可选依赖仍会在报告工具 help / import 验证时输出非阻塞告警,但不影响本轮结构收口结论。
    • 根目录仍保留少量高兼容价值入口,后续若继续清理,应优先评估 app.pycrawler_web.pyDockerfiledocker-compose*.yml
  • 下一步:
    • 继续沿两条主线推进:T-114 受控运行时空间回收,以及剩余根级文件兼容入口的保留边界评估。

2026-04-09 / T-520

  • 任务:T-520
  • 状态:todo -> done
  • 变更:
    • 删除根目录低价值兼容 shim:config.pycrawler_web.pyopenai_compat.py
    • 更新 README.mdREADME-EN.md,移除这三个根目录 shim 的当前入口描述,并把配置 / crawler / OpenAI 兼容导入说明统一切回 canonical 路径。
    • 更新 docs/root-compatibility-shim-inventory.mddocs/project-structure-root-cleanup-inventory.mdservices/shared/config/README.mddocs/project-structure-optimization-tasks.md 与本记忆文档,使“当前保留的根级兼容入口”与仓库实际状态一致。
    • 继续清理仓内残留的运行时 shim 依赖,把 backend/apps/services/engines/services/crawler/mindspider/ 中仍直接导入 config / openai_compat 的模块统一切到 services.shared.configutils.openai_compat
  • 关键决策:
    • 在确认仓内代码已不再依赖 import configimport crawler_webimport openai_compat 后,优先回收这三类低价值 shim,而不是继续冻结它们。
    • 删除根目录 shim 后立刻补做运行时 import 验证;一旦发现 backend.crawlerapps.web_api.app 仍在隐式依赖旧入口,就在同一轮内继续顺着堆栈修到 canonical 链路完全打通。
    • 保留 app.pystart_local.bat 与根目录 Docker / Compose 清单,避免本轮同时打断 Web API 启动兼容入口和部署兼容入口。
  • 影响范围:
    • backend/config_admin.py
    • backend/crawler/routes.py
    • backend/crawler/runtime.py
    • backend/crawler/managers.py
    • services/engines/forum/llm_host.py
    • services/engines/report/llms/base.py
    • services/engines/query/llms/base.py
    • services/engines/media/llms/base.py
    • services/engines/media/tools/search.py
    • services/engines/insight/llms/base.py
    • services/engines/insight/tools/keyword_optimizer.py
    • services/engines/insight/tools/sentiment_analyzer.py
    • services/crawler/mindspider/main.py
    • services/crawler/mindspider/BroadTopicExtraction/database_manager.py
    • services/crawler/mindspider/BroadTopicExtraction/topic_extractor.py
    • services/crawler/mindspider/DeepSentimentCrawling/keyword_manager.py
    • services/crawler/mindspider/DeepSentimentCrawling/platform_crawler.py
    • services/crawler/mindspider/schema/db_manager.py
    • services/crawler/mindspider/schema/init_database.py
    • apps/engine_console/insight_engine_streamlit_app.py
    • apps/engine_console/media_engine_streamlit_app.py
    • apps/engine_console/query_engine_streamlit_app.py
    • apps/web_api/app.py
    • README.md
    • README-EN.md
    • docs/root-compatibility-shim-inventory.md
    • docs/project-structure-root-cleanup-inventory.md
    • services/shared/config/README.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
    • 已删除:config.py
    • 已删除:crawler_web.py
    • 已删除:openai_compat.py
  • 验证:
    • 基于 Python 脚本的全仓扫描已确认源码树内不存在真实的 from config importimport configfrom openai_compat importimport openai_compatfrom crawler_web importimport crawler_web 语句。
    • python -m py_compile services/shared/config/app_settings.py utils/openai_compat.py backend/config_admin.py backend/crawler/__init__.py backend/crawler/managers.py backend/crawler/routes.py backend/crawler/runtime.py services/crawler/mindspider/main.py services/crawler/mindspider/BroadTopicExtraction/database_manager.py services/crawler/mindspider/BroadTopicExtraction/topic_extractor.py services/crawler/mindspider/DeepSentimentCrawling/keyword_manager.py services/crawler/mindspider/DeepSentimentCrawling/platform_crawler.py services/crawler/mindspider/schema/db_manager.py services/crawler/mindspider/schema/init_database.py services/engines/forum/llm_host.py services/engines/report/llms/base.py services/engines/query/llms/base.py services/engines/media/llms/base.py services/engines/media/tools/search.py services/engines/insight/llms/base.py services/engines/insight/tools/keyword_optimizer.py services/engines/insight/tools/sentiment_analyzer.py apps/engine_console/insight_engine_streamlit_app.py apps/engine_console/media_engine_streamlit_app.py apps/engine_console/query_engine_streamlit_app.py apps/web_api/app.py 通过。
    • 运行时导入验证通过:services.shared.configutils.openai_compatbackend.crawlerapps.web_api.app 当前均可成功 import。
    • 已确认根目录 config.pycrawler_web.pyopenai_compat.py 不再存在。
  • 遗留问题:
    • 根目录仍保留少量高兼容价值入口,后续若继续清理,应优先评估 app.pystart_local.batDockerfiledocker-compose*.yml
  • 下一步:
    • 结构清理主线继续执行 T-114 受控运行时空间回收。
    • 或继续评估剩余根级文件入口的长期保留边界。

2026-04-09 / T-114

  • 任务:T-114
  • 目标:在不误删数据库卷、浏览器登录态目录或冷备份的前提下,对 var/ 下最合适的 scratch 目录执行一次真实、受控的空间回收。
  • 结果:
    • 已确认 5000 端口当前由 python -m apps.web_api 提供主站服务,scripts.dev.start_local 监控进程也仍在运行,因此本轮不采用停服式清理。
    • 已确认活跃 Chrome / Playwright 进程使用的是用户目录下的 ms-playwright profile,而不是仓内 var/output/chrome-headless/
    • 已删除 var/output/chrome-headless/ 整棵 scratch 子树,且删除后路径不存在。
    • 已确认 var/output/ 当前仅剩 screens/ 截图产物与 .gitkeep,总量约 2.49 MB
  • 关键决策:
    • 运行时空间回收不以“主站是否仍在监听”为唯一判断条件,而以“活跃进程是否真正使用目标路径”为准。
    • var/ 下优先回收 scratch/cache 子树,而不是继续触碰 var/db/postgres/var/crawler/browser_data/t108 冷备份。
  • 影响范围:
    • var/output/chrome-headless/(已删除)
    • docs/runtime-retention-assessment.md
    • docs/project-structure-root-cleanup-inventory.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • Get-CimInstance Win32_Process 已确认 20288python -m apps.web_api56300python -m scripts.dev.start_local --env-file ...\\.env.local
    • Get-CimInstance Win32_Process 已确认活跃 Chrome / Playwright 进程使用的 --user-data-dir 位于 C:\\Users\\wanzh\\AppData\\Local\\ms-playwright\\...,而非仓内 var/output/chrome-headless/
    • Test-Path var/output/chrome-headless 返回 False
    • 目录统计已确认 var/output/ 当前为 4 个文件、1 个目录、0 个空目录,约 2.49 MB
  • 遗留问题:
    • var/backups/runtime-migration/20260407/t108/var/crawler/browser_data/var/db/postgres/ 仍属于高价值运行态/冷备份目录,不在本轮回收范围。
    • 若未来再次生成 chrome-headless 类 scratch 目录,仍需按相同前置条件重新复核。
  • 下一步:
    • 如继续推进根级入口收尾,优先评估 app.py 的最终保留窗口。

2026-04-09 / T-521

  • 任务:T-521
  • 目标:移除根目录 Docker / compose 兼容副本,把 Docker 入口彻底收口为 infra/docker/ 下的唯一 canonical 路径。
  • 结果:
    • 已删除根目录 Dockerfiledocker-compose.ymldocker-compose.override.yml
    • 已更新 README.mdREADME-EN.md,把 Docker 启动说明统一改为只使用 infra/docker/ 下的 canonical 文件。
    • 已更新 docs/root-compatibility-shim-inventory.mddocs/project-structure-root-cleanup-inventory.mddocs/project-structure-optimization-tasks.md 与本记忆文档,移除“根目录 Docker 兼容副本仍保留”的过时表述。
  • 关键决策:
    • 在 pure local 已经成为默认体验后,Docker 兼容链路只需保留一套 canonical 文件,继续保留 repo-root 副本只会增加维护噪音。
    • start_local.bat 这种薄转发入口可以保留,但 Docker / compose 文件属于真实配置副本,不再适合长期冻结。
  • 影响范围:
    • Dockerfile(已删除)
    • docker-compose.yml(已删除)
    • docker-compose.override.yml(已删除)
    • README.md
    • README-EN.md
    • docs/root-compatibility-shim-inventory.md
    • docs/project-structure-root-cleanup-inventory.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • git grep 已确认仓内 CI 与 README 的 Docker 入口均指向 infra/docker/Dockerfileinfra/docker/docker-compose.ymlinfra/docker/docker-compose.override.yml
    • 已确认根目录 Dockerfiledocker-compose.ymldocker-compose.override.yml 不再存在。
  • 遗留问题:
    • 旧的 repo-root docker build . / docker compose up 使用习惯已不再兼容,需要改为显式使用 infra/docker/ 下的 canonical 文件。
    • 根目录仍保留 app.pystart_local.bat 两个高价值入口,其中 app.py 仍需后续继续评估。
  • 下一步:
    • 如继续做根目录收尾,优先评估 app.py 的最终保留窗口;start_local.bat 建议继续保留。

2026-04-09 / T-522

  • 任务:T-522
  • 目标:把当前本地工作流从“Flask 直接托管前端静态产物”切换为“前端 Vite 热更新 + 后端 Flask API”分离模式,同时保留兼容构建路径。
  • 结果:
    • 已重写 scripts/dev/start_local.py,引入 --frontend-mode dev|build|skip,默认以 dev 模式同时启动前端热更新和后端 API。
    • 已重写 scripts/dev/start_local_stack.ps1,让 Windows 一键入口默认对齐双进程开发模式,并处理 5000/5173 端口的复用、分支启动和 -FrontendPort 覆盖。
    • 已更新 apps/web_ui/vite.config.ts,在开发模式下切换到 / 基础路径,并为 /api/socket.io 增加到 http://127.0.0.1:5000 的代理。
    • 已在 apps/web_api/app.py 中加入基于 BETTAFISH_FRONTEND_DEV_URL 的开发态跳转逻辑,使 5000 在 dev 模式下不再直接承载前端页面本身。
    • 已更新 README.mdREADME-EN.md,把默认访问入口调整为前端 http://127.0.0.1:5173,同时保留 --frontend-mode build / --build-frontend 兼容说明。
  • 关键决策:
    • 开发环境优先保证前后端分离和热更新体验,生产兼容链路继续允许静态构建输出到 static/frontend/,避免一次性切断现有部署方式。
    • 前端继续保留 hash 路由,先稳定切换开发链路,再视后续部署形态决定是否切换到 history 模式。
  • 影响范围:
    • scripts/dev/start_local.py
    • scripts/dev/start_local_stack.ps1
    • apps/web_ui/vite.config.ts
    • apps/web_api/app.py
    • README.md
    • README-EN.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • python -m compileall scripts/dev/start_local.py apps/web_api/app.py
    • npm --prefix apps/web_ui run typecheck
    • npm --prefix apps/web_ui run build
    • python -m scripts.dev.start_local --help
    • powershell -ExecutionPolicy Bypass -NoProfile -File .\scripts\dev\start_local_stack.ps1 -?
    • 临时使用 cmd.exe /c npm --prefix apps/web_ui run dev -- --host 127.0.0.1 --port 5174 --strictPort 拉起前端后,http://127.0.0.1:5174 返回 200,且 http://127.0.0.1:5174/api/status 通过 Vite 代理返回 200
  • 遗留问题:
    • apps/web_api/app.py 仍保留静态前端兼容托管逻辑,以支撑 build 模式与现有兼容发布方式;若后续要彻底 API-only,还需要单独收口。
    • 本机默认端口 5173 当前被另一个 Vite 项目占用,因此本轮真实连通性验证改用 5174;Windows 一键入口与 Python 启动入口都已补上前端端口覆盖能力。
    • 当前尚未做一轮“真实启动后修改 Vue 文件并观察浏览器 HMR”的交互式人工验证,后续可以在本机开发时补上这一步。
  • 下一步:
    • 如继续推进工程化收尾,可把 Flask 根路由进一步降级为仅 API 状态页,并评估把静态前端托管完全迁出后端服务。

2026-04-09 / T-523

  • 任务:T-523
  • 目标:把本地前端开发默认端口从 5173 统一调整到 9527,让 Vite、本地启动脚本与文档说明保持一致。
  • 结果:
    • 已将 scripts/dev/start_local.pyDEFAULT_FRONTEND_PORT 调整为 9527
    • 已将 scripts/dev/start_local_stack.ps1 的默认 -FrontendPort 调整为 9527
    • 已将 apps/web_ui/vite.config.ts 的开发端口调整为 9527
    • 已将 README.mdREADME-EN.md 中的默认前端入口、端口冲突提示和端口覆盖示例同步更新到 9527 / 9528
  • 关键决策:
    • 这是默认值切换,不改变 --frontend-port / -FrontendPort 的可覆盖能力,因此不会影响已有自定义启动习惯。
    • 继续保留 9528 作为文档中的冲突兜底示例,避免默认端口与备用端口混用。
  • 影响范围:
    • scripts/dev/start_local.py
    • scripts/dev/start_local_stack.ps1
    • apps/web_ui/vite.config.ts
    • README.md
    • README-EN.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • python -m scripts.dev.start_local --help
    • powershell -ExecutionPolicy Bypass -NoProfile -Command "Get-Help '.\scripts\dev\start_local_stack.ps1'"
    • npm --prefix apps/web_ui run typecheck
  • 遗留问题:
    • 若本机已有其他程序占用 9527,仍需通过 --frontend-port 9528-FrontendPort 9528 显式覆盖。
    • 当前仅做了默认端口调整,没有在本轮重新拉起整套服务;如果需要,我可以继续直接按 9527 帮你启动并验证。
  • 下一步:
    • 如需,我可以继续直接启动前后端,并确认 http://127.0.0.1:9527 的页面热更新是否正常。

2026-04-09 / T-524

  • 任务:T-524
  • 目标:评估根目录 app.py 是否仍需长期保留,并明确它在当前项目中的真实职责与删除风险边界。
  • 结果:
    • 已确认根目录 app.py 只是薄兼容 shim,本体仅转发 appmainsocketioapps/web_api.app
    • 已确认仓内代码与启动脚本不再依赖根目录 app.py;当前剩余引用主要是 README、兼容清单和历史说明文档中的旧命令兼容表述。
    • 已确认当前 canonical 后端入口是 python -m apps.web_api / apps/web_api/app.py,而不是根目录 app.py
    • 已形成评估结论:app.py 没有继续长期保留的内部工程价值,可进入删除窗口;保留它的唯一主要理由是为仓外旧命令和历史自动化留缓冲。
  • 关键决策:
    • 这轮先记录评估结论,不直接删除 app.py,避免在没有同步收口文档与外部习惯前制造额外断点。
    • start_local.batapp.py 的性质已经分化:前者仍有明确的一键启动价值,后者则已经退化为纯兼容历史命令的文件级跳板。
  • 影响范围:
    • app.py
    • apps/web_api/app.py
    • apps/web_api/__main__.py
    • README.md
    • README-EN.md
    • apps/README.md
    • apps/web_api/README.md
    • docs/root-compatibility-shim-inventory.md
    • docs/project-structure-root-cleanup-inventory.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • python -m py_compile app.py apps/web_api/app.py apps/web_api/__main__.py
    • python -c "import importlib; root = importlib.import_module('app'); web_api = importlib.import_module('apps.web_api.app'); print(root.app is web_api.app); print(callable(root.main)); print(hasattr(root, 'socketio'))" 输出 True / True / True
    • 全仓静态检索确认:真实代码未发现 from app importimport apppython app.py 的运行依赖;相关命中主要位于文档。
  • 遗留问题:
    • 仍需在真正删除前同步更新 README、中英文兼容说明与可能的仓外自动化。
    • 由于导入 apps.web_api.app 仍会触发部分日志与初始化副作用,后续若继续做架构收尾,可再评估是否把装配层初始化进一步懒加载化。
  • 下一步:
    • 如继续做根级收尾,优先执行 app.py 删除与文档同步收口;start_local.bat 继续保留。

2026-04-10 / T-525

  • 任务:T-525
  • 目标:删除根目录 app.py,并把所有当前有效启动说明统一收口到 python -m apps.web_apipython -m scripts.dev.start_local
  • 结果:
    • 已删除根目录 app.py,仓库根目录不再保留 Web API 的 Python 文件级入口。
    • 已更新 README.mdREADME-EN.mdapps/README.mdapps/web_api/README.md,移除对 python app.py 的当前入口说明。
    • 已更新 docs/root-compatibility-shim-inventory.mddocs/project-structure-root-cleanup-inventory.md,明确当前根级兼容入口仅剩 start_local.bat
    • 已更新任务文档与本记忆文件,使 app.py 的状态从“已完成评估”进一步收口为“已删除”。
  • 关键决策:
    • T-524 已完成风险评估后,当前优先直接删除 app.py,而不是继续保留一个没有内部工程价值的根级跳板。
    • 当前仍保留 start_local.bat,因为它代表的是面向 Windows 用户的一键启动体验,而不是历史 Python 入口兼容债务。
  • 影响范围:
    • app.py(已删除)
    • README.md
    • README-EN.md
    • apps/README.md
    • apps/web_api/README.md
    • docs/root-compatibility-shim-inventory.md
    • docs/project-structure-root-cleanup-inventory.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • python -m py_compile apps/web_api/app.py apps/web_api/__main__.py
    • 定向静态检索确认:当前 README、应用 README 与兼容清单不再把 python app.py 作为可用入口。
    • 区分大小写的定向静态检索覆盖 apps/backend/services/scripts/tests/.github/infra/start_local.bat,未发现 from app importimport apppython app.py 的真实运行依赖。
    • Test-Path app.py 返回 False
  • 遗留问题:
    • 仓外自动化或个人脚本如仍使用 python app.py,将需要手动迁移。
    • 历史任务与记忆记录中仍会保留 app.py 的迁移与评估过程,这属于结构演进审计信息,不代表当前入口仍保留。
  • 下一步:
    • 如继续做工程化收尾,可进一步评估 apps/web_api/app.py 中的前端 build 兼容托管是否还需要继续保留。

2026-04-10 / T-526

  • 任务:T-526
  • 目标:评估 apps/web_api/app.py 中前端 build 托管逻辑是否还能继续删除,并明确它在当前架构中的真实职责边界。
  • 结果:
    • 已确认 apps/web_api/app.py 根路由当前采用三段式逻辑:开发态优先重定向到 BETTAFISH_FRONTEND_DEV_URL,否则返回 static/frontend/index.html,若构建产物缺失再回落到 templates/index.html 兜底页。
    • 已确认当前本地 canonical 开发链路是 python -m scripts.dev.start_local 默认拉起 9527 Vite HMR + 5000 Flask API;--frontend-mode build 已不再承担主开发职责。
    • 已确认 apps/web_ui/vite.config.ts 仍把构建产物输出到 static/frontend/,而 infra/docker/Dockerfile 会在镜像构建阶段显式复制该目录,因此当前 Docker / 单服务部署仍依赖 Flask 托管前端构建产物。
    • 已识别 BETTAFISH_FRONTEND_MODE 当前只在 scripts/dev/start_local.py 中写入,后端并未消费它;真正驱动开发态行为的是 BETTAFISH_FRONTEND_DEV_URL
  • 关键决策:
    • 当前不应直接删除 apps/web_api/app.py 中的前端 build 托管逻辑;它虽然已经不是本地主开发路径,但仍是现有 Docker / 单服务部署方式的交付层。
    • 后续应把这层逻辑从“开发兼容模式”重新定义为“部署兼容模式”,避免文档和启动参数继续模糊它的职责。
  • 影响范围:
    • apps/web_api/app.py
    • scripts/dev/start_local.py
    • scripts/dev/start_local_stack.ps1
    • apps/web_ui/vite.config.ts
    • apps/web_ui/README.md
    • infra/docker/Dockerfile
    • templates/index.html
    • README.md
    • README-EN.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • PowerShell 代码定位确认:apps/web_api/app.py 当前只有 / 入口承担前端相关行为,分支顺序为“开发跳转 -> 静态构建产物 -> fallback 模板”。
    • 定向静态检索确认:BETTAFISH_FRONTEND_DEV_URLapps/web_api/app.py 用于开发态跳转,而 BETTAFISH_FRONTEND_MODE 当前只在 scripts/dev/start_local.py 中被写入,未被后端消费。
    • 定向静态检索确认:infra/docker/Dockerfileapps/web_ui/vite.config.ts 仍共同依赖 static/frontend/ 作为当前单服务部署产物目录。
  • 遗留问题:
    • 若立即删除 Flask 对 static/frontend/index.html 的托管,将直接打断当前 Docker 镜像与任何单服务部署方式。
    • 文档口径仍容易把 build 模式误读为本地开发的替代入口,而不是部署兼容入口。
    • BETTAFISH_FRONTEND_MODE 作为未被消费的环境变量,后续仍应清理,避免制造错误心智模型。
  • 下一步:
    • 优先执行 T-527:统一 dev/build 的命名口径,并清理 BETTAFISH_FRONTEND_MODE 之类的冗余环境变量。
    • 只有在补齐独立前端交付方案后,才建议继续推进真正的 API-only 后端收口。

2026-04-10 / T-527

  • 任务:T-527
  • 目标:收口前端 build 托管的文档口径,并清理 scripts/dev/start_local.py 中未被消费的前端模式环境变量。
  • 结果:
    • 已从 scripts/dev/start_local.py 中移除 BETTAFISH_FRONTEND_MODE 写入,避免继续制造“后端显式识别 dev/build 模式”的错误心智模型。
    • 已更新 README.mdREADME-EN.mdapps/web_ui/README.mdapps/web_api/README.md,把 build 模式统一重新定义为 Flask / Docker 单服务部署兼容用途。
    • 已更新 templates/index.html fallback 页,引导用户优先使用 python -m scripts.dev.start_local 进行本地开发,仅在需要让 Flask / Docker 直接承载前端时再生成 static/frontend/ 构建产物。
    • 已更新 scripts/dev/start_local_stack.ps1 的 build 分支提示文案,使一键启动入口的用户提示与新的部署兼容口径保持一致。
  • 关键决策:
    • 保留 --build-frontend 兼容别名,但不再把它作为推荐入口;推荐入口继续固定为默认 dev 模式。
    • 不在本轮动 static/frontend/ 与 Docker 复制链路,只先完成“命名收口 + 冗余信号清理”,避免部署回归。
  • 影响范围:
    • scripts/dev/start_local.py
    • scripts/dev/start_local_stack.ps1
    • README.md
    • README-EN.md
    • apps/web_ui/README.md
    • apps/web_api/README.md
    • templates/index.html
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • python -m py_compile scripts/dev/start_local.py apps/web_api/app.py apps/web_api/__main__.py
    • 定向静态检索确认:运行时代码中已不再出现 BETTAFISH_FRONTEND_MODE
    • 定向静态检索确认:当前 README、模块 README、fallback 页与启动脚本文案均已把 build 模式描述为部署兼容用途。
  • 遗留问题:
    • static/frontend/ 的构建与托管链路仍继续存在,Docker / 单服务部署仍依赖它。
    • --build-frontend 兼容别名仍保留,后续若要删除,需要单独确认外部使用面。
  • 下一步:
    • 继续执行 T-528,先设计独立前端交付方案,再决定 API-only 后端的最终收口路径。

2026-04-10 / T-528

  • 任务:T-528
  • 目标:设计替代 static/frontend/ 托管的独立前端交付方案,并明确 API-only 后端收口前必须满足的前置条件。
  • 结果:
    • 已确认当前前端具备独立静态托管的基础条件:使用 createWebHashHistory,因此不依赖服务端 history fallback。
    • 已确认当前不适合直接切到“跨域前端直连 API”:前端仍大量使用相对 /api/...、报告 /api/report/...,且未发现通用 HTTP CORS 配置。
    • 已确认引擎工作台是当前最关键的额外约束:buildEngineUrl() 仍按当前 hostname 拼接 :8501/:8502/:8503,这意味着独立前端交付前必须先处理 Streamlit 代理路径或 URL 抽象。
    • 已新增 docs/frontend-independent-delivery-plan.md,明确推荐方案是“同源反向代理的独立前端服务”,并给出分阶段实施顺序、风险边界与完成标准。
  • 关键决策:
    • 第一阶段不走跨域前端直连 API,而是优先走同源代理方案,尽量复用当前前端的相对路径心智模型。
    • 只有在前端 URL 配置、报告 URL 与引擎 iframe 都完成收口后,才适合真正推进 Flask API-only。
  • 影响范围:
    • apps/web_ui/vite.config.ts
    • apps/web_ui/src/router/index.ts
    • apps/web_ui/src/utils/http.ts
    • apps/web_ui/src/utils/format.ts
    • apps/web_ui/src/composables/useReportStudio.ts
    • apps/web_ui/src/composables/useSystemController.ts
    • apps/web_api/app.py
    • infra/docker/Dockerfile
    • infra/docker/docker-compose.yml
    • infra/docker/docker-compose.override.yml
    • docs/frontend-independent-delivery-plan.md
    • docs/project-structure-optimization-tasks.md
    • docs/project-structure-optimization-memory.md
  • 验证:
    • 定向静态检索确认:前端当前使用 createWebHashHistory,因此独立静态托管不依赖服务端 history rewrite。
    • 定向静态检索确认:前端 HTTP、报告下载和报告预览当前仍主要依赖相对 /api/... 路径。
    • 定向静态检索确认:buildEngineUrl() 仍按 window.location.hostname + port 生成引擎 iframe 地址。
    • 定向静态检索确认:当前仅发现 SocketIO(..., cors_allowed_origins=\"*\"),未发现通用 HTTP CORS 配置。
  • 遗留问题:
    • 当前只是完成方案设计,还没有实际引入独立前端静态服务、代理层或 Streamlit 子路径能力。
    • static/frontend/ 和后端根路由托管仍需继续保留,直到新拓扑验证完成。
  • 下一步:
    • 执行 T-529,先实现前端 URL 配置层与引擎 URL 收口。
    • 再执行 T-530,建立独立前端服务、同源代理与新的部署拓扑。

8. 后续更新模板

后续继续推进时,建议在本文件中追加如下结构:

YYYY-MM-DD / Task ID

  • 任务:
  • 目标:
  • 结果:
  • 关键决策:
  • 影响范围:
  • 验证:
  • 遗留问题:
  • 下一步: