feat(v2): 架构与算法优化——语义缓存 2.37x、拓扑排序 O(V+E)、分类器确定性决胜

算法:
- RouterCache:语义条目写入时预计算向量范数、语义查找单遍完成(消除命中后二次 O(N) 查找)、
  相似度=1.0 提前终止;微基准(3000 条目×200 查询):3986ms -> 1685ms,2.37x
- TaskGraph.topo_order:O(V²logV) 重排序/成员扫描 -> 邻接表+deque 的 O(V+E) Kahn,
  输出顺序契约不变(初始就绪层按插入序、循环依赖按插入序兜底、未知依赖忽略)
- RuleClassifier:同分决胜按领域名字典序(与规则表排列无关),次高分 O(n) 扫描

工程卫生:
- .mimosa/(扫描器工作目录)加入 .gitignore 并移出索引
- test_review 抽样测试改用内联确定性 LCG,消除 2 个低危(不安全随机数)

测试:新增 11 项(topo 契约 6 + 缓存回归 3 + 分类器 2)
pytest 230 passed(基线 219 全绿 + 11)
This commit is contained in:
tzt
2026-09-18 08:35:36 +08:00
parent e9cfb29b75
commit b2fa8c3c81
28 changed files with 319 additions and 3725 deletions
+23 -20
View File
@@ -9,6 +9,7 @@
"""
from __future__ import annotations
from collections import deque
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
@@ -47,31 +48,33 @@ class TaskGraph:
return list(self._nodes.values())
def topo_order(self) -> List[TaskNode]:
"""Kahn 拓扑排序:依赖在前。循环依赖时按插入序兜底(不崩溃)。"""
indeg: Dict[str, int] = {}
for n in self._nodes.values():
indeg[n.id] = 0
"""Kahn 拓扑排序:依赖在前;初始就绪层按插入序稳定输出。
O(V+E) 实现(邻接表 + deque);循环依赖时按插入序兜底(不崩溃)。
"""
insert_pos = {nid: i for i, nid in enumerate(self._nodes)}
indeg: Dict[str, int] = {nid: 0 for nid in self._nodes}
dependents: Dict[str, List[str]] = {nid: [] for nid in self._nodes}
for n in self._nodes.values():
for d in n.deps:
if d in indeg:
if d in indeg: # 未知依赖 id 忽略(与入度统计口径一致)
indeg[n.id] += 1
ready = [n for n in self._nodes.values() if indeg[n.id] == 0]
ready.sort(key=lambda n: list(self._nodes.keys()).index(n.id))
order: List[TaskNode] = []
dependents[d].append(n.id)
ready = deque(sorted((nid for nid, deg in indeg.items() if deg == 0),
key=insert_pos.__getitem__))
order_ids: List[str] = []
while ready:
n = ready.pop(0)
order.append(n)
for m in self._nodes.values():
if n.id in m.deps:
indeg[m.id] -= 1
if indeg[m.id] == 0 and m not in order:
ready.append(m)
if len(order) < len(self._nodes):
nid = ready.popleft()
order_ids.append(nid)
for m in dependents[nid]:
indeg[m] -= 1
if indeg[m] == 0:
ready.append(m)
if len(order_ids) < len(self._nodes):
# 循环依赖兜底:剩余节点按插入序追加
for n in self._nodes.values():
if n not in order:
order.append(n)
return order
placed = set(order_ids)
order_ids.extend(nid for nid in self._nodes if nid not in placed)
return [self._nodes[nid] for nid in order_ids]
def all_done(self) -> bool:
return all(n.status == "done" for n in self._nodes.values())