diff --git a/docs/README.md b/docs/README.md index 7c9746e..df10bba 100644 --- a/docs/README.md +++ b/docs/README.md @@ -71,6 +71,7 @@ AgentEvalTool 是一个智能体质量评估工具集平台,用于评估 AI | **归档索引** | 已验收阶段的目标、决策、证据与后续约束 | [archive/README.md](archive/README.md) | | **Campaign 架构深化** | 耐久作业、读模型、运行时和正式线交付总结 | [archive/campaign-architecture-deepening-20260811-v1.0.md](archive/campaign-architecture-deepening-20260811-v1.0.md) | | **智能评估架构深化** | scheduler 抽取、去重内化、状态机归一、残留清理、标签单一出口 | [archive/intelligent-eval-architecture-deepening-20260821-v1.2.html](archive/intelligent-eval-architecture-deepening-20260821-v1.2.html) | +| **架构审查(2026-08-24)** | 五个深化候选:可见性接缝、智能作业归一、repository 拆分、前端读取接缝、api.ts 拆域;候选已全部实施 | [archive/codebase-architecture-review-20260824.html](archive/codebase-architecture-review-20260824.html) | ### 2.7 外部参考 diff --git a/docs/archive/README.md b/docs/archive/README.md index 63beb6e..f1bb5b0 100644 --- a/docs/archive/README.md +++ b/docs/archive/README.md @@ -6,3 +6,4 @@ |---|---|---|---| | 2026-08-11 | Campaign 架构深化与正式线交付 | 已完成并部署 | [campaign-architecture-deepening-20260811-v1.0.md](campaign-architecture-deepening-20260811-v1.0.md) | | 2026-08-21 | 智能评估架构深化(scheduler 抽取 / 去重内化 / 状态机归一 / 残留清理 / 标签单一出口) | 已完成并推送 | [intelligent-eval-architecture-deepening-20260821-v1.2.html](intelligent-eval-architecture-deepening-20260821-v1.2.html) | +| 2026-08-24 | 架构审查:五个深化候选(可见性接缝 / 智能作业归一 / repository 拆分 / 前端读取接缝 / api.ts 拆域) | 审查报告,候选已全部实施 | [codebase-architecture-review-20260824.html](codebase-architecture-review-20260824.html) | diff --git a/docs/archive/codebase-architecture-review-20260824.html b/docs/archive/codebase-architecture-review-20260824.html new file mode 100644 index 0000000..114d4b1 --- /dev/null +++ b/docs/archive/codebase-architecture-review-20260824.html @@ -0,0 +1,397 @@ + + + + + +架构审查 · AgentEvalTool · 2026-08-24 + + + + + + +
+
+

/improve-codebase-architecture · 第三轮

+

架构审查总结 — AgentEvalTool

+

+ 2026-08-24 · 前两轮(campaign v1.0、智能评估 v1.2)的候选已全部落地。本轮聚焦落地后新暴露与新残留的摩擦: + 耐久作业结算序列、逻辑删除可见性、存储 monolith、前端读模型采纳面、api.ts 单文件。 +

+
+
5 深化候选
+
2 已落地前轮(v1.0 / v1.2)
+
3 处"已删 eval 仍可见"泄漏
+
1180 行 repository.py · 12 个类
+
+
+
+ +
+ + +
+

摩擦全景

+
+
+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

+
+ Strong +
+
+
+
+

Files

+
    +
  • evaluation/intelligence_jobs.py — 两个 executor(53–150、153–263)
  • +
  • storage/repository.pyAsyncJobRepository(782–863)
  • +
  • evaluation/campaign_runner.py:242-247routers/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

+
长接口:调用方须知认领顺序 / 截断 / 幂等
+
分析 executor:~100 行编排
+
对比 executor:~110 行同形编排
+
+
+

After · 单一执行器

+
小接口:execute(kind, work_fn)
+
深 implementation
认领·结算·归一·幂等
+
+
分析 adapter
+
对比 adapter
+
+
+
+
+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

+
+ Strong +
+
+
+
+

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 — 第三份实现,且路由直接 import IntelligentEvalDB
  • +
  • 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_decisionsfunc.count

+
+
+

Benefits

+

Locality:删除语义改动(如未来回收站、级联策略)只改一处。正确性:消除三处分歧风险——当前任务监控已实际泄漏已删评估。测试面:逻辑删除契约从分散的集成断言收敛为 repo 接口一组测试。这是新功能知识正在扩散的窗口期,越早收敛成本越低。

+
+
+
+

Before / After

+
+
+

Before · 三处裁决"已删即 404"

+
+
repo.get() 隐藏删除
+
decision_logs 直查 ORM
+
路由直查 ORM(import 表)
+
task_queue 完全不知情
+
+
+
+

After · 单一可见性接缝

+
require_live_eval(id)
+
repository:_visible 谓词 + get/get_including_deleted
+
+
decision_logs
+
路由
+
task_queue
+
+
+
+
+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 行)

+
+ Worth exploring +
+
+
+
+

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.pystorage/async_job_repository.py(或随候选①并入执行器模块);db.py 可暂不动(表定义集中有 create_all 的便利)。可选第二步:把三份 CAS 收敛为一个共享条件写原语——但需先过删除测试:三处的条件字段与冲突语义确有差异,只有当共享原语接口小到不泄漏这些差异时才值得。

+
+
+

Benefits

+

每域一个文件 = 改动、bug、知识同址;导航成本从"扫 1180 行"降到"开对应文件"。本身不产生 leverage,故标记为 Worth exploring——它是其他候选(①②)的天然搭车项,单独做价值有限。

+
+
+
+

Before / After

+
+
+

Before · 一文件 12 类 5 域

+
+

storage/repository.py — 1180 行

+
+
Target
+
Scenario
+
Run
+
Result
+
Campaign
+
AnalysisJob
+
ComparisonJob
+
ExplSession
+
ExplMessage
+
+
+
+
+

After · 每域一文件

+
+
catalog_repository
+
run_repository
+
campaign_repository
+
exploration_repository
+
+
+
+
+
+
+ + +
+
+
+

④ 前端读取模块:src/read 从单消费者扩展为统一资源接缝

+

src/read/* · components/intelligent_eval/* · hooks/useCampaignReport.ts · hooks/useResource.ts

+
+ Strong +
+
+
+
+

Files

+
    +
  • read/intelligentEval.ts(118 行,有测试)· read/useIntelligentEvalRead.ts(83 行,有测试)
  • +
  • components/intelligent_eval/ — ExecutionProcess(480 行)、DecisionProcess、ConfigSnapshots、TaskQueueMonitor
  • +
  • hooks/useCampaignReport.ts:16-21hooks/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 · 深模块闲置,样板四散

+
+
src/read
深·有测试
1 个消费者
+
+
ExecutionProcess
手写 5 个 fetch 槽
+
DecisionProcess
手写样板
+
ConfigSnapshots
手写样板
+
TaskQueueMonitor
手写样板
+
+
+
+
+

After · 资源接缝统一供给

+
useReadResource(slot, adapter) — 竞态/轮询/静默刷新内置
+
+
列表页
+
5 个 drawer 标签
+
活动报告
+
+
+
+
+
+
+ + +
+
+
+

⑤ api.ts 单文件按域拆分

+

frontend/web/src/api.ts(909 行 · 12 域)

+
+ Speculative +
+
+
+
+

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 行

+
+ authtargetsscenariosmodelConfigsrunsreportsstatsexplorationcampaignsintelligentEvals +DOM 副作用files +
+
+
+
+
api/index.ts
实例 + 拦截器
+
api/<domain>.ts ×11
+
+
+
+
+
+ + +
+

Top recommendation — 先做 ②

+

+ 候选②(智能评估可见性接缝)应当第一个做。 + 理由有三:其一,逻辑删除是一周前刚落地的新功能,其领域知识正在扩散——三处独立实现 + 路由直 import ORM 表 + 任务监控已实际泄漏已删评估,每晚一天收敛成本更高;其二,它是五个候选里爆炸半径最小的——纯收敛、零行为变化、已有 +100 行集成测试做行为锁;其三,它顺带把快照序列化与 expired 标注移出路由,巩固 v1.2 已确立的"路由只做 HTTP 翻译"纪律。 +

+

+ 紧随其后做 候选①(结算序列——真有两个 adapter,接缝是真的),③⑤ 作为搭车项,④ 作为前端独立战役(涉及组件改造与补测试,体量最大)。 +

+

词汇:module / interface / implementation / depth / seam / adapter / leverage / locality(codebase-design);领域名词取自 CONTEXT.md。

+
+ +
+ +