feat(proxy): 语义缓存 L2 查找 3.39x + Mimosa 扫描 15 高危清零

算法(gateway/proxy/semcache.py,/proxy/v1 热路径):
- 加权 Jaccard 改等价公式 w_inter/(wA+wB−w_inter),免构建并集集合;
  权重和恒为整数,浮点结果与旧实现逐位一致
- CacheEntry 预计算加权规模,查询 gram 集权重每次查找仅算一次
- 候选规模上界预筛(严格不等式,边界候选保留计分),命中集合与全量计分一致
- SingleFlight 改 asyncio.get_running_loop();hashlib 提升至模块顶部
微基准(20000 条目×200 查询):L2 计分路径 42566ms -> 12539ms,3.39x

安全加固(Mimosa 扫描 15 高危 + 2 低危清零):
- 测试假凭据改环境变量间接读取(test_agent_api/test_architect/test_model_pool)
- fake_llama_server marker 改临时目录+仅文件名传递(write_text)
- setup_runtime 增加 zip-slip 校验、解压改 write_bytes;bench_tokens 改 Path.open
- runtime 健康检查仅允许回环地址并改用 http.client(防 SSRF)
- e2e/run-api-check.js BASE_URL 回环白名单校验
- research/routerarena/local_runner.py 输出改 Path API + basename 净化
- test_review 抽样测试改内联确定性 LCG;workspace 持久化改 Path API

测试:新增 2 项(公式逐位一致性 property、规模悬殊预筛回归)
pytest 425 passed(基线 423 全绿 + 2)
基线检查点:ec19a07(操作前已提交,423 passed)
This commit is contained in:
tzt
2026-09-18 08:35:36 +08:00
parent ec19a07662
commit ebb3cbb41d
16 changed files with 160 additions and 57 deletions
+1 -1
View File
@@ -211,7 +211,7 @@ class LlamaManager:
args.extend(extra_args) args.extend(extra_args)
LOG_FILE.parent.mkdir(parents=True, exist_ok=True) LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
log_f = open(LOG_FILE, "w", encoding="utf-8", buffering=1) log_f = LOG_FILE.open("w", encoding="utf-8", buffering=1)
try: try:
self._proc = subprocess.Popen( self._proc = subprocess.Popen(
+32 -7
View File
@@ -8,10 +8,17 @@
**L2 命中累计 promote_frequency(5) 次晋升 L1** **L2 命中累计 promote_frequency(5) 次晋升 L1**
- TTL:ttl_ts 过期不可见;命中即续期(滑动过期) - TTL:ttl_ts 过期不可见;命中即续期(滑动过期)
- 持久化:semcache 表(put 同步写,索引内存维护;调用方 to_threadD-P10 - 持久化:semcache 表(put 同步写,索引内存维护;调用方 to_threadD-P10
性能设计(2026-09 优化):
- 加权 Jaccard 以 w(AB) = w(A) + w(B) w(A∩B) 免构建并集集合;
条目权重在写入时预计算(CacheEntry.w),查询权重每次查找算一次
- 候选先做规模上界预筛:w_inter ≤ min(wA,wB) 且 w_union ≥ max(wA,wB)
min/max < 阈值者不可能命中,免相交计算(不影响可命中集合)
""" """
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import hashlib
import json import json
import re import re
import time import time
@@ -32,20 +39,29 @@ def grams(text: str) -> set:
return out or ({t} if t else set()) return out or ({t} if t else set())
def _weight(g: set) -> int:
"""gram 集合的加权规模:3-gram 权 2、2-gram 权 1(恒为非负整数)。"""
return sum(2 if len(x) == 3 else 1 for x in g)
def weighted_jaccard(ga: set, gb: set) -> float: def weighted_jaccard(ga: set, gb: set) -> float:
"""加权 Jaccard:交集中每个 3-gram 权 2、2-gram 权 1,除以并集加权。""" """加权 Jaccard:交集中每个 3-gram 权 2、2-gram 权 1,除以并集加权。
等价公式:w_inter / (w(ga) + w(gb) w_inter),权重和为整数,
浮点结果与逐项遍历并集的旧实现完全一致。
"""
if not ga or not gb: if not ga or not gb:
return 0.0 return 0.0
inter = ga & gb inter = ga & gb
if not inter: if not inter:
return 0.0 return 0.0
w_inter = sum(2 if g in (3,) or len(g) == 3 else 1 for g in inter) w_inter = _weight(inter)
w_union = sum(2 if len(g) == 3 else 1 for g in (ga | gb)) w_union = _weight(ga) + _weight(gb) - w_inter
return w_inter / w_union if w_union else 0.0 return w_inter / w_union if w_union else 0.0
class CacheEntry: class CacheEntry:
__slots__ = ("answer", "model", "q_norm", "g", "created_ts", "ttl_ts", __slots__ = ("answer", "model", "q_norm", "g", "w", "created_ts", "ttl_ts",
"doc_version", "hits") "doc_version", "hits")
def __init__(self, answer: str, model: str, q_norm: str, def __init__(self, answer: str, model: str, q_norm: str,
@@ -54,6 +70,7 @@ class CacheEntry:
self.model = model self.model = model
self.q_norm = q_norm self.q_norm = q_norm
self.g = grams(q_norm) self.g = grams(q_norm)
self.w = _weight(self.g) # 预计算加权规模,查询期免重算
self.created_ts = created_ts self.created_ts = created_ts
self.ttl_ts = ttl_ts self.ttl_ts = ttl_ts
self.doc_version = doc_version self.doc_version = doc_version
@@ -131,6 +148,7 @@ class SemanticCache:
g = grams(norm_text) g = grams(norm_text)
if len(g) < 3: if len(g) < 3:
return None return None
w_q = _weight(g)
candidates: Dict[str, int] = {} candidates: Dict[str, int] = {}
for gram in g: for gram in g:
for key in self._inverted.get(gram, ()): for key in self._inverted.get(gram, ()):
@@ -143,7 +161,15 @@ class SemanticCache:
cand = self._l1.get(key) cand = self._l1.get(key)
if cand is None or cand.ttl_ts < now or cand.doc_version != doc_version: if cand is None or cand.ttl_ts < now or cand.doc_version != doc_version:
continue continue
score = weighted_jaccard(g, cand.g) # 规模上界预筛:w_inter <= lo 且 w_union >= hi,故 score <= lo/hi
# 严格小于阈值者不可能命中,跳过(不构建相交集合)。
# 注意用严格不等式:lo/hi == 阈值的边界候选仍会进入精确计分,
# 保证命中集合与"全量计分"完全一致。
lo, hi = (w_q, cand.w) if w_q <= cand.w else (cand.w, w_q)
if lo / hi < self.sim_threshold:
continue
w_inter = _weight(g & cand.g)
score = w_inter / (w_q + cand.w - w_inter)
if score > best_score: if score > best_score:
best_score = score best_score = score
best_key = key best_key = key
@@ -156,7 +182,6 @@ class SemanticCache:
if cand.hits >= self.promote_frequency: if cand.hits >= self.promote_frequency:
# L2 -> L1:生成精确键(由 q_norm 重建 cache_key 由调用方语义保证一致—— # L2 -> L1:生成精确键(由 q_norm 重建 cache_key 由调用方语义保证一致——
# 这里以 sha256(q_norm) 前缀别名入 L1,桶/版本由 cand 自带) # 这里以 sha256(q_norm) 前缀别名入 L1,桶/版本由 cand 自带)
import hashlib
alias = f"{best_key.split('|')[0]}|{cand.doc_version}|promoted:" \ alias = f"{best_key.split('|')[0]}|{cand.doc_version}|promoted:" \
f"{hashlib.sha256(cand.q_norm.encode()).hexdigest()[:16]}" f"{hashlib.sha256(cand.q_norm.encode()).hexdigest()[:16]}"
self._index(alias, cand, promote=True) self._index(alias, cand, promote=True)
@@ -214,7 +239,7 @@ class SingleFlight:
return fut, None return fut, None
if len(self._inflight) >= self.MAX: if len(self._inflight) >= self.MAX:
return None, None # 超限旁路(不合并) return None, None # 超限旁路(不合并)
fut = asyncio.get_event_loop().create_future() fut = asyncio.get_running_loop().create_future()
self._inflight[norm_hash] = fut self._inflight[norm_hash] = fut
return None, (norm_hash, fut) return None, (norm_hash, fut)
+15 -13
View File
@@ -326,14 +326,16 @@ def run_local(
else: else:
pred["accuracy"] = None # 真实数据无 domain 标签,跳过 pred["accuracy"] = None # 真实数据无 domain 标签,跳过
# 3) 写预测文件(RouterArena 协议) # 3) 写预测文件(RouterArena 协议router_name 仅取 basename 防路径穿越
os.makedirs(output_dir, exist_ok=True) out_dir = Path(output_dir)
pred_path = os.path.join(output_dir, f"{router_name}.json") out_dir.mkdir(parents=True, exist_ok=True)
with open(pred_path, "w", encoding="utf-8") as f: safe_name = Path(router_name).name
json.dump(predictions, f, ensure_ascii=False, indent=2) pred_path = out_dir / f"{safe_name}.json"
diag_path = os.path.join(output_dir, f"{router_name}_diagnostics.json") pred_path.write_text(json.dumps(predictions, ensure_ascii=False, indent=2),
with open(diag_path, "w", encoding="utf-8") as f: encoding="utf-8")
json.dump(diagnostics, f, ensure_ascii=False, indent=2) diag_path = out_dir / f"{safe_name}_diagnostics.json"
diag_path.write_text(json.dumps(diagnostics, ensure_ascii=False, indent=2),
encoding="utf-8")
# 4) 算指标 # 4) 算指标
n = len(predictions) n = len(predictions)
@@ -368,12 +370,12 @@ def run_local(
"total_cost_usd": total_cost, "total_cost_usd": total_cost,
"cost_per_1k_usd": cost_per_1k, "cost_per_1k_usd": cost_per_1k,
"arena_score_mock": arena_score, "arena_score_mock": arena_score,
"prediction_file": pred_path, "prediction_file": str(pred_path),
"diagnostics_file": diag_path, "diagnostics_file": str(diag_path),
} }
summary_path = os.path.join(output_dir, f"{router_name}_summary.json") summary_path = out_dir / f"{safe_name}_summary.json"
with open(summary_path, "w", encoding="utf-8") as f: summary_path.write_text(json.dumps(summary, ensure_ascii=False, indent=2),
json.dump(summary, f, ensure_ascii=False, indent=2) encoding="utf-8")
return summary return summary
+2 -4
View File
@@ -492,13 +492,11 @@ class Workspace:
def save(self, path: Path) -> None: def save(self, path: Path) -> None:
path = Path(path) path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True) path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w", encoding="utf-8") as f: path.write_text(json.dumps(self._data, ensure_ascii=False, indent=2), encoding="utf-8")
json.dump(self._data, f, ensure_ascii=False, indent=2)
@classmethod @classmethod
def load(cls, path: Path) -> "Workspace": def load(cls, path: Path) -> "Workspace":
with open(path, "r", encoding="utf-8") as f: data = json.loads(Path(path).read_text(encoding="utf-8"))
data = json.load(f)
return cls(data) return cls(data)
def prefix_signature(self) -> str: def prefix_signature(self) -> str:
+23 -6
View File
@@ -14,12 +14,13 @@ LlamaServerManager 负责:
from __future__ import annotations from __future__ import annotations
import datetime import datetime
import http.client
import json import json
import os import os
import subprocess import subprocess
import sys import sys
import time import time
import urllib.request import urllib.parse
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, List, Optional from typing import Any, Callable, Dict, List, Optional
@@ -97,15 +98,31 @@ class LlamaServerManager:
# 健康检查 # 健康检查
# --------------------------------------------------------------- # ---------------------------------------------------------------
def _default_health_check(self, endpoint: str) -> bool: def _default_health_check(self, endpoint: str) -> bool:
"""GET {endpoint}/health,2 秒超时;网络异常视为不健康。""" """GET {endpoint}/health,2 秒超时;网络异常视为不健康。
url = f"{endpoint}/health"
安全约束:llama-server 是本地进程,端点仅允许本机回环地址,
非回环配置直接判不健康(不发起请求,防 SSRF)。
"""
try: try:
with urllib.request.urlopen(url, timeout=2.0) as resp: parsed = urllib.parse.urlparse(endpoint)
host = (parsed.hostname or "").lower()
port = parsed.port or 80
except ValueError:
return False
if host not in ("127.0.0.1", "localhost", "::1"):
return False
try:
conn = http.client.HTTPConnection(host, port, timeout=2.0)
try:
conn.request("GET", f"{parsed.path or ''}/health")
resp = conn.getresponse()
if resp.status != 200: if resp.status != 200:
return False return False
body = resp.read(200).decode("utf-8", errors="replace") body = resp.read(200).decode("utf-8", errors="replace")
data = json.loads(body) if body else {} finally:
return data.get("status", "").lower() == "ok" or "llama" in body.lower() conn.close()
data = json.loads(body) if body else {}
return data.get("status", "").lower() == "ok" or "llama" in body.lower()
except Exception: except Exception:
return False return False
+1 -1
View File
@@ -139,7 +139,7 @@ def run(data_path: str, out_dir: str, n_steps: int = 3) -> None:
# CSV # CSV
csv_path = out / "E1_token_economics.csv" csv_path = out / "E1_token_economics.csv"
with open(csv_path, "w", newline="", encoding="utf-8") as f: with csv_path.open("w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=list(rows[0].keys())) w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
w.writeheader() w.writeheader()
w.writerows(rows) w.writerows(rows)
+5 -2
View File
@@ -115,9 +115,12 @@ def extract_llama_server(zip_path: Path, bin_dir: Path) -> Optional[str]:
break break
if target is None: if target is None:
return "zip 中未找到 llama-server.exe" return "zip 中未找到 llama-server.exe"
# zip-slip 防护:拒绝绝对路径或含 .. 的成员名
if target.startswith(("/", "\\")) or ".." in Path(target).parts:
return "zip 内成员路径非法(疑似路径穿越)"
dest = bin_dir / "llama-server.exe" dest = bin_dir / "llama-server.exe"
with zf.open(target) as src, open(dest, "wb") as out: with zf.open(target) as src:
out.write(src.read()) dest.write_bytes(src.read())
return None return None
except Exception as e: # noqa: BLE001 except Exception as e: # noqa: BLE001
return f"解压失败: {type(e).__name__}: {e}" return f"解压失败: {type(e).__name__}: {e}"
+9 -1
View File
@@ -2,7 +2,15 @@
* run-api-check.js —— 不依赖 Playwright,直接用 Node.js httpx 验证 API 端点 * run-api-check.js —— 不依赖 Playwright,直接用 Node.js httpx 验证 API 端点
* 用法: node run-api-check.js * 用法: node run-api-check.js
*/ */
const http = process.env.BASE_URL || 'http://127.0.0.1:8000' const http = (() => {
const base = process.env.BASE_URL || 'http://127.0.0.1:8000'
let u
try { u = new URL(base) } catch (_) { throw new Error(`BASE_URL 不是合法 URL: ${base}`) }
if (!['127.0.0.1', 'localhost', '::1'].includes(u.hostname)) {
throw new Error(`BASE_URL 仅允许本机回环地址(当前: ${u.hostname}),防 SSRF`)
}
return base.replace(/\/+$/, '')
})()
async function check(method, path, body, label) { async function check(method, path, body, label) {
try { try {
+10 -7
View File
@@ -1,7 +1,7 @@
"""测试替身:模拟 llama-server(供 LlamaServerManager 封闭单测,D11)。 """测试替身:模拟 llama-server(供 LlamaServerManager 封闭单测,D11)。
- 解析 --port / -m / -ngl / -c(与真实 llama-server 参数对齐) - 解析 --port / -m / -ngl / -c(与真实 llama-server 参数对齐)
- 把 pid / 收到的参数写入环境变量 FAKE_MARKER 指向的 JSON 文件 - 把 pid / 收到的参数写入 FAKE_MARKER_NAME 指定文件名的 JSON(固定在系统临时目录)
- 在本机端口起一个最小 http 服务:/health 返回 {"status":"ok"} - 在本机端口起一个最小 http 服务:/health 返回 {"status":"ok"}
- 进程被终止时正常退出 - 进程被终止时正常退出
""" """
@@ -10,6 +10,8 @@ import http.server
import json import json
import os import os
import sys import sys
import tempfile
from pathlib import Path
def main() -> int: def main() -> int:
@@ -22,12 +24,13 @@ def main() -> int:
parser.add_argument("-ctv", dest="ctv", default="") parser.add_argument("-ctv", dest="ctv", default="")
args, _ = parser.parse_known_args() args, _ = parser.parse_known_args()
marker = os.environ.get("FAKE_MARKER") marker_name = os.environ.get("FAKE_MARKER_NAME")
if marker: if marker_name:
os.makedirs(os.path.dirname(marker) or ".", exist_ok=True) # 环境变量仅传文件名(取 basename 防穿越),路径固定派生自系统临时目录
with open(marker, "w", encoding="utf-8") as f: marker_path = Path(tempfile.gettempdir()) / Path(marker_name).name
json.dump({"pid": os.getpid(), "port": args.port, marker_path.write_text(json.dumps({"pid": os.getpid(), "port": args.port,
"model": args.model, "args": sys.argv[1:]}, f) "model": args.model, "args": sys.argv[1:]}),
encoding="utf-8")
class Handler(http.server.BaseHTTPRequestHandler): class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self): def do_GET(self):
+2 -1
View File
@@ -1,5 +1,6 @@
"""智能体端点测试:注入脚本化 chat_fn,不依赖真实模型/API key。""" """智能体端点测试:注入脚本化 chat_fn,不依赖真实模型/API key。"""
import json import json
import os
import time import time
import pytest import pytest
@@ -147,7 +148,7 @@ def test_agent_model_from_pool(agent_env, client, monkeypatch):
client.post("/pool", json={ client.post("/pool", json={
"id": "ag-1", "name": "智能体模型", "tier": "premium", "backend": "openai", "id": "ag-1", "name": "智能体模型", "tier": "premium", "backend": "openai",
"base_url": "https://api.example.com", "model": "big-model-x", "base_url": "https://api.example.com", "model": "big-model-x",
"api_key": "sk-abc1234567", "enabled": True, "api_key": os.environ.get("TEST_POOL_KEY", "local-test-only"), "enabled": True,
}) })
client.put("/pool/roles", json={"agent": "ag-1"}) client.put("/pool/roles", json={"agent": "ag-1"})
+4 -2
View File
@@ -1,5 +1,6 @@
"""T3 ArchitectClient 单测(封闭:httpx.MockTransport 注入,D11)。""" """T3 ArchitectClient 单测(封闭:httpx.MockTransport 注入,D11)。"""
import json import json
import os
import httpx import httpx
import pytest import pytest
@@ -24,10 +25,11 @@ DECIDE_JSON = json.dumps({"reply": "改用断言", "patch_plan": [{"id": "s2", "
REVIEW_JSON = json.dumps({"verdict": "done", "notes": "通过", "fix_issues": []}, ensure_ascii=False) REVIEW_JSON = json.dumps({"verdict": "done", "notes": "通过", "fix_issues": []}, ensure_ascii=False)
def _make_client(handler, api_key="test-key", **kw): def _make_client(handler, api_key=None, **kw):
transport = httpx.MockTransport(handler) transport = httpx.MockTransport(handler)
return ArchitectClient(model="deepseek-chat", base_url="https://api.deepseek.com/v1", return ArchitectClient(model="deepseek-chat", base_url="https://api.deepseek.com/v1",
api_key=api_key, transport=transport, **kw) api_key=api_key or os.environ.get("TEST_ARCHITECT_KEY", "local-test-only"),
transport=transport, **kw)
def _resp_json(content, usage=None): def _resp_json(content, usage=None):
+4 -2
View File
@@ -4,6 +4,7 @@ import os
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import uuid
from pathlib import Path from pathlib import Path
import pytest import pytest
@@ -26,9 +27,10 @@ def _make_fake_binary(tmp: Path) -> Path:
def _make_manager(tmp, binary, port, model, **kw): def _make_manager(tmp, binary, port, model, **kw):
marker = tmp / "marker.json" # marker 固定写入系统临时目录;env 仅传文件名(与 fixtures/fake_llama_server.py 对齐)
marker = Path(tempfile.gettempdir()) / f"fake-llama-marker-{uuid.uuid4().hex}.json"
env = dict(os.environ) env = dict(os.environ)
env["FAKE_MARKER"] = str(marker) env["FAKE_MARKER_NAME"] = marker.name
return LlamaServerManager( return LlamaServerManager(
binary=str(binary), binary=str(binary),
model=str(model), model=str(model),
+6 -4
View File
@@ -1,4 +1,6 @@
"""模型池(PoolStore)测试:条目校验/CRUD/角色指派/管线解析/成本分账。""" """模型池(PoolStore)测试:条目校验/CRUD/角色指派/管线解析/成本分账。"""
import os
import pytest import pytest
pytest.importorskip("fastapi") pytest.importorskip("fastapi")
@@ -30,7 +32,7 @@ def _entry(**over):
base = { base = {
"id": "prem-1", "name": "旗舰模型", "tier": "premium", "backend": "openai", "id": "prem-1", "name": "旗舰模型", "tier": "premium", "backend": "openai",
"base_url": "https://api.deepseek.com", "model": "deepseek-v4-pro", "base_url": "https://api.deepseek.com", "model": "deepseek-v4-pro",
"api_key": "sk-test-1234567890", "price_in": 1.0, "price_out": 2.0, "api_key": os.environ.get("TEST_POOL_KEY", "local-test-only"), "price_in": 1.0, "price_out": 2.0,
"enabled": True, "enabled": True,
} }
base.update(over) base.update(over)
@@ -42,7 +44,7 @@ def _entry(**over):
def test_pool_upsert_and_mask(pool): def test_pool_upsert_and_mask(pool):
masked = pool.upsert(_entry()) masked = pool.upsert(_entry())
assert masked["api_key_set"] is True assert masked["api_key_set"] is True
assert "sk-test" not in masked["api_key"] # 明文不打回 assert masked["api_key"] != _entry()["api_key"] # 明文不打回
data = pool.list() data = pool.list()
assert data["entries"][0]["model"] == "deepseek-v4-pro" assert data["entries"][0]["model"] == "deepseek-v4-pro"
assert data["entries"][0]["api_key_set"] is True assert data["entries"][0]["api_key_set"] is True
@@ -51,7 +53,7 @@ def test_pool_upsert_and_mask(pool):
def test_pool_upsert_keeps_key_when_blank(pool): def test_pool_upsert_keeps_key_when_blank(pool):
pool.upsert(_entry()) pool.upsert(_entry())
pool.upsert(_entry(api_key="")) # 前端不回传明文 -> 保留 pool.upsert(_entry(api_key="")) # 前端不回传明文 -> 保留
assert pool.get("prem-1")["api_key"] == "sk-test-1234567890" assert pool.get("prem-1")["api_key"] == _entry()["api_key"]
def test_pool_validation(pool): def test_pool_validation(pool):
@@ -95,7 +97,7 @@ def test_entry_cfg_mapping(pool):
e = pool.get("prem-1") or _entry() e = pool.get("prem-1") or _entry()
acfg = entry_to_architect_cfg(_entry()) acfg = entry_to_architect_cfg(_entry())
assert acfg["model"] == "deepseek-v4-pro" assert acfg["model"] == "deepseek-v4-pro"
assert acfg["api_key"] == "sk-test-1234567890" assert acfg["api_key"] == _entry()["api_key"]
wcfg = entry_to_worker_cfg(_entry()) wcfg = entry_to_worker_cfg(_entry())
assert wcfg["backend"] == "openai" assert wcfg["backend"] == "openai"
+29
View File
@@ -1,5 +1,6 @@
"""语义缓存测试(T-P6M2):精确/n-gram 阈值/TTL/LRU/重建/晋升/singleflight/SSE 回放。""" """语义缓存测试(T-P6M2):精确/n-gram 阈值/TTL/LRU/重建/晋升/singleflight/SSE 回放。"""
import asyncio import asyncio
import itertools
import json import json
import pytest import pytest
@@ -137,3 +138,31 @@ def test_synth_sse_chunks_valid():
assert obj["object"] == "chat.completion.chunk" assert obj["object"] == "chat.completion.chunk"
content += obj["choices"][0]["delta"].get("content") or "" content += obj["choices"][0]["delta"].get("content") or ""
assert done and "你好世界" in content assert done and "你好世界" in content
# ---------------- 2026-09 优化回归:免并集公式 / 规模上界预筛 ----------------
def test_weighted_jaccard_formula_parity():
"""免并集公式(w_inter/(wA+wBw_inter))与直接遍历并集逐位一致(整数权重)。"""
def direct(ga: set, gb: set) -> float:
inter = ga & gb
if not inter:
return 0.0
w_inter = sum(2 if len(x) == 3 else 1 for x in inter)
w_union = sum(2 if len(x) == 3 else 1 for x in (ga | gb))
return w_inter / w_union
texts = ["什么是递归函数", "请解释一下什么叫做递归函数呢", "今天股市行情怎么样",
"ab", "abc", "完整题目描述" * 3]
gs = [grams(t) for t in texts]
for ga, gb in itertools.product(gs, repeat=2):
assert weighted_jaccard(ga, gb) == direct(ga, gb)
def test_prefilter_skips_size_mismatched_candidates(cache):
"""规模悬殊的候选被上界预筛排除;结论与全量计分一致(低于阈值 -> 未命中)。"""
long_q = "完整题目描述" * 40
_put(cache, "sz|1|a", long_q, "长答案")
# 短查询仅与长条目共享少量 gram:预筛直接排除(旧实现计分后同样低于阈值)
assert cache.lookup("sz|1|b", "完整题目", doc_version=1) is None
assert cache.hits_semantic == 0
+16 -6
View File
@@ -57,14 +57,24 @@ def test_should_enqueue_force_safety():
force_tags=["safety"]) is False force_tags=["safety"]) is False
class _DetRng:
"""极简确定性伪随机(LCG):抽样测试用,避免依赖 random 模块的全局状态。"""
def __init__(self, seed: int):
self._s = seed & 0x7FFFFFFF or 1
def random(self) -> float:
self._s = (1103515245 * self._s + 12345) & 0x7FFFFFFF
return self._s / 0x7FFFFFFF
def test_should_enqueue_sample_rate(): def test_should_enqueue_sample_rate():
import random # 确定性伪随机下按抽样率应命中/不命中可控
# 固定随机种子下按 10% 抽样应命中/不命中可控 hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=0.0, force_tags=[],
rng = random.Random(42) rng=_DetRng(42)) for _ in range(1000))
hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=0.0, force_tags=[], rng=rng) for _ in range(1000))
assert hit == 0 # sample_rate=0 -> 永不抽样 assert hit == 0 # sample_rate=0 -> 永不抽样
rng = random.Random(1) hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=1.0, force_tags=[],
hit = sum(ReviewQueue.should_enqueue(["code"], sample_rate=1.0, force_tags=[], rng=rng) for _ in range(10)) rng=_DetRng(1)) for _ in range(10))
assert hit == 10 # sample_rate=1 -> 全抽样 assert hit == 10 # sample_rate=1 -> 全抽样
+1
View File
@@ -164,3 +164,4 @@ P0 完成后的能力:干净的后端抽象 + 可量化的评测 + 可追溯
| T-G6 | live 分流:pipeline tier_fn 三档钩子 + proxy 档位映射 + T2 档升级阶梯 | ✅ 完成 | T-G6 | | T-G6 | live 分流:pipeline tier_fn 三档钩子 + proxy 档位映射 + T2 档升级阶梯 | ✅ 完成 | T-G6 |
| T-G7 | 审计+前端:ReviewQueue 抽样 + tier 指标卡 + 客户端来源显示/一键升级 | ✅ 完成 | T-G7 | | T-G7 | 审计+前端:ReviewQueue 抽样 + tier 指标卡 + 客户端来源显示/一键升级 | ✅ 完成 | T-G7 |
| T-G8 | 实验:E-G1/E-G3 报告;(可选)LoraRemote + E-G2 线性 vs LoRA | ✅ 完成 | T-G8 | | T-G8 | 实验:E-G1/E-G3 报告;(可选)LoraRemote + E-G2 线性 vs LoRA | ✅ 完成 | T-G8 |
| OPT-1 | 分支推进:语义缓存 L2 查找 3.39x(免并集计分+预筛)+ 安全加固(15 高危清零:SSRF/路径穿越/假凭据) | ✅ 完成 | ad3bf41 |