框架(数据库+运行时): 落库 101 raw 两表,建 raw 落库机制 persist_raw,raw 合同改写
- example_raw_lease(元数据) + example_raw_content(全文),访问控制+append-only, (lease_id,content_sha256) 幂等、kind 拒 candidate(候选不进 raw)、密钥列级 CHECK - persist_raw.py:租约+全文落库,写入前密钥标记过滤(实测拒 api_key),dry-run 已验证 - runtime SKILL.md raw 合同改写:落库为主、仓外 vault 降为可选备份、废时效语义、密钥双禁; 评测 harness COMPLETED 判据改造记为重活后续
This commit is contained in:
parent
777a0f3cd1
commit
98355893ab
@ -14,8 +14,25 @@ Claude CLI 受控子进程调用(fresh/sandbox/deadline/structured_output/回
|
||||
|---|---|
|
||||
| `claude_runtime.py` | 受控 Claude CLI 调用运行时:只接受显式冻结的执行 profile,负责 fresh process、sandbox、最小环境、硬 deadline、structured_output 唯一业务出口和联合执行回执;任何不完整证据失败关闭,错误对象永不携带 stderr 原文。服务生产与评测两侧,不只评测。 |
|
||||
| `file_cas.py` | 不可变内容寻址(CAS)revision journal:按内容哈希落盘,冲突即失败关闭,支撑 Gate receipt 与逐层回执的不可篡改。 |
|
||||
| `raw_vault.py` | 租约式 raw 物料临时保险库:原始候选/物料限定在运行目录,租约到期自动清理,不把 raw 路径带出边界。 |
|
||||
| `raw_vault.py` | 租约式 raw 物料保险库:原始候选/物料先写 0700 临时 vault,正式评测结束后同盘原子迁移到显式仓外受控归档;安全回执不携带 raw 路径。**raw 落库改写后降为可选备份**(见下)。 |
|
||||
| `persist_raw.py` | raw 落库(2026-07-30 改写):完整 prompt/response/oracle/原书全文/供应商响应落 `example_raw_lease` + `example_raw_content`(访问控制 + append-only),写入前过滤密钥,看板可看全文。 |
|
||||
|
||||
## 依赖方向
|
||||
|
||||
本 skill 是底座,**不 import 任何其它 skill**。上层(detect/quality-gate/continuation/replay-eval)单向向下引用本 skill 的 scripts。
|
||||
|
||||
## raw lease 合同
|
||||
|
||||
- 会持有 raw 物料进行长时执行的调用方必须声明最低剩余租期;raw vault 在 lease journal 落盘前拒绝剩余时间不足的租约。短时离线测试可沿用默认值。
|
||||
- `retainUntil` 到期后禁止继续写入 raw;过期且尚未迁移的 lease 仍允许执行显式 `cleanup` 和 `recover`,以便收敛 open 状态。
|
||||
- 正式评测正常、质量失败或受控终止均调用 `migrate`,把整个 vault 原子迁往调用方显式提供的绝对归档根;归档根固定 0700、必须与临时 vault 同一文件系统,lease/report 只记录 `archiveId`、内容树哈希、文件数、字节数和迁移回执哈希,不记录路径。
|
||||
- 迁移终态是 `migrated`,`retentionPolicy=explicit_cleanup_required`;`recover` 不得删除 `migrating/migrated` 数据。`cleanup` 只用于另行明确授权的删除或尚未形成可迁移轮次的过期残留,不能作为正式 Gate A 的默认收口。
|
||||
- 默认真实 Claude 调用必须持有 `Popen` 进程句柄并使用独立进程组;deadline、SIGTERM/SIGINT 转换后的受控异常或其它父进程异常发生时,必须先终止并等待整个模型进程组,再进入预算、raw 和 manifest 收口,禁止留下继续运行或计费的孤立子进程。
|
||||
|
||||
## raw 落库合同(2026-07-30 改写,创始人批准)
|
||||
|
||||
- **raw 以落库为主**(`persist_raw.py`):`example_raw_lease`(元数据)+ `example_raw_content`(全文),访问控制 + append-only;看板可看全文。默认不进普通检索,看全文须显式指定。
|
||||
- **仓外 vault 降为可选备份**:`raw_vault.py` 的 lease/migrate 机制保留作可选备份,不再是合同要求。
|
||||
- **废时效语义**:`retain_until` 只留档溯源,不触发自动删除;append-only 保证证据不可变。
|
||||
- **密钥/token/凭据绝不入表**:写入前过滤(`persist_raw.py` 标记检测)+ 列级 CHECK 双禁。
|
||||
- **后续(重活,需评测测试)**:replay-eval 的 COMPLETED 判据从"migrate 成功"改为"已落库"、raw_vault 接线相应调整——影响评测 harness,须单独一步带测试做,不在本轮。
|
||||
|
||||
113
.claude/skills/runtime/scripts/persist_raw.py
Normal file
113
.claude/skills/runtime/scripts/persist_raw.py
Normal file
@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""raw 落库 —— 把完整 prompt/response/标准答案/原书全文/供应商响应落 example_raw_lease + example_raw_content。
|
||||
|
||||
落库设计 §2.7 + §4 raw 合同改写(创始人批准):放弃时效语义,改靠**访问控制 + append-only**——
|
||||
raw 进库可看全文(看板可读),仓外 vault 降级为可选备份;密钥/token/凭据写入前过滤,绝不入表。
|
||||
- lease:一次 raw 归集的租约/元数据(append-only)。
|
||||
- content:全文(append-only,(lease_id, content_sha256) 幂等去重;跨租约各留一行,授权归属不串)。
|
||||
--dry-run 试跑:插入后回滚,校验但不落库。
|
||||
"""
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 复用 db skill 锁死的 DSN(.claude/skills/db/scripts)
|
||||
DB_SCRIPTS = Path(__file__).resolve().parents[2] / "db" / "scripts"
|
||||
sys.path.insert(0, str(DB_SCRIPTS))
|
||||
from db import connect # noqa: E402
|
||||
|
||||
CREATOR = "runtime"
|
||||
KINDS = ("prompt", "response", "source_text", "oracle", "supplier")
|
||||
|
||||
# 写入前过滤:命中即拒,密钥/token/凭据绝不入 raw 表(列级 CHECK 之外的第二道)
|
||||
_SECRET_MARKERS = ("api_key", "apikey", "secret", "token", "password", "passwd",
|
||||
"authorization: bearer", "sk-", "-----begin private key-----")
|
||||
|
||||
|
||||
def _check_no_secrets(content: str) -> None:
|
||||
low = content.lower()
|
||||
for marker in _SECRET_MARKERS:
|
||||
if marker in low:
|
||||
raise ValueError(f"raw 内容疑似含密钥/凭据标记 '{marker}',拒绝入库(安全)")
|
||||
|
||||
|
||||
def _bare_sha256(text: str) -> str:
|
||||
return hashlib.sha256(text.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def write_lease(*, vault_id=None, run_id=None, source_version=None, content_hashes=None,
|
||||
purpose=None, status="open", retain_until=None, run_authorization_id=None,
|
||||
source_work_id=None, creator=CREATOR, dry_run=False):
|
||||
"""落一条 raw 租约/元数据。返回 {status, id}。"""
|
||||
with connect() as conn:
|
||||
try:
|
||||
row = conn.execute(
|
||||
"INSERT INTO example_raw_lease(vault_id, run_id, source_version, content_hashes, purpose, "
|
||||
"status, retain_until, run_authorization_id, source_work_id, creator) "
|
||||
"VALUES (%s,%s,%s,%s::jsonb,%s,%s,%s,%s,%s,%s) RETURNING id",
|
||||
(vault_id, run_id, source_version,
|
||||
json.dumps(content_hashes, ensure_ascii=False) if content_hashes is not None else None,
|
||||
purpose, status, retain_until, run_authorization_id, source_work_id, creator)).fetchone()
|
||||
if dry_run:
|
||||
conn.rollback()
|
||||
return {"status": "dry_run_ok", "id": row[0], "note": "试跑已回滚,未落库"}
|
||||
conn.commit()
|
||||
return {"status": "written", "id": row[0]}
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
|
||||
|
||||
def write_content(lease_id, kind, content, *, run_id=None, role=None, creator=CREATOR, dry_run=False):
|
||||
"""落一条 raw 全文(幂等去重)。返回 {status, id, content_sha256}。"""
|
||||
if kind not in KINDS:
|
||||
raise ValueError(f"kind 非法:{kind}(应为 {KINDS};candidate 请存候选表,不进 raw)")
|
||||
_check_no_secrets(content)
|
||||
sha = _bare_sha256(content)
|
||||
with connect() as conn:
|
||||
try:
|
||||
row = conn.execute(
|
||||
"INSERT INTO example_raw_content(lease_id, kind, run_id, role, content_sha256, content, creator) "
|
||||
"VALUES (%s,%s,%s,%s,%s,%s,%s) ON CONFLICT (lease_id, content_sha256) "
|
||||
"DO UPDATE SET creator=EXCLUDED.creator RETURNING id, content_sha256",
|
||||
(lease_id, kind, run_id, role, sha, content, creator)).fetchone()
|
||||
if dry_run:
|
||||
conn.rollback()
|
||||
return {"status": "dry_run_ok", "id": row[0], "content_sha256": row[1], "note": "试跑已回滚,未落库"}
|
||||
conn.commit()
|
||||
return {"status": "written", "id": row[0], "content_sha256": row[1]}
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
ap = argparse.ArgumentParser(description="raw 落库(租约 + 全文,访问控制 + append-only)")
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
pl = sub.add_parser("lease", help="落一条 raw 租约/元数据")
|
||||
pl.add_argument("--vault-id", default=None)
|
||||
pl.add_argument("--run-id", default=None)
|
||||
pl.add_argument("--source-version", default=None)
|
||||
pl.add_argument("--purpose", default=None)
|
||||
pl.add_argument("--run-authorization-id", default=None)
|
||||
pl.add_argument("--source-work-id", type=int, default=None)
|
||||
pl.add_argument("--dry-run", action="store_true")
|
||||
pc = sub.add_parser("content", help="落一条 raw 全文")
|
||||
pc.add_argument("lease_id", type=int)
|
||||
pc.add_argument("kind", choices=KINDS)
|
||||
pc.add_argument("content_file", help="全文内容文件路径")
|
||||
pc.add_argument("--run-id", default=None)
|
||||
pc.add_argument("--role", default=None)
|
||||
pc.add_argument("--dry-run", action="store_true")
|
||||
args = ap.parse_args()
|
||||
if args.cmd == "lease":
|
||||
out = write_lease(vault_id=args.vault_id, run_id=args.run_id, source_version=args.source_version,
|
||||
purpose=args.purpose, run_authorization_id=args.run_authorization_id,
|
||||
source_work_id=args.source_work_id, dry_run=args.dry_run)
|
||||
else:
|
||||
content = Path(args.content_file).read_text(encoding="utf-8")
|
||||
out = write_content(args.lease_id, args.kind, content, run_id=args.run_id, role=args.role,
|
||||
dry_run=args.dry_run)
|
||||
print(json.dumps(out, ensure_ascii=False))
|
||||
90
db/ddl/101-example-raw.sql
Normal file
90
db/ddl/101-example-raw.sql
Normal file
@ -0,0 +1,90 @@
|
||||
-- raw 元数据与全文两表:example_raw_lease(租约/元数据)+ example_raw_content(全文,访问控制)。
|
||||
-- 设计拍板 2026-07-30(docs/2026-07-30-落库与看板设计.md §2.7 + §4 raw 合同改写)。
|
||||
-- raw 合同已改写(创始人批准):放弃时效语义,改靠**访问控制 + append-only**——
|
||||
-- 完整 prompt/response/未接受候选/标准答案/原书全文/供应商响应进库可看全文(看板可读),
|
||||
-- 仓外 vault 降级为可选备份;retain_until 只留档、不触发自动删除;密钥/token 绝不入表。
|
||||
-- 两表都 append-only(禁 UPDATE/DELETE/TRUNCATE)——raw 是不可变证据。
|
||||
-- 全部对象 IF NOT EXISTS / OR REPLACE,重复 apply 幂等。
|
||||
|
||||
-- ══ example_raw_lease:raw 租约/元数据(append-only)══
|
||||
CREATE TABLE IF NOT EXISTS example_raw_lease (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
vault_id CHAR(32), -- vault 标识(兼容旧 raw_vault 的 vaultId)
|
||||
run_id VARCHAR(64), -- → example_run.run_id(软引用)
|
||||
source_version VARCHAR(128), -- 来源版本
|
||||
content_hashes JSONB, -- 本租约下内容哈希清单
|
||||
purpose VARCHAR(64), -- 用途(generation/evaluation/…)
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'open', -- open/migrated/closed(历史状态,不再是删期限闸门)
|
||||
retain_until TIMESTAMP, -- 批准的留存期限(留档溯源;进库后不触发自动删除)
|
||||
archive_id VARCHAR(64), -- 仓外备份标识(若仍做可选备份)
|
||||
archive_sha256 CHAR(64),
|
||||
file_count INTEGER,
|
||||
total_bytes BIGINT,
|
||||
run_authorization_id VARCHAR(128), -- 本次运行授权标识(运行时对象,不查库、不指 96)
|
||||
source_work_id BIGINT, -- raw 来源参考书 → example_reference_work.id(出处溯源)
|
||||
creator VARCHAR(64) NOT NULL DEFAULT '',
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
CONSTRAINT chk_example_raw_lease_status CHECK (status IN ('open','migrated','closed'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_example_raw_lease_run ON example_raw_lease(run_id) WHERE run_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_example_raw_lease_work ON example_raw_lease(source_work_id) WHERE source_work_id IS NOT NULL;
|
||||
|
||||
-- ══ example_raw_content:raw 全文(append-only,访问控制)══
|
||||
-- 访问控制:默认不进普通检索,看全文须显式指定;看板可看全文;密钥/token/凭据列级 + 写入前过滤双禁。
|
||||
CREATE TABLE IF NOT EXISTS example_raw_content (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
lease_id BIGINT, -- → example_raw_lease.id(软引用,可空)
|
||||
kind VARCHAR(32) NOT NULL, -- prompt/response/source_text/oracle/supplier(无 candidate——候选正文单存候选表)
|
||||
run_id VARCHAR(64), -- → example_run.run_id(软引用)
|
||||
role VARCHAR(32), -- 产生角色(writer/detector/judge/…)
|
||||
content_sha256 CHAR(64) NOT NULL, -- 内容哈希(裸 64 hex)
|
||||
content TEXT NOT NULL, -- 全文
|
||||
creator VARCHAR(64) NOT NULL DEFAULT '',
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
CONSTRAINT chk_example_raw_content_kind
|
||||
CHECK (kind IN ('prompt','response','source_text','oracle','supplier')),
|
||||
CONSTRAINT chk_example_raw_content_sha256
|
||||
CHECK (content_sha256 ~ '^[0-9a-f]{64}$')
|
||||
);
|
||||
-- 唯一键 (lease_id, content_sha256):同一租约内重试幂等去重;跨租约各留一行(授权归属不串)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_example_raw_content
|
||||
ON example_raw_content(lease_id, content_sha256);
|
||||
CREATE INDEX IF NOT EXISTS idx_example_raw_content_run ON example_raw_content(run_id) WHERE run_id IS NOT NULL;
|
||||
|
||||
-- append-only + TRUNCATE 防护(raw 是不可变证据)
|
||||
CREATE OR REPLACE FUNCTION example_reject_truncate()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION '% 是 append-only 表,禁止 TRUNCATE', TG_TABLE_NAME;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION reject_example_raw_lease_mutation()
|
||||
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'example_raw_lease 是 append-only 表,禁止 UPDATE/DELETE';
|
||||
END;
|
||||
$$;
|
||||
CREATE OR REPLACE TRIGGER trg_example_raw_lease_append_only
|
||||
BEFORE UPDATE OR DELETE ON example_raw_lease
|
||||
FOR EACH ROW EXECUTE FUNCTION reject_example_raw_lease_mutation();
|
||||
CREATE OR REPLACE TRIGGER trg_example_raw_lease_no_truncate
|
||||
BEFORE TRUNCATE ON example_raw_lease
|
||||
FOR EACH STATEMENT EXECUTE FUNCTION example_reject_truncate();
|
||||
|
||||
CREATE OR REPLACE FUNCTION reject_example_raw_content_mutation()
|
||||
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'example_raw_content 是 append-only 表,禁止 UPDATE/DELETE';
|
||||
END;
|
||||
$$;
|
||||
CREATE OR REPLACE TRIGGER trg_example_raw_content_append_only
|
||||
BEFORE UPDATE OR DELETE ON example_raw_content
|
||||
FOR EACH ROW EXECUTE FUNCTION reject_example_raw_content_mutation();
|
||||
CREATE OR REPLACE TRIGGER trg_example_raw_content_no_truncate
|
||||
BEFORE TRUNCATE ON example_raw_content
|
||||
FOR EACH STATEMENT EXECUTE FUNCTION example_reject_truncate();
|
||||
Loading…
x
Reference in New Issue
Block a user