配置与数据库:迁移 V0056–V0060 与流程模板

- 迁移:调用结算与配置验证(V0056)、来源当前授权(V0057)、成品补充验证回执(V0058)、检索索引代次(V0059)、
  评测单元复用来源(V0060);新表按既有约定加只追加守卫与角色授权。
- 流程模板与载入口径同步(内联保护字段改为登记类型约束);旧库迁移工具链按新表结构对齐。
- 配置:提供方模板与运行配置同步角色策略版本;角色策略白名单新增 qwen3.8-flash(见收尾报告待裁决项:
  该模型精确身份与独立性尚未核验,且策略版本号未随白名单升版)。
This commit is contained in:
zizi 2026-09-18 01:15:25 +08:00
parent 0260bcd8e2
commit c154ca9085
21 changed files with 217 additions and 49 deletions

View File

@ -10,7 +10,7 @@ import tempfile
from collections import Counter
from pathlib import Path
from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 迁移错误
from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 检查目标快照, 迁移错误
from 知识记录分流 import 分流批次
文件上限 = 16 * 1024 * 1024
@ -159,6 +159,14 @@ def _目标命令(值) -> int:
if not isinstance(输入, list) or not all(isinstance(r, dict) for r in 输入):
raise 迁移错误("snapshot_invalid", "快照必须是记录数组")
快照 = [旧快照.从载荷(r) for r in 输入]
检查目标快照(快照)
当前 = None
if 值.command == "切换检查":
当前输入 = _读文件(值.当前快照)
if not isinstance(当前输入, list) or not all(isinstance(r, dict) for r in 当前输入):
raise 迁移错误("snapshot_invalid", "当前快照必须是记录数组")
当前 = [旧快照.从载荷(r) for r in 当前输入]
检查目标快照(当前, 范围名称="当前")
映射 = 映射配置(_读文件(值.映射))
配置 = 读取配置(值.配置)
if 配置.HTTP is None:
@ -170,20 +178,20 @@ def _目标命令(值) -> int:
"目标回执": 值.目标回执,
"文件回执": 值.文件回执,
}
if 值.command == "导入目标":
结果 = 导入快照(装配, 身份, 快照, 映射, **参数)
通过 = 结果["all_accounted"] and not 结果["states"].get("quarantined")
elif 值.command == "核对目标":
请求 = _读文件(值.来源回放) if 值.来源回放 else None
结果 = 核对迁移结果(装配, 身份, 快照, 映射, 来源回放=请求, **参数)
通过 = 结果["data_checks_passed"]
else:
当前输入 = _读文件(值.当前快照)
if not isinstance(当前输入, list) or not all(isinstance(r, dict) for r in 当前输入):
raise 迁移错误("snapshot_invalid", "当前快照必须是记录数组")
结果 = 检查切换条件(装配, 身份, 快照, [旧快照.从载荷(r) for r in 当前输入], 映射, **参数)
通过 = 结果["data_checks_passed"]
_保存文件(值.输出, 稳定JSON(结果) + "\n")
# 常驻数据库工厂只在生命周期内开池;目标命令借的还是同一个受控目标。
with 装配.生命周期():
if 值.command == "导入目标":
结果 = 导入快照(装配, 身份, 快照, 映射, **参数)
通过 = 结果["all_accounted"] and not 结果["states"].get("quarantined")
elif 值.command == "核对目标":
请求 = _读文件(值.来源回放) if 值.来源回放 else None
结果 = 核对迁移结果(装配, 身份, 快照, 映射, 来源回放=请求, **参数)
通过 = 结果["data_checks_passed"]
else:
assert 当前 is not None
结果 = 检查切换条件(装配, 身份, 快照, 当前, 映射, **参数)
通过 = 结果["data_checks_passed"]
_保存文件(值.输出, 稳定JSON(结果) + "\n")
print(
值.command
+ (":本范围数据检查通过" if 通过 else ":有隔离或未闭合项")

View File

@ -2,7 +2,7 @@
from __future__ import annotations
from 建立映射 import 旧快照
from 建立映射 import 旧快照, 检查目标快照
from 核对结果 import 核对迁移结果
@ -22,6 +22,8 @@ def 核对快照变化(基线: list[旧快照], 当前: list[旧快照]) -> dict
def 检查切换条件(装配, 身份, 基线: list[旧快照], 当前: list[旧快照], 映射, **目标参数) -> dict:
检查目标快照(基线, 范围名称="基线")
检查目标快照(当前, 范围名称="当前")
核对 = 核对迁移结果(装配, 身份, 基线, 映射, **目标参数)
增量 = 核对快照变化(基线, 当前)
return {

View File

@ -24,7 +24,7 @@ from muse.基础设施.数据库.连接 import 数据库工厂, 解析连接串
from muse.正式变更.接口 import 变更错误
from muse.配置 import 数据库引用
from 关联已确认 import 关联源指针, 冻结模式, 正式旧表
from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 迁移错误
from 建立映射 import 稳定JSON, 读取JSON, 旧快照, 映射配置, 检查目标快照, 迁移错误
from 映射台账 import 迁移台账
from 知识记录分流 import 分流批次
from 转换业务对象 import 准备目标请求, 提交目标请求, 核对目标产物
@ -334,6 +334,7 @@ def 导入快照(
文件回执: Path | None = None,
) -> dict:
"""重放真实S01命令补齐映射;终态台账不是业务对象存在的替代证明。"""
检查目标快照(快照)
数据库 = 数据库工厂(装配.配置.数据库, 装配.配置.运行用途)
核对隔离目标(数据库, 允许实例=允许实例, 管理引用=管理引用, 目标回执=目标回执)
if (

View File

@ -111,6 +111,27 @@ class 旧快照:
return str(uuid5(NAMESPACE_URL, "muse:legacy:" + 稳定JSON(材料)))
def 检查目标快照(快照: list[旧快照], *, 范围名称: str = "输入") -> None:
"""目标操作只接受具名非空记录范围;空数组不证明已扫描任何源表。"""
if not isinstance(快照, list) or not 快照:
raise 迁移错误("snapshot_empty", 范围名称 + "快照必须包含明确来源记录")
for 项 in 快照:
if not isinstance(项, 旧快照):
raise 迁移错误("snapshot_invalid", 范围名称 + "快照包含无来源身份的记录")
# 直接构造的dataclass也须有完整来源;不重编码整份内容或重复计算源哈希。
if not isinstance(项.source, 源身份):
raise 迁移错误("snapshot_invalid", 范围名称 + "快照缺少来源身份")
try:
原行 = 项.原行
except (ValueError, TypeError) as 错:
raise 迁移错误("snapshot_invalid", 范围名称 + "快照原行不是有效JSON") from 错
if not isinstance(原行, dict):
raise 迁移错误("snapshot_invalid", 范围名称 + "快照原行必须是对象")
for 键 in ("id", "revision"):
if 键 in 原行 and str(原行[键]) != getattr(项.source, 键):
raise 迁移错误("source_invalid", "原行与快照来源身份不一致:" + 键)
def 数据集命名空间(标识: str, **范围) -> str:
if (
not isinstance(标识, str)

View File

@ -13,7 +13,7 @@ from muse.基础设施.数据库.连接 import 数据库工厂
from muse.配置 import 数据库引用
from 关联已确认 import 冻结模式
from 导入新库 import 核对隔离目标
from 建立映射 import 稳定JSON, 旧快照, 映射配置, 迁移错误
from 建立映射 import 稳定JSON, 旧快照, 映射配置, 检查目标快照, 迁移错误
from 映射台账 import 迁移台账
from 来源回放 import 只读回放来源
from 读取旧文件 import 核对文件保全
@ -32,6 +32,7 @@ def 核对迁移结果(
文件回执: Path | None = None,
来源回放: dict | None = None,
) -> dict:
检查目标快照(快照)
库 = 数据库工厂(装配.配置.数据库, 装配.配置.运行用途)
核对隔离目标(库, 允许实例=允许实例, 管理引用=管理引用, 目标回执=目标回执)
正式 = 装配.要求知识方法().正式

View File

@ -5,11 +5,32 @@ from __future__ import annotations
from dataclasses import asdict, dataclass, field, replace
from typing import Any
from PG作品映射 import PG内容表
from 关联已确认 import 生成关联建议, 请求历史关联
from 建立映射 import 读取JSON, 旧快照, 映射配置, 迁移错误
from 转换结构 import 取一致值, 转换动态字段
from 转换质量资产 import 质量案例ID, 例证表, 案例表, 规则历史表, 规则表
历史运行表 = {"example_run", "example_run_receipt", "example_llm_call", "runs", "events"}
历史决定表 = {"reviews", "revisions", "example_user_decision", "example_candidate_cas"}
延后表 = {
"example_quality_result": "deferred_W22_23",
"example_candidate": "deferred_W25",
"example_lesson": "deferred_W26_27",
"lessons": "deferred_W26_27",
}
知识表 = {
"muse_knowledge_draft",
"muse_knowledge_entity",
"muse_knowledge_relation",
"cards",
"snapshots",
"example_knowledge_embedding",
}
质量表 = {规则表, 例证表, 案例表, 规则历史表}
已登记表 = PG内容表 | 历史运行表 | 历史决定表 | set(延后表) | 知识表 | 质量表
方法五型 = {"craft", "combat", "emotion", "scene_pattern", "trope"}
实体六型 = {"character", "location", "item", "faction", "power_system", "event"}
已知类型 = 方法五型 | 实体六型 | {"character_relation", "style", "pacing"}
@ -214,8 +235,19 @@ def 分流知识记录(
return 分流结果(源.记录键, 快照.源哈希, 归宿, 规则, 原因, 快照.导出(), **参数)
try:
if 源.system != "file" and 表 not in 已登记表:
return 结果("隔离", "unknown_table")
if 表 in {"cards", "snapshots"}:
# 这两张SQLite镜像表没有当前授权列;快照内补列不能替代独立授权证据。
if 源.system != "sqlite":
return 结果("隔离", "unknown_table")
镜像列 = {"id", "kind", "payload_json", "created_at"}
if 表 == "cards":
镜像列 |= {"title", "embedding", "work_id", "source_path", "content_hash"}
if set(行) - 镜像列:
return 结果("隔离", "unknown_fields")
质量绑定 = 映射.质量绑定(快照, 必须=False)
是质量表 = 表 in {规则表, 例证表, 案例表, 规则历史表}
是质量表 = 表 in 质量表
if 是质量表 or 质量绑定 is not None:
规则 = "Q01"
if 表 == 规则历史表:
@ -325,26 +357,13 @@ def 分流知识记录(
return 结果(
"待转换", "linked_existing_confirmation", **生成关联建议(快照, 映射, 已知源 or {})
)
if 表 in {"example_run", "example_run_receipt", "example_llm_call", "runs", "events"}:
if 表 in 历史运行表:
return 结果("历史保留", "legacy_runtime", owner="S02", state=状态)
if 表 in {"reviews", "revisions", "example_user_decision", "example_candidate_cas"}:
if 表 in 历史决定表:
return 结果("历史保留", "legacy_decision", owner="S01", state=状态)
延后 = {
"example_quality_result": "deferred_W22_23",
"example_candidate": "deferred_W25",
"example_lesson": "deferred_W26_27",
"lessons": "deferred_W26_27",
}
if 表 in 延后:
return 结果("隔离", 延后[表], state=状态)
if 表 not in {
"muse_knowledge_draft",
"muse_knowledge_entity",
"muse_knowledge_relation",
"cards",
"snapshots",
"example_knowledge_embedding",
}:
if 表 in 延后表:
return 结果("隔离", 延后表[表], state=状态)
if 表 not in 知识表:
return 结果("隔离", "unknown_table")
载荷 = _载荷(行, 表)
work_id = 行.get("work_id")

View File

@ -166,7 +166,8 @@ def 提交PG请求(装配, 身份, 冻结):
连, 身份.作者, TypeAdapter(导入请求).validate_python(p["request"])
)
# 重复位只是本次调用情况,不冒充另一个不可变导入回执。
版本 = 装配.要求资料().读取版本(连, 结果.source_id, 结果.revision)
# 参考导入零授权用途,原文须当前用途才能返回,迁移只回读回执与存储哈希。
版本 = 装配.要求资料().读取版本回执(连, 结果.source_id, 结果.revision)
return {
"source_id": 结果.source_id,
"revision": 结果.revision,
@ -182,15 +183,15 @@ def 核对PG产物(装配, 身份, 冻结, 回执):
raise 迁移错误("target_denied", "PG核对需要应用作者身份")
if kind == "reference":
with 装配.要求数据库().连接(只读=True) as 连:
v = 装配.要求资料().读取版本(连, 回执["source_id"], 回执["revision"])
# B03公开版本带原文;与冻结请求逐码点核对,不仅比较自报哈希。
v = 装配.要求资料().读取版本回执(连, 回执["source_id"], 回执["revision"])
# 参考导入零授权用途,原文受当前用途门禁保护,迁移不自开用途;
# 核对冻结请求重算哈希、库内存储哈希与不可变回执三者一致。
import hashlib
h = hashlib.sha256(p["request"]["content"].encode()).hexdigest()
if (
v["content_hash"] != h
or 回执["content_hash"] != h
or v["content"] != p["request"]["content"]
or v["import_result"] != 回执["import_result"]
or v["import_result"].get("导入者") != 身份.作者
):

View File

@ -0,0 +1,55 @@
-- 结算不产生执行事件;账目变化由独立版本及只追加命令回执表达。
ALTER TABLE public.muse_task ADD COLUMN accounting_revision bigint NOT NULL DEFAULT 0;
ALTER TABLE evaluation.muse_task ADD COLUMN accounting_revision bigint NOT NULL DEFAULT 0;
CREATE TABLE public.muse_call_settlement (
task_id uuid NOT NULL REFERENCES public.muse_task(task_id),
command_id text NOT NULL CHECK (btrim(command_id) <> ''),
receipt_id uuid NOT NULL UNIQUE,
request_hash text NOT NULL CHECK (request_hash ~ '^[0-9a-f]{64}$'),
receipt jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
PRIMARY KEY(task_id,command_id)
);
CREATE TABLE evaluation.muse_call_settlement (LIKE public.muse_call_settlement INCLUDING ALL);
ALTER TABLE evaluation.muse_call_settlement ADD FOREIGN KEY(task_id) REFERENCES evaluation.muse_task(task_id);
CREATE TRIGGER muse_call_settlement_immutable BEFORE UPDATE OR DELETE ON public.muse_call_settlement
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history();
CREATE TRIGGER muse_call_settlement_immutable BEFORE UPDATE OR DELETE ON evaluation.muse_call_settlement
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history();
GRANT SELECT,INSERT ON public.muse_call_settlement TO muse_app;
GRANT SELECT,INSERT ON evaluation.muse_call_settlement TO muse_eval;
REVOKE ALL ON public.muse_call_settlement FROM muse_eval;
-- 验证专用绑定不能被业务配置读取入口消费,不需要草案先启用。
CREATE TABLE public.muse_config_probe_binding (
task_id uuid PRIMARY KEY REFERENCES public.muse_task(task_id),
config_id text NOT NULL,
version text NOT NULL,
content_hash text NOT NULL CHECK (content_hash ~ '^[0-9a-f]{64}$'),
content jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT clock_timestamp()
);
CREATE TABLE evaluation.muse_config_probe_binding (LIKE public.muse_config_probe_binding INCLUDING ALL);
ALTER TABLE evaluation.muse_config_probe_binding ADD FOREIGN KEY(task_id) REFERENCES evaluation.muse_task(task_id);
CREATE TRIGGER muse_config_probe_immutable BEFORE UPDATE OR DELETE ON public.muse_config_probe_binding
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history();
CREATE TRIGGER muse_config_probe_immutable BEFORE UPDATE OR DELETE ON evaluation.muse_config_probe_binding
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history();
GRANT SELECT,INSERT ON public.muse_config_probe_binding TO muse_app;
GRANT SELECT,INSERT ON evaluation.muse_config_probe_binding TO muse_eval;
REVOKE ALL ON public.muse_config_probe_binding FROM muse_eval;
CREATE TABLE public.muse_call_reconciliation (
task_id uuid NOT NULL REFERENCES public.muse_task(task_id),
command_id text NOT NULL CHECK (btrim(command_id) <> ''),
request_hash text NOT NULL CHECK (request_hash ~ '^[0-9a-f]{64}$'),
receipt jsonb NOT NULL,
PRIMARY KEY(task_id,command_id)
);
CREATE TABLE evaluation.muse_call_reconciliation (LIKE public.muse_call_reconciliation INCLUDING ALL);
ALTER TABLE evaluation.muse_call_reconciliation ADD FOREIGN KEY(task_id) REFERENCES evaluation.muse_task(task_id);
CREATE TRIGGER muse_call_reconciliation_immutable BEFORE UPDATE OR DELETE ON public.muse_call_reconciliation
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history();
CREATE TRIGGER muse_call_reconciliation_immutable BEFORE UPDATE OR DELETE ON evaluation.muse_call_reconciliation
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history();
GRANT SELECT,INSERT ON public.muse_call_reconciliation TO muse_app;
GRANT SELECT,INSERT ON evaluation.muse_call_reconciliation TO muse_eval;
REVOKE ALL ON public.muse_call_reconciliation FROM muse_eval;

View File

@ -0,0 +1,3 @@
-- 当前消费授权与不可变内容版本独立;旧授权集合保持原值。
ALTER TABLE muse_source ADD COLUMN authorization_revision bigint NOT NULL DEFAULT 1
CHECK (authorization_revision >= 1);

View File

@ -0,0 +1,17 @@
-- B08显式完整审计的追加证明;既有成品字节及原始验证记录保持不可变。
CREATE TABLE public.muse_delivery_artifact_validation (
artifact_id uuid NOT NULL REFERENCES public.muse_delivery_artifact,
validation_version text NOT NULL CHECK(validation_version = 'delivery-validation-v2'),
author_id text NOT NULL,
verification jsonb NOT NULL,
audited_at timestamptz NOT NULL DEFAULT clock_timestamp(),
PRIMARY KEY(artifact_id,validation_version),
CHECK((verification->>'validation_version' = validation_version) IS TRUE),
CHECK((verification->'verified' = 'true'::jsonb) IS TRUE),
CHECK((jsonb_typeof(verification->'binding') = 'object') IS TRUE)
);
CREATE TRIGGER muse_delivery_artifact_validation_immutable BEFORE UPDATE OR DELETE
ON public.muse_delivery_artifact_validation FOR EACH ROW
EXECUTE FUNCTION public.muse_guard_literary_history();
REVOKE ALL ON public.muse_delivery_artifact_validation FROM PUBLIC,muse_app,muse_eval;
GRANT SELECT,INSERT ON public.muse_delivery_artifact_validation TO muse_app;

View File

@ -0,0 +1,31 @@
-- B09:索引现有条目和头的事务变更守卫;不产生第二套索引或业务权威。
CREATE TABLE public.muse_method_index_guard (
singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton),
generation bigint NOT NULL DEFAULT 0 CHECK (generation >= 0)
);
INSERT INTO public.muse_method_index_guard (singleton, generation) VALUES (true, 0);
CREATE FUNCTION public.muse_method_index_changed() RETURNS trigger
LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog AS $guard$
BEGIN
UPDATE public.muse_method_index_guard
SET generation = generation + 1
WHERE singleton;
IF NOT FOUND THEN
RAISE EXCEPTION 'method index mutation guard is missing';
END IF;
RETURN NULL;
END
$guard$;
-- statement 级别也覆盖批量改写和 TRUNCATE;事务失败时代次一并回滚。
CREATE TRIGGER muse_method_index_changed
AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE ON public.muse_method_index
FOR EACH STATEMENT EXECUTE FUNCTION public.muse_method_index_changed();
CREATE TRIGGER muse_method_index_head_changed
AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE ON public.muse_method_index_version
FOR EACH STATEMENT EXECUTE FUNCTION public.muse_method_index_changed();
REVOKE ALL ON public.muse_method_index_guard FROM PUBLIC, muse_app, muse_eval;
GRANT SELECT ON public.muse_method_index_guard TO muse_app, muse_eval;
REVOKE ALL ON FUNCTION public.muse_method_index_changed() FROM PUBLIC;

View File

@ -0,0 +1,14 @@
-- B10逐例复用只追加原来源,不复制任务、响应或费用账。
CREATE TABLE evaluation.muse_evaluation_reuse (
unit_id uuid PRIMARY KEY REFERENCES evaluation.muse_evaluation_unit(unit_id),
source_unit_id uuid NOT NULL REFERENCES evaluation.muse_evaluation_unit(unit_id),
source_experiment_id uuid NOT NULL REFERENCES evaluation.muse_experiment(experiment_id),
payload jsonb NOT NULL CHECK (jsonb_typeof(payload)='object'),
payload_hash text NOT NULL CHECK (payload_hash ~ '^[0-9a-f]{64}$'),
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
CHECK (unit_id <> source_unit_id)
);
CREATE INDEX evaluation_reuse_source ON evaluation.muse_evaluation_reuse(source_experiment_id);
CREATE TRIGGER evaluation_reuse_guard
BEFORE UPDATE OR DELETE ON evaluation.muse_evaluation_reuse
FOR EACH ROW EXECUTE FUNCTION public.muse_guard_literary_history();

View File

@ -1,7 +1,8 @@
# 本文件是运行配置草案,保存不等于验证或启用。不填写凭据值。
"宿主" = "direct"
"宿主版本" = "1"
"角色策略版本" = "role-policy-r2-v1"
# 来源:配置/角色策略.yaml 的 version,生成后应与发布包一致。
"角色策略版本" = "role-policy-r2-v2"
"资源发布身份" = "<环境预检返回的resource_build_id>"
"预算策略引用" = "<已登记且不可变的预算账户身份>"
"计价版本" = "<后端已登记计价实现的版本>"

View File

@ -5,7 +5,6 @@ steps:
- step_id: 冻结背景
processor: review.prepare
processor_version: "1"
protection: 冻结上下文
- step_id: 审阅全文
processor: review.literary
processor_version: "1"

View File

@ -4,7 +4,6 @@ steps:
- step_id: 冻结修订
processor: revision.freeze
processor_version: "1"
protection: 冻结上下文
- step_id: 模型修订
processor: revision.generate
processor_version: "1"

View File

@ -5,7 +5,6 @@ steps:
- step_id: 准备
processor: eval.prepare
processor_version: "1"
protection: 冻结上下文
- step_id: 调用
processor: eval.call.detector
processor_version: "1"

View File

@ -5,7 +5,6 @@ steps:
- step_id: 准备
processor: eval.prepare
processor_version: "1"
protection: 冻结上下文
- step_id: 调用
processor: eval.call.judge
processor_version: "1"

View File

@ -5,7 +5,6 @@ steps:
- step_id: 准备
processor: eval.prepare
processor_version: "1"
protection: 冻结上下文
- step_id: 调用
processor: eval.call.writer
processor_version: "1"

View File

@ -16,7 +16,6 @@ steps:
processor: ctx.freeze
processor_version: "1"
depends_on: [受限探索]
protection: 冻结上下文
- step_id: 无工具写作
processor: ctx.write
processor_version: "1"

View File

@ -5,7 +5,6 @@ steps:
- step_id: 冻结规划
processor: plan.freeze
processor_version: "1"
protection: 冻结上下文
- step_id: 模型规划
processor: plan.generate
processor_version: "1"

View File

@ -15,6 +15,7 @@ models:
- MiniMax-M2.7
- glm-5.2
- deepseek-v4-flash
- qwen3.8-flash
actual_model_ids:
claude-opus-4-8[1M]: ["claude-opus-4-8[1M]", "claude-opus-4-8"]
claude-opus-4-8: ["claude-opus-4-8", "claude-opus-4-8[1M]"]