feat(proxy): T-X10 采纳 cortiq 语义缓存路由签名分桶——不同路由意图不互串答案

- normalizer.canonical_hash 增加可选 route_sig 段:键 = bucket|doc_version|sig|sha256
  (旧三段格式向后兼容,旧条目随 TTL 自然淘汰)
- ProxyConfig 新增 semcache.route_sig_scope:capabilities(默认,vision/tools 需求
  签名)/ model(按模型隔离)/ none(旧行为);非法值回落 capabilities
- semcache:签名升级为条目属性并分区 L2 语义扫描(仅键分桶不够——语义层仍会
  跨签名命中);签名从缓存键第四段解析,重启重建零 schema 变更;
  晋升别名键携带签名段;route_sig=None 的旧调用零过滤完全兼容
- routes:lookup/put 共用同一 norm_hash(消除 put 侧重复哈希),签名贯穿两层
- 新增 tests/test_route_sig.py 6 项(键格式/键空间分割/scope 三态/两层隔离/
  重建存活/旧调用兼容)
This commit is contained in:
tzt
2026-09-19 09:59:15 +08:00
parent 9ce8718812
commit e5470a3715
5 changed files with 173 additions and 23 deletions
+7 -1
View File
@@ -83,13 +83,19 @@ def is_cacheable(body: dict) -> bool:
return len(non_system) <= 1
def canonical_hash(bucket: str, doc_version: int, body: dict) -> str:
def canonical_hash(bucket: str, doc_version: int, body: dict,
route_sig: str = "") -> str:
"""规则 3:缓存键 = bucket + '|' + doc_version + '|' + sha256(norm)。
route_sig 非空时插入为第三段(T-X10,采纳 cortiq 路由签名分桶:
不同路由意图——vision/tools 需求或模型——不互串答案);为空时保持
旧三段格式(既有调用与旧缓存条目兼容,旧条目随 TTL 自然淘汰)。
桶模板/资料前缀不参与哈希(由 bucket+doc_version 表达,资料更新 = 版本+1)。
"""
norm = normalize_messages(body)
digest = hashlib.sha256(norm.encode("utf-8")).hexdigest()
if route_sig:
return f"{bucket}|{doc_version}|{route_sig}|{digest}"
return f"{bucket}|{doc_version}|{digest}"