# 架构审查总结 · AgentEvalTool

2026-08-20 ～ 21 · 智能评估子系统深化 · 5 个候选全部落地 · 零行为变化

5
候选落地
4
提交推送
870
测试通过（原 863）
−412
app.py 行数（586→174）
0
行为变化

## 深化后的智能评估子系统

四个深模块各守一职，web 层退回纯 HTTP 翻译与启停接线。

    flowchart TB
      APP["web/app.py（174 行）
    lifespan 只做启停"] -->|"start() / stop()"| SCH
      ROUTER["routers（HTTP 翻译）"] -->|"approve / cancel / submit_report"| LC
      subgraph SCH["scheduler.py — 节奏（deep）"]
        direction TB
        S1["scan_once 扫描节拍"]
        S2["触发原语 docker exec"]
      end
      SCH -.调用.-> LC
      SCH -.补录.-> DL
      subgraph LC["lifecycle.py — 状态机（deep）"]
        direction TB
        L1["_TRANSITIONS 唯一真相"]
        L2["fail_eval 判失败接缝（表校验+CAS）"]
      end
      subgraph DL["decision_logs.py — 留痕（deep）"]
        direction TB
        D1["create（agent 上报·去重）"]
        D2["append（平台落账·纯追加）"]
        D3["count_decisions 计数原语"]
      end
      subgraph DOM["domain.py — 调度知识（deep）"]
        direction TB
        M1["时段解析 · 欠账 · 优先级"]
      end
      LC -.计数.-> DL
      SCH -.入队.-> TQ["task_queue.py"]
      TQ -.调度知识.-> DOM
      LC -.落账.-> DL
      classDef deep fill:#0f172a,color:#e2e8f0,stroke:#0f172a,stroke-width:3px;
      class SCH,S1,S2,LC,L1,L2,DL,D1,D2,D3,DOM,M1 deep

## ① scheduler 抽取

182b0e5 已落地

- • 痛点：web/app.py 住着约 400 行触发式执行编排，"worker 触发"一个概念散落四个模块
- • 深化：`scheduler.py` runtime 单例（`start()/stop()/scan_once()`），worker/planner 两处重复触发合并为一个触发原语
- • 红利：locality——触发式执行知识收敛一处；interface 即测试面——10 个测试改驱动 `scan_once()`，摆脱 TestClient

## ② 决策日志去重内化

182b0e5 已落地

- • 痛点：接缝泄漏——内部去重机制逼着 5 处调用方塞递增 `attempt` "骗过"它
- • 深化：双入口——`create_decision_log`（agent 上报·去重，Gitea #6 契约）与 `append_decision_log`（平台落账·纯追加）；新增 `count_decisions` 计数原语收编 5 处裸查询
- • 红利：调用方不再需要知道去重存在；"数日志条数"知识只住在 decision_logs

## ③ 状态机归一

58c2ad0 已落地

- • 痛点：`_TRANSITIONS` 表存在却被 3 处 watchdog 直写绕过，每道 ADR-0011 新闸门都是一次新绕道机会
- • 深化：公开接缝 `fail_eval`——表校验 → repo CAS 条件写 → 决策日志留痕；CAS 冲突跳过不当故障
- • 红利：判失败只有一个写入口，非法转换与并发竞争各在一处被挡；4 个新契约测试锁定

## ④ 删除 ADR-0008 过渡残留

2a0bcdd 已落地

- • 痛点：过渡期"保签名"wrapper 完成使命后成噪声——2 个零调用死函数 + 6 个 1–3 行委托
- • 深化：直接删除，调用方改用 domain；5 个行为锁测试迁到 `test_intelligent_eval_domain.py` 直接锁定 domain
- • 红利：删除测试通过——复杂度直接消失；时段/欠账/优先级知识只剩 domain 一处

## ⑤ 前端标签单一出口

d87aad5 已落地

- • 痛点：三处手写标签映射与后端枚举字符串隐式耦合——后端加新类型前端静默显示裸字符串
- • 深化：`DECISION_TYPE_META` + `decisionTypeOf()` 收进 status.ts（含审查报告外的第三处双轨 ExecutionProcess）；STAT_CHIPS 从 EVAL_STATUS 派生
- • 红利：改标签只动一处；未识别类型显式标注"未识别·xxx"

## 边界与余下的机会

- • 刻意不动：ADR-0008 S3（domain 接受 Session 的解耦）——无第二个 adapter，抽象是为假想需求付账；会话/任务状态机不立转换表——写入点已集中，一个 adapter 是假想接缝
- • 余下的浅点：`repository.py`（1180 行跨 10 域，文件级 locality 欠佳，可按域拆分）；`api.ts`（908 行单文件，可分域）；前端 intelligent_eval 组件群无测试（5 个组件零覆盖）
- • 纪律提醒：新增决策日志类型时在 `DECISION_TYPE_META` 补一行；新增平台"置 failed"必须穿过 `fail_eval`；平台落账走 `append`，agent 上报走 `create`