摩擦全景
++flowchart TB + subgraph BE["后端"] + IJ["intelligence_jobs.py+
两套手写结算序列"] --> AR["AsyncJobRepository
claim/recovery 已深"] + R1["routers/intelligent_evals.py
直 import ORM 表"] -.泄漏.-> VIS["逻辑删除可见性
三处独立实现"] + DL["decision_logs._require_eval"] -.泄漏.-> VIS + REPO["storage/repository.py
1180 行 · 12 类 · 5 域"] + APP["web/app.py
手工编排 5 个 registry 关停顺序"] + end + subgraph FE["前端"] + READ["src/read 深模块
仅 1 个消费者"] -.绕过.-> TABS["drawer 标签页群
各自手写 fetch/polling"] + API["api.ts 909 行 · 12 域"] + end + classDef warn fill:#fef3c7,stroke:#f59e0b,stroke-width:2px; + class IJ,VIS,REPO,APP,TABS,API warn; +
① 耐久智能作业:结算序列收进单一执行器
+intelligence_jobs.py · AsyncJobRepository · campaigns 路由 · app.py
+Files
+-
+
evaluation/intelligence_jobs.py— 两个 executor(53–150、153–263)
+ storage/repository.py—AsyncJobRepository(782–863)
+ evaluation/campaign_runner.py:242-247、routers/campaigns.py:128-134
+ evaluation/comparison.py:245-280(死代码 + 投影越界)
+
Problem
+v2 计划已建出 AsyncJobRepository(claim / recovery / orphan 结算都深),但编排序列仍按 adapter 手抄:分析与周期对比两个 executor 各自复制「缺则入队 → claim_queued → upsert(generating) → try/except → failed/completed」。错误截断 str(exc)[:500] 出现两次(123、250);正式线跳过守卫两处(campaign_runner.py:242 与 intelligence_jobs.py:136);分析触发校验在路由与 executor 各一份(周期对比已有共享入口,分析没有);build_comparison_payload 是零调用死代码且与 _build_auto_baseline_payload 逐字节相同。
Solution
+在执行器层提供单一深接缝 execute(job_kind, work_fn):入队幂等、认领、generating、失败归一(截断)、成功结算、重复触发幂等观察——全部收进 implementation。分析与周期对比退化为纯领域工作 adapter;顺带删除死代码、把 load_comparison_view 投影移回读模型、分析校验对齐周期对比的共享入口。
Benefits
+Locality:结算顺序知识(先认领再 generating、失败先归一再落库)只住一处。Leverage:第三个作业类型只需一个 work adapter。测试面从"两套序列各测一遍"收敛为执行器接口一组契约测试 + 每 adapter 的纯领域测试。两个 adapter 已存在——这是真接缝,不是假想接缝。
+Before / After
+Before · 序列手抄 ×2
+After · 单一执行器
+认领·结算·归一·幂等
+sequenceDiagram + participant C as 触发方(路由/自动) + participant E as execute() 深模块 + participant W as work adapter + C->>E: execute(kind, work_fn) + E->>E: 幂等入队 + claim_queued + E->>E: upsert(generating) + E->>W: 领域工作(无 Session 编排) + alt 成功 + E->>E: upsert(completed + 结果) + else 异常 + E->>E: 归一 + 截断 + upsert(failed) + end ++
② 智能评估可见性接缝:逻辑删除语义三处收敛为一
+intelligent_eval/repository.py · decision_logs.py · routers/intelligent_evals.py · task_queue.py
+Files
+-
+
intelligent_eval/repository.py— 可见性谓词重复 6 处(103、117、130、137、147、171)
+ decision_logs.py:30-33—_require_eval绕过 repo 直查 ORM
+ routers/intelligent_evals.py:73-80— 第三份实现,且路由直接 importIntelligentEvalDB
+ task_queue.list_tasks:330— 删除不感知,监控面板泄漏已删评估
+
Problem
+逻辑删除(2d2c5a2,一周前)落地后,「已删即 404」由三个独立实现分别裁决:repo 的 get() 隐藏 + get_including_deleted()、decision_logs._require_eval 直查 ORM、路由 _require_eval_exists 再直查 ORM——路由跨过 repository 接缝直接握表,等于把删除语义焊死在 HTTP 层。附带浅点:配置快照 11 字段序列化 dict 在路由里逐字写了两遍(399–416、430–442);expired 会话的 Markdown 标注住在路由(242–252);count_decisions 全量载入后 len()(112–121)。
Solution
+可见性收进 repository 单一谓词(内部 _visible()),对外只留 require_live_eval(session, id) 服务接缝——三处调用方改走它,路由删掉 ORM import。快照序列化与 expired 标注下沉到读模型投影;count_decisions 改 func.count。
Benefits
+Locality:删除语义改动(如未来回收站、级联策略)只改一处。正确性:消除三处分歧风险——当前任务监控已实际泄漏已删评估。测试面:逻辑删除契约从分散的集成断言收敛为 repo 接口一组测试。这是新功能知识正在扩散的窗口期,越早收敛成本越低。
+Before / After
+Before · 三处裁决"已删即 404"
+After · 单一可见性接缝
++flowchart LR + subgraph NOW["现状:三份语义"] + A["router 直查+
IntelligentEvalDB"] --> D["DB"] + B["decision_logs
session.get()"] --> D + C["repo.get()
隐藏 deleted"] --> D + end + classDef bad fill:#fee2e2,stroke:#ef4444; + class A,B bad; +
③ 存储层 monolith:repository.py / db.py 按域拆分
+storage/repository.py(1180 行)· storage/db.py(709 行)
+Files
+-
+
storage/repository.py— 1180 行、12 个 repository 类、跨约 5 个域
+ storage/db.py— 709 行、20 张表一个模块
+ - 对比项:
intelligent_eval/repository.py(510 行、单域)是良好范本
+
Problem
+文件级 locality 欠佳:目录(Target/Scenario)、执行(Run/Result)、评估活动(Campaign + 两个智能作业)、探索式评测(ExplorationSession/Message)同居一文件;探索对(1057–1163)与异步作业仓库(782–996)完全自包含,却被迫与无关域共享导航成本。AI 与新成员定位"活动的 CAS 写入"必须扫过全部 12 个类。CAS 条件写模式也在此三度独立实现(680–739、802–828 与 intelligent_eval 的 190–248),同文件却不共享原语。
+Solution
+纯移动不改行为:拆出 storage/exploration_repository.py、storage/async_job_repository.py(或随候选①并入执行器模块);db.py 可暂不动(表定义集中有 create_all 的便利)。可选第二步:把三份 CAS 收敛为一个共享条件写原语——但需先过删除测试:三处的条件字段与冲突语义确有差异,只有当共享原语接口小到不泄漏这些差异时才值得。
Benefits
+每域一个文件 = 改动、bug、知识同址;导航成本从"扫 1180 行"降到"开对应文件"。本身不产生 leverage,故标记为 Worth exploring——它是其他候选(①②)的天然搭车项,单独做价值有限。
+Before / After
+Before · 一文件 12 类 5 域
+storage/repository.py — 1180 行
+After · 每域一文件
+④ 前端读取模块:src/read 从单消费者扩展为统一资源接缝
+src/read/* · components/intelligent_eval/* · hooks/useCampaignReport.ts · hooks/useResource.ts
+Files
+-
+
read/intelligentEval.ts(118 行,有测试)·read/useIntelligentEvalRead.ts(83 行,有测试)
+ components/intelligent_eval/— ExecutionProcess(480 行)、DecisionProcess、ConfigSnapshots、TaskQueueMonitor
+ hooks/useCampaignReport.ts:16-21、hooks/useResource.ts
+
Problem
+src/read 是一个真正的深模块(ReadSlot 相位机、竞态守卫、静默刷新、可注入 adapter,且有测试)——但只有 IntelligentEvals.tsx 一个消费者,连它自己都绕过读模块做 mutation。五个 drawer 标签页各自手写同形 fetch 样板(DecisionProcess:25-35、ConfigSnapshots:32-42、TaskQueueMonitor:39-50);ACTIVE_STATUSES 定义两份;ReadPhase/ReadSlot 在 useCampaignReport.ts 里平行重造;Blob 导出仪式重复三遍;同一份决策日志有三种新鲜度策略(父级 5s 轮询详情 + ExecutionProcess 独立 5s 轮询 + DecisionProcess 只挂载取一次)。组件群零测试(EvalReport / EvalOverview / DecisionProcess / ConfigSnapshots / TaskQueueMonitor)。
Solution
+把 ReadSlot 泛化为通用资源接缝(或强制 drawer 标签页走既有 useResource + 轮询门控),决策日志的新鲜度策略由父级读模块统一供给一次;常量与导出仪式各自归位单一出口。组件退化为纯渲染,状态经 props/reducer 注入。
Benefits
+Leverage:竞态守卫、静默刷新、轮询暂停这些已写好且已测的行为,一次学习全组件受益。可测性:接口即测试面——五个零覆盖组件的测试从"mock 整个 api + 时序"变成对纯 reducer/props 的断言,这正是 sessionReducer 已验证的形状。深模块已存在、adapter 需求已出现(campaign 报告平行重造)——符合"两个 adapter 才是真接缝"。
+Before / After
+Before · 深模块闲置,样板四散
+深·有测试
1 个消费者
手写 5 个 fetch 槽
手写样板
手写样板
手写样板
After · 资源接缝统一供给
+⑤ api.ts 单文件按域拆分
+frontend/web/src/api.ts(909 行 · 12 域)
+Files
+src/api.ts — auth、targets、scenarios、modelConfigs、runs、reports、stats、exploration、campaigns、intelligentEvals(16 个端点)、files,全部类型内联。
Problem
+v1.2 已点名的残留浅点。单文件本身不深不浅——axios 拦截器统一错误是真实深度——但 12 域同居迫使每次改动载入全部类型;报告 Markdown 下载的 DOM 副作用(835–843)住在"接口定义单一出口"里属越界。注意:它是locality 问题而非 depth 问题,单独拆不产生 leverage。
+Solution
+保留 api/index.ts 作为拦截器与 axios 实例的唯一住处(现有唯一出口语义不丢),各域拆为 api/intelligentEvals.ts 等;Blob 导出仪式与候选④合并到同一个下载工具。
Benefits
+导航与 diff 噪声下降;域类型就近。无行为变化、无测试变化——因此只做搭车项,不单独立项。
+Before / After
+api.ts — 909 行
+实例 + 拦截器
Top recommendation — 先做 ②
++ 候选②(智能评估可见性接缝)应当第一个做。 + 理由有三:其一,逻辑删除是一周前刚落地的新功能,其领域知识正在扩散——三处独立实现 + 路由直 import ORM 表 + 任务监控已实际泄漏已删评估,每晚一天收敛成本更高;其二,它是五个候选里爆炸半径最小的——纯收敛、零行为变化、已有 +100 行集成测试做行为锁;其三,它顺带把快照序列化与 expired 标注移出路由,巩固 v1.2 已确立的"路由只做 HTTP 翻译"纪律。 +
++ 紧随其后做 候选①(结算序列——真有两个 adapter,接缝是真的),③⑤ 作为搭车项,④ 作为前端独立战役(涉及组件改造与补测试,体量最大)。 +
+词汇:module / interface / implementation / depth / seam / adapter / leverage / locality(codebase-design);领域名词取自 CONTEXT.md。
+