BettaFish 架构重构任务清单
目标:将当前 BettaFish 从“运行时重入口 + 局部全局状态驱动”的模块化单体,逐步重构为“任务驱动 + 应用层编排 + 统一运行时模型”的可持续架构。
原则:不推倒重来;先拆运行时,再统一模型,再补应用层,最后做测试、可观测性与后台执行体系。
0. 使用说明
- 本文档是整个项目后续优化开发的执行基线。
- 开发时严格按阶段推进,避免跨阶段大范围同时改动。
- 每完成一个任务,必须更新状态与备注。
- 状态标记:
-
[ ]未开始 -
[-]进行中 -
[x]已完成 -
[!]阻塞/需决策
-
1. 总体重构目标
1.1 需要解决的核心问题
-
apps/web_api/app.py过度中心化,承担了 app 初始化、蓝图注册、Socket.IO、系统状态、进程管理、Forum 启停、引擎构造、任务分发等多重职责。 - 系统主链路缺乏统一任务模型,研究任务、采集任务、分析任务、报告任务没有形成标准化状态对象与状态迁移机制。
- API 层与业务编排层耦合,HTTP 请求处理逻辑直接碰运行时状态与引擎对象。
-
services/shared/尚未完成 shared config / dto / errors / events / observability 的真正收口。 - 前端虽然已经形成“任务准备 → 采集 → 分析 → 报告”主流程,但状态来源仍分散,页面需要自行拼装多个接口结果。
- 当前测试偏局部模块,系统主链路缺少稳定的集成与冒烟测试。
1.2 最终目标架构
apps/
├─ web_api/
│ ├─ app.py
│ ├─ factory.py
│ ├─ bootstrap/
│ ├─ runtime/
│ └─ interfaces/
├─ web_ui/
└─ engine_console/
backend/
├─ api/
│ ├─ research_tasks.py
│ ├─ crawler.py
│ ├─ analysis.py
│ ├─ reports.py
│ └─ system.py
services/
├─ application/
│ ├─ research/
│ ├─ crawler/
│ ├─ analysis/
│ └─ report/
├─ engines/
│ ├─ query/
│ ├─ media/
│ ├─ insight/
│ ├─ forum/
│ └─ report/
├─ crawler/
│ ├─ domain/
│ ├─ application/
│ └─ adapters/
└─ shared/
├─ config/
├─ dto/
├─ errors/
├─ events/
├─ logging/
├─ models/
├─ llm/
└─ observability/
2. 执行策略与阶段划分
P0:结构止血期
目标:先把运行时中心化问题拆开,建立统一模型基础。
P1:业务收口期
目标:建立应用服务层,统一 API / 前端 / 引擎协作方式。
P2:系统增强期
目标:补系统级测试、可观测性、持久化恢复与后台任务执行体系。
3. P0:结构止血期
3.1 拆分 apps/web_api/app.py
目标
将当前单文件重入口改造成“入口壳 + bootstrap + runtime”结构。
当前进展快照(2026-04-17 16:05)
- 已完成
runtime/system_state.py,app.py已切换为导入get_system_state()/set_system_state()/prepare_system_start()/mark_shutdown_requested()。 - 已完成
runtime/process_manager.py,app.py已切换为依赖 Streamlit 启停/状态检查/清理逻辑;process_manager.py中遗留的processes兼容出口也已移除。 -
runtime/log_stream.py文件已落地,process_manager.py已改为依赖其中的write_log_to_file()与read_process_output()。 - 已完成
runtime/forum_runtime.py、runtime/search_dispatch.py,Forum 启停 / 日志读取、搜索分发主链路已从app.py移出。 - 已完成
runtime/system_lifecycle.py,app.py的系统启动、异步关机、Socket.IO 停止调度已改为委托SystemLifecycleService。 - 已新增
bootstrap/runtime.py,把 runtime service 构造、route dependency 拼装从app.py迁出;同时已新增bootstrap/http_config.py承接前端 dev URL 解析、bootstrap/cleanup.py承接 reload-safe cleanup 注册、bootstrap/import_path.py承接项目根路径注入。http_routes.py中 Forum/日志相关操作以及剩余的 Streamlit/process-manager 行为(STREAMLIT_SCRIPTS、状态刷新、启动/停止、启动等待)也已切到通过HttpRouteDependencies注入,路由层不再直接 importprocess_manager.py。 -
interfaces/socket_events.py也已切换为通过SocketEventDependencies接收process_registry和状态刷新 callable,默认装配由bootstrap/runtime.py承接,Socket.IO handlers 不再依赖process_manager的模块级默认 import。 - 已新增
bootstrap/search_hooks.py,承接app.py原先保留的 search monkeypatch 兼容逻辑;当前入口只保留_APP_MODULE装配结果、app/socketio/main(),兼容逻辑已迁出到独立 helper。 - 已新增
bootstrap/app_module.py,统一承接 app module 级别的 Flask/Socket.IO/runtime services/cleanup/search hooks/route wiring 装配;app.py当前只保留 import-path 入口壳、bootstrap_app_module(...)结果导出与main()。helper 内部对 runtime builders 改为经apps.web_api.bootstrap.runtime模块属性读取,避免测试里的 monkeypatch 因导入时 capture 符号而失效。 -
SystemLifecycleService已新增SystemLifecycleDependencies,启动与关机两条链路都改为消费同一组显式 runtime callables;默认装配也已迁到bootstrap/runtime.py,不再在 service 方法体里直接调用模块级 runtime 函数。 -
process_manager.py的 cleanup 链路已新增ProcessCleanupPort,串行/并发清理都改为消费同一 cleanup port;bootstrap/runtime.py的 reload-safe cleanup handler 与SystemLifecycleService.start_async_shutdown()已同步切换到该显式端口,并补了直接 cleanup 单测覆盖 linger/二次 kill 分支。 -
services/application/analysis/analysis_service.py已落地,/api/search的 query 解析、运行中引擎判定、任务状态推进与后台分发接单逻辑已开始迁入 application 层;runtime/search_dispatch.py当前主要保留本地引擎构造/执行 adapter,以及SearchDispatchRuntime/ submitter builder 等 runtime 装配入口。 -
build_runtime_services()已开始显式构造analysis_service,app.py的 search alias 当前改为指向RUNTIME_SERVICES.analysis_service的 bound methods;同时check_app_status()已支持绑定指定process_registry,HttpRouteDependencies、SocketEventDependencies与SystemLifecycleDependencies现在都优先使用注入 registry,而不是隐式落回全局单例。 -
bootstrap/runtime.py的 search route contract 已进一步收敛为单一submit_search_requestsubmitter;process_registry、check_app_status、log_dir、write_log等运行时协作依赖的预绑定,当前统一由runtime/search_dispatch.py的 builder 承接,build_http_route_dependencies()只接收最终 submitter 并注入路由依赖。 - 2026-04-20:
bootstrap/runtime.py/bootstrap/app_module.py本轮继续把 search route contract 再收一层:route/bootstrap 公共契约当前只暴露单一submit_search_requestsubmitter,build_http_route_dependencies()不再直接接收resolve_search_query/dispatch_search_request两段式依赖;后续轮次中,search_hooks的动态 binding 装配也已继续从app_module收回到bootstrap/runtime.py,同时仍保持search_hooks.dispatch_search_requestmonkeypatch 兼容链路。 - 2026-04-20:
runtime/search_dispatch.py本轮继续接管 submitter 最终装配:新增build_search_request_submitter()后,bootstrap/runtime.py当前不再自己手搓execution_context + dispatch closure,而是转为委托 runtime adapter builder;bootstrap/app_module.py也不再手动构造AnalysisExecutionContext。至此/api/search的 route submitter 绑定知识已进一步集中到 runtime/search adapter 层,app_module只负责把 search hooks 与 route wiring 接起来。 - 2026-04-20:
runtime/system_lifecycle.py本轮继续接管 cleanup port 的装配:新增共享build_process_cleanup_port()后,bootstrap/runtime.py的 reload-safe cleanup handler 与SystemLifecycleService.start_async_shutdown()当前都改为消费同一 cleanup builder,不再各自重复手搓ProcessCleanupPort。这样 system shutdown / reload cleanup 的状态回写约定已进一步收口到 lifecycle runtime 单点来源。 - 2026-04-20:
bootstrap/runtime.py本轮也继续移除了只作中转的 lifecycle dependency builder 壳;build_runtime_services()现在会直接委托runtime/system_lifecycle.py的build_default_system_lifecycle_dependencies(...),不再在 bootstrap 侧保留平行的build_system_lifecycle_dependencies()转发层。 -
AnalysisService对任务服务的依赖已收敛为显式ResearchTaskAnalysisPort,_update_task_status()/_get_task_query()中的 legacyhasattrfallback 已移除;当前 production wiring 统一走ResearchTaskService。 - 已新增
bootstrap/search_hooks.py,search monkeypatch 兼容面当前通过_APP_MODULE.search_hooks承接;resolve_search_query/dispatch_search_request不再直接以 app-module 级全局 alias 暴露。 - 2026-04-21:本轮继续把这条 search seam 再压薄一层:
bootstrap/app_module.py当前已不再额外构造SearchHookContainer,而是让_APP_MODULE.search_hooks直接暴露同一个search_dispatch_runtime对象;随后bootstrap/runtime.py也已继续接管动态 binding 的装配,app_module不再手工创建 search hook bindings,build_search_hook_bindings()当前只保留“对任意可变 hook source 做调用时解析”的最小 monkeypatch helper。 - 已新增
runtime/process_manager.py的ProcessManager类,build_runtime_services()/RuntimeServices当前会显式注入该对象;HttpRouteDependencies、SocketEventDependencies与SystemLifecycleDependencies已开始优先消费注入的ProcessManagerbound methods,而模块级函数仅保留兼容壳。 - 已新增
runtime/forum_runtime.py的ForumRuntime/build_forum_runtime();build_runtime_services()/RuntimeServices现已显式持有 forum runtime 对象,bootstrap/runtime.py、SystemLifecycleService与 HTTP route dependency 装配当前都优先消费同一 forum runtime 实例的 bound methods,而不再在主链路上直接依赖模块级start_forum_engine()/stop_forum_engine()默认函数。 -
ForumRuntime.get_log_history()已修复文本迭代后调用file.tell()触发OSError的问题,Forum 日志 helper 的 cursor contract 已重新纳入单测。 - 2026-04-20:
app.py本轮已进一步收口为更纯的入口壳,不再导出_REGISTERED_CLEANUP_HANDLER、SEARCH_HOOKS;热重载时改为从上一轮_APP_MODULE.cleanup_handler续接 reload-safe cleanup,测试侧 monkeypatch 也已同步改走_APP_MODULE.search_hooks。 - 2026-04-20:
forum_runtime.py曾先把模块级 wrapper 改为在调用时经build_forum_runtime()解析默认 runtime;search_dispatch.py也补了build_analysis_execution_context()与_resolve_analysis_service(),先把 execution context / analysis service 的缺省解析收口到明确 helper。 - 2026-04-21:本轮继续把这两条兼容壳彻底收掉:
forum_runtime.py当前仅保留ForumRuntime/build_forum_runtime()/parse_forum_log_line(),search_dispatch.py也已移除get_default_search_dispatch_runtime()、模块级execute_search_dispatch_async()、dispatch_search_request()与resolve_search_query();搜索侧剩余兼容面当前主要转移到bootstrap/search_hooks.py的 app/bootstrap monkeypatch seam,而不是 runtime 模块级入口。
任务清单
- [-] 盘点
app.py当前职责边界,按以下类别拆分:- app/factory 初始化
- blueprint 注册
- socketio 初始化
- system state 管理
- process 管理
- forum runtime 管理
- log/output 管理
- search dispatch 管理
- system start/shutdown 管理
- 备注(2026-04-15 14:23):当前已明确三块已落地 runtime:
system_state、process_manager、log_stream(部分接线);Forum、搜索分发、系统生命周期仍集中在app.py。
-
新建
apps/web_api/factory.py,提供create_app()。- 备注(2026-04-15 14:35):已落地
create_app(),收拢 Flask app 构造、SECRET_KEY、Socket.IO 创建与 blueprint 注册;后续轮次中app.py也已稳定改为通过 factory 获取app, socketio。
- 备注(2026-04-15 14:35):已落地
-
新建
apps/web_api/bootstrap/blueprints.py,集中注册 blueprint。- 备注(2026-04-15 14:35):已迁出 crawler/research/config/report blueprint 注册;ReportEngine 仍保持惰性导入与降级日志,但 blueprint 可用性判断已从
app.py移除。
- 备注(2026-04-15 14:35):已迁出 crawler/research/config/report blueprint 注册;ReportEngine 仍保持惰性导入与降级日志,但 blueprint 可用性判断已从
-
新建
apps/web_api/bootstrap/socketio.py,集中创建/配置 Socket.IO。- 备注(2026-04-15 14:35):已抽出
create_socketio(app);当前虽然仍只封装cors_allowed_origins,但 Socket.IO 创建/绑定职责已稳定离开入口文件。
- 备注(2026-04-15 14:35):已抽出
-
新建
apps/web_api/bootstrap/logging.py,迁移 eventlet 断连补丁与日志初始化。- 备注(2026-04-15 14:35):已迁出 Python 运行时环境变量设置与 eventlet 断连补丁;
ensure_runtime_dirs()与业务日志/生命周期日志初始化仍在 runtime/bootstrap 其他模块侧,但本条 bootstrap logging 拆分任务已完成。
- 备注(2026-04-15 14:35):已迁出 Python 运行时环境变量设置与 eventlet 断连补丁;
-
新建
apps/web_api/runtime/system_state.py,迁移_system_state、_get_system_state()、_set_system_state()、_prepare_system_start()、_mark_shutdown_requested()。- 备注(2026-04-15 14:23):当前模块导出为
get_system_state()/set_system_state()/prepare_system_start()/mark_shutdown_requested(),app.py已完成接线。
- 备注(2026-04-15 14:23):当前模块导出为
-
新建
apps/web_api/runtime/process_manager.py,迁移:-
processes(历史兼容别名,现已移除) STREAMLIT_SCRIPTSstart_streamlit_app()stop_streamlit_app()check_app_status()wait_for_app_startup()cleanup_processes()cleanup_processes_concurrent()- 备注(2026-04-16 13:52):
process_manager.py已直接依赖runtime/log_stream.py,并已移除processes兼容 alias;cleanup 链路中的 forum 停止与 system state 标记已收敛为ProcessCleanupPort,当前剩余较明显的 callback-style 依赖主要是启动链路上的emit_output。
-
-
新建
apps/web_api/runtime/log_stream.py,迁移:write_log_to_file()read_log_from_file()read_process_output()-
parse_forum_log_line()相关依赖梳理 - 备注(2026-04-15 17:55):
log_stream.py已承接日志写入、日志读取、子进程输出转发,app.py中的重复日志实现已移除;Forum 日志解析仍可继续独立清理,但主链路接线已完成。
-
新建
apps/web_api/runtime/forum_runtime.py,迁移:start_forum_engine()stop_forum_engine()- forum status 维护
- 备注(2026-04-17):
runtime/forum_runtime.py已进一步对象化为ForumRuntime/build_forum_runtime(),通过显式注入的ProcessRuntimeRegistry管理 forum 状态;build_runtime_services()/RuntimeServices、route dependency 与SystemLifecycleDependencies当前都优先消费同一个 forum runtime 对象的 bound methods。 - 备注(2026-04-20):本轮继续把旧兼容壳往对象路径压缩,forum 启停与日志读取相关 contract 已补到
tests/unit/web_api/test_forum_runtime.py/tests/unit/web_api/test_forum_runtime_helpers.py。 - 备注(2026-04-21):本轮已移除
FORUM_RUNTIME、get_default_forum_runtime()以及剩余模块级 forum wrapper;forum 侧公开面当前收敛为ForumRuntime/build_forum_runtime()/parse_forum_log_line()。
-
新建
apps/web_api/runtime/search_dispatch.py,迁移:_build_query_agent()_build_media_agent()_build_insight_agent()_run_local_engine_research()_execute_search_dispatch_async()- 备注(2026-04-16 14:54):
runtime/search_dispatch.py已退化为本地引擎构造/执行 adapter 与 search runtime builder;query 解析与接单分发编排已开始委托给services/application/analysis/AnalysisService。 - 备注(2026-04-20):本轮继续把 search runtime 默认依赖集中化:新增
build_analysis_execution_context()与_resolve_analysis_service(),先把 execution context / analysis service 的缺省解析收口到显式 helper;build_search_request_submitter()也已接管 route submitter 的最终装配。 - 备注(2026-04-21):本轮已新增
SearchDispatchRuntime/build_search_dispatch_runtime()并移除剩余模块级 search wrapper;搜索侧 runtime 公开面当前收敛为 runtime object、本地 agent builder、execution-context builder 与 submitter builder,兼容 monkeypatch seam 则保留在bootstrap/search_hooks.py。
-
新建
apps/web_api/runtime/system_lifecycle.py,迁移:_schedule_server_shutdown()_start_async_shutdown()-
initialize_system_components()调用协作逻辑 - 备注(2026-04-15 14:35):已新增
SystemLifecycleService,承接系统启动、异步关机、Socket.IO 停止调度;app.py的/api/system/start与/api/system/shutdown已改为委托 runtime 模块,但 Flask/Socket.IO 初始化仍在入口文件中。
-
将
app.py收缩为:- 导入
create_app() - 暴露
app/socketio/main - 少量根级启动逻辑
- 备注(2026-04-17):
app.py已进一步收缩为 import-path 入口壳、bootstrap_app_module(...)装配结果消费,以及main();create_app()、runtime services、cleanup handler、search hook seam、route/socket dependency wiring 与 handler 注册现统一由apps/web_api/bootstrap/app_module.py承接。后续轮次已继续把SEARCH_HOOKS、SEARCH_HOOK_BINDINGS、_REGISTERED_CLEANUP_HANDLER等旧模块级兼容出口收回到私有_APP_MODULE之下。 - 备注(2026-04-20):本轮已继续移除
RUNTIME_SERVICES、SEARCH_HOOK_BINDINGS、ROUTE_DEPENDENCIES、SOCKET_EVENT_DEPENDENCIES四个未再被消费的兼容导出;入口壳当前公开面已进一步收缩到app、socketio、main。 - 备注(2026-04-20):本轮继续把 bootstrap 内部装配结果从入口契约中收回:
AppModuleBootstrapResult当前只暴露app、socketio、cleanup_handler、search_hooks,runtime_services、search_hook_bindings、route_dependencies、socket_event_dependencies等内部装配产物不再作为 app-module 结果对象向外暴露。 - 备注(2026-04-20):本轮继续移除入口级测试型兼容出口:
app.py已不再导出_REGISTERED_CLEANUP_HANDLER、SEARCH_HOOKS,相关 reload-safe cleanup 与 search monkeypatch 夹具已统一改为通过私有_APP_MODULE访问cleanup_handler、search_hooks。
- 导入
-
更新
apps/web_api/__init__.py,确保导出路径稳定。- 备注(2026-04-16 09:10):包级入口已稳定导出
create_app,未再依赖旧入口文件侧效应。
- 备注(2026-04-16 09:10):包级入口已稳定导出
-
启动验证:确保主服务可正常启动,关键接口不报错。
- 备注(2026-04-16 16:00):当前已通过定向 unit tests、
tests/unit/application/test_analysis_run_store.py、tests/unit/application/test_analysis_service.py、tests/unit/web_api/test_process_manager_cleanup.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_system_controls.py验证/首页 dev redirect、/api/status、/api/start/<app_name>、/api/stop/<app_name>、/api/system/status、/api/system/start、/api/system/shutdown成功/失败透传、/api/search、/api/research/tasks/<task_id>/analysis-run、factory/bootstrap 装配、Forum/日志/Streamlit 路由依赖注入、route-ready search wrappers、Socket.IO handlers 关键链路、app-module search monkeypatch 兼容链路、process-manager cleanup 串行/并发分支,以及AnalysisService/AnalysisRun的 application-layer contract;当前定向回归为 64 条测试通过。现存噪音主要是 eventlet deprecation warning 与.pytest_cache权限告警。 - 备注(2026-04-16):analysis query 链路已从 route 侧临时拼装的 dummy
AnalysisService中拆出,改为独立AnalysisRunQueryService;同时补齐AnalysisRunStore.list_runs_for_task(...)与GET /api/research/tasks/<task_id>/analysis-runs,并继续把 analysis 查询输出标准化到AnalysisRunDTO/AnalysisRunListDTO。本轮还补了更资源化的GET /api/research/tasks/<task_id>/analysis视图,以及 crawler 侧的CrawlerJobDTO/CrawlerJobListDTO、CrawlerJobQueryService、GET /api/crawler/jobs/GET /api/crawler/jobs/{id}只读查询边界,当前相关定向测试已更新为 89 条通过,warning 仍仅为 eventlet deprecation 与.pytest_cache权限告警。 - 备注(2026-04-16):已继续沿 task-scoped analysis 资源化方向推进,新增
GET /api/research/tasks/<task_id>/analysis,由AnalysisRunQueryService复用现有 run/history payload helper 输出嵌套analysis资源视图;本轮相关回归为tests/unit/application/test_analysis_query_service.py+tests/integration/test_web_api_smoke.py共 20 条通过。 - 备注(2026-04-17):已把 task-scoped analysis 资源视图进一步收敛到共享
TaskAnalysisResourceDTO,将summary/history/stats的派生逻辑从AnalysisRunQueryService中抽离,同时保持/api/research/tasks/<task_id>/analysis外部 payload 不变;crawler 读侧则继续补强tests/unit/application/test_crawler_query_service.py、tests/unit/backend/test_crawler_routes.py与tests/integration/test_web_api_factory_smoke.py,锁定idle + empty history、单 job 成功查询和 factory app 下/api/crawler/jobs//api/crawler/jobs/{id}的注册与最小 smoke。当前扩展后的定向回归为 97 条测试通过,warning 仍仅为 eventlet deprecation 与.pytest_cache权限告警。 - 备注(2026-04-17):已新增
services/application/crawler/crawler_service.py与CrawlerStateDTO,将/api/crawler/state、/api/crawler/jobs、/api/crawler/jobs/{id}以及 login/crawler start-stop 路由统一收口到CRAWLER_APP_SERVICE;route 层当前只保留请求解析、异常映射与响应封装。相关回归已扩展到tests/unit/shared/test_crawler_state_dto.py、tests/unit/application/test_crawler_service.py,并补了/api/crawler/start、/api/crawler/stop的 app smoke,当前组合回归为 118 条测试通过,warning 仍仅为 eventlet deprecation 与.pytest_cache权限告警。 - 备注(2026-04-17):已继续打通 task-scoped crawler 闭环:
ResearchTaskService.set_task_status()、backend/research_tasks.py与map_legacy_research_task()现已支持crawler_job_idround-trip,CrawlerService.start_crawler()会在携带research_task_id时把history_id -> crawler_job_id回写到任务;同时backend/research_routes.py已新增GET /api/research/tasks/<task_id>/crawler,以共享TaskCrawlerResourceDTO输出“仅 linked job 可见”的 task-scoped crawler 资源视图,不再回退到全局 current job。围绕这条链路补齐tests/unit/shared/test_task_crawler_resource_dto.py、tests/unit/application/test_research_task_service.py、tests/unit/application/test_crawler_service.py与tests/integration/test_web_api_smoke.py后,已重新执行主定向回归共 126 条测试通过;warning 仍仅为 eventlet deprecation 与.pytest_cache权限告警。 - 备注(2026-04-17):Forum runtime 对象化后,已新增
tests/unit/web_api/test_forum_runtime.py、恢复tests/unit/web_api/test_forum_runtime_helpers.py的 log history cursor contract,并补齐tests/unit/web_api/test_runtime_bootstrap.py对 “同一 forum runtime 对象贯穿 runtime services / route wiring” 的断言;相关定向回归31 passed、33 passed,warning 仍仅包含既有eventletdeprecation、datetime.utcnow()deprecation 与.pytest_cache权限提示。 - 备注(2026-04-17):已新增
tests/unit/web_api/test_app_module_bootstrap.py,覆盖bootstrap_app_module(...)对 app/socketio/runtime services/cleanup/search hooks/route wiring 的装配 contract;同时修复 helper 直接导入 runtime builders 导致tests/integration/test_web_api_system_controls.py中 monkeypatch 失效的问题。相关回归已补跑tests/unit/web_api/test_app_module_bootstrap.py、tests/unit/web_api/test_search_hooks.py、tests/unit/web_api/test_cleanup.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_system_controls.py共34 passed,以及tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_socket_events.py加同一组 integration suites 共35 passed;warning 仍仅包含既有eventletdeprecation、datetime.utcnow()deprecation 与.pytest_cache权限提示。 - 备注(2026-04-20):本轮继续补跑
tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py、tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_runtime_service.py、tests/unit/shared/test_config_access.py,组合回归为50 passed;其中tests/integration/test_web_api_smoke.py已补充断言,锁定app.py不再导出RUNTIME_SERVICES、SEARCH_HOOK_BINDINGS、ROUTE_DEPENDENCIES、SOCKET_EVENT_DEPENDENCIES这四个历史兼容对象。 - 备注(2026-04-20):本轮又补跑
tests/unit/web_api/test_app_module_bootstrap.py、tests/unit/web_api/test_process_manager_cleanup.py、tests/unit/web_api/test_forum_runtime.py、tests/unit/web_api/test_forum_runtime_helpers.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_runtime_service.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py,组合回归为58 passed;其中test_app_module_bootstrap.py已同步锁定 bootstrap 结果对象只保留入口真正需要的公开字段。 - 备注(2026-04-20):本轮继续补跑
tests/unit/web_api/test_process_manager_cleanup.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_app_module_bootstrap.py、tests/unit/web_api/test_system_lifecycle.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_runtime_service.py,组合回归为57 passed;新增断言锁定app.py不再导出_REGISTERED_CLEANUP_HANDLER/SEARCH_HOOKS、STREAMLIT_SCRIPTS改为实时兼容视图,以及build_system_lifecycle_dependencies()统一委托 runtime lifecycle 默认 builder。 - 备注(2026-04-20):本轮又补跑
tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_forum_runtime.py、tests/unit/web_api/test_forum_runtime_helpers.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_runtime_service.py,组合回归为64 passed;新增断言锁定 forum wrapper 改为调用时解析默认 runtime、search dispatch wrapper 统一通过 helper 解析 execution context / analysis service,以及 crawler manager 继续通过build_execution_context()透传 runtimecwd/env。 - 备注(2026-04-20):本轮继续把
web_apisearch bootstrap contract 收口为单一 submitter:tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_app_module_bootstrap.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py与tests/integration/test_web_api_smoke.py已联合回归41 passed,并额外通过python -m py_compile apps/web_api/bootstrap/runtime.py apps/web_api/bootstrap/app_module.py语法检查;新增断言锁定HttpRouteDependencies只消费submit_search_request,而search_hooks的动态 monkeypatch smoke 继续保持兼容。 - 备注(2026-04-20):本轮继续把 search submitter 的最终装配从 bootstrap 收回 runtime/search adapter:
tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_app_module_bootstrap.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py与tests/integration/test_web_api_smoke.py已联合回归49 passed,并额外通过python -m py_compile apps/web_api/runtime/search_dispatch.py apps/web_api/bootstrap/runtime.py apps/web_api/bootstrap/app_module.py语法检查;新增断言锁定build_search_request_submitter()统一绑定AnalysisExecutionContext、research task service 与动态 dispatch hook,且bootstrap_app_module()不再手工拼 execution context。 - 备注(2026-04-20):本轮继续把 shutdown/cleanup wiring 的重复装配收回 lifecycle runtime:
tests/unit/web_api/test_system_lifecycle.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/integration/test_web_api_system_controls.py与tests/integration/test_web_api_smoke.py已联合回归45 passed,并额外通过python -m py_compile apps/web_api/runtime/system_lifecycle.py apps/web_api/bootstrap/runtime.py语法检查;新增断言锁定build_process_cleanup_port()同时服务 bootstrap cleanup handler 与 async shutdown,不再在两处平行维护ProcessCleanupPort约定。 - 备注(2026-04-20):本轮继续收掉 bootstrap 侧仅作转发的 lifecycle dependency builder,并与前述 cleanup/search 收口一起补跑
tests/unit/web_api/test_system_lifecycle.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_app_module_bootstrap.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_system_controls.py;联合 crawler 最小 compat 壳回归后,当前组合回归为79 passed。
- 备注(2026-04-16 16:00):当前已通过定向 unit tests、
建议下一步顺序(按当前依赖收敛)
- 继续检查
bootstrap/search_hooks.py与_APP_MODULE.search_hooks这条 monkeypatch seam 是否还能进一步缩薄;当前 forum/search 的 runtime 模块级 wrapper 已清理完成,下一步更适合评估 app/bootstrap 侧兼容面是否还能继续收口而不破坏 smoke contract。 - 在 crawler/runtime path 侧继续评估 browser data / writable path 的归属,优先检查
utils/runtime_paths.py与services/crawler/adapters/mediacrawler_adapter.py是否仍存在双轨策略,等 backend/runtime 兼容壳进一步变薄后再决定是否收口行为。 - 在 P1 中继续沿 application service 主线补剩余编排入口,优先看 crawler/report 还有哪些 route/runtime 仍在做非 HTTP 适配之外的工作,避免在 P0 已收紧后又把 orchestration 逻辑回流到 backend/runtime。
验收标准
-
apps/web_api/app.py体积显著下降,不再承载大部分实现。 -
所有运行时状态来源可追溯到
runtime/。 - 系统启动、状态查询、引擎启动/停止、搜索分发仍然可用。
风险与注意事项
-
注意避免循环导入,尤其是
app.py、socketio、runtime/*之间的双向依赖。 -
注意 Flask app / socketio 初始化顺序;
system_lifecycle与log_stream后续若依赖 emit 能力,需要继续采用回调或显式注入,避免 runtime 反向 import Flask 入口。 -
注意旧代码仍依赖模块级全局变量与函数名兼容;当前 search hook 兼容面已收敛到
_APP_MODULE.search_hooks,若后续继续收缩入口或替换 hook 实现,需要先锁定现有 monkeypatch 出口。 -
当前
process_manager.py的 cleanup 路径已收敛为ProcessCleanupPort,但启动链路仍通过回调接收emit_output;后续若继续对象化 process runtime,需要注意不要把 Socket.IO/Flask 入口反向耦合回 runtime。
3.2 建立统一任务模型
目标
统一研究任务主链路中的核心对象与状态机。
当前进展快照(2026-04-15 14:35)
- 已在
services/shared/models/下新增common.py、research_task.py、crawler_job.py、analysis_run.py、report_job.py、mappers.py以及__init__.py。 - 当前统一模型采用 Pydantic v2,包含
ProgressInfo、ErrorInfo、TaskMetadata等公共基元,四类任务模型均保留legacy_payload以兼容旧结构渐进迁移。 - 已补
map_legacy_research_task()与map_crawler_history_entry()mapper 骨架,可将backend/research_tasks.py与backend/crawler/state_store.py的现有 JSON 结构投影到统一模型;AnalysisRun/ReportJob先提供 stub mapper,为 P1 应用层接线预留入口。 - 2026-04-20:
services/shared/models/mappers.py已继续补齐真实map_analysis_run_record()与map_report_task_record(),开始把 analysis persisted payload / report runtime payload 直接投影到统一模型;ReportJobQueryService也已能在只有 runtime dict、尚无unified_task字段时回退走共享 mapper,再输出ReportJobDTO。 - 2026-04-20:
ResearchTaskService本轮已开始直接消费services/shared/events/与services/shared/timeline/的最小骨架:create_task()、set_task_status()、sync_analysis_runtime_status()、sync_report_runtime_status()与transition_task()现在都会产出标准 task lifecycle/timeline 记录,build_runtime_services()也已默认注入TaskTimelineTracker。 - 2026-04-21:
map_legacy_research_task()已继续把crawler_keywords_text映射进统一metadata.extras;ReportJobQueryService也已把/api/report/status|tasks|progress|result的 runtime payload 统一经ReportJobDTO/shared mapper归一后再输出,同时保留顶层status/progress/report_file_*等兼容字段,避免 report/crawler 主链继续直接扩散裸 runtime dict /legacy_payload读取。 - 暂未改动现有 API 返回与前端消费路径,本次属于“模型骨架已落地、接线尚未开始”。
任务清单
-
在
services/shared/models/下新增:research_task.pycrawler_job.pyanalysis_run.pyreport_job.py- 备注(2026-04-15 14:35):同时补充了
common.py、mappers.py与包级__init__.py,统一承载公共字段和旧结构映射入口。
-
为
ResearchTask定义统一字段:idtitle/briefgenerated_querystatusprogresscreated_atupdated_atcrawler_job_idanalysis_run_idreport_job_idmetadatalast_actionerror- 备注(2026-04-15 14:35):额外加入
legacy_payload作为过渡期兼容字段,避免旧 JSON store 字段立即丢失。
-
为
CrawlerJob定义统一字段:idresearch_task_idplatformkeywordsstatusprogressstarted_atfinished_aterror- 备注(2026-04-15 14:35):当前额外保留
config、result_summary、legacy_payload,用于覆盖 crawler runtime/UI state 的现有结构。
-
为
AnalysisRun定义统一字段:idresearch_task_idenginesstatusprogresspartial_resultsmetricsstarted_atfinished_aterror- 备注(2026-04-15 14:35):当前只落模型与 stub mapper,尚未绑定真实运行态来源。
-
为
ReportJob定义统一字段:idresearch_task_idstatusoutput_pathoutput_formatstarted_atfinished_aterror- 备注(2026-04-15 14:35):当前额外补了
progress、artifacts、legacy_payload,便于后续报告引擎产物收口。
-
统一状态枚举:
ResearchTaskStatusCrawlerJobStatusAnalysisRunStatusReportJobStatus- 备注(2026-04-15 14:35):枚举以“渐进兼容”为目标,已覆盖 draft/ready/running/completed/failed/cancelled 等主链路状态,并对旧状态值建立初版映射。
-
梳理当前研究任务接口与状态存储结构,建立旧结构到新模型的 mapper。
- 备注(2026-04-15 14:35):
ResearchTaskStore与CrawlerUiStateStore已有首版 mapper;分析与报告链路仍缺真实旧结构梳理,目前仅提供 stub。 - 备注(2026-04-15 15:21):
services/engines/report/flask_interface.py已为旧ReportTask增加to_model()/to_dto()投影,并引入ReportJobDTO/ReportJobListDTO适配/status、/tasks、/progress、SSE 事件载荷等边界输出;当前仍以task_id回填research_task_id做兼容占位,尚未与真实 research task 建立稳定关联。 - 备注(2026-04-20):analysis/report 两条链路的共享 mapper 已不再停留在 stub:
map_analysis_run_record()与map_report_task_record()已补齐状态、进度、错误、产物字段映射,并新增tests/unit/shared/test_task_model_mappers.py锁定 unified model 投影 contract。 - 备注(2026-04-20):本轮又把 unified task model 的“状态变化事件”也接到了主链路上:
ResearchTaskService现已通过TaskTimelineTracker为 create/set_status/transition/sync_* 入口记录标准 lifecycle entry,避免 shared event/timeline 骨架继续只停留在未接线状态。
- 备注(2026-04-15 14:35):
-
将研究任务相关 API 返回逐步过渡为统一 task 视角。
- 备注(2026-04-15 15:13):
backend/research_routes.py已切换为通过services.application.research.ResearchTaskService输出ResearchTaskDTO/ResearchTaskListDTO,/api/research/tasks的读写响应开始携带unified_task与统一progress/metadata/error字段,同时继续保留旧task_id/status_label/crawler_defaults等兼容字段;任务激活入口仍暂时复用旧 store 的activate(),后续可继续收敛为 application service 专用用例。 - 备注(2026-04-20):
ReportJobQueryService当前已支持把 raw runtime dict 通过共享 mapper 回退转换为ReportJobDTO,减少 report 链路继续扩散“裸 dict / 裸 runtime object”输出的情况;相关断言已补到tests/unit/application/test_report_query_service.py。 - 备注(2026-04-21):本轮继续把 report query 读侧的“DTO 视图是否可用”条件分支收平:
/api/report/status、/api/report/tasks、/api/report/progress/<task_id>与/api/report/result/<task_id>/json当前都会优先经过ReportJobDTO/shared mapper 归一,再输出兼容 payload,并在响应中稳定附带unified_task供新链路消费。 - 备注(2026-04-21):research task 资源接口这条主读链路也已基本切换到统一 task 视角:
backend/research_routes.py当前会通过ResearchTaskService/ResearchTaskDTO输出/api/research/tasks、/api/research-tasks、/api/research-tasks/<id>,analysis/crawler/report 的 task-scoped 资源接口也统一先解析ResearchTaskDTO再委托各 query service 构建 payload。
- 备注(2026-04-15 15:13):
-
[-] 前端核心流程页先兼容读取统一任务字段。
- 备注(2026-04-15 15:13):已在
apps/web_ui/src/composables/useTaskViewModel.ts新增最小 task-centered view model 骨架,聚合ResearchTask / crawler / system-analysis / report四段状态;当前TaskCenterView.vue已接入只读摘要卡作为统一入口,AnalyzeView.vue、DeliverView.vue仍保留现有直接 controller/composable 读取方式,但后续可直接切换到该 view model。
- 备注(2026-04-15 15:13):已在
验收标准
- 主链路可通过统一 task model 表达。
- 旧状态结构不再继续扩散。
- 新接口/新逻辑默认使用统一模型。
3.3 收口 shared 基础设施
目标
将 config / dto / errors / events / logging 的标准入口集中到 services/shared/。
任务清单
- [-] 梳理当前各引擎与 backend 中重复的配置读取方式。
- 备注(2026-04-20):
backend/crawler/managers.py与apps/engine_console/query_engine_streamlit_app.py、media_engine_streamlit_app.py、insight_engine_streamlit_app.py已开始把直接settings/reload_settings()读取迁到get_settings()/get_database_runtime_settings();当前剩余工作主要是继续盘点更深层 engine/runtime 内部的直接配置访问。 - 备注(2026-04-21):本轮继续沿主线做小步收口:
backend/crawler/routes.py的/api/crawler/options默认值读取已不再由 route 直接触碰 shared config,而是改由CrawlerService.get_default_save_option()统一暴露;同时services/engines/forum/llm_host.py也已从直接 import 模块级settings切到构造时调用get_settings(),减少 engine 在模块导入阶段冻结 shared config 单例。
- 备注(2026-04-20):
-
在
services/shared/config/中补齐统一 settings 访问边界。- 备注(2026-04-20):已新增
services/shared/config/access.py与包级导出,补齐get_settings()、get_setting_value()、read_settings_values()、resolve_settings_write_env_file()、DatabaseRuntimeSettings/get_database_runtime_settings()等 helper;backend/config_admin.py、backend/crawler/routes.py、backend/crawler/runtime.py已开始改走这条 shared config 边界。 - 备注(2026-04-20):本轮继续把 shared config 边界落到 crawler / 单引擎入口:
backend/crawler/managers.py现已不再自己读取 shared config,而是通过 runtime/application helper 解析默认save_option;三个apps/engine_console/*_streamlit_app.py入口也已改为在运行时按需调用get_settings()/get_database_runtime_settings(),避免模块导入阶段继续 capture 全局 settings 单例。 - 备注(2026-04-20):本轮也继续把这条 shared config 边界落到 Web API runtime adapter:
apps/web_api/runtime/search_dispatch.py中 Query/Media/Insight 的本地 agent builder 当前已全部改为调用时读取get_settings()/get_database_runtime_settings(),不再直接 import 模块级settings单例。 - 备注(2026-04-21):本轮又继续把 shared config access 往 crawler/forum 主链推了一步:
CrawlerService已新增get_default_save_option()作为 application 边界,route 侧只负责透传;ForumHost也已切换到在实例初始化时读取get_settings(),并补了定向单测锁定这条 access contract。
- 备注(2026-04-20):已新增
-
在
services/shared/errors/中新增标准错误模型:AppErrorEngineErrorExternalServiceErrorValidationError- 备注(2026-04-15 15:13):已先落
AppError、ExternalServiceError与错误分类/上下文骨架,采用 Pydantic 共享模型以便渐进迁移;EngineError、ValidationError先不提前细化,避免在现有错误语义尚未统一前制造重复类型。
-
在
services/shared/dto/中新增主链路 DTO:ResearchTaskDTOCrawlerJobDTOAnalysisRunDTOReportJobDTO- 备注(2026-04-16):
ResearchTaskDTO、ReportJobDTO、AnalysisRunDTO与CrawlerJobDTO已落地;当前backend/research_routes.py上的 analysis 查询输出已切到AnalysisRunDTO/AnalysisRunListDTO组装 response payload,crawler 共享边界也已具备CrawlerJobListDTO。剩余工作主要是让更多 route/resource 持续改用这些 DTO,而不是继续直接暴露 model/store 结构。 - 备注(2026-04-21):
ResearchTaskDTO已补get_crawler_defaults()/get_crawler_keywords_text()轻量 accessor,把 crawler 默认参数与关键词文本的 legacy fallback 收回 DTO 边界;ReportJobDTO.to_response_item()也已补齐report_file_*/state_file_*/markdown_file_*等 artifact flattening,并将顶层progress维持为兼容数值视图,便于旧前端继续消费。
-
在
services/shared/events/中新增统一事件 envelope。- 备注(2026-04-15 15:13):已新增传输无关的
EventEnvelope与TaskLifecycleEvent骨架,统一event_type/task_id/source/timestamp/payload并补 trace/correlation/version 字段,为后续 SSE/Socket.IO/日志桥接和统一任务状态流转预留契约。
- 备注(2026-04-15 15:13):已新增传输无关的
-
建立最小可用的 task timeline 聚合入口。
- 备注(2026-04-15 15:21):已新增
services/shared/timeline/,提供TaskTimelineStore/InMemoryTaskTimelineStore与TaskTimelineTracker;当前先把TaskLifecycleEvent聚合为可查询 timeline entry,并同步镜像到 observability recorder,runtime/application 后续可通过依赖注入挂载,暂未直接接入现有 API 或持久化存储。 - 备注(2026-04-20):本轮已把这条 shared timeline 骨架正式挂到
ResearchTaskService:build_runtime_services()当前会默认创建TaskTimelineTracker并注入 application service,研究任务创建、显式状态回写、analysis/report runtime 同步与合法状态迁移都已开始产出标准 lifecycle/timeline 记录。
- 备注(2026-04-15 15:21):已新增
-
统一事件字段:
event_typetask_idsourcetimestamppayload- 备注(2026-04-15 15:13):上述核心字段已在
EventEnvelope固化,同时补充event_id、trace_id、correlation_id、version以兼容后续 observability 与跨边界事件传输。
-
[-] 统一主链路日志上下文格式,至少包含:
task_idenginestageaction- 备注(2026-04-20):已新增
services/shared/logging/context.py与包级导出,提供build_log_context()/bind_logger();当前apps/web_api/app.py、apps/web_api/factory.py已开始通过 shared helper 绑定source与启动参数,后续仍需继续把 task/engine/stage/action 逐步挂到主链路日志上。 - 备注(2026-04-21):本轮已先把研究任务主链的统一日志上下文接到最小闭环上:
services/application/research/task_service.py现在会在 lifecycle/timeline 记录时统一绑定task_id/stage/action/operation,backend/research_routes.py也已在 create/activate/task-scoped resource 的错误路径补齐task_id/action上下文;这条 shared logging contract 已从入口启动日志延伸到一条真实业务主链。
-
[-] 识别并迁移各 engine 中的重复 shared 能力实现。
- 备注(2026-04-20):本轮已先从最浅层入口着手,把三个
engine_consoleStreamlit 入口的重复 settings 读取收口到 shared config access helper;后续可继续沿 engine 内部 runtime/config helper 往下推进,避免再次在各引擎里复制 settings 解析逻辑。 - 备注(2026-04-21):本轮已继续把 forum engine 的 shared config 读取往统一 helper 迁移:
services/engines/forum/llm_host.py不再直接依赖模块级settings单例,而是与其它主链入口一样改走services.shared.config.access.get_settings()。 - 备注(2026-04-20):本轮已补跑
tests/unit/shared/test_config_access.py、tests/unit/shared/test_logging_context.py、tests/unit/shared/test_task_model_mappers.py、tests/unit/backend/test_config_admin.py、tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_runtime_service.py、tests/unit/backend/test_crawler_routes.py、tests/unit/application/test_crawler_service.py、tests/unit/application/test_report_query_service.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_system_lifecycle.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_smoke.py,当前组合回归为102 passed;warning 仍仅包含既有eventletdeprecation、datetime.utcnow()deprecation 与.pytest_cache权限提示。 - 备注(2026-04-20):本轮另补跑
tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py、tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_runtime_service.py、tests/unit/shared/test_config_access.py,组合回归为50 passed;三个engine_console入口也已额外通过py_compile语法检查。 - 备注(2026-04-20):本轮围绕 shared timeline 接线与 crawler writable path contract 又补跑了
tests/unit/application/test_research_task_service.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/integration/test_web_api_system_controls.py、tests/unit/shared/test_task_timeline_tracker.py、tests/unit/backend/test_mediacrawler_adapter.py、tests/unit/backend/test_crawler_runtime_service.py,当前组合回归为47 passed;warning 仍仅包含既有eventletdeprecation 与.pytest_cache权限提示。 - 备注(2026-04-21):本轮围绕 crawler options 收口与 forum config access 又补跑
tests/unit/application/test_crawler_service.py、tests/unit/backend/test_crawler_routes.py、tests/unit/shared/test_forum_llm_host.py、tests/integration/test_web_api_smoke.py,组合回归为59 passed;同时backend/crawler/routes.py、services/application/crawler/crawler_service.py、services/engines/forum/llm_host.py已额外通过py_compile语法检查。
- 备注(2026-04-20):本轮已先从最浅层入口着手,把三个
验收标准
- 新代码不再新增重复 config/dto/error/event 结构。
- 主链路已有统一 shared 类型可复用。
3.4 建立运行时 registry/store
目标
将局部全局变量式的状态管理改为显式运行时对象。
当前进展快照(2026-04-17 16:05)
- 已新增
apps/web_api/runtime/process_registry.py,落地ProcessRuntimeRegistry与默认PROCESS_RUNTIME_REGISTRY,统一承载insight/media/query/forum的进程、端口、状态与输出快照。 -
apps/web_api/runtime/system_state.py已从函数式模块状态升级为SystemStateRegistry,并保留get_system_state()/set_system_state()/prepare_system_start()/mark_shutdown_requested()兼容出口。 -
http_routes.py与socket_events.py已改为通过注入的 registry 读取/api/status、/api/system/status与 Socket 状态快照;system_lifecycle.py、forum_runtime.py也已开始通过 registry 读写运行态。 -
bootstrap/runtime.py现已将 Forum 启停、Forum 日志读取以及普通 app 日志读写以 callable 形式注入HttpRouteDependencies,http_routes.py不再直接耦合这些 runtime 模块实现。 -
SystemLifecycleService已补齐SystemLifecycleDependencies,启动/关机关联的streamlit_scripts、Forum 启停、状态刷新、清理编排、关机日志与子进程探测都改为经依赖 bundle 消费,默认 wiring 由bootstrap/runtime.py承接。 - 已新增
apps/web_api/runtime/task_runtime_store.py,落地TaskRuntimeStore与默认TASK_RUNTIME_STORE,统一承载 task-scoped 的运行期投影(status/generated_query/analysis_run_id/last_action/progress/error/partial_results/metrics)。 -
build_runtime_services()/RuntimeServices已开始显式注入task_runtime_store;AnalysisService当前会在“无可用引擎 / 接单 / 运行中 / 部分成功 / 失败”各阶段同步回写TaskRuntimeStore,形成独立于 durableResearchTaskStore/AnalysisRunStore的运行态快照。 - 已新增
ProcessManager类,当前bootstrap/runtime.py已会为RuntimeServices显式构造并注入该对象;route/socket/system lifecycle 这三条 wiring 已开始消费ProcessManager实例方法,而模块级start_streamlit_app()/stop_streamlit_app()/check_app_status()/cleanup_processes*()仅作为兼容 wrapper 保留。 - 已新增
apps/web_api/runtime/engine_registry.py,落地EngineRuntimeRegistry与默认ENGINE_RUNTIME_REGISTRY,统一承载引擎静态运行元数据(launch_mode、script_path、local_runner、available);search_dispatch.py的本地引擎分发现已改为只从该 registry 读取 runner 元数据。 -
bootstrap/runtime.py/RuntimeServices/build_system_lifecycle_dependencies()现已显式注入engine_registry;ProcessManager默认使用的 Streamlit 脚本清单也改为从EngineRuntimeRegistry派生,避免再维护平行引擎表。 -
tests/conftest.py现在会在reload_web_api_app()前重置EngineRuntimeRegistry、ProcessRuntimeRegistry、SystemStateRegistry与TaskRuntimeStore,避免组合回归时 runtime 单例状态互相污染。 - 已新增
ForumRuntime/build_forum_runtime();build_runtime_services()现会在未显式注入 forum runtime 时,根据当前process_registry构造同源 forum runtime,避免 forum 状态继续写回到默认单例 registry。RuntimeServices当前也已显式持有forum_runtime字段,route / lifecycle 装配使用的是同一对象的 bound methods。 -
forum_runtime.py的 log history helper 也已修复file.tell()游标读取问题,并恢复对应 helper contract test。 - 2026-04-20:
system_lifecycle.py、search_dispatch.py与bootstrap/runtime.py已继续把默认依赖从“定义时绑定全局单例”改为“运行时显式解析/注入优先”;cleanup_handler现在也会回写当前注入的SystemStateRegistry,不再偷偷落回全局set_system_state(...)。 - 2026-04-20:
SystemLifecycleService当前已不再依赖导入时冻结的DEFAULT_SYSTEM_LIFECYCLE_DEPENDENCIES常量,改为在构造时通过build_default_system_lifecycle_dependencies(...)基于当前注入的process_registry解析默认 bundle;search_dispatch.py中 Query/Media/Insight 的本地 agent builder 也已切到在调用时读取get_settings()/get_database_runtime_settings(),避免 runtime 模块在 import 阶段继续 capture 全局 settings。 - 2026-04-20:
process_manager.py中的STREAMLIT_SCRIPTS兼容导出已改为实时视图,不再在模块导入阶段冻结EngineRuntimeRegistry的 script snapshot;ProcessManager在未显式传入streamlit_scripts时也统一复用这条运行时解析 helper。 - 2026-04-20:
bootstrap/runtime.py中的build_system_lifecycle_dependencies()已改为直接委托runtime/system_lifecycle.py的默认 builder,避免 lifecycle 默认 wiring 继续维持两份平行实现。 - 2026-04-20:
forum_runtime.py曾先把旧模块级 wrapper 改为调用时通过build_forum_runtime()解析默认 runtime;search_dispatch.py则新增build_analysis_execution_context()与_resolve_analysis_service(),把 search runtime 的默认依赖解析进一步集中到 helper,为后续彻底移除模块级 wrapper 做准备。 - 2026-04-20:
SystemStateRegistry本轮已新增clear_shutdown_request();tests/conftest.py的reload_web_api_app()夹具也已改为通过公有 API 清理 shutdown 标记,不再直接写私有_state["shutdown_requested"]。 - 2026-04-21:本轮继续把状态查询 contract 从 route/socket 侧收口到 runtime facade:已新增
apps/web_api/runtime/status_service.py的RuntimeStatusService,/api/status、/api/system/status与 Socket.IOrequest_status现都改为通过同一状态服务查询 payload,不再分别在接口层拼check_status + registry.snapshot()或直接读取system_state_registry;HttpRouteDependencies里未再使用的research_task_service也已移除。 - 2026-04-21:本轮继续把
search_dispatch.py朝与forum_runtime.py对称的对象化模式推进:已新增SearchDispatchRuntime与build_search_dispatch_runtime(),并在后续轮次移除剩余模块级 search wrapper;原先分散在 wrapper 内部的_resolve_analysis_service(...)+ execution-context 兜底逻辑现已沉到 runtime object / helper builder。 - 2026-04-21:本轮又继续把这层 search runtime 真正接进主链路 wiring:
RuntimeServices已显式持有search_dispatch_runtime,bootstrap/app_module.py的search_hooks当前也直接暴露同一个 search runtime 对象;这样 app-module search hook、route submitter 与 search runtime object 现在共享同一 service graph,而不是“一条走 analysis service、一条走 wrapper/runtime builder”。
任务清单
-
新建
SystemStateRegistry。- 备注(2026-04-15 16:05):
runtime/system_state.py已落地SystemStateRegistry,并由app.py、system_lifecycle.py、http_routes.py通过显式注入或兼容函数接线。
- 备注(2026-04-15 16:05):
-
新建
TaskRuntimeStore。- 备注(2026-04-17):已新增
runtime/task_runtime_store.py,提供 clone-safe 的 task runtime snapshot / upsert / clear 能力;当前已先接入AnalysisService写侧,用于同步analysis_run_id、运行阶段、进度、错误与 partial results,不改动现有 API 读模型。
- 备注(2026-04-17):已新增
-
新建
ProcessManager类封装进程状态与操作。- 备注(2026-04-17):已在
runtime/process_manager.py中补ProcessManager,统一封装describe_running_children()、Streamlit 启停、健康检查、启动等待以及串行/并发 cleanup;bootstrap/runtime.py现已改为优先将ProcessManager实例注入 route/socket/system lifecycle wiring,相关对象级单测与 Web API smoke 已回归通过。
- 备注(2026-04-17):已在
-
新建
EngineRegistry或EngineRuntimeRegistry,统一记录引擎可用性与运行状态。- 备注(2026-04-17):已新增
runtime/engine_registry.py,提供EngineRuntimeRegistry的 clone-safe snapshot、可用性切换与 reset 能力;search_dispatch.py的本地 runner 选择、bootstrap/runtime.py的 runtime service 装配以及process_manager.py的默认 Streamlit 脚本来源都已切到从该 registry 派生。引擎 live process status 仍继续由ProcessRuntimeRegistry负责,避免双写。
- 备注(2026-04-17):已新增
-
将
/api/status、/api/system/status等状态接口改为从 registry/store 读取。- 备注(2026-04-15 16:05):
http_routes.py已通过process_registry.status_snapshot()与system_state_registry.snapshot()返回状态;socket_events.py也已通过 registry 推送状态快照。
- 备注(2026-04-15 16:05):
-
清理入口文件中散落的状态读写逻辑。
- 备注(2026-04-17):runtime service 构造与 route dependency 拼装已迁入
apps/web_api/bootstrap/runtime.py,system_lifecycle.py也已切到依赖 bundle;当前ProcessManager、TaskRuntimeStore、EngineRuntimeRegistry与ForumRuntime都已通过 runtime services 显式注入。现阶段更明显的残余已经转向 search monkeypatch seam 与少量默认注入兜底,而不是大块入口实现仍滞留在app.py。 - 备注(2026-04-20):
SystemLifecycleService、build_runtime_services()、build_system_lifecycle_dependencies()与 search dispatch 相关 builder 当前都已调整为None -> resolve default模式,避免模块导入阶段 capture 默认单例;相关断言已补到tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_system_lifecycle.py。 - 备注(2026-04-20):本轮进一步移除了
SystemLifecycleService对导入时默认依赖 bundle 的隐式绑定,并让search_dispatch.py的本地 agent builder 改为运行时读取 shared config helper;同时新增单测锁定“构造时解析默认 lifecycle bundle”与“Insight/Query builder 使用 shared settings/database helper”两条 contract。 - 备注(2026-04-20):本轮继续把同类尾巴从
process_manager.py与forum_runtime.py中收掉:ProcessManager/build_process_manager()/ wrapper helpers 以及ForumRuntime/build_forum_runtime()现在都不再通过默认参数冻结PROCESS_RUNTIME_REGISTRY,而是改为运行时读取当前模块上的 registry;FORUM_RUNTIME这类死兼容导出也已在后续轮次移除。 - 备注(2026-04-20):本轮继续清理运行时默认来源的散点:
process_manager.py的STREAMLIT_SCRIPTS已改为实时兼容视图,不再冻结 engine registry snapshot;bootstrap/runtime.py里的 lifecycle dependency 装配也已统一委托给runtime/system_lifecycle.py的默认 builder,避免同一套默认 wiring 在 bootstrap 与 runtime 侧各维护一份。 - 备注(2026-04-20):本轮继续把 forum/search 这两块 runtime 的默认兜底压成单点:forum 与 search 都先经历了一轮“调用时解析默认对象/依赖”的收口,避免 wrapper 内部继续散落多份默认单例逻辑。
- 备注(2026-04-20):本轮又把 runtime state reset 的最后一处私有字段写入收回到 registry API:
SystemStateRegistry.clear_shutdown_request()已落地并接入tests/conftest.py,reload fixture 不再越过封装直接操作_state["shutdown_requested"]。 - 备注(2026-04-21):本轮又收掉一层 route/socket 侧的散落状态读取:
http_routes.py与socket_events.py现统一依赖RuntimeStatusService的get_process_status_snapshot()/get_system_status_payload(),不再直接持有ProcessRuntimeRegistry、SystemStateRegistry或额外的check_app_statuscallback;相关 wiring 已同步收回bootstrap/runtime.py。 - 备注(2026-04-21):本轮又把 search runtime 这一层再对象化了一步:
search_dispatch.py当前已新增SearchDispatchRuntime,并移除了剩余模块级 search wrapper;这样 search 侧兼容面已不再停留在 runtime 模块级入口,而是转移到 app/bootstrap 的 monkeypatch seam。 - 备注(2026-04-21):本轮继续把 search runtime 从“模块里有对象”推进到“bootstrap 真正复用同一个对象”:
RuntimeServices当前已显式持有search_dispatch_runtime,bootstrap_app_module()的search_hooks也直接暴露这同一个 runtime 对象;主链路上的 search wiring 已与 forum/status 一样优先消费显式 runtime object。 - 备注(2026-04-21):本轮又继续把 app/bootstrap 侧这条 monkeypatch seam 收薄:
SearchHookContainer/build_search_hook_container()已移除,tests/unit/web_api/test_search_hooks.py当前改为直接针对通用 hook source 验证动态 binding 行为;兼容 seam 现已更明确地收敛为_APP_MODULE.search_hooks + build_search_hook_bindings(...)。
- 备注(2026-04-17):runtime service 构造与 route dependency 拼装已迁入
验收标准
-
系统运行态来源唯一。
- 备注(2026-04-17):
EngineRuntimeRegistry、ProcessRuntimeRegistry、SystemStateRegistry、TaskRuntimeStore与ForumRuntime都已落地并接入 runtime wiring;search_dispatch.py现只读 engine/process registry,process_manager.py的processes兼容出口也已移除,bootstrap/runtime.py/SystemLifecycleService/ route / socket wiring 现也优先通过显式注入的ProcessManager、EngineRuntimeRegistry、ForumRuntime与TaskRuntimeStore工作。 - 备注(2026-04-20):runtime 读写路径已进一步避免“定义时绑定默认单例”;
SystemLifecycleService的默认 lifecycle dependencies、search_dispatch.py的 shared settings 读取,以及 forum/search 缺省依赖解析都已改为调用时或 builder 侧解析。 - 备注(2026-04-21):本轮已把 HTTP route / Socket.IO 的状态查询统一压到
RuntimeStatusService,并把 forum/search 的模块级 runtime wrapper 彻底移除;当前生产代码里的运行态读写来源已经统一可追溯到apps/web_api/runtime/*,剩余尾项主要是 app/bootstrap monkeypatch seam 和少量 builder 兼容注入形式,而不再是状态来源分散。
- 备注(2026-04-17):
-
状态查询接口不再拼接多个散变量。
- 备注(2026-04-15 16:05):
/api/status、/api/system/status与 Socket 状态推送已完成切换,但其他运行态读取路径仍有渐进兼容逻辑。 - 备注(2026-04-21):本轮已新增
RuntimeStatusService并接入http_routes.py、socket_events.py与bootstrap/runtime.py,/api/status、/api/system/status、Socketrequest_status现在都会统一通过同一 facade 生成状态 payload;接口层已不再自行拼接多个散落状态变量。
- 备注(2026-04-15 16:05):
-
运行时对象可被单独测试。
- 备注(2026-04-17):仓库已补
TaskRuntimeStore、EngineRuntimeRegistry、ProcessManager、ForumRuntime、registry、search dispatch、system lifecycle、bootstrap/runtime 单测,并重新跑通 Web API 最小冒烟测试;SystemLifecycleService、TaskRuntimeStore、EngineRuntimeRegistry、ForumRuntime与ProcessManager现在都可通过 fake dependencies / isolated store 独立测试启动、幂等关机、异步清理协作与 analysis runtime 投影,后续仍可继续补更多运行时协作场景的 contract test。 - 预备断言(待主线程接口落地后补):
build_runtime_services()应显式构造 forum runtime service;build_http_route_dependencies()与build_system_lifecycle_dependencies()应消费同一对象的 bound methods;隔离process_registry的 factory/integration smoke 应能覆盖 forum start/stop 后状态只回写注入 registry、而非默认单例。 - 备注(2026-04-20):本轮继续补跑
tests/unit/web_api/test_system_lifecycle.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py,组合回归为45 passed;新增断言覆盖 runtime 默认依赖的延迟解析和 search builder 对 shared helper 的调用路径。 - 备注(2026-04-20):本轮又新增
tests/unit/web_api/test_process_manager_cleanup.py与tests/unit/web_api/test_forum_runtime.py中对“未显式注入时跟随模块当前 registry”的断言,并联合 app bootstrap / crawler runtime / integration smoke 重新回归为58 passed。 - 备注(2026-04-20):本轮又新增
tests/unit/web_api/test_process_manager_cleanup.py、tests/unit/web_api/test_runtime_bootstrap.py与 integration smoke 中对“STREAMLIT_SCRIPTS跟随当前 engine registry”“bootstrap lifecycle dependency builder 统一委托 runtime 默认 builder”“app.py 不再导出旧兼容符号”的断言,相关定向回归为57 passed。 - 备注(2026-04-20):本轮继续补
tests/unit/web_api/test_search_dispatch.py与tests/unit/web_api/test_forum_runtime.py,先锁定 forum/search 旧兼容壳向 runtime object / helper 收口的 contract;这些断言已在后续轮次随着模块级 wrapper 移除而同步收敛。联合 smoke/crawler 相关回归后组合结果为64 passed。 - 备注(2026-04-20):本轮又补
tests/unit/web_api/test_system_lifecycle.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py,锁定SystemStateRegistry.clear_shutdown_request()的 public contract 与 reload fixture 不再直改私有_state;相关定向回归为37 passed。 - 备注(2026-04-21):本轮新增
tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_socket_events.py与tests/integration/test_web_api_system_controls.py/tests/integration/test_web_api_smoke.py对RuntimeStatusService接线路径的断言,并联合回归为49 passed;同时已通过python -m py_compile apps/web_api/runtime/status_service.py apps/web_api/bootstrap/runtime.py apps/web_api/interfaces/http_routes.py apps/web_api/interfaces/socket_events.py ...的语法检查。 - 备注(2026-04-21):本轮又补充并收敛了
tests/unit/web_api/test_search_dispatch.py中对“dispatch_search_request()/resolve_search_query()在调用时解析默认 search runtime”的断言,并联合tests/unit/web_api/test_forum_runtime.py、tests/unit/web_api/test_forum_runtime_helpers.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/integration/test_web_api_system_controls.py回归为30 passed;同时已通过python -m py_compile apps/web_api/runtime/forum_runtime.py apps/web_api/runtime/search_dispatch.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_search_dispatch.py的语法检查。 - 备注(2026-04-21):本轮又补
tests/unit/web_api/test_app_module_bootstrap.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_search_dispatch.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_system_controls.py,锁定RuntimeServices/bootstrap_app_module()开始显式复用同一个search_dispatch_runtime;相关组合回归为49 passed,并额外通过python -m py_compile apps/web_api/bootstrap/runtime.py apps/web_api/bootstrap/app_module.py apps/web_api/runtime/search_dispatch.py ...。
- 备注(2026-04-17):仓库已补
3.3 提前收敛 services/crawler/ 边界
目标
在不破坏现有 mindspider / backend/crawler 调用的前提下,先建立 crawler 域接口与第三方适配层骨架,为后续 application 层收口铺路。
当前进展快照(2026-04-15 14:41)
- 已新增
services/crawler/domain/,落地entities.py、contracts.py、services.py与包级导出,明确CrawlerProvider、CrawlerRuntimePaths、MediaCrawlerContract、CrawlerProviderLocator等边界类型。 - 已将
services/crawler/adapters/mediacrawler_adapter.py升级为MediaCrawlerAdapter,使其实现 domain contract,同时继续兼容get_mediacrawler_paths()/ensure_mediacrawler_available()这些旧调用入口。 - 已更新
services/crawler/README.md与services/crawler/adapters/README.md,明确vendor/mediacrawler/只能经 adapters 暴露,不应被业务层直接引用。 - 当前
services/crawler/mindspider/与backend/crawler/runtime.py已通过 adapter 访问 vendor 路径,但配置文件改写、依赖安装、运行时环境拼装仍分散在mindspider/backend中,尚未收敛为 application service。 - 2026-04-20:已新增
services/crawler/application/mediacrawler_runtime.py与包级导出,补齐MediaCrawlerRuntimeService最小骨架,开始统一承接 runtime env 组装、依赖安装与db_config.py/base_config.py写入;backend/crawler/runtime.py、backend/crawler/managers.py、services/crawler/mindspider/main.py、services/crawler/mindspider/DeepSentimentCrawling/platform_crawler.py已开始改走该 application boundary。 - 2026-04-20:
MediaCrawlerRuntimeService本轮继续补了resolve_save_data_option();backend/crawler/managers.py已不再直接从 shared config 读取数据库运行时配置来推导默认save_option,而是统一通过backend/crawler/runtime.py -> services/crawler/application这条 runtime/application 边界解析。 - 2026-04-20:
MediaCrawlerRuntimeService本轮继续新增MediaCrawlerCommandSpec/build_crawl_command_spec();CrawlTaskManager.start()的长串 crawl CLI 参数拼装与 canonicalsave_option解析现已进一步收回到 runtime service,经backend/crawler/runtime.py兼容壳调用,manager 仅保留 payload 归一化、history/UI state 与子进程生命周期管理。 - 2026-04-20:
MediaCrawlerRuntimeService本轮继续新增build_login_status_command_spec()与build_login_command_spec();LoginTaskManager.check_login()/start_login()当前也已改为消费 runtime service 返回的 canonical command spec,manager 侧不再自己展开 status/login CLI 参数。 - 2026-04-20:
MediaCrawlerRuntimeService本轮继续新增build_completed_process_kwargs()与build_streaming_process_kwargs();backend/crawler/managers.py当前已统一通过这些 helper 构建 login status / login / crawl 三条链路的 subprocess kwargs,manager 侧剩余的 runtime 细节已进一步集中到 execution context 消费、进程生命周期控制与事件/UI state 解析。 - 2026-04-20:
backend/crawler/runtime.py本轮继续压成最小 compat 壳:PROJECT_ROOT、MEDIACRAWLER_PATHS、RUNTIME_PATH、RUNTIME_CWD、build_runtime_env()、build_runtime_command()、resolve_save_data_option()与build_execution_context()这些仅剩历史兼容或测试用途的门面当前已移除,只保留 managers 仍在消费的build_*command_spec()与build_*process_kwargs()helper,以及EVENT_PREFIX/MAX_LOG_LINES/MEDIACRAWLER_RUNTIME_SERVICE三个最小导出。 - 2026-04-20:
MediaCrawlerAdapter本轮继续统一了 crawler writable path contract:browser_data默认已改为走utils/runtime_paths.py中的CRAWLER_BROWSER_DATA_DIR,不再默认落到 vendor 根目录;MediaCrawlerRuntimeService也已显式暴露get_browser_data_dir(),相关默认路径/override contract 已补到tests/unit/backend/test_mediacrawler_adapter.py与tests/unit/backend/test_crawler_runtime_service.py。 - 2026-04-21:
MediaCrawlerRuntimeService本轮继续把这条 writable path contract 往真实运行时推进:build_runtime_env()现已显式导出BETTAFISH_CRAWLER_BROWSER_DATA_DIR与MEDIACRAWLER_USER_DATA_DIR_PATTERN,write_base_config()也会同步把 vendorUSER_DATA_DIR改写为 runtime-managed absolute path pattern。这样 MediaCrawler 各平台里通过config.USER_DATA_DIR % platform派生的持久化浏览器 profile 路径,当前已开始默认落到var/crawler/browser_data/...,而不是继续回到 vendor 工作目录。 - 2026-04-21:本轮继续把这条 contract 打到 vendor 实际消费层:
vendor/mediacrawler/tools/utils.py已新增统一的 browser user-data-dir helper,tools/cdp_browser.py与media_platform/xhs/core.py现都改为通过该 helper 解析 profile 目录,不再各自硬编码cwd/browser_data/...。这样普通持久化上下文与 CDP 分支现在都会优先消费 runtime/application 写回的绝对USER_DATA_DIRpattern,并仅在未提供 runtime-managed 路径时回退到 vendor 侧相对目录。
任务清单
-
建立
services/crawler/domain/骨架:entities.pycontracts.pyservices.py__init__.py- 备注(2026-04-15 14:41):当前仅承载 provider 边界与路径值对象,不引入行为性编排,避免过早牵动现有 crawler 流程。
-
收敛
services/crawler/adapters/:- 为
vendor/mediacrawler/提供稳定 adapter 入口 - 保持旧函数式调用兼容
- 备注(2026-04-15 14:41):
MediaCrawlerAdapter已实现MediaCrawlerContract,现有模块仍可继续使用get_mediacrawler_paths()/ensure_mediacrawler_available()。
- 为
- [-] 盘点并记录当前耦合点:
-
services/crawler/mindspider/DeepSentimentCrawling/platform_crawler.py仍负责直接改写 vendor 配置文件 -
services/crawler/mindspider/main.py仍负责依赖安装与环境准备 -
backend/crawler/runtime.py仍直接以 adapter 返回路径组装运行时环境 - 备注(2026-04-15 14:41):目前是“路径解析已隔离,运行时行为未隔离”的状态,适合下一步补
application/。
-
- [-] 新建
services/crawler/application/,把以下能力从mindspider//backend/crawler/迁出:- vendor 配置写入
- 依赖安装
- 运行时环境变量组装
- crawl job 启停编排
- 备注(2026-04-20):
MediaCrawlerRuntimeService已落地并接管运行时环境变量组装、依赖安装、db_config.py与base_config.py写入;crawler job 启停编排仍主要在 manager/service 层,后续可继续向 application service 收口。 - 备注(2026-04-20):本轮继续沿 application boundary 补齐了可复用 runtime helper:
MediaCrawlerRuntimeService当前已显式提供get_database_runtime_settings()、get_default_save_data_option()、build_async_database_url()与build_python_command();mindspider/main.py、DeepSentimentCrawling/platform_crawler.py与backend/crawler/runtime.py已开始消费这组 helper,而不是继续各自重复推导 DB/save option/runtime 命令细节。 - 备注(2026-04-20):本轮又把
CrawlTaskManager.start()中默认save_option的推导收回到 application/runtime 边界:MediaCrawlerRuntimeService.resolve_save_data_option()统一处理“显式值优先,否则回退默认值”,backend/crawler/managers.py已不再直接 import shared config helper。 - 备注(2026-04-20):本轮也补强了 manager/runtime contract 测试:
tests/unit/backend/test_crawler_managers.py当前已锁定LoginTaskManager/CrawlTaskManager通过build_execution_context()透传 runtimecwd/env,避免 manager 侧再回退到自己拼接运行时上下文。 - 备注(2026-04-20):本轮继续把
CrawlTaskManager.start()的运行时命令细节收回到MediaCrawlerRuntimeService:新增build_crawl_command_spec()后,backend/crawler/managers.py不再自己展开 crawl CLI 参数或默认save_option,而是消费 runtime service 返回的 canonical command spec;tests/unit/backend/test_crawler_runtime_service.py与tests/unit/backend/test_crawler_managers.py也同步改为锁定这条委托链路。 - 备注(2026-04-20):本轮继续把 login 相关 runtime 命令细节收回到
MediaCrawlerRuntimeService:新增build_login_status_command_spec()与build_login_command_spec()后,LoginTaskManager.check_login()/start_login()不再直接手写 status/login CLI 参数;相关 contract 已补到tests/unit/backend/test_crawler_runtime_service.py与tests/unit/backend/test_crawler_managers.py。 - 备注(2026-04-20):本轮也开始把 crawler 的 runtime writable directory 语义收回到 application/adaptor 边界:
browser_data默认现已统一走CRAWLER_BROWSER_DATA_DIR,并保留显式 override 入口供测试/特殊部署使用;当前仍待后续进一步确认 vendor 真实运行时是否完全消费这一路径。
- [-] 进一步压缩
mindspider/对 vendor 文件结构的认知:- 避免业务代码直接拼接
config/*.py、requirements.txt等路径 - 通过 adapter / application 返回显式能力对象
- 备注(2026-04-20):
mindspider/main.py与platform_crawler.py当前已开始复用MediaCrawlerRuntimeService,不再各自重复维护依赖安装与 vendor 配置写入逻辑;仍有部分 vendor 目录认知散落在 legacy 调用路径中,尚未完全去除。 - 备注(2026-04-20):本轮
platform_crawler.py已进一步把默认save_data_option判断收口到MediaCrawlerRuntimeService.get_default_save_data_option();backend/crawler/runtime.py也继续通过 runtime service 暴露 execution context / runtime command,不再在 backend 侧展开 DB/save option 推导逻辑。 - 备注(2026-04-20):本轮
backend/crawler/managers.py也开始通过resolve_save_data_option()使用同一条 runtime/application 边界,不再在 manager 侧自己 reload shared settings。 - 备注(2026-04-20):本轮
backend/crawler/runtime.py又进一步退化为兼容壳:crawl 启动命令当前也改为通过build_crawl_command_spec()暴露给 manager,而不是让 manager 继续手写 vendor runtime 参数序列。 - 备注(2026-04-20):本轮
backend/crawler/runtime.py继续沿同一方向退化为兼容壳:login/status 命令当前也改为通过build_login_command_spec()/build_login_status_command_spec()暴露给 manager,而不是继续在 manager 侧手写 runtime 参数序列。
- 避免业务代码直接拼接
验收标准
-
services/crawler/domain/与services/crawler/adapters/已具备可持续扩展的最小骨架。 - 现有 crawler 相关调用点未被破坏,旧入口仍可使用。
- [-]
mindspider/与backend/crawler/不再直接承担第三方 vendor 运行时行为。- 备注(2026-04-20):backend/mindspider 已开始改为经
services/crawler/application访问 MediaCrawler runtime 能力,但 crawl job orchestration 仍未完全从 legacy 模块抽离。 - 备注(2026-04-20):本轮已补跑
tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_runtime_service.py共10 passed,并额外通过py_compile校验backend/crawler/runtime.py、services/crawler/application/mediacrawler_runtime.py、services/crawler/mindspider/main.py、services/crawler/mindspider/DeepSentimentCrawling/platform_crawler.py;说明这条 application-boundary 收口在当前工作区内已保持兼容。 - 备注(2026-04-20):本轮
CrawlTaskManager.start()的 crawl command 构建已继续迁入MediaCrawlerRuntimeService,当前 manager 侧残留的运行时细节已进一步收缩到LoginTaskManager.check_login()/start_login()的 status/login 命令拼装与真正的进程生命周期管理;相关定向回归为tests/unit/backend/test_crawler_runtime_service.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py、tests/integration/test_web_api_factory_smoke.py -k crawler共22 passed。 - 备注(2026-04-20):本轮又补跑
tests/unit/backend/test_crawler_managers.py与tests/unit/backend/test_crawler_runtime_service.py,锁定 manager 默认save_option解析也已通过 runtime/application 边界完成;相关两组定向回归为1 passed+9 passed,并额外通过py_compile校验backend/crawler/managers.py、backend/crawler/runtime.py、services/crawler/application/mediacrawler_runtime.py。 - 备注(2026-04-20):本轮继续把
LoginTaskManager.check_login()/start_login()的 status/login command 构建迁入MediaCrawlerRuntimeService后,manager 侧残留的 runtime 细节已进一步收缩到 execution context 消费、子进程生命周期与事件/UI state 解析;相关定向回归为tests/unit/backend/test_crawler_runtime_service.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py、tests/integration/test_web_api_factory_smoke.py -k crawler共25 passed。 - 备注(2026-04-20):本轮继续把 manager 侧的 subprocess kwargs 构建也收回到
MediaCrawlerRuntimeService:新增build_completed_process_kwargs()与build_streaming_process_kwargs()后,LoginTaskManager.check_login()、LoginTaskManager.start_login()与CrawlTaskManager.start()当前都改为经backend/crawler/runtime.py兼容壳消费 runtime/application helper 生成 canonicalsubprocess.run()/subprocess.Popen()kwargs。至此 manager 侧剩余的 runtime 细节已主要集中在 execution context 消费、进程生命周期控制与事件/UI state 解析;相关定向回归为tests/unit/backend/test_crawler_runtime_service.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py共26 passed,另补跑tests/integration/test_web_api_factory_smoke.py -k crawler为1 passed。 - 备注(2026-04-20):本轮继续把
backend/crawler/runtime.py收薄为仅服务 managers 的最小 compat 壳后,tests/unit/backend/test_crawler_runtime.py已改为锁定“旧 env/path/save_option/execution_context 门面已移除、剩余 helper 继续委托MEDIACRAWLER_RUNTIME_SERVICE”这条 contract;相关定向回归为tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py共17 passed,并额外通过python -m py_compile backend/crawler/runtime.py tests/unit/backend/test_crawler_runtime.py。 - 备注(2026-04-20):本轮围绕
browser_datawritable path contract 又补跑了tests/unit/backend/test_mediacrawler_adapter.py、tests/unit/backend/test_crawler_runtime_service.py、tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py,当前组合回归为35 passed;并额外通过python -m py_compile services/crawler/adapters/mediacrawler_adapter.py services/crawler/application/mediacrawler_runtime.py tests/unit/backend/test_mediacrawler_adapter.py tests/unit/backend/test_crawler_runtime_service.py。 - 备注(2026-04-21):本轮继续把
browser_datacontract 落到 runtime env / vendor config 写入层后,又补跑了tests/unit/backend/test_crawler_runtime_service.py、tests/unit/backend/test_mediacrawler_adapter.py、tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py,当前组合回归仍为35 passed;并额外通过python -m py_compile services/crawler/application/mediacrawler_runtime.py tests/unit/backend/test_crawler_runtime_service.py。 - 备注(2026-04-21):本轮继续把 vendor 真实运行时的
browser_data消费收口到同一 helper 后,又新增tests/unit/backend/test_mediacrawler_vendor_paths.py,并联合tests/unit/backend/test_crawler_runtime_service.py、tests/unit/backend/test_mediacrawler_adapter.py、tests/unit/backend/test_crawler_runtime.py、tests/unit/backend/test_crawler_managers.py、tests/unit/backend/test_crawler_routes.py回归为38 passed;同时已通过python -m py_compile vendor/mediacrawler/tools/utils.py vendor/mediacrawler/tools/cdp_browser.py vendor/mediacrawler/media_platform/xhs/core.py tests/unit/backend/test_mediacrawler_vendor_paths.py。
- 备注(2026-04-20):backend/mindspider 已开始改为经
风险与注意事项
- 当前仅做边界骨架与适配器重构,尚未处理 vendor 配置文件写入副作用;后续切 application 层时需要额外测试。
-
browser_data的默认路径已统一到 runtime-managedvar/crawler/browser_data,USER_DATA_DIR也已经 runtime/application 边界写回 vendor config;tools/cdp_browser.py与media_platform/xhs/core.py这两条剩余 vendor 分支现已改为消费同一 user-data-dir helper,不再直接按cwd/browser_data/...推导 profile,CDP 分支与普通持久化上下文分支的目录来源已重新对齐。 -
vendor 侧仍存在少量依赖当前工作目录的相对资源路径约定(例如部分
libs/、docs/资源访问);这不再阻塞当前browser_data主线,但后续若继续收口 runtime cwd 语义,仍需要单独盘点。 -
runtime_paths.py目前仍作为兼容转发层存在,后续若应用层稳定,可考虑删除或降级为 deprecated shim。 -
MediaCrawlerPaths目前继承CrawlerRuntimePaths以兼容旧类型名,后续若外部调用完全切到 domain 层,可进一步收口命名。
4. P1:业务收口期
4.1 建立 services/application/ 应用服务层
目标
在 API 与 engine 之间补齐用例编排层。
任务清单
-
新建
services/application/research/task_service.py- 备注(2026-04-15 14:41):已落地
ResearchTaskService骨架与ResearchTaskCreateInput,当前只封装创建、列表查询、单任务查询、状态迁移四类接口;底层仍复用backend.research_tasks.ResearchTaskStore与 mapper,将统一ResearchTaskStatus渐进映射回旧 JSON store,暂不接入 crawler / analysis / report 编排逻辑。 - 备注(2026-04-15 15:13):已补
ResearchTaskDTO/ResearchTaskListDTO输出边界,研究任务创建、列表快照、单任务查询、状态迁移现在都可直接返回统一 DTO;backend/research_routes.py已开始退化为“参数解析 + 调 service + 返回 DTO”的适配层。
- 备注(2026-04-15 14:41):已落地
- 备注(2026-04-16 13:33):已补
tests/unit/application/test_research_task_service.py,使用 fake store 锁定set_task_status()、activate_task()、非法transition_task()的 application-layer contract;同时修复transition_task()在当前状态为字符串时可能抛出AttributeError的问题,统一先走_coerce_status(...)。 - 备注(2026-04-16 15:44):为承接
AnalysisRun最小闭环,ResearchTaskService.set_task_status()与底层ResearchTaskStore.update_status()已补可选analysis_run_id写回,map_legacy_research_task()也已开始把该字段映射到统一模型;当前analysis_run_id已能在 legacy JSON store、统一ResearchTask、DTO 响应之间 round-trip。 - 备注(2026-04-17):本轮已补齐
crawler_job_id的同路径回写:ResearchTaskService.set_task_status()、backend/research_tasks.py与map_legacy_research_task()现已支持在不覆盖analysis_run_id的前提下单独写回crawler_job_id,并由tests/unit/application/test_research_task_service.py锁定双链接字段可并存的 round-trip contract。 - 备注(2026-04-17):已继续补齐
report_job_id的同路径回写:ResearchTaskService.set_task_status()、backend/research_tasks.py与map_legacy_research_task()现已支持在不覆盖crawler_job_id/analysis_run_id的前提下单独写回report_job_id;services/engines/report/flask_interface.py在创建 report task 时也会在携带research_task_id的情况下把该关联回写到研究任务,并在 report 状态变更时同步研究任务阶段。 - 备注(2026-04-17):本轮继续沿“更新任务阶段状态”主线补了 application 层统一入口:
ResearchTaskService新增sync_report_runtime_status(...),把 report runtime 的pending/running/completed/error/cancelled状态映射统一收回 research application service,再复用set_task_status(...)落库;report_job_id与既有crawler_job_id/analysis_run_id共存语义保持不变。相关断言已补到tests/unit/application/test_research_task_service.py,当前该文件回归为9 passed。 备注(2026-04-17):继续沿“更新任务阶段状态”主线推进 analysis 收口:
ResearchTaskService已新增sync_analysis_runtime_status(...),统一承接pending/queued/running/partial/completed/failed/cancelled到 research task unified status 的映射,并保持现有“analysis partial/completed 仍停留在analyzing、failed 回落ready以便重试”的兼容语义;AnalysisService也已优先改为通过该入口推进任务阶段,不再在 orchestrator 内部散落维护analyzing/ready映射。-
新建
services/application/crawler/crawler_service.py- 备注(2026-04-16):虽然完整
crawler_service.py尚未开始承接启动/停止编排,但已新增只读CrawlerJobQueryService,当前通过backend.crawler.state_store的 history 快照和map_crawler_history_entry()输出统一CrawlerJobDTO;backend/crawler/routes.py已开始提供/api/crawler/jobs、/api/crawler/jobs/{id}以及在/api/crawler/state中附带crawler_jobs的 DTO 化查询出口,为后续真正的 crawler application service 铺路。 - 备注(2026-04-17):读侧 contract 已继续补强,
tests/unit/application/test_crawler_query_service.py现已覆盖 idle/空历史时current_job is None、history 命中与 live job 命中三条主要分支;tests/unit/backend/test_crawler_routes.py已补/api/crawler/jobs/{id}成功路径,tests/integration/test_web_api_factory_smoke.py也已锁定 factory app 中 crawler jobs 路由注册与最小 GET smoke。下一步仍建议优先保持/api/crawler/jobs+current_job这一现有 contract,而不是立刻再加/api/crawler/jobs/current。 - 备注(2026-04-17):本轮已落地
CrawlerService,统一承接 crawler read/write application boundary:读侧通过CrawlerStateDTO+CrawlerJobQueryService输出/api/crawler/state//api/crawler/jobs//api/crawler/jobs/{id},写侧则封装check_login()、start_login()、cancel_login()、start_crawler()、stop_crawler()并以CrawlerValidationError/CrawlerOperationConflictError向 route 暴露稳定错误边界。backend/crawler/routes.py现已改为主要依赖CRAWLER_APP_SERVICE,相关单测/冒烟测试已覆盖 service 委托、校验失败、冲突失败与最小 app-level start/stop contract。 - 备注(2026-04-17):已继续把 crawler 读写与 task 资源接起来:
CrawlerService.start_crawler()现在会在 payload 中存在research_task_id时把 crawl snapshot 的history_id回写为任务的crawler_job_id;同时新增build_task_crawler_resource_payload()/get_task_crawler_resource_dto(),由backend/research_routes.py暴露GET /api/research/tasks/<task_id>/crawler。当前 task-scoped crawler 资源刻意只返回该任务 linked job,不回退展示与任务无关的全局 crawler history。 - 备注(2026-04-17):
CrawlerService.start_crawler()现已在 application 层补齐“研究任务默认采集参数同步”这条主线:当 payload 携带research_task_id时,会先读取统一ResearchTaskDTO,将crawler_defaults/crawler_keywords_text中的默认keywords、crawler_type、login_type、分页与数量限制补齐到 crawler payload,但不会覆盖显式传入的字段;同时会把研究任务的generated_query一并带入状态回写。相关回归已补tests/unit/application/test_crawler_service.py对“默认参数同步 / 显式参数优先 / 缺失任务报错”的断言,并联合tests/unit/backend/test_crawler_routes.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py重新验证 crawler route / app smoke,当前组合回归为57 passed。 - 备注(2026-04-21):本轮已把
CrawlerService对linked_task.legacy_payload的直接读取收回到 DTO/mapper 边界:crawler 默认参数和crawler_keywords_text当前统一经ResearchTaskDTOaccessor +metadata.extras解析,application 层不再直接依赖legacy_payload["crawler_defaults"]/legacy_payload["crawler_keywords_text"]字段名;相关定向回归为tests/unit/application/test_crawler_service.py、tests/unit/shared/test_task_model_mappers.py共20 passed。
- 备注(2026-04-16):虽然完整
-
新建
services/application/analysis/analysis_service.py- 备注(2026-04-16 16:00):
AnalysisService已进一步收口AnalysisRun聚合逻辑,当前在接单时会创建AnalysisRun、把analysis_run_id回写到研究任务,并在异步执行过程中持续更新partial_results、metrics、运行中/部分成功/完成/失败状态;默认 store 已切到 JSON-backedAnalysisRunStore,同时仍保留InMemoryAnalysisRunStore供单测/隔离场景使用。已补tests/unit/application/test_analysis_service.py与tests/unit/application/test_analysis_run_store.py,锁定 query 回填、无引擎回退、接单后启动后台线程、analysis_run_id关联回写、异步执行成功/异常分支下的聚合结果,以及 JSON store 的 round-trip。 - 备注(2026-04-16):已新增只读
AnalysisRunQueryService,把/api/research/tasks/<task_id>/analysis-run的查询责任从 dummyAnalysisService中拆出;同时AnalysisRunStorePort已补list_runs_for_task(...),backend/research_routes.py现在同时暴露单条最新 run 查询、GET /api/research/tasks/<task_id>/analysis-runs历史列表查询,以及更资源化的GET /api/research/tasks/<task_id>/analysis视图,相关单测/冒烟测试已覆盖 linked run 命中、无analysis_run_id时回落到最新持久化 run、task-scoped history 返回,以及嵌套 analysis 资源返回。 - 备注(2026-04-16):analysis query service 已进一步补
AnalysisRunDTO/payload helper,route 不再直接model_dump(...)拼 analysis 返回体;当前 analysis 查询接口已统一返回run_id、unified_run等 DTO 视角字段,便于后续继续资源化。 - 备注(2026-04-17):analysis 读侧资源视图已继续收敛为共享
TaskAnalysisResourceDTO,AnalysisRunQueryService现在只负责解析current_run/runs并委托 DTO 统一输出summary、history、stats;相关测试已补tests/unit/shared/test_task_analysis_resource_dto.py,并在tests/unit/application/test_analysis_query_service.py、tests/integration/test_web_api_smoke.py中锁定 linked-run 缺失时的 fallback、聚合统计和资源视图关键字段,便于下一步继续围绕 task-scoped analysis API 做字段收口或筛选能力扩展。 - 备注(2026-04-17):本轮已继续把
/api/search的用例入口往 application 层收口:AnalysisService新增submit_search_request(...)façade,在 service 内部串起resolve_search_query()、空 query 判定、dispatch_search_request()与 HTTP status 映射;http_routes.py当前只保留 JSON payload 读取和submit_search_request()返回体透传,不再显式编排两段式 search choreography。bootstrap/runtime.py也新增了 route-readysubmit_search_requestwiring,同时继续通过传入的 search hook callables 保持_APP_MODULE.search_hooks这条 monkeypatch 兼容面。相关回归已补tests/unit/application/test_analysis_service.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/unit/web_api/test_runtime_bootstrap.py,并联合tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_system_controls.py重新验证 search/app bootstrap 主链路,当前组合回归为51 passed。
- 备注(2026-04-16 16:00):
备注(2026-04-17):已继续完成上一轮提出的 runtime 协作收口:
AnalysisService现新增AnalysisExecutionContext,把process_registry、check_app_status、log_dir、write_log四类运行态依赖收束为单一 execution port;submit_search_request(...)与dispatch_search_request(...)当前优先消费该 bundle,apps/web_api/bootstrap/runtime.py与apps/web_api/runtime/search_dispatch.py也已改为预先装配该 context,再通过 route-ready dispatcher 保持_APP_MODULE.search_hooks这条 monkeypatch 链路可替换。旧的显式 kwargs 入口仍暂时保留为兼容层。相关回归已扩展tests/unit/application/test_analysis_service.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_search_dispatch.py,并联合tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_system_controls.py重新验证,当前组合回归为52 passed。备注(2026-04-24):本轮继续把
/api/search的 transport 语义从 application 层往外收:AnalysisService.submit_search_request(...)当前已改为只返回 application submission 结果对象,不再直接产出(payload, status_code)HTTP tuple;对应200/400状态码映射现在统一回退到apps/web_api/runtime/search_dispatch.py的 route/runtime adapter 边界,外部/api/searchcontract 保持不变。相关回归已补到tests/unit/application/test_analysis_service.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/unit/web_api/test_runtime_bootstrap.py与tests/integration/test_web_api_smoke.py。备注(2026-04-28):本轮继续沿
4.2主线把 analysis application 内部编排从裸结果结构收口到统一结果对象:AnalysisService.execute_search_dispatch_async(...)当前已改为在 application 层先把 engine runner 输出归一成EngineResult,随后统一基于EngineResult.success/summary/error生成状态文案、记录 partial result、统计 success/failure 与收尾消息,不再在 service 内部继续混用裸dict.get(\"success\"/\"message\")。同时_record_analysis_run_result(...)也已改为直接消费EngineResult,并补了“engine runner 直接返回EngineResult实例”定向单测,锁定这条 typed contract。备注(2026-04-28):同一条 typed-contract 主线本轮也继续往 runtime 边界推了一步:
apps/web_api/runtime/search_dispatch.py中run_local_engine_research(...)当前已直接返回EngineResult/EngineExecutionError,不再只回 raw dict;apps/web_api/runtime/task_runtime_store.py也已开始在写入时统一把partial_results归一成 canonical engine payload,避免 task runtime snapshot 再扩散混杂的 legacystring/dict结构。相关回归已补到tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_task_runtime_store.py,并联合 analysis 读写单测重新验证。备注(2026-04-28):本轮继续把这条 contract 收口到底:
services/application/analysis/analysis_service.py中的EngineRunner现已正式收紧为只返回EngineResult,过渡性的_run_engine(...)兼容层已经移除,execute_search_dispatch_async(...)直接消费统一结果对象;与之对应,tests/unit/application/test_analysis_service.py里的 engine-runner 场景也已全部切到EngineResult/EngineExecutionError语义,当前定向回归为12 passed。备注(2026-04-28):本轮继续沿 task-centered application façade 收口 analysis 读侧:
ResearchTaskViewService当前已开始直接消费AnalysisRunQueryService.get_task_analysis_run_dto(...)/list_task_analysis_run_dtos(...)两条 typed 入口,并在 application 层组装/analysis-run与/analysis-runs的 task-scoped payload;原先只作中转的 query-service payload helpers 已移除。相关回归已补跑tests/unit/application/test_research_task_view_service.py、tests/unit/application/test_analysis_query_service.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/integration/test_web_api_smoke.py,组合为36 passed;叠加本轮 analysis/search/factory 主线回归后,最新组合结果为79 passed, 21 deselected。备注(2026-04-22):本轮继续收口 research route 的跨服务聚合:新增
services/application/research/task_view_service.py,将GET /api/research-tasks/task-views原先留在backend/research_routes.py的 task/crawler/report 三段聚合视图拼装迁入 application facade;当前 route 已退化为“取 snapshot -> 调 task view service -> 返回 payload”的薄适配器。相关覆盖已补到tests/unit/application/test_research_task_view_service.py,并联合tests/integration/test_web_api_smoke.py重新验证 task-views 资源输出。备注(2026-04-24):
ResearchTaskViewService本轮继续沿 task-centered 主线补齐了 analysis 聚合;GET /api/research-tasks/task-views当前已从“task + crawler + report”扩展为统一返回task + analysis + crawler + report四段 task-scoped 摘要,便于后续前端页面不再额外拼接 analysis 资源。相关覆盖已同步扩展到tests/unit/application/test_research_task_view_service.py与tests/integration/test_web_api_smoke.py。备注(2026-04-24):本轮又继续把 task-scoped resource orchestration 从
backend/research_routes.py收回ResearchTaskViewService:/api/research/tasks/<task_id>/analysis-run、/analysis、/analysis-runs当前都改为经 application facade 统一解析任务与 linkedanalysis_run_id,route 侧只保留404 + jsonify映射;同时为避免测试/运行时动态替换RESEARCH_TASK_APP_SERVICE时 capture 旧实例,research route wiring 也已切到通过惰性 task-service proxy 解析当前 task service。相关定向回归pytest -q tests/unit/application/test_research_task_view_service.py tests/integration/test_web_api_smoke.py -k "analysis or task_view or resource_alias"为12 passed, 34 deselected。备注(2026-04-24):同一条主线本轮也已补齐 crawler/report 两条 task-scoped resource façade:
ResearchTaskViewService新增build_task_crawler_resource_payload(...)与build_task_report_resource_payload(...),backend/research_routes.py中/api/research/tasks/<task_id>/crawler|report现都已退化为“调 application facade -> 映射404/503->jsonify”的薄路由,旧_build_task_resource_response(...)中转壳也已移除。相关回归pytest -q tests/unit/application/test_research_task_view_service.py tests/integration/test_web_api_smoke.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py -k "task_view or analysis or crawler_resource or report_resource or resource_alias or report_service"为18 passed, 34 deselected。备注(2026-04-28):本轮又继续把 analysis 读侧的 task-view 聚合留在 application 内部 typed 化:
ResearchTaskViewService当前已支持消费带to_response_payload()的 analysis resource DTO,而不是要求 analysis builder 直接返回裸 payload dict;backend/research_routes.py中RESEARCH_TASK_VIEW_APP_SERVICE现已改为向AnalysisRunQueryService注入get_task_analysis_resource_dto(...),从而让task-views聚合在 application 层内部优先围绕TaskAnalysisResourceDTO运行,最终只在最外层 materialize 成 response payload。相关回归已补到tests/unit/application/test_research_task_view_service.py与tests/integration/test_web_api_smoke.py。备注(2026-04-28):本轮继续沿 P1.1/P1.4 瘦 route 主线收掉 research route 里的 task-view 模块级装配:
backend/research_routes.py当前只保留configure_research_task_view_app_service(...) + getter/proxy,真正的ResearchTaskViewService(...)实例化已迁到apps/web_api/bootstrap/runtime.py的 runtime wiring 中统一注入;同时继续刻意复用RESEARCH_TASK_APP_SERVICE与 analysis/crawler/report 几条 lazy seam,避免 task-view facade capture 旧 store/service。相关回归已补到tests/unit/web_api/test_runtime_bootstrap.py,并联合tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py重新验证 task-views / analysis resource / factory bootstrap 路径。新建
services/application/report/report_service.py备注(2026-04-17):虽然完整写侧
report_service.py仍未落地,但本轮已先补只读ReportJobQueryService与 task-scopedGET /api/research/tasks/<task_id>/report,并新增共享TaskReportResourceDTO统一 report 资源输出;当前 route 层会先解析 research task,再把 linkedreport_job_id显式交给 report query service,保持“仅 linked job 可见、不回退全局最近任务”的保守语义。备注(2026-04-17):已继续落地写侧
ReportService,统一封装/api/report/generate的任务冲突检查、引擎就绪校验、report task 创建、研究任务report_job_id/status回写以及后台线程启动;services/engines/report/flask_interface.py的generate_report()现已退化为“解析请求 -> 调REPORT_APP_SERVICE.generate_report(...)-> 映射 service error”的薄路由。当前仍保留其余 report status/download/SSE 接口在 runtime 模块内,后续可继续向 application 层收口。备注(2026-04-22):本轮继续沿 report 写侧主线收口:
ReportService已新增cancel_report(...),统一承接/api/report/cancel/<task_id>的任务解析、可取消状态判定、取消事件发布与 current-task 清理;services/engines/report/flask_interface.py中对应 route 已退化为“参数透传 -> 调REPORT_APP_SERVICE.cancel_report(...)-> 统一映射 service error”的薄适配器。相关覆盖已补到tests/unit/application/test_report_service.py与tests/integration/test_web_api_smoke.py。备注(2026-04-17):report 读侧本轮继续向 application/query 层收口:
ReportJobQueryService现已新增build_report_status_payload()、build_report_tasks_payload()与build_report_progress_payload()三个 façade,统一承接/api/report/status、/api/report/tasks、/api/report/progress/<task_id>的 payload 编排,并保留“任务缺失时返回 completed fallback”兼容语义。services/engines/report/flask_interface.py当前已退化为jsonify(REPORT_QUERY_SERVICE....)的薄适配器。相关回归已补tests/unit/application/test_report_query_service.py与tests/integration/test_web_api_smoke.py。备注(2026-04-17):report 读侧继续沿相同模式收口,
ReportJobQueryService本轮已新增build_report_result_payload()、build_report_html_payload()、build_report_download_descriptor()与build_report_export_source(),统一承接/api/report/result/<task_id>、/api/report/result/<task_id>/json、/api/report/download/<task_id>以及export/md|pdf/<task_id>的任务解析、完成态校验、HTML 延迟加载、文件定位与 IR 读取。services/engines/report/flask_interface.py中对应 route 已收缩为“HTTP 参数解析 -> 调 query façade -> 做 Flask response / 异常映射”的薄适配器,SSE 未改动。相关回归已扩展到tests/unit/application/test_report_query_service.py与tests/integration/test_web_api_smoke.py。备注(2026-04-21):本轮继续把 report read-side 的 raw runtime dict fallback 收口到 shared DTO/mapper:
ReportJobQueryService当前对 snapshot/progress/result 几条只读链路都会优先解析为ReportJobDTO,并统一产出带unified_task的兼容 payload;_FakeRuntimeReportTask.to_dict()这类 runtime object 也已纳入同一 DTO coercion 链。相关定向回归为tests/unit/application/test_report_query_service.py共21 passed。备注(2026-04-22):本轮继续把 report query application 边界从 runtime 私有实现里抽离:
services/application/report/query_service.py已移除默认反向 importservices.engines.report.flask_interface的 fallback,改为仅消费显式注入的 runtime/query callables;services/engines/report/flask_interface.py当前会在构造REPORT_QUERY_SERVICE时注入report_job_getter/check_engines_ready/snapshot/result_loader,backend/research_routes.py也改为复用同一个 runtime-wired query service,而不是自行 new 一个默认实例。这样 report read-side 的 application 层不再默认反向依赖 engine/runtime 模块。备注(2026-04-23):本轮继续沿同一主线把 report 辅助读接口收口到 application 层:新增
services/application/report/export_service.py,/api/report/export/md/<task_id>、/api/report/export/pdf/<task_id>与/api/report/export/pdf-from-ir已统一委托ReportExportService;同时ReportJobQueryService已新增build_report_log_payload()与build_report_templates_payload(),统一承接/api/report/log、/api/report/templates的日志/模板目录读取,ReportService也已新增clear_report_log()承接/api/report/log/clear。当前services/engines/report/flask_interface.py中这些 route 均已退化为“HTTP 参数解析 -> 调 application/query façade -> 异常映射”的薄适配器。备注(2026-04-28):
backend/research_routes.py的 report 读侧 wiring 本轮继续收口到 bootstrap 主线:apps/web_api/bootstrap/runtime.py当前会在build_runtime_services()中解析 runtime-wiredREPORT_QUERY_SERVICE,并通过configure_research_route_services(..., report_query_service=...)统一注入给 research routes;services/engines/report/flask_interface.py中原先“模块导入时反向注入 research routes”的 side effect 已移除。相关回归已补到tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/engines/report/test_flask_interface_runtime_wiring.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py,report/runtime 定向组合为26 passed,叠加 application/task-view/report 宽回归后本轮最新组合结果为92 passed, 13 deselected。备注(2026-04-23):同一条 wiring 主线本轮也继续收紧了失败语义:
backend/research_routes.py中 task-scoped report 资源与 task view 聚合当前不再在 report service 未注入时静默 new 一个裸ReportJobQueryService()退化为空能力,而是显式返回503配置错误,便于尽早暴露 bootstrap/runtime 接线回归。备注(2026-04-23):report runtime 本轮也开始沿 P0/P1 主线拆基础设施:已新增
services/engines/report/stream_runtime.py与services/engines/report/task_runtime.py,分别承接 SSE subscriber/event-history helper 以及 current-task/task-registry snapshot helper;services/engines/report/flask_interface.py当前已切到复用StreamSubscriberRegistry、TaskEventStream、ReportTaskRuntimeStore,并把相关 smoke 改为经REPORT_TASK_RUNTIME访问运行时状态,而不再直接依赖散落的模块级全局变量。备注(2026-04-24):本轮继续把 report runtime 启动期的过渡 shim 收掉:
services/engines/report/flask_interface.py当前已不再先把恢复结果写入模块级current_task/tasks_registry再回灌给REPORT_TASK_RUNTIME,而是改为由_restore_report_tasks_from_disk()直接返回恢复任务列表并初始化REPORT_TASK_RUNTIME;旧全局别名已从主链路移除,相关 wiring smoke 仍保持通过。备注(2026-04-23):本轮继续把 report stream 主线往 application 层收口:新增
services/application/report/stream_service.py,将/api/report/stream/<task_id>的任务解析、Last-Event-ID解析、历史事件回放、heartbeat 与 subscriber 注册/释放编排迁入ReportStreamService;services/engines/report/flask_interface.py中对应 route 当前已退化为“HTTP/SSE 外壳 + response headers + disconnect 检测”的薄适配器。同时,report runtime 当前生效的 stream/task helper 也已统一改为经REPORT_STREAM_SUBSCRIBERS、REPORT_TASK_RUNTIME与serialize_sse_event(...)收口,减少 route/runtime 主文件里的平行实现。备注(2026-04-23):本轮继续收口 report runtime 主文件的遗留实现:
services/engines/report/flask_interface.py中已删除一整段被stream_runtime.py/task_runtime.py覆盖的旧 stream/task helper,并清理/api/report/stream/<task_id>route 中yield from REPORT_STREAM_SERVICE...之后不再可达的旧 SSE 循环 body;同时run_report_generation(...)已进一步拆成“stream handler 构造 / 输入准备 / 带重试的 agent 调用 / 结果持久化”四个 helper,后续再继续把剩余编排迁出 runtime 时切口会更清晰。备注(2026-04-23):本轮又继续把 report runtime wiring 压薄了一层:
REPORT_APP_SERVICE/REPORT_QUERY_SERVICE/REPORT_STREAM_SERVICE当前已直接复用REPORT_TASK_RUNTIME、REPORT_STREAM_SUBSCRIBERS与serialize_sse_event(...)的显式注入,不再在flask_interface.py内维护平行 task/stream store wrapper;同时保留check_engines_ready()的动态解析 seam,以兼容现有 smoke/unit tests 的 monkeypatch 入口。备注(2026-04-24):report runtime 本轮又补了一处并发边界收口:
run_report_generation(...)在取消分支和通用异常分支里,当前都改为通过REPORT_TASK_RUNTIME.clear_current_task(task_id=...)按任务 ID 条件清理 current task,而不再无条件清空。这样旧任务晚到的失败/取消不会误清掉已经切换成 current 的新任务。-
将以下核心用例迁入 application 层:
- 创建/更新研究任务
- 从研究任务同步爬虫参数
- 启动采集
- 启动分析
- 汇总分析结果
- 触发报告生成
- [-] 更新任务阶段状态
-
backend 层改为仅做:
- 请求参数解析
- 校验
- 调用 service
- 输出响应 DTO
备注(2026-04-23):report 侧又有一批 route 完成了这条收口:
/api/report/log、/api/report/log/clear、/api/report/templates、/api/report/export/md/<task_id>、/api/report/export/pdf/<task_id>当前都已通过 application/query service delegation 输出响应,route 本身不再直接读取日志文件、扫描模板目录或构造导出描述符。-
[-] 将跨引擎编排逻辑从 API 或 runtime 中迁移到 application 层。
- 备注(2026-04-16 16:00):
/api/search当前已通过AnalysisService执行 query 解析、接单分发与AnalysisRun聚合编排,runtimesearch_dispatch.py主要保留本地引擎构造/执行 adapter 与兼容 wrapper;当前已形成analysis_run_id -> partial_results/metrics/status -> task-scoped query endpoint的最小闭环,并已在backend/research_routes.py暴露GET /api/research/tasks/<task_id>/analysis-run。 - 备注(2026-04-16):analysis 查询职责已进一步沉淀为独立
AnalysisRunQueryService,并补齐 task-scoped history 边界GET /api/research/tasks/<task_id>/analysis-runs;下一步更适合继续把“最新 run 选择 / 历史列表 / 未来筛选”抽成更稳定的资源化 analysis API,而不是继续把查询逻辑留在 route 适配层里。 - 备注(2026-04-17):
/api/searchroute 现已不再显式执行resolve -> dispatch两段编排,而是统一委托给AnalysisService.submit_search_request(...);runtimebootstrap/runtime.py侧只保留 route-ready dependency binding。下一步更适合继续把process_registry/check_app_status/log_dir/write_log这类 runtime 协作依赖收敛为更明确的 analysis execution port,进一步减少 service 入口暴露的运行时细节。 - 备注(2026-04-17):上一条建议已完成,analysis dispatch 入口现在统一通过
AnalysisExecutionContext吸收 runtime 协作依赖;route/bootstrap 与runtime/search_dispatch.py已不再把process_registry/check_app_status/log_dir/write_log四元组逐个摊开传入AnalysisService。下一步更适合继续沿主线把“任务阶段状态更新”或 report 读写剩余用例继续收口到 application service,而不是回头扩张 route/runtime 侧逻辑。
- 备注(2026-04-16 16:00):
备注(2026-04-17):report 主链本轮也继续往 application 层收口了两步:一是研究任务的 report 阶段同步已不再由 runtime helper 自行维护映射,而是统一委托
ResearchTaskService.sync_report_runtime_status(...);二是/api/report/status、/api/report/tasks、/api/report/progress/<task_id>的读侧编排已迁到ReportJobQueryService,runtime route 仅保留 HTTP 适配与异常映射。后续可继续按相同模式收口 report 的result/download/export读侧。备注(2026-04-17):上述 report 读侧收口已进一步延伸到
/api/report/result/<task_id>、/api/report/result/<task_id>/json、/api/report/download/<task_id>与export/md|pdf/<task_id>;当前 route 不再直接自己查 runtime task / 判定完成态 / 定位 HTML/IR 文件,而是统一通过ReportJobQueryServicefaçade 获取 payload / descriptor,并集中映射not found / not ready / artifact missing三类读侧异常。备注(2026-04-22):除 route 变薄外,本轮还补了 report 生成线程的最小协作取消:
run_report_generation(...)当前会在启动、输入检查、载入数据、调用 agent、持久化与 completed 前检查任务是否已是cancelled,stream_handler也不再把已取消任务重新推进为running。这样cancel_report(...)不再只是表面改状态,而能阻止后续running/completed覆盖取消结果。备注(2026-04-23):report runtime/API 的跨层编排又继续减少了一批:模板列表、日志读取、日志清空、task-scoped Markdown/PDF 导出以及
/api/report/stream/<task_id>的流式编排现在都已迁入ReportJobQueryService/ReportService/ReportExportService/ReportStreamService;当前 report route 中仍然相对“重”的主线残留主要收缩为 runtime 侧的任务仓库/生成编排,以及flask_interface.py内尚未彻底移除的部分 legacy helper 痕迹。
验收标准
- 主链路关键接口全部通过 application service 执行。
- API 层代码显著变薄。
- 引擎层不直接处理 HTTP 语义。
4.2 统一引擎输入输出契约
目标
减少 Query / Media / Insight / Forum / Report 之间的结果结构差异。
任务清单
-
定义统一
EngineContext。 -
定义统一
EngineResult。 -
定义统一
EngineExecutionError。- 备注(2026-04-22):已在
services/shared/models/engine_contract.py落地最小共享契约:EngineContext、EngineResult、EngineExecutionError当前已具备engine_name/status/summary/artifacts/metrics/logs_ref/raw_data_ref/error基本字段,并补了EngineResult.from_raw(...)/to_runtime_payload()归一化 helper。AnalysisService._record_analysis_run_result(...)现已开始用该统一结果模型对 Query/Media/Insight 的原始执行结果做标准化后再写入partial_results,同时保持现有 task runtime / DTO 输出兼容。相关回归已补tests/unit/shared/test_engine_contract_models.py与tests/unit/application/test_analysis_service.py,本轮定向验证为13 passed。 - 备注(2026-04-22):本轮继续沿最小路径推进 P1.2:
apps/web_api/runtime/search_dispatch.py的 Query/Media/Insight 本地 adapter 现已开始直接输出 canonical raw shape(status/summary/artifacts/metrics/error),不再只返回{success,message,report_preview};EngineResult.from_raw(...)也同步补齐了 string error、report_preview/results/themes/sourcesartifact 映射以及常见统计字段 metrics 推断。相关回归已扩展到tests/unit/web_api/test_search_dispatch.py与tests/unit/shared/test_engine_contract_models.py,并联合tests/unit/application/test_analysis_service.py重新验证为25 passed。
- 备注(2026-04-22):已在
- 备注(2026-04-28):本轮继续把 typed input contract 贯通到 runtime adapter:
AnalysisService.execute_search_dispatch_async(...)当前会为每个 engine 显式构造EngineContext(engine_name, research_task_id, query, trace_id, metadata),apps/web_api/runtime/search_dispatch.py的本地 runner 也已统一改为接收EngineContext;同时services.application.analysis包级出口现已补导出EngineContext,方便 application/runtime/test 共享同一 typed contract。相关定向回归为tests/unit/application/test_analysis_service.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/shared/test_engine_contract_models.py共29 passed。 -
EngineResult至少包含:engine_namestatussummaryartifactsmetricslogs_refraw_data_referror
- 为 Query/Media/Insight 先增加 adapter/mapper,输出统一结果。
- 备注(2026-04-22):本轮已先沿最小 adapter 路线把
apps/web_api/runtime/search_dispatch.py的 Query/Media/Insight 本地执行结果收口到 canonical raw shape;同时services/shared/dto/analysis_run.py也新增了partial_results读模型归一化,task-scoped analysis run/resource 接口当前会把 legacymessage/themes/sources等旧结构投影成统一的engine_name/status/success/summary/artifacts/metrics/error。相关回归已补到tests/unit/shared/test_analysis_run_dto.py、tests/unit/shared/test_task_analysis_resource_dto.py、tests/unit/application/test_analysis_query_service.py与tests/integration/test_web_api_smoke.py,本轮组合验证为46 passed。 - 为 Forum/Report 增补统一结果表示。
- 备注(2026-04-22):
apps/web_api/runtime/forum_runtime.py当前已为get_output()、get_log_payload()与get_log_history()统一附带engine_result字段,基于 parsed forum messages / host summary / runtime status 投影成 canonicalEngineResultpayload;services/shared/dto/report_job.py、services/shared/dto/task_report.py与services/application/report/query_service.py也已为 report 读侧统一补齐engine_result,覆盖/api/report/status、/api/report/tasks、/api/report/progress/<id>、/api/report/result/<id>/json以及 task-scoped/api/research-tasks/<task_id>/report的输出,同时保持旧字段完全兼容。相关定向回归已补到tests/unit/web_api/test_forum_runtime.py、tests/unit/web_api/test_forum_runtime_helpers.py与tests/unit/application/test_report_query_service.py。 - [-] application 层只消费统一结果对象。
- 备注(2026-04-22):本轮继续把 analysis application/read-model 链路往统一结果对象收口:
services/application/analysis/analysis_service.py当前在统计 success/failure 时已不再直接假设partial_results是裸 dict,而是统一经EngineResult.from_raw(...)归一后计算;services/shared/dto/analysis_run.py也新增了get_engine_results(),services/shared/dto/task_analysis.py当前的 summary/stats 派生改为优先消费统一EngineResult视角,而不是直接读原始 payload 结构。相关定向回归已补到tests/unit/application/test_analysis_service.py、tests/unit/shared/test_analysis_run_dto.py与tests/unit/shared/test_task_analysis_resource_dto.py。 - 备注(2026-04-28):本轮又继续把这条边界往前推进了一步:analysis application 层当前不只在“统计/读模型”阶段消费
EngineResult,连异步执行编排本身也已经先统一归一为EngineResult再生成 status message / final summary / partial result 写回。相关回归pytest -q tests/unit/application/test_analysis_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_analysis_query_service.py tests/unit/web_api/test_search_dispatch.py -k "analysis or engine_result or search"为39 passed。 - 备注(2026-04-28):本轮继续补强 typed contract 的 integration coverage:
tests/integration/test_web_api_smoke.py现在额外锁定了GET /api/research/tasks/<task_id>/analysis-runs在“canonical + legacy partial result 混合 history”下仍统一输出标准engine_name/status/success/summary/error字段,以及GET /api/research/tasks/<task_id>/analysis在 linkedanalysis_run_id失效时,仍会回退到最新 persisted run 并保留summary.linked_run_id、history.linked_run_available=false、history.linked_run_is_current=false这条资源语义。 - 备注(2026-04-28):本轮继续把 contract 从“可兼容 raw dict”推进到“只消费 typed result object”:
AnalysisService的EngineRunner现已不再声明dict | EngineResult联合返回,而是完全收紧为EngineResult;与此同时,tests/unit/application/test_analysis_query_service.py与tests/integration/test_web_api_smoke.py也新增了current_run.partial_results的 legacy/string/missing-field 归一化断言,进一步锁定 task-scoped analysis resource 在 current run 视图上的 canonicalengine_name/status/success/summary/error输出。 - 备注(2026-04-22):本轮继续沿 report 读链推进同一目标:
ReportJobQueryService._build_missing_progress_task_payload()当前也已改为统一通过ReportJobDTO.to_response_item()产出兼容 payload,并补回 legacyreport_file_* / state_file_* / error_message空字段;缺省 progress fallback 现同样携带unified_task与 canonicalengine_result,减少 report 侧手写 raw dict 分叉。
验收标准
- 主链路中的核心引擎具备统一输出结构。
- 汇总逻辑明显简化。
4.3 前端围绕 ResearchTaskViewModel 收口
目标
前端不再在页面里手工拼多个接口结果,而是围绕统一任务视图模型渲染。
当前进展快照(2026-04-21)
-
apps/web_ui/src/composables/useTaskViewModel.ts已收敛为真正的组合层,不再各自实例化useResearchWorkspace()/useCrawlerController()/useSystemController()/useReportStudio(),而是改为复用WorkbenchController提供的统一状态源。 -
useResearchWorkspace()已新增activeTaskCrawlerResource与refreshActiveTaskCrawlerResource(),由useWorkbenchController.ts在初始化、任务保存/切换、研究触发、crawler start-stop 以及 4 秒轮询中统一刷新 task-scoped crawler 资源。 - 前端启动 crawler 时现在会显式携带
research_task_id,因此后端CrawlerService.start_crawler()写回的crawler_job_id已能被 UI 主链路稳定消费;TaskViewModel的 crawler 卡片也已切换为优先展示/api/research-tasks/<task_id>/crawler返回的 linked job,而不是全局/api/crawler/state的 current job。 -
useResearchWorkspace()本轮已继续新增activeTaskReportResource与refreshActiveTaskReportResource();useTaskViewModel.ts的 report 卡片已切换为优先消费/api/research-tasks/<task_id>/report返回的 task-scoped report 资源,不再依赖全局 report 列表按 query/task_id 做启发式匹配。 -
useReportStudio.generate()现已支持透传research_task_id(默认会回退到当前持久化的 active research task),因此报告生成链路已能把 task-scoped report 资源和研究任务主链路关联起来;useWorkbenchController.ts在初始化、任务保存/切换与 4 秒轮询中也已统一刷新 report 资源。 -
useWorkbenchController.ts本轮已继续把“刷新报告 / 生成报告”两条动作显式收口到当前 active research task:generateReport()现在会直接传入task_id调用reports.generate(query, taskId),随后刷新research.refreshTasks(taskId)与research.refreshActiveTaskReportResource(taskId);refreshReports()也会同步刷新 research task snapshot 与 task-scoped report 资源,避免 UI 继续依赖持久化 fallback 才能看到最新 report link/status。 -
useReportStudio.generate()本轮还补了失败分支的generating=false复位,避免/api/report/generate请求失败后前端一直卡在 “Generating...” 状态。 -
useResearchWorkspace.ts已在 2026-04-20 切到资源化别名路径:任务集合读写、任务激活,以及 active task 的 crawler/report 资源刷新当前统一走/api/research-tasks前缀,减少前端同时混用两套 task URL 的情况。 - 当前
apps/web_ui的vue-tsc --noEmit已恢复通过:AnalyzeView.vue依赖的refreshSystemStatus()/refreshAppStatus()已通过useSystemController()正式暴露,views/modes/AnalyzeView.vue/DeliverView.vue中的旧图标导入也已替换为当前@element-plus/icons-vue可用成员。
任务清单
-
设计
ResearchTaskViewModel。 -
聚合字段至少包含:
task base infocrawler statusanalysis statusreport statusrecent actionerrorsnext available actions
- 在 store/composable 中建立统一 task vm 映射层。
- 备注(2026-04-20):
useTaskViewModel.ts已开始围绕WorkbenchController的 research/crawler/system/report 四类状态构建统一任务视图,并已把 crawler/report 数据源切换到 task-scoped/api/research-tasks/<task_id>/crawler|report资源;下一步仍需继续把 analysis 的细粒度状态和next available actions明确纳入统一 VM。 - 备注(2026-04-21):
useTaskViewModel.ts本轮已补齐lastAction、errors、nextActions三组主线字段;TaskCenterView.vue现已直接消费统一 VM 渲染 recent action、open issues 与 next actions,并完成旧activeTaskView零散引用收口,页面不再自行拼 crawler/report/analysis 的局部展示字段。 -
逐页迁移:
TasksViewObserveViewAnalyzeViewDeliverView- 备注(2026-04-17):
TaskCenterView当前已消费useTaskViewModel()输出的统一任务视图,其中 crawler 展示已切换到 task-scoped resource;其余工作台页面仍有部分直接读取局部 composable 状态的逻辑,需后续继续迁移。 - 备注(2026-04-17):
TasksView.vue与DeliverView.vue本轮已先完成页面层接线收口,改为复用useWorkbenchControllerContext()/useTaskViewModel(),不再各自新建useResearchWorkspace()/useReportStudio()实例。DeliverView现已在选中任务时同步激活对应 research task,并优先消费 active task 的 task-scoped report VM;TasksView也已优先按report_job_id关联全局 report 列表,仅在非 active task 且缺少稳定 link 时保留按 query 的兜底匹配。剩余待迁页面主要集中在ObserveView/AnalyzeView的细粒度交互状态收口,以及列表页对“非当前任务”的真正 task-scoped report/crawler 批量视图能力。 - 备注(2026-04-21):
ObserveView.vue本轮已改为复用WorkbenchController与useTaskViewModel(),不再自行实例化useResearchWorkspace()/useCrawlerController()或直接调用/api/search;AnalyzeView.vue也已切到消费统一 controller/system 状态与 active task VM,移除独立useSystemController()生命周期。当前 P1.3 剩余最主要的页面级拼装仍集中在TasksView.vue的列表聚合逻辑。 - 备注(2026-04-21):
useTaskViewModel.ts本轮已继续补出taskViews列表投影视图,并把列表态 report fallback 关联逻辑收回共享 VM;TasksView.vue现已改为直接消费taskViews渲染,不再在页面内混合researchTasks + reportStatus + activeTaskReport三段状态,也移除了按report_job_id/generated_query的页面级手工匹配。 - 备注(2026-04-21):本轮继续把页面壳再压薄一层:
CanvasView.vue已改为以activeTaskView + selectedTask派生 workflow stage/tip/notice,不再用 watch 手工回填阶段状态;DeliverView.vue也已进一步切到优先消费taskViews/activeTaskView与taskView.report.taskId,移除页面内按 query 手工匹配 report 的逻辑;ObserveView.vue则开始优先以 active task 的 task-scoped crawler 资源构建关键词、平台、recent action 和分析移交流程,减少继续回退到全局 crawler snapshot/form 的范围。 - 备注(2026-04-22):本轮继续沿同一主线收缩剩余启发式逻辑:
useTaskViewModel.ts当前对非 active task 的列表态 report 关联已不再按generated_query做 query heuristic,而是只认稳定的report_job_idlink;ObserveView.vue的 recent crawl 展示也开始优先消费 active task 的 linked crawler jobs,而不是默认回退到全局 crawler logs。 - 备注(2026-04-22):本轮继续收口页面层状态来源:
AnalyzeView.vue当前已按targetTaskId -> taskViews/activeTaskView解析目标任务,并由共享TaskViewModel统一构建selectedTaskContext,go-deliver、forum 消息、engine 状态和资源刷新也开始优先跟随同一个 task vm;ObserveView.vue则进一步移除了展示链路上对全局crawler.snapshot/logs的默认依赖,recent crawl 回退改为 task-scoped summary,页面挂载时也不再主动刷新全局 crawler state。 - 备注(2026-04-22):本轮又继续把
ObserveView.vue的目标任务解析收回统一 vm:页面当前已新增targetTaskId -> observeTaskView(taskViews/activeTaskView)解析链,关键词、平台、crawler 状态、recent action、recent crawl、handoff query 与 buildTaskContext 等展示逻辑都开始优先跟随目标任务 vm,而不是默认混用 active task / props fallback。 - 备注(2026-04-22):本轮继续把“目标任务解析 + 页面上下文构建”收回共享 VM:
useTaskViewModel.ts新增resolveTaskView()/resolveTaskContext(),AnalyzeView.vue、DeliverView.vue、CanvasView.vue当前都已改为优先复用这组 helper 来解析targetTaskId对应的 task vm 和页面上下文,不再各自维护taskViewById + buildTaskContext(...)的重复拼装。相关前端npm run typecheck已再次通过。 - 备注(2026-04-22):本轮又继续把
ObserveView.vue并入同一收口路径:当前页面已改为优先复用resolveTaskView()/resolveTaskContext()解析当前观察任务与页面上下文,currentTaskId/currentTaskQuery、crawler platform/keywords/status/recentAction、draft hydration 与 emit 出去的 task context 现在都尽量跟随共享 task vm,而不是继续分散混用activeTask、activeTaskCrawlerResource与props.selectedTask。相关前端npm run typecheck已再次通过。
- 备注(2026-04-22):本轮继续收掉
TasksView.vue的最后一段旧链路:后端已新增/api/research-tasks/task-views列表态 task-scoped 摘要接口,useResearchWorkspace.ts与useTaskViewModel.ts当前会为非 active task 注入对应的 task-scoped crawler/report 资源;TasksView.vue也已改为直接消费这组列表态资源,不再为了展开卡片详情而先activateTask()再刷新 active task report 资源。相关回归为tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py与前端npm run typecheck。 - 备注(2026-04-22):本轮又继续把
useTaskViewModel.ts与AnalyzeView.vue里剩余的 report/task-context 启发式 fallback 再压薄了一层:active task 的 report 关联当前也已只认稳定的report_job_idlink,不再回退到全局 report 列表按 query 匹配;AnalyzeView.vue也已去掉页面层本地重建 task context 的平行逻辑,统一复用共享resolveTaskContext()。结合前面几轮收口,TaskCenterView、TasksView、ObserveView、AnalyzeView、DeliverView与CanvasView当前主路径都已切到统一 task vm / task context 解析链。 - 页面层去除跨多个接口的分散状态拼装逻辑。
验收标准
- 核心页面统一消费 task vm。
- 页面逻辑显著收敛。
- 状态展示与后端主模型一致。
4.4 API 资源化收口
目标
围绕核心业务对象组织接口。
当前进展快照(2026-04-21)
- 已先沿现有兼容前缀
/api/research/tasks/<task_id>/...落地一组 task-scoped 资源接口:/analysis-run、/analysis-runs、/analysis、/crawler。 - 其中 analysis 资源已收敛到共享
TaskAnalysisResourceDTO,crawler 资源已收敛到共享TaskCrawlerResourceDTO,report 资源本轮也已收敛到共享TaskReportResourceDTO;三者都由 route 层统一先解析任务,再把 linked resource id 显式传给各自 application service。 - 2026-04-24:task-scoped
analysis-run/analysis-runs/analysis/crawler/report五条 research resource 主读链已继续收口到services/application/research/task_view_service.py;backend/research_routes.py当前不再自己解析 task 再手搓 resource payload,而是统一委托 application facade,仅保留404/503异常映射与jsonify。 - 当前
/api/research/tasks/<task_id>/crawler采取保守策略:仅当任务存在crawler_job_id且可解析到对应 job 时返回 linked job 视图,不回退到全局current_job,避免错误串联其他任务的 crawler 状态。 - 当前
/api/research/tasks/<task_id>/report同样采取保守策略:仅当任务存在report_job_id且可解析到对应 report job 时返回 linked job 视图,不回退到全局最近 report task,避免错误串联其他任务的交付状态。 - 前端
ResearchTaskViewModel已开始切换到 task-scoped crawler/report 资源,同时 crawler start 与 report generate 请求都已补传research_task_id,使crawler_job_id/report_job_id写回与 UI 展示形成闭环;下一步更适合继续把更多页面逻辑从局部 snapshot 迁移到统一 task VM,并考虑是否把 URL 前缀统一切到/api/research-tasks/{id}/...。 - 已新增资源化兼容别名蓝图
/api/research-tasks:当前已落地GET/POST /api/research-tasks、GET /api/research-tasks/{id}、GET /api/research-tasks/{id}/analysis|crawler|report,统一复用现有 application service / task-scoped resource payload,不影响旧/api/research/tasks前缀继续工作。该路径目前作为兼容别名存在,便于后续前端与接口文档渐进切换。 - 2026-04-20:
/api/research-tasks兼容层已继续补齐POST /api/research-tasks/{id}/activate、GET /api/research-tasks/{id}/analysis-run与GET /api/research-tasks/{id}/analysis-runs;同时useResearchWorkspace.ts已把任务集合读写、激活与 active task 的 crawler/report 刷新统一切到新前缀,避免 Research workspace 继续混用旧/api/research/tasks路径。 - 2026-04-20:已补跑
tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py共30 passed,并重新执行apps/web_ui的vue-tsc --noEmit通过;warning 仍仅包含既有eventletdeprecation、datetime.utcnow()deprecation 与.pytest_cache权限提示。 - 2026-04-22:本轮已继续补上面向
TasksView的列表态任务摘要接口GET /api/research-tasks/task-views,以 task 为中心聚合 research task 基本信息以及 task-scoped crawler/report 读资源,前端列表页已切到直接消费该接口返回的非 active task 摘要,不再依赖先切 active task 才能拿到准确详情。
任务清单
- 盘点当前与任务流相关的 API。
-
设计新的资源化路径:
/api/research-tasks/api/research-tasks/task-views/api/research-tasks/{id}/api/research-tasks/{id}/crawler/api/research-tasks/{id}/analysis/api/research-tasks/{id}/report- 备注(2026-04-22):当前资源化别名已覆盖 collection、list-oriented
task-views、detail、activate 以及 task-scopedanalysis-run/analysis-runs/analysis/crawler/report,并继续复用既有 application service / DTO;这一轮新增的task-views已用于收口TasksView对非 active task 的 task-scoped 摘要读取。
-
提供兼容层,避免一次性废弃旧接口。
- 备注(2026-04-20):
/api/research-tasks现已覆盖研究任务主链路中的集合、详情、激活以及分析/采集/报告读侧资源,旧/api/research/tasks路径仍保持兼容可用,核心 task flow 已具备渐进迁移所需的双轨接口。
- 备注(2026-04-20):
-
前端逐步切换到资源化接口。
- 备注(2026-04-20):
useResearchWorkspace.ts当前已把任务集合读写、激活以及 active task 的 crawler/report 资源刷新切到/api/research-tasks;后续仍需继续检查 Analyze/Observe 等页面是否存在 task-related 旧路径或局部 snapshot 依赖。 - 备注(2026-04-21):本轮再次扫描
apps/web_ui/src,未再发现前端直接请求旧/api/research/tasks/...路径;当前剩余尾项主要是页面语义上继续减少对全局 snapshot 的回退,而不再是 research-task 资源路径的切换问题。
- 备注(2026-04-20):
-
文档化接口契约与字段说明。
- 备注(2026-04-21):已新增
docs/api-research-task-resources.md,集中说明/api/research-taskscollection/detail/activate、task-scopedanalysis-run/analysis-runs/analysis/crawler/report资源,以及与旧/api/research/tasks/...前缀的兼容映射。
- 备注(2026-04-21):已新增
验收标准
- 新增主链路接口以 task 为中心。
- 旧接口只作为兼容层存在。
5. P2:系统增强期
当前进展快照(2026-04-15 14:41)
- 已在
services/shared/observability/下新增context.py、events.py、metrics.py与包级导出,先落地trace_id/task_id/span_id关联上下文、事件 envelope 与指标采集接口骨架。 - 已在
services/shared/timeline/下新增timeline.py、tracker.py与包级导出,提供生命周期事件到 timeline entry 的聚合与查询入口,并兼容 observability recorder 镜像,供 runtime/application 后续直接挂载。 - 当前 observability 仅提供无后端依赖的端口与内存实现,尚未接入
apps/web_api/runtime/、services/application/或结构化日志链路。 - 共享包入口
services/shared/__init__.py已补充observability、timeline导出,后续 runtime / application 可直接按统一路径引用。
5.1 crawler 域隔离
目标
隔离 vendor crawler 实现,避免业务层直接依赖第三方源码。
任务清单
-
建立
services/crawler/domain/。 -
建立
services/crawler/application/。 -
建立
services/crawler/adapters/mediacrawler_adapter.py。 - 统一 crawler 参数模型与错误映射。
- 业务层改为只依赖 crawler domain/application。
验收标准
-
上层代码不直接 import
vendor/mediacrawler。 - crawler 替换成本可控。
5.2 补系统级测试
目标
让主链路具备稳定回归保障。
当前进展快照(2026-04-16 13:33)
- 已新增
tests/integration/test_web_api_smoke.py,通过 Flasktest_client()建立 Web API 最小冒烟测试入口。 - 已新增
tests/unit/web_api/test_cleanup.py,覆盖 reload-safe cleanup 注册 helper 的“替换旧 handler / 忽略反注册异常”行为。 - 已新增
tests/unit/web_api/test_import_path.py,覆盖项目根路径注入 helper 的“插入到sys.path[0]/ 重复调用不重复插入”行为。 - 已新增
tests/unit/web_api/test_runtime_bootstrap.py,覆盖 runtime service 构造、cleanup handler 委托和 route dependency 装配映射,补齐bootstrap/runtime.py的 contract test。 - 已新增
tests/unit/web_api/test_forum_runtime.py,覆盖ForumRuntime对注入ProcessRuntimeRegistry的状态写入与输出委托;tests/unit/web_api/test_forum_runtime_helpers.py也已恢复 Forum log history cursor contract,锁定 helper 不再因为file.tell()使用方式回退为OSError。 - 已新增
tests/unit/web_api/test_http_routes_runtime_dependencies.py,覆盖 Forum 启停、Forum 日志读取、/api/status状态刷新,以及普通 app 启停/日志路由对注入依赖的委托行为,避免http_routes.py回退到模块级直接 import。 - 已新增
tests/unit/web_api/test_socket_events.py,覆盖connect与request_status事件对注入依赖的使用顺序,锁定 Socket.IO handlers 不回退到模块级 runtime 默认 import。 - 已新增
tests/unit/web_api/test_search_hooks.py,覆盖 search hook helper 对 app 模块命名空间的动态解析行为,锁定 monkeypatch 兼容链路不因 wrapper 迁出app.py而回归。 - 已新增
tests/unit/web_api/test_app_module_bootstrap.py,覆盖bootstrap/app_module.py的 app module 级装配 contract,并锁定 helper 通过apps.web_api.bootstrap.runtime模块属性读取 runtime builders,避免 reload/monkeypatch 测试因导入时 capture 符号而回归。 - 已新增
tests/unit/application/test_research_task_service.py,使用 fake store 锁定ResearchTaskService的状态回写、任务激活与非法状态迁移 contract,避免测试被本地文件系统权限噪音干扰。 - 已新增
tests/unit/web_api/test_http_config.py,覆盖前端 dev URL helper 的空值/去尾斜杠行为。 - 已新增
tests/integration/test_web_api_factory_smoke.py,覆盖create_app()的 factory/bootstrap 装配、关键 blueprint/runtime 路由挂载和 Socket.IO handler 注册的最小 smoke。 -
tests/unit/web_api/test_runtime_bootstrap.py现已进一步锁定RuntimeServices对同一forum_runtime对象的装配,以及 route dependency 对该对象 bound methods 的委托;tests/integration/test_web_api_factory_smoke.py也已补/api/status读取注入 forum 状态的 smoke,避免 factory app 回退到测试外的默认 runtime 状态。 - 已新增
tests/integration/test_web_api_system_controls.py,覆盖/api/system/start与/api/system/shutdown的无子进程模式 contract test,并验证 shutdown 默认透传cleanup_timeout=6.0。 - 当前通过 monkeypatch 将
backend.research_tasks.research_task_service重定向到临时 store,并对测试用 store 的_save_locked()做 no-op 处理,避免当前 Windows 环境的文件写权限问题和真实日志目录污染。 -
tests/conftest.py已新增共享 fixture,统一收敛隔离ResearchTaskStore、apps.web_api.appreload 与_APP_MODULE.cleanup_handler注销逻辑,减少多份 integration test 的重复接线。 - 已覆盖
/api/system/statushappy path,验证系统未启动时的最小返回结构。 - 已覆盖
/首页在BETTAFISH_FRONTEND_DEV_URL存在时的 redirect/URL 规范化行为。 - 已覆盖
/api/research/tasksPOST + GET happy path,验证 ResearchTask 创建、active_task 快照与任务列表响应主链路。 - 已覆盖运行时路由/Socket 注册检查、
/api/system/start成功/失败透传、/api/system/shutdown成功/失败透传以及/api/search的 app-module monkeypatch 链路,确保入口重构后关键 hook 仍可用。 - 已覆盖
/api/research/tasks/<task_id>/analysis-run的 null/linked-run/fallback-to-latest persisted run 三种查询分支、/api/research/tasks/<task_id>/analysis-runs的 task-scoped history 返回,以及/api/research/tasks/<task_id>/analysis的资源化 analysis 视图返回。 - 已新增
TaskAnalysisResourceDTO及其 shared/unit coverage,锁定summary/history/stats的派生语义,并在/api/research/tasks/<task_id>/analysis的 query/integration tests 中继续覆盖 linked-run 缺失时的 fallback、聚合计数和关键资源字段。 - 已新增
CrawlerStateDTOshared/unit coverage,并继续覆盖AnalysisRunDTO与CrawlerJobDTO的 response payload contract,确保run_id/job_id、unified_run/unified_job与 legacy payload 合并行为稳定。 - 已覆盖
/api/crawler/jobs、/api/crawler/jobs/{id}、/api/crawler/state以及/api/crawler/start//api/crawler/stop的 application-service 委托链路,锁定 crawler history 到 unified DTO payload 的读侧输出,以及CRAWLER_APP_SERVICE的写侧异常映射行为,并额外补了 Web API app 级别的/api/crawler/jobs、/api/crawler/start、/api/crawler/stopsmoke、factory app 中 crawler jobs 路由注册与最小 GET smoke。 - 已新增
TaskCrawlerResourceDTOshared/unit coverage,并继续覆盖/api/research/tasks/<task_id>/crawler在“无 linked job”与“命中 linked job”两种分支下的 task-scoped crawler 资源输出;同时tests/unit/application/test_research_task_service.py与tests/unit/application/test_crawler_service.py已锁定crawler_job_id的 round-trip 与start_crawler(research_task_id=...)回写行为。 - 已新增
TaskReportResourceDTOshared/query coverage,并继续覆盖/api/research/tasks/<task_id>/report在“无 linked job”与“命中 linked job”两种分支下的 task-scoped report 资源输出;tests/unit/application/test_report_query_service.py现已锁定 report query service 的 linked-only 读侧语义,tests/integration/test_web_api_smoke.py也已补齐 report resource 路由 smoke 与/api/report/generate携带research_task_id时report_job_id回写研究任务的主链路 contract。 - 已继续补强 report-task linkage 的边界测试:
tests/unit/application/test_report_query_service.py现在额外覆盖“task 挂着report_job_id,但 linked report job 实际缺失”时的保守返回语义;tests/unit/application/test_research_task_service.py也已锁定report_job_id=None清链路时不会误伤既有crawler_job_id/analysis_run_id。此外tests/integration/test_web_api_smoke.py已新增闭环 smoke,验证/api/report/generate(research_task_id=...) -> GET /api/research/tasks/<task_id>/report会返回同一个 linked runtime report job,current_job_id/linked_job_id与research_task_id都能正确对齐。 - 已新增
tests/unit/application/test_report_service.py,锁定ReportService.generate_report(...)的 application-layer contract:覆盖“已有运行中任务冲突”、“Report Engine 未初始化”、“输入文件缺失”与“成功创建任务并启动后台线程”四条主分支,同时验证 queued 事件发布与report_job_id回写触发时机。 - 当前测试刻意不触发真实 Streamlit / Forum / 搜索分发 / Socket 运行流程,优先保证重构期间入口稳定。
- 已执行相关定向回归:
tests/unit/shared/test_analysis_run_dto.py、tests/unit/shared/test_crawler_job_dto.py、tests/unit/shared/test_crawler_state_dto.py、tests/unit/shared/test_task_analysis_resource_dto.py、tests/unit/shared/test_task_crawler_resource_dto.py、tests/unit/application/test_analysis_run_store.py、tests/unit/application/test_analysis_query_service.py、tests/unit/application/test_analysis_service.py、tests/unit/application/test_crawler_query_service.py、tests/unit/application/test_crawler_service.py、tests/unit/application/test_research_task_service.py、tests/unit/backend/test_crawler_routes.py、tests/unit/web_api/test_cleanup.py、tests/unit/web_api/test_http_config.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/unit/web_api/test_import_path.py、tests/unit/web_api/test_process_manager_cleanup.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_socket_events.py、tests/unit/web_api/test_search_hooks.py、tests/unit/web_api/test_search_dispatch.py、tests/unit/web_api/test_system_lifecycle.py、tests/unit/web_api/test_process_registry.py、tests/unit/web_api/test_system_state_registry.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_system_controls.py,当前共 126 条测试通过;其中test_runtime_bootstrap.py已额外校验 Forum/日志/Streamlit-process-manager、SystemLifecycleDependencies以及 Socket.IO 依赖通过 bootstrap wiring 正确映射,test_http_routes_runtime_dependencies.py进一步锁定/api/status、/api/start/<app_name>、/api/stop/<app_name>以及 Forum/日志路由对这些注入 callable 的委托行为,test_socket_events.py锁定 Socket.IO handlers 对显式注入依赖的调用顺序,test_search_hooks.py锁定 app-module search monkeypatch 兼容链路的动态解析行为,test_research_task_service.py则锁定应用层任务状态边界,新增的 analysis/crawler task-resource DTO tests、crawler state/service tests 与 crawler route/app smoke tests 则进一步补齐 task-scoped analysis/crawler 资源视图和 crawler 读写 contract;共享 fixture teardown 仍会显式注销 app 模块注册的 cleanup handler,避免退出阶段的旧版清理异常。 - 本轮又补跑了
tests/unit/application/test_report_query_service.py、tests/unit/application/test_research_task_service.py、tests/integration/test_web_api_smoke.py三组与 report-task linkage 直接相关的定向回归,当前共30 passed;warning 仍仅包含既有eventletdeprecation、datetime.utcnow()deprecation 与.pytest_cache权限提示。 - 本轮围绕 report write-side application service 又补跑了
tests/unit/application/test_report_service.py、tests/unit/application/test_report_query_service.py、tests/unit/application/test_research_task_service.py、tests/integration/test_web_api_smoke.py,当前共34 passed;warning 仍仅包含既有eventletdeprecation、datetime.utcnow()deprecation 与.pytest_cache权限提示。
任务清单
-
为 application service 补测试:
- 创建任务/状态回写
- 状态迁移
- 分析触发
- 报告生成触发
- [-] 为 API 补集成测试:
- 创建任务
- 启动爬虫
- 触发分析
- 查询报告状态
- 备注(2026-04-16 10:35):已落地
/首页 redirect、/api/system/status、/api/system/start(成功/失败透传)、/api/system/shutdown(成功/失败透传)与/api/research/tasks的最小 happy path/contract coverage,后续按 runtime/application 稳定度继续扩充 crawler、analysis、report 主链路。 - 备注(2026-04-21):
tests/integration/test_web_api_smoke.py当前已覆盖/api/crawler/start|stop、/api/search、/api/report/status|tasks|progress、task-scopedanalysis/crawler/report资源,以及/api/report/generate(research_task_id=...)到 task-scoped report 资源的闭环链路;本轮再次执行 smoke 为27 passed。 - 备注(2026-04-22):本轮又继续补了
/api/research-tasks/task-views、/api/forum/log|history的engine_result断言,以及 report task-scoped/result/status/tasks/progress 对engine_result的兼容透传断言;结合 analysis 读侧与 factory smoke 回归后,本轮组合验证tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_report_query_service.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py为90 passed。
- [-] 增加一条主链路冒烟测试:
- 建立 Flask app/test_client 冒烟测试入口
- 校验 system status 基础返回
- 校验 ResearchTask 创建与快照读取
- mock crawler 完成
- mock query/media/insight 返回
- forum 汇总
- report 生成
- 校验 completed 状态与输出文件
- 备注(2026-04-15 15:21):当前骨架聚焦低依赖入口验证,故意不耦合未稳定的引擎与后台流程。
- 备注(2026-04-22):
tests/integration/test_web_api_smoke.py本轮继续沿同一主线补齐低依赖 smoke:新增 task-scoped analysis 资源对query/media/insight多引擎 partial results 的保留断言、/api/crawler/start -> task-scoped /crawler的 mock completed crawler 输出断言,以及/api/forum/log|history的 forum host summary 读取断言,并重新回归整个 smoke 文件32 passed;同时已确认既有 report smoke 继续覆盖/api/report/generate -> completed -> task-scoped report resource/result/download的输出链路。
后续接入建议
- 待
services/application/research真正承接搜索编排后,在同一测试文件追加/api/search的 fake dispatcher happy path,用 stub 代替真实引擎。 - 当前
/api/system/start、/api/system/shutdown已有无子进程模式的 contract test,后续可继续补启动失败、重复关机、部分引擎启动异常等失败路径。 -
bootstrap/runtime.py装配层 contract test 与 app factory/bootstrap smoke 已补齐;当前公共 fixture 已先收敛到tests/conftest.py,后续若 Web API 测试继续增多,可再拆分到tests/fixtures子模块。 - 后续若引入统一任务仓储接口,可继续把当前 monkeypatch store fixture 升级为更明确的仓储/应用服务 fixture,减少测试对全局 patch 的依赖。
验收标准
- 主链路改动后可自动回归。
- 关键阶段状态迁移有测试覆盖。
5.3 建设可观测性
目标
从“日志可看”升级到“任务全链路可观测”。
任务清单
- [-] 在
services/shared/observability/下建立:tracing.pymetrics.pyevent_log.pycorrelation.py- 备注(2026-04-15 14:41):已先以
context.py、events.py、metrics.py落地最小骨架,统一trace_id/task_id/span_id关联上下文、事件 envelope、指标采集端口,并提供内存实现供测试/迁移使用;真实 exporter 与 runtime/application 接线尚未开始。
-
为所有主链路动作补
task_id/trace_id。 - 为每个引擎记录执行耗时。
- 为状态迁移记录标准事件日志。
- 为主链路失败增加阶段级错误记录。
- 如条件允许,补一个最小 metrics 面板或可查询接口。
验收标准
- 任意任务可追踪完整生命周期。
- 可定位慢点与失败点。
5.4 任务持久化与恢复
目标
将主链路任务状态从内存状态升级为可恢复状态。
任务清单
- 评估使用 PostgreSQL 还是轻量过渡存储。
-
持久化以下信息:
- task metadata
- status timeline
- engine result refs
- report artifact refs
- error history
- 启动时恢复未完成任务的状态快照。
- 定义失败任务重试/恢复策略。
验收标准
- 系统重启后任务状态可恢复。
- 报告产物与任务关系明确。
5.5 后台任务执行体系
目标
将重任务从 Flask 请求生命周期中解耦。
任务清单
- 评估 Celery / RQ / Dramatiq / 自研轻量 job runner。
-
选型时对比:
- 部署复杂度
- 重试能力
- 状态回写便利性
- 与现有 Flask 架构兼容性
- 为分析任务与报告任务建立异步 job 执行原型。
- 保证异步状态回写 task model。
- 补失败重试与超时控制。
验收标准
- 主分析/报告任务可异步执行。
- 状态一致且可追踪。
6. 推荐执行顺序
第 1 批(必须先做)
-
P0.1 拆
app.py - P0.4 建 runtime registry/store
- [-] P0.2 建统一任务模型
第 2 批(完成主语义收口)
- [-] P0.3 收口 shared 基础设施
[-] P1.1 建 application service
[-] P1.2 统一引擎输出
第 3 批(产品流稳定化)
- [-] P1.3 前端 task view model 化
- [-] P1.4 API 资源化收口
第 4 批(系统增强)
- P2.1 crawler 域隔离
- [-] P2.2 系统级测试
- P2.3 可观测性
- P2.4 持久化与恢复
- P2.5 后台任务体系
7. 当前立即执行项(本轮开发起点)
当前第一刀
-
拆分
apps/web_api/app.py为 factory / bootstrap / runtime 结构
子任务
- 提取 system state 模块
- 提取 process manager 模块
- 提取 forum runtime 模块
- 提取 log stream 模块
- 提取 search dispatch 模块
- 提取 bootstrap blueprints / socketio / logging
-
收缩
app.py到最小入口 - 验证现有接口不回归
8. 更新记录
2026-04-21
- 继续推进 P1.3 前端 task view model 主线:
apps/web_ui/src/composables/useTaskViewModel.ts本轮已补齐lastAction、errors、nextActions聚合字段,apps/web_ui/src/views/TaskCenterView.vue也已切到直接消费统一activeTaskView,新增 recent action、open issues、next actions 展示并清理旧的零散字段引用;npm run typecheck已重新通过。 - 继续推进 P1.3 剩余页面迁移:
apps/web_ui/src/views/ObserveView.vue现已改为复用WorkbenchController与useTaskViewModel(),不再自行 newuseResearchWorkspace()/useCrawlerController()或直接请求/api/search;apps/web_ui/src/views/AnalyzeView.vue也已切到消费共享 controller/system 状态与 active task VM,不再维护独立useSystemController()生命周期。相关前端npm run typecheck已再次通过。 - 继续推进 P1.3 列表页收口:
apps/web_ui/src/composables/useTaskViewModel.ts本轮已新增taskViews列表 VM,把 report fallback 关联与列表态任务摘要统一收回共享映射层;apps/web_ui/src/views/TasksView.vue也已重建为直接消费taskViews的页面壳,不再在页面内部混合 research/report/active-task 三段状态。相关前端npm run typecheck已再次通过。 - 继续推进 P1.3 页面壳收口:
apps/web_ui/src/views/CanvasView.vue本轮已改为基于activeTaskView + selectedTask派生 workflow stage、tip 与 notice,不再用 watch 手工回填阶段状态;apps/web_ui/src/views/DeliverView.vue也进一步切到优先消费taskViews/activeTaskView和taskView.report.taskId,移除页面内按 query 手工匹配 report 的逻辑;apps/web_ui/src/views/ObserveView.vue则开始优先使用 active task 的 task-scoped crawler 资源来构建关键词、平台、recent action 与 handoff 上下文。相关前端npm run typecheck已再次通过。 - 继续推进 P1.4 API 资源化收口:新增
docs/api-research-task-resources.md,明确/api/research-taskscollection/detail/activate 与 task-scopedanalysis-run/analysis-runs/analysis/crawler/report资源契约,并记录与旧/api/research/tasks/...的兼容映射。 - 继续推进 P1.4 前端路径切换收尾:本轮再次扫描
apps/web_ui/src,未再发现前端直接调用旧/api/research/tasks/...的代码;当前剩余问题已主要转为页面语义上的 task-scoped / task-vm 收口,而不是 research-task 资源路径迁移。 - 继续推进 P2.2 系统级测试收口:本轮重新执行
tests/integration/test_web_api_smoke.py得到27 passed,并补跑tests/unit/application/test_analysis_service.py、tests/unit/application/test_report_service.py得到14 passed,进一步锁定 analysis trigger、report generation trigger 与 research-task 主链路 smoke contract。
2026-04-22
- 继续推进 P1.1 report 写侧主线:
services/application/report/report_service.py本轮已新增cancel_report(...),统一承接 report task 的取消用例;services/engines/report/flask_interface.py的/api/report/cancel/<task_id>当前已改为复用同一个REPORT_APP_SERVICE,不再在 route 里直接操作current_task/tasks_registry/task_lock。相关覆盖已补tests/unit/application/test_report_service.py与tests/integration/test_web_api_smoke.py。 - 继续推进 P1.1 report 运行时语义收口:report runtime 现已补
_ensure_task_not_cancelled(...)与ReportTaskCancelled,run_report_generation(...)在关键阶段和流式 progress 更新时都会尊重cancelled状态,避免取消后又被后续running/completed回写覆盖。 - 继续推进 P1.1/P1.4 主线:新增
services/application/research/task_view_service.py,把backend/research_routes.py中原本由 route 手工完成的task -> crawler/report resource聚合视图拼装迁入 application facade;GET /api/research-tasks/task-views当前已退化为直接委托该 service。相关回归已补tests/unit/application/test_research_task_view_service.py,并与tests/integration/test_web_api_smoke.py重新通过。 - 继续推进 P1.1/P1.2 主线:
services/application/report/query_service.py本轮已去除默认反向 import report runtime 的 fallback,改为只消费显式注入的 runtime/query callables;services/engines/report/flask_interface.py当前在构造REPORT_QUERY_SERVICE时统一注入 report job getter / runtime status / snapshot / result loader,backend/research_routes.py也改为复用这一个 runtime-wired query service。与此同时,report progress 的 missing fallback 已统一改走ReportJobDTO.to_response_item(),补齐unified_task与 canonicalengine_result。 - 本轮重新执行
pytest -q tests/unit/application/test_report_service.py tests/unit/application/test_report_query_service.py tests/integration/test_web_api_smoke.py -k "report",结果为33 passed;同时apps/web_ui下npm run typecheck继续通过。 - 本轮重新执行
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_report_query_service.py tests/unit/application/test_research_task_view_service.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py,结果为91 passed;同时apps/web_ui下npm run typecheck继续通过。 - 继续推进 P1.3 前端 task view model 主线:
apps/web_ui/src/composables/useTaskViewModel.ts本轮继续收掉非 active task 的 report query heuristic;列表态taskViews现在对非当前任务只认稳定的report_job_idlink,不再按generated_query去启发式匹配全局 report 任务。 - 继续推进 P1.3 Observe 页面收口:
apps/web_ui/src/views/ObserveView.vue的 recent crawl 展示当前已开始优先消费 active task 的 linked crawler jobs,并继续让关键词、平台、recent action 与 handoff query 优先跟随 task-scoped crawler 资源,而不是默认回退到全局 crawler snapshot/logs。 - 继续推进 P1.3 页面层目标任务收口:
apps/web_ui/src/views/ObserveView.vue本轮已新增targetTaskId -> observeTaskView(taskViews/activeTaskView)解析链,crawler 平台/关键词/状态、recent action、recent crawl、handoff query 与 buildTaskContext 等展示字段都开始优先跟随目标任务 vm,而不是默认混用 active task 与 props fallback。 - 继续推进 P1.2 统一引擎输入输出契约:
apps/web_api/runtime/search_dispatch.py本轮已把 Query/Media/Insight 本地执行 adapter 收口到 canonical raw shape,成功结果现统一返回status/summary/artifacts/metrics,失败结果补齐标准error对象;services/shared/models/engine_contract.py也同步补了 string error、artifact/metrics 推断逻辑。相关定向回归pytest -q tests/unit/shared/test_engine_contract_models.py tests/unit/application/test_analysis_service.py tests/unit/web_api/test_search_dispatch.py为25 passed。
2026-04-23
- 继续推进 P1.1 report route thinning 主线:新增
services/application/report/export_service.py,把 task-scoped/api/report/export/md/<task_id>与/api/report/export/pdf/<task_id>的 IR 读取、Markdown/PDF renderer 调用、导出文件命名与响应描述符构造统一迁入ReportExportService;services/engines/report/flask_interface.py中对应 route 当前已退化为“参数解析 -> 调 service -> Flask response / 异常映射”的薄适配器。 - 继续推进 P1.1 report export 主线:
ReportExportService本轮继续接管/api/report/export/pdf-from-ir的 payload 校验、document_ir必填校验、optimize归一化、PDF renderer 调用与下载文件名构造;services/engines/report/flask_interface.py中对应 route 当前只保留 JSON 读取与400/503异常映射。 - 继续推进 P1.1/P1.2 report query façade:
ReportJobQueryService本轮已新增build_report_log_payload()与build_report_templates_payload(),统一承接/api/report/log、/api/report/templates的日志文件定位/截断读取/权限错误映射以及模板目录扫描;同时ReportService.clear_report_log()也已承接/api/report/log/clear。这样 report 侧又减少了一批 route 直接碰文件系统与 renderer 的逻辑。 - 继续推进 P1.1/P1.4 wiring 收口:
backend/research_routes.py当前已去掉对services.engines.report.flask_interface的反向 lazy import,改为通过configure_report_app_service(...)接收 runtime-wiredREPORT_QUERY_SERVICE;这让 task-scoped report 资源 route 对 runtime 的依赖路径与 bootstrap/application 主线更一致。 - 继续推进 P0/P1 report runtime 基础设施拆分:新增
services/engines/report/stream_runtime.py与services/engines/report/task_runtime.py,将flask_interface.py中原先散落的 SSE subscriber 管理、事件历史以及 current-task/task-registry snapshot helper 抽成独立 runtime helper;ReportTask当前已改为复用TaskEventStream维护事件历史,REPORT_TASK_RUNTIME也已开始承接 runtime 任务仓库与 snapshot 读侧。 - 当前 report application 边界已形成读/写/导出三段公开面:
services/application/report/query_service.py、services/application/report/report_service.py、services/application/report/export_service.py与services/application/report/__init__.py已统一导出对应 façade 与错误类型,便于后续继续从 runtime route 中抽走剩余 report 主线逻辑。 - 继续推进 P1.1 report stream 主线:新增
services/application/report/stream_service.py,把/api/report/stream/<task_id>的任务解析、Last-Event-ID解析、历史回放、heartbeat 与终态收口迁入 application façade;services/engines/report/flask_interface.py中对应 route 已进一步退化为REPORT_STREAM_SERVICE调用 + FlaskResponse壳。与此同时,report runtime 当前生效的 stream/task helper 也已统一改走REPORT_STREAM_SUBSCRIBERS、REPORT_TASK_RUNTIME与serialize_sse_event(...),不再继续扩大旧的模块级平行实现。 - 本轮重新执行
pytest -q tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/application/test_report_stream_service.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/integration/test_web_api_smoke.py -k "report",结果为69 passed;随后补跑pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_report_stream_service.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/unit/application/test_research_task_view_service.py tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py,结果为135 passed;apps/web_ui下npm run typecheck继续通过。 - 本轮重新执行
pytest -q tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/integration/test_web_api_smoke.py -k "report",结果为44 passed;随后补跑pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/unit/application/test_research_task_view_service.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py,结果为112 passed;apps/web_ui下npm run typecheck继续通过。 - 本轮还额外执行
pytest -q tests/unit/application/test_report_query_service.py tests/unit/application/test_research_task_view_service.py tests/integration/test_web_api_smoke.py -k "report or task_view",结果为34 passed;用于锁定 report query wiring 与 research task-scoped report 资源链路在去除反向 import 后仍保持稳定。 - 本轮继续补跑
pytest -q tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/integration/test_web_api_smoke.py -k "report",结果为55 passed;随后把这两组 runtime helper 一并纳入宽回归pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/unit/application/test_research_task_view_service.py tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py,结果为121 passed;apps/web_ui下npm run typecheck继续通过。 - 继续推进 P1.3 共享 task vm 收口:
apps/web_ui/src/composables/useTaskViewModel.ts本轮新增resolveTaskView()/resolveTaskContext(),apps/web_ui/src/views/AnalyzeView.vue、DeliverView.vue、CanvasView.vue当前都已开始直接复用共享 helper 来解析目标任务上下文,进一步减少页面层重复的taskViewById + buildTaskContext(...)拼装;前端npm run typecheck继续通过。 - 继续推进 P1.2 analysis 读模型收口:
services/shared/dto/analysis_run.py本轮已补上partial_results读模型归一化,analysis run / task-scoped analysis resource 接口当前会把 legacymessage/themes/sources结构投影成统一的engine_name/status/success/summary/artifacts/metrics/error;相关样例和断言也已同步更新到tests/unit/shared/test_analysis_run_dto.py、tests/unit/shared/test_task_analysis_resource_dto.py、tests/unit/application/test_analysis_query_service.py与tests/integration/test_web_api_smoke.py,组合验证为46 passed。 - 继续推进 P1.3 Observe 页面状态来源收口:
apps/web_ui/src/views/ObserveView.vue本轮也已切到优先复用resolveTaskView()/resolveTaskContext()解析观察任务与页面上下文,currentTaskId/currentTaskQuery、crawler 平台/关键词/状态、recent action、draft hydration 与 emit 出去的 task context 现在都尽量跟随共享 task vm,而不是继续分散混用activeTask、activeTaskCrawlerResource与props.selectedTask;前端npm run typecheck已再次通过。 - 继续推进 P2.2 主链路 smoke:
tests/integration/test_web_api_smoke.py本轮新增/api/crawler/start -> /api/research/tasks/<task_id>/crawler的 mock completed crawler smoke,验证crawler_job_id、task-scoped crawler status、result summary 与 linked current job 输出;连同前一轮 analysis/report smoke 重新回归后,当前pytest -q tests/integration/test_web_api_smoke.py为31 passed。 - 继续推进 P2.2 forum 汇总 smoke:
tests/integration/test_web_api_smoke.py本轮新增/api/forum/log与/api/forum/log/history的低依赖 smoke,直接向 runtimeforum.log注入 Query/Insight/HOST 三段日志,并校验 host summary 的 parsed message 与 history 分页读取;整组 smoke 现为32 passed。 - 本轮再次执行
apps/web_ui下的npm run typecheck,vue-tsc --noEmit通过。 - 本轮修复了一处 report runtime wiring 回归:
REPORT_APP_SERVICE恢复通过动态check_engines_ready()seam 兼容测试侧 monkeypatch,随后重新执行pytest -q tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py tests/unit/application/test_report_stream_service.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/integration/test_web_api_smoke.py -k "report",结果为81 passed, 24 deselected。 - 本轮还继续收紧了
backend/research_routes.py的 report wiring fallback:task-scoped/api/research/tasks/<task_id>/report以及 task-views 聚合在 report service 未配置时当前会显式返回503,不再静默退化为空ReportJobQueryService()。相关 smokepytest -q tests/integration/test_web_api_smoke.py -k "report_resource or task_views or report"为19 passed。 - 本轮又补跑
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_report_stream_service.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/unit/application/test_research_task_view_service.py tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py,结果为148 passed;apps/web_ui下npm run typecheck继续通过。
2026-04-28
- 继续推进 P1.2 analysis typed contract 主线:
services/application/analysis/analysis_service.py本轮已把execute_search_dispatch_async(...)内部剩余的裸dict编排收口到统一EngineResult对象;当前EngineRunner也已正式收紧为只返回EngineResult,过渡性的_run_engine(...)兼容层已经移除,随后统一基于EngineResult.success/summary/error生成状态文案、写回partial_results和统计 success/failure,_record_analysis_run_result(...)也同步改为直接消费EngineResult。 - 同一轮已新增/扩展
tests/unit/application/test_analysis_service.py,补上“engine runner 直接返回EngineResult实例时仍能正确落库与聚合”的定向断言,进一步锁定 application 层只消费统一结果对象的 contract。 - 本轮重新执行
pytest -q tests/unit/application/test_analysis_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_analysis_query_service.py tests/unit/web_api/test_search_dispatch.py -k "analysis or engine_result or search",结果为39 passed。 - 本轮又补跑
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/application/test_research_task_view_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_http_routes_runtime_dependencies.py tests/unit/web_api/test_runtime_bootstrap.py tests/integration/test_web_api_smoke.py -k "search or analysis or task_view or resource_alias",结果为64 passed, 35 deselected。 - 继续推进 P1.2 typed contract 到 runtime snapshot:
apps/web_api/runtime/search_dispatch.py中 Query/Media/Insight 的本地 adapter 当前已直接返回EngineResult/EngineExecutionError;apps/web_api/runtime/task_runtime_store.py也已在写入partial_results时统一经过EngineResult.from_raw(...).to_runtime_payload()归一,避免 task runtime snapshot 再扩散 legacystring/dict混合结构。 - 继续推进 P1.1/P1.4 analysis task-view 聚合 typed 化:
services/application/research/task_view_service.py当前已支持在 application 层内部直接聚合带to_response_payload()的 analysis resource DTO,backend/research_routes.py中RESEARCH_TASK_VIEW_APP_SERVICE也已切到注入AnalysisRunQueryService.get_task_analysis_resource_dto(...),因此/api/research-tasks/task-views的 analysis 聚合现在会优先围绕TaskAnalysisResourceDTO运转,再在最外层 materialize 成 response payload。 - 本轮重新执行
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_task_runtime_store.py -k "analysis or engine_result or task_runtime or search",结果为43 passed。 - 本轮又补跑
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/application/test_research_task_view_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_task_runtime_store.py tests/unit/web_api/test_http_routes_runtime_dependencies.py tests/unit/web_api/test_runtime_bootstrap.py tests/integration/test_web_api_smoke.py -k "search or analysis or task_view or task_runtime or resource_alias",结果为68 passed, 35 deselected。 - 本轮还补跑
pytest -q tests/unit/application/test_research_task_view_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_task_runtime_store.py tests/integration/test_web_api_smoke.py -k "task_view or analysis or task_runtime or resource_alias or task_views",结果为34 passed, 44 deselected。 - 本轮最终又补跑
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/application/test_research_task_view_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_task_runtime_store.py tests/unit/web_api/test_http_routes_runtime_dependencies.py tests/unit/web_api/test_runtime_bootstrap.py tests/integration/test_web_api_smoke.py -k "search or analysis or task_view or task_runtime or resource_alias or task_views",结果为69 passed, 35 deselected。 - 继续推进 P1.1/P1.4 research route thinning 主线:
backend/research_routes.py本轮已去掉ResearchTaskViewService(...)的模块级装配,改为只保留configure_research_task_view_app_service(...) + getter/proxy;真正的 task-view facade 现在统一由apps/web_api/bootstrap/runtime.py在build_runtime_services()阶段注入,同时继续复用 task/analysis/crawler/report 几条 lazy seam,避免 capture 旧 store/service。 - 本轮还补了一处 report runtime import-order wiring 缺口:
services/engines/report/flask_interface.py中REPORT_APP_SERVICE对clear_report_log的绑定已改成调用时解析,避免 factory/bootstrap 路径在未预先 monkeypatch builtins 时因定义顺序触发NameError。 - 本轮重新执行
pytest -q tests\\integration\\test_web_api_factory_smoke.py,结果为4 passed;并补跑pytest -q tests\\unit\\engines\\report\\test_flask_interface_runtime_wiring.py -k "research_report_proxy or services_bind_directly",结果为2 passed, 4 deselected。 - 本轮最终又补跑
pytest -q tests\\unit\\application\\test_analysis_service.py tests\\unit\\application\\test_analysis_query_service.py tests\\unit\\application\\test_research_task_view_service.py tests\\unit\\web_api\\test_runtime_bootstrap.py tests\\unit\\web_api\\test_app_module_bootstrap.py tests\\unit\\web_api\\test_search_dispatch.py tests\\unit\\web_api\\test_task_runtime_store.py tests\\unit\\engines\\report\\test_flask_interface_runtime_wiring.py tests\\integration\\test_web_api_smoke.py tests\\integration\\test_web_api_factory_smoke.py -k "analysis or task_view or runtime or search or task_runtime or resource_alias or task_views or factory_app or research_report_proxy",结果为84 passed, 21 deselected。
2026-04-24
- 继续推进 P1.1/P1.4 task-centered 聚合主线:
services/application/research/task_view_service.py本轮已补上 analysis 聚合,backend/research_routes.py中的RESEARCH_TASK_VIEW_APP_SERVICE也开始统一注入 analysis/crawler/report 三条 task-scoped resource builder;GET /api/research-tasks/task-views当前已能稳定输出task + analysis + crawler + report四段摘要,而不再只覆盖 crawler/report。 - 继续推进 P1.1 analysis service 边界收口:
services/application/analysis/analysis_service.py本轮已去掉submit_search_request(...)里的 HTTP tuple 语义,改为只返回 application submission 结果对象;apps/web_api/runtime/search_dispatch.py当前再负责把 submission 映射回 route 侧(payload, status_code),因此/api/search的 200/400 行为保持兼容,但 service 本身不再直接处理 transport 语义。 - 继续推进 P1.1/P1.4 research route thinning 主线:
ResearchTaskViewService本轮已继续接管 task-scopedanalysis-run / analysis / analysis-runs三条 research resource 的任务解析与 linkedanalysis_run_id绑定,backend/research_routes.py中对应路由已退化为“调 façade +404映射 +jsonify”;同一轮里 research route wiring 也切到通过惰性 task-service proxy 解析RESEARCH_TASK_APP_SERVICE,避免测试/运行时切换 store 后RESEARCH_TASK_VIEW_APP_SERVICE捕获旧 service 实例。 - 继续推进 P1.1/P1.4 crawler/report task-scoped resource 收口:
ResearchTaskViewService本轮又补上build_task_crawler_resource_payload(...)与build_task_report_resource_payload(...),backend/research_routes.py中/api/research/tasks/<task_id>/crawler|report当前也已改为复用同一个 application facade;route 层旧_build_task_resource_response(...)中转壳已删除,report service 未配置时仍显式返回503,不再静默退化。 - 继续推进 P1.1 report runtime 语义收口:
services/engines/report/flask_interface.py本轮把run_report_generation(...)在ReportTaskCancelled与通用异常分支中的 current-task 清理改为REPORT_TASK_RUNTIME.clear_current_task(task_id=...)条件清理,避免旧任务晚到的失败/取消把新 current task 一并清掉。 - 继续推进 P1.1 report runtime 启动收口:
services/engines/report/flask_interface.py本轮也已移除启动恢复阶段的current_task/tasks_registry过渡 shim;磁盘恢复结果当前会直接初始化REPORT_TASK_RUNTIME,不再先写一层模块级旧全局再回灌。 - 本轮重新执行
pytest -q tests/unit/application/test_analysis_service.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_http_routes_runtime_dependencies.py tests/unit/web_api/test_runtime_bootstrap.py tests/integration/test_web_api_smoke.py -k "search or submit_search_request or task_view",结果为43 passed, 37 deselected。 - 本轮重新执行
pytest -q tests/unit/application/test_research_task_view_service.py tests/integration/test_web_api_smoke.py -k "analysis or task_view or resource_alias",结果为12 passed, 34 deselected。 - 本轮重新执行
pytest -q tests/unit/application/test_research_task_view_service.py tests/integration/test_web_api_smoke.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py -k "task_view or analysis or crawler_resource or report_resource or resource_alias or report_service",结果为18 passed, 34 deselected。 - 本轮还补跑
pytest -q tests/unit/application/test_research_task_view_service.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py tests/unit/engines/report/test_task_runtime.py tests/integration/test_web_api_smoke.py -k "analysis or task_view or report or runtime_wiring or resource_alias",结果为41 passed, 15 deselected。 - 本轮重新执行
pytest -q tests/unit/application/test_research_task_view_service.py tests/unit/engines/report/test_task_runtime.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py tests/integration/test_web_api_smoke.py -k "task_view or runtime_wiring or clear_current_task or report_generation_error or report_resource or report",结果为30 passed, 23 deselected。 - 本轮重新执行
pytest -q tests/unit/application/test_analysis_service.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_http_routes_runtime_dependencies.py tests/unit/web_api/test_runtime_bootstrap.py tests/unit/engines/report/test_task_runtime.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py tests/unit/application/test_report_stream_service.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/integration/test_web_api_smoke.py -k "search or runtime_wiring or report or clear_current_task or report_generation_error or task_view",结果为118 passed, 22 deselected。 - 本轮又补跑
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_research_task_view_service.py tests/unit/application/test_report_stream_service.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py,结果为151 passed;apps/web_ui下npm run typecheck继续通过。 - 本轮最终又补跑
pytest -q tests/unit/application/test_analysis_service.py tests/unit/application/test_analysis_query_service.py tests/unit/shared/test_analysis_run_dto.py tests/unit/shared/test_task_analysis_resource_dto.py tests/unit/application/test_research_task_view_service.py tests/unit/application/test_report_stream_service.py tests/unit/application/test_report_export_service.py tests/unit/application/test_report_query_service.py tests/unit/application/test_report_service.py tests/unit/engines/report/test_stream_runtime.py tests/unit/engines/report/test_task_runtime.py tests/unit/engines/report/test_flask_interface_runtime_wiring.py tests/unit/web_api/test_forum_runtime.py tests/unit/web_api/test_forum_runtime_helpers.py tests/unit/web_api/test_search_dispatch.py tests/unit/web_api/test_http_routes_runtime_dependencies.py tests/unit/web_api/test_runtime_bootstrap.py tests/integration/test_web_api_factory_smoke.py tests/integration/test_web_api_smoke.py,结果为177 passed;apps/web_ui下npm run typecheck继续通过。
2026-04-17
- 新增
apps/web_api/bootstrap/app_module.py,统一承接 app module 级别的 Flask/Socket.IO/runtime services/cleanup/search hooks/route wiring 装配;apps/web_api/app.py现仅保留 import-path 入口壳、装配结果导出与main()。 - 新增
tests/unit/web_api/test_app_module_bootstrap.py,覆盖bootstrap_app_module(...)的装配 contract。 - 修复
bootstrap/app_module.py因直接导入 runtime builders 导致tests/integration/test_web_api_system_controls.pymonkeypatch 失效的问题;本轮相关定向回归为34 passed,扩大回归为35 passed。 - 继续推进 P1.1 crawler application service:
services/application/crawler/crawler_service.py现已在start_crawler()中补齐研究任务默认采集参数同步,会在携带research_task_id时从统一ResearchTaskDTO读取crawler_defaults/crawler_keywords_text填充缺省 crawler payload,同时保持显式传入参数优先,并在状态回写时同步generated_query。 - 新增/扩展
tests/unit/application/test_crawler_service.py,锁定“研究任务默认 crawler 参数同步 / 显式参数优先 / 缺失任务报错”的 application-layer contract;并补跑tests/unit/application/test_research_task_service.py、tests/unit/application/test_crawler_service.py、tests/unit/backend/test_crawler_routes.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py,当前共57 passed。 - 继续推进 P0/P1 主线的 analysis 收口:
services/application/analysis/analysis_service.py现已新增submit_search_request(...)façade,把/api/search原本留在 route 的resolve_search_query -> dispatch_search_request两段式 choreography 收进 application service;apps/web_api/interfaces/http_routes.py当前只保留 payload 读取与响应透传。 -
apps/web_api/bootstrap/runtime.py已同步新增 route-readysubmit_search_requestwiring,并继续通过传入的 search hook callables 保持_APP_MODULE.search_hooks这条 monkeypatch 兼容链路不回退。 - 新增/扩展
tests/unit/application/test_analysis_service.py、tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/unit/web_api/test_runtime_bootstrap.py,锁定新的 analysis façade 行为与 bootstrap wiring;并补跑tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_system_controls.py,当前相关组合回归为51 passed。 - 继续推进 P0/P1 主线的 analysis runtime 收口:新增
AnalysisExecutionContext,把process_registry、check_app_status、log_dir、write_log统一封装为 application 层 execution port;AnalysisService.submit_search_request(...)/dispatch_search_request(...)已优先消费该 context,同时保留旧 kwargs 入口作为兼容层。 -
apps/web_api/bootstrap/runtime.py与apps/web_api/runtime/search_dispatch.py已同步切换为预先装配AnalysisExecutionContext,并把 route-ready dispatcher 传给submit_search_request(...),从而继续保持_APP_MODULE.search_hooks这条 monkeypatch 链路可替换,但不再把四元 runtime 依赖逐个暴露到 route-level submit façade。 - 新增/扩展
tests/unit/application/test_analysis_service.py、tests/unit/web_api/test_runtime_bootstrap.py、tests/unit/web_api/test_search_dispatch.py,并联合tests/unit/web_api/test_http_routes_runtime_dependencies.py、tests/integration/test_web_api_smoke.py、tests/integration/test_web_api_factory_smoke.py、tests/integration/test_web_api_system_controls.py重新回归,当前相关组合回归为52 passed。 - 继续推进 P0/P1 主线的 report 收口:
ResearchTaskService新增sync_report_runtime_status(...),把 report runtime 阶段到 research task unified status 的映射收回 application 层;services/engines/report/flask_interface.py中的 report 状态同步桥接现已改为调用这一统一入口,不再在 runtime 侧维护独立状态映射表。 -
ReportJobQueryService已继续新增build_report_status_payload()、build_report_tasks_payload()与build_report_progress_payload(),/api/report/status、/api/report/tasks、/api/report/progress/<task_id>三个路由当前都已退化为 query façade 的薄适配器,同时保持 progress 在任务缺失时返回 completed fallback 的兼容语义。 - 新增/扩展
tests/unit/application/test_research_task_service.py、tests/unit/application/test_report_query_service.py、tests/unit/application/test_report_service.py与tests/integration/test_web_api_smoke.py,锁定 report 状态同步入口、report runtime query façade 以及对应 route 适配行为;并补跑tests/integration/test_web_api_factory_smoke.py,当前相关组合回归为47 passed。 - 继续推进 P0/P1 主线的 analysis 状态同步收口:
ResearchTaskService新增sync_analysis_runtime_status(...),AnalysisService当前已改为通过 runtime 状态queued/running/partial/failed驱动 research task 阶段回写,保持既有analysis_run_id透传、runtime store 行为以及“部分成功仍保持 analyzing、失败回落 ready”的兼容语义;相关覆盖已补到tests/unit/application/test_research_task_service.py、tests/unit/application/test_analysis_service.py与tests/unit/web_api/test_search_dispatch.py。
2026-04-16
- 继续推进
apps/web_api/app.py瘦身:cleanup 注册已直接复用RuntimeServices.cleanup_handler,去掉仅作中转的_cleanup_runtime包装层。 - 继续收缩入口壳:新增
apps/web_api/bootstrap/http_config.py承接前端 dev URL 配置解析,app.py不再保留该 helper。 - 继续收缩入口壳:新增
apps/web_api/bootstrap/cleanup.py承接 reload-safe cleanup 注册,app.py不再直接持有atexit.unregister/register细节。 - 继续收缩入口壳:新增
apps/web_api/bootstrap/import_path.py承接项目根路径注入,app.py不再直接持有sys.path变更实现细节。 - 新增
tests/integration/test_web_api_factory_smoke.py,补齐create_app()、route wiring 与 Socket.IO handler 注册的最小 factory/bootstrap smoke coverage。 - 新增
tests/integration/test_web_api_system_controls.py,补齐/api/system/start与/api/system/shutdown的无子进程模式 contract test。 - 补齐首页 dev redirect contract 与
/api/system/start失败透传 contract。 - 补齐
/api/system/shutdown失败透传 contract,以及tests/unit/web_api/test_import_path.py对导入路径 helper 的行为锁定。 - 收敛
tests/conftest.py中的共享 fixture,统一隔离ResearchTaskStore、app reload 与 cleanup handler 注销逻辑。 - 新增
tests/unit/web_api/test_cleanup.py,固定 cleanup helper 的注册行为。 - 将 Forum 启停、Forum 日志读取与普通 app 日志读写 callable 纳入
HttpRouteDependencies/bootstrap.runtime装配,并新增tests/unit/web_api/test_http_routes_runtime_dependencies.py锁定路由层委托。 - 联合既有 unit/integration 测试完成定向回归,当前相关 36 条测试通过。
2026-04-15
- 建立本文档,作为项目整体架构优化与开发执行基线。
确定先从
apps/web_api/app.py拆分开始,作为整个重构的第一阶段入口。继续推进
apps/web_api/app.py瘦身:新增apps/web_api/interfaces/http_routes.py与apps/web_api/interfaces/socket_events.py,将运行时 HTTP 路由和 Socket.IO 事件注册从入口文件中抽离。apps/web_api/app.py现已收缩为应用创建、依赖装配、注册调用、清理钩子和main()启动入口;同时修复SystemLifecycleService.start_async_shutdown()漏传stop_forum_engine/set_system_state导致关机清理失败的问题。继续推进运行态收口:新增
apps/web_api/runtime/process_registry.py与SystemStateRegistry,把进程状态快照与系统启动状态封装为显式 registry,并保留get_system_state()等兼容出口;processes兼容出口已在后续轮次移除。http_routes、socket_events、forum_runtime、system_lifecycle已开始通过 registry 读取或写入运行态;同时补充了 registry 单测与/api/status冒烟验证,手工校验确认/api/status、/api/system/status与/api/searchmonkeypatch 链路保持可用。