Files
projectAIpopular/router_system/review.py
T
tzt ce3ba44de3 fix(v3): T30 安全加固(Mimosa 深度扫描驱动)
- 路径参数 ID 白名单(runs/agent/sessions),杀灭 Windows 反斜杠穿越(..%5C 直读 .env)
- artifacts 端点关押 + pipeline 工件名消毒(模型输出名剥路径成分)
- /llama/download dest 关押 models/ 内 + URL 协议白名单(先于 HF 别名转换)
- GET /config architect.api_key 打码(api_key_set + 前 6 位),PUT 空串=保留
- TrustedHostMiddleware 信任围栏(GATEWAY_TRUSTED_HOSTS 可覆盖)+ __main__ 默认 127.0.0.1
- review 抽样改 CSPRNG
- 新增 tests/test_security_hardening.py(13 项,全部离线)
2026-09-02 00:01:33 +08:00

168 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""人工检验队列(ReviewQueue)—— 第三个协作者(纯标准库 sqlite3)。
复用同一"交流文本"协议:异步队列,不阻塞响应路径。系统交付后按抽样率或
safety 标签强制规则入队,由人工审核给出 verdictapprove/edit/reject)并可
回写修正数据(correction),用于后续质量分析(论文 E 实验的人工地基)。
对齐《实现方案_v2》5.1 T8
- SQLite 存储(data/review.sqlite3),零第三方依赖(sqlite3 为标准库)。
- enqueue / get / list / submit(verdict, correction)。
- 抽样规则:brief.tags 命中 force_tags(如 safety)强制入队,否则按 sample_rate 随机抽样。
"""
from __future__ import annotations
import random
import sqlite3
import threading
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional
def _now_iso() -> str:
return datetime.now(timezone.utc).isoformat(timespec="seconds")
# 抽样用系统级随机源(CSPRNG
_SYSTEM_RANDOM = random.SystemRandom()
_SCHEMA = """
CREATE TABLE IF NOT EXISTS reviews (
id INTEGER PRIMARY KEY AUTOINCREMENT,
request_id TEXT NOT NULL,
query TEXT NOT NULL,
response TEXT NOT NULL,
tags TEXT NOT NULL DEFAULT '[]',
status TEXT NOT NULL DEFAULT 'pending', -- pending | reviewed
verdict TEXT, -- approve | edit | reject
correction TEXT,
reviewer TEXT,
reason TEXT,
workspace_path TEXT,
created_at TEXT NOT NULL,
reviewed_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_reviews_status ON reviews(status);
"""
class ReviewQueue:
"""人工检验队列(sqlite 后端)。方法为同步;调用方按需自行放入线程池。"""
def __init__(self, db_path: str = "data/review.sqlite3"):
self.db_path = Path(db_path)
self.db_path.parent.mkdir(parents=True, exist_ok=True)
self._lock = threading.Lock()
self._init_db()
def _connect(self) -> sqlite3.Connection:
conn = sqlite3.connect(str(self.db_path))
conn.row_factory = sqlite3.Row
return conn
def _init_db(self) -> None:
with self._lock, self._connect() as conn:
conn.executescript(_SCHEMA)
# ---------------------------------------------------------------
# 抽样策略
# ---------------------------------------------------------------
@staticmethod
def should_enqueue(tags: List[str], sample_rate: float = 0.10,
force_tags: Optional[List[str]] = None,
rng: Optional[random.Random] = None) -> bool:
"""是否应入队:tags 命中 force_tags 强制;否则按 sample_rate 抽样。
缺省用系统级 CSPRNG(不可预测,不可被时间种子影响抽样公平性)。
"""
force = force_tags or []
if any(t in force for t in tags):
return True
rng = rng or _SYSTEM_RANDOM
return rng.random() < sample_rate
# ---------------------------------------------------------------
# 入队
# ---------------------------------------------------------------
def enqueue(self, request_id: str, query: str, response: str,
tags: Optional[List[str]] = None, reason: str = "sample",
workspace_path: Optional[str] = None) -> int:
"""入队一条待审记录,返回 review id。"""
tags_json = _json_dumps(tags or [])
with self._lock, self._connect() as conn:
cur = conn.execute(
"INSERT INTO reviews (request_id, query, response, tags, reason, workspace_path, created_at)"
" VALUES (?,?,?,?,?,?,?)",
(request_id, query, response, tags_json, reason, workspace_path, _now_iso()),
)
return int(cur.lastrowid)
# ---------------------------------------------------------------
# 查询
# ---------------------------------------------------------------
def get(self, review_id: int) -> Optional[Dict[str, Any]]:
with self._lock, self._connect() as conn:
row = conn.execute("SELECT * FROM reviews WHERE id=?", (review_id,)).fetchone()
return _row_to_dict(row) if row else None
def list(self, status: Optional[str] = None, limit: int = 50) -> List[Dict[str, Any]]:
with self._lock, self._connect() as conn:
if status:
rows = conn.execute(
"SELECT * FROM reviews WHERE status=? ORDER BY id DESC LIMIT ?",
(status, limit)).fetchall()
else:
rows = conn.execute(
"SELECT * FROM reviews ORDER BY id DESC LIMIT ?", (limit,)).fetchall()
return [_row_to_dict(r) for r in rows]
def count(self, status: Optional[str] = None) -> int:
with self._lock, self._connect() as conn:
if status:
row = conn.execute("SELECT COUNT(*) AS c FROM reviews WHERE status=?",
(status,)).fetchone()
else:
row = conn.execute("SELECT COUNT(*) AS c FROM reviews").fetchone()
return int(row["c"]) if row else 0
# ---------------------------------------------------------------
# 审核提交
# ---------------------------------------------------------------
def submit(self, review_id: int, verdict: str,
correction: Optional[str] = None,
reviewer: Optional[str] = None) -> bool:
"""提交审核结论。verdict: approve | edit | reject。返回是否更新成功。"""
if verdict not in ("approve", "edit", "reject"):
raise ValueError(f"非法 verdict: {verdict}(支持 approve|edit|reject")
with self._lock, self._connect() as conn:
cur = conn.execute(
"UPDATE reviews SET status='reviewed', verdict=?, correction=?, reviewed_at=?, reviewer=?"
" WHERE id=? AND status='pending'",
(verdict, correction, _now_iso(), reviewer, review_id),
)
return cur.rowcount > 0
def _row_to_dict(row: Optional[sqlite3.Row]) -> Optional[Dict[str, Any]]:
if row is None:
return None
d = dict(row)
try:
import json
d["tags"] = json.loads(d.get("tags") or "[]")
except Exception:
d["tags"] = []
return d
def _json_dumps(obj) -> str:
import json
return json.dumps(obj, ensure_ascii=False)
def build_review_queue(cfg: Dict[str, Any]) -> ReviewQueue:
"""cfg 为 config.review 段。"""
return ReviewQueue(db_path=cfg.get("queue_db", "data/review.sqlite3"))