zizi 6d1f635003 feat(上下文): 场景检索入口与片段参考进冻结链路——参考书场景原文可检索可改写
- 场景检索.py:切片语料两路等权RRF(键带slice:前缀),授权join过滤、代次陈旧拒绝、结果带书名预览双路得分;RRF抽公共融合入基础设施
- 参考材料双载体:references节判别式(方法/片段),片段冻结身份带来源内容哈希哨兵,现读核当前版本一致否则SOURCE_STALE
- 选段授权slice_references→范围授权→校验→依赖复检→装配→快照绑定全链贯通;片段不登记方法消费
- 范围校验source_ids基集、服务包装、依赖锁定键分派同步;B03接口增读取来源当前指纹(架构门禁禁私表)
- 集成a10032-a10036:检索知情候选/未建拒绝/撤回空/片段进快照/来源漂移失效
2026-09-20 00:08:29 +08:00

341 lines
14 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.

"""冻结来源的授权、撤回与版本变化核对;恢复重验以真实检查阻断旧任务。
合同依据:模块设计/B09-上下文.md、接口契约/任务工具与事件.md「恢复与停机」。
- 输入冻结范围(或快照)加事务连接:核对授权仍在(作者/作品/用途)、来源未撤回
(目录版本、文稿版本与哈希、细纲身份三元组、事实系统版本)、结构与投影版本未变。
- 输出当前有效性及锁定依据:有效附各来源当前版本绑定,无效附具体变化项;
只判断有效性,不修改任何模块内容。
- 恢复重验检查供 S02 流程登记挂接:漂移即抛错阻断任务重验,不自报已重验。
"""
from __future__ import annotations
from collections.abc import Callable
from psycopg import Connection
from muse.上下文.任务范围 import 上下文服务
from muse.上下文.偏好材料 import 投影版本 as 偏好投影
from muse.上下文.偏好材料 import 核对范围偏好
from muse.上下文.偏好材料 import 结构哈希 as 偏好结构
from muse.上下文.声音材料 import 核对范围声音
from muse.上下文.来源读取 import 投影版本 as 可见投影版本
from muse.上下文.来源读取 import 读取作品范围
from muse.上下文.模型 import (
上下文错误,
依赖状态,
依赖锁定,
允许内容用途,
允许执行用途,
来源绑定,
)
from muse.任务运行.接口 import 任务快照
from muse.作品规划.接口 import 作品错误, 读取细纲投影
from muse.共享.错误 import Muse错误
from muse.故事世界.接口 import 事实错误, 读取当前事实系统版本
from muse.正式变更.接口 import 固定哈希
from muse.正文写作.接口 import 正文错误, 读取正文元数据
def _核对文稿(连, 作者, 冻结范围, 目录, 基准, 投影) -> tuple[list[依赖锁定], list[str]]:
锁定: list[依赖锁定] = []
变化项: list[str] = []
for 目标 in 冻结范围.get("documents", ()):
章节 = next((c for c in 目录["chapters"] if c["chapter_id"] == 目标["chapter_id"]), None)
if 章节 is None or 章节["position"] != 目标["position"]:
变化项.append(f"章节 {目标['chapter_id']} 的目录位置已漂移或已被移除;来源锁定失效")
continue
try:
依据 = 读取正文元数据(
连, 作者, 目标["chapter_id"], 目标["revision"], 分支=目标["branch_id"]
)
except 正文错误 as 错误:
变化项.append(
f"文稿 {目标['chapter_id']} 版本 {目标['revision']} 已不可读取:{错误.说明}"
)
continue
if 依据["current_revision"] != 目标["revision"]:
变化项.append(
f"文稿 {目标['chapter_id']} 已从版本 {目标['revision']} 前进到 "
f"{依据['current_revision']};来源锁定失效"
)
continue
if 依据["document_hash"] != 目标["document_hash"]:
变化项.append(f"文稿 {目标['chapter_id']} 的结构哈希与冻结绑定不一致;来源锁定失效")
continue
基 = 基准.get(依据["document_id"])
if 基 is not None and (基.数据版本, 基.结构哈希, 基.投影版本) != (
str(目标["revision"]),
目标["document_hash"],
投影,
):
变化项.append(
f"来源 {依据['document_id']} 与冻结消费绑定的版本、结构或投影版本不一致;"
"来源锁定失效"
)
continue
锁定.append(
依赖锁定(
依据["document_id"],
"document",
str(目标["revision"]),
依据["document_hash"],
投影,
)
)
return 锁定, 变化项
def _核对细纲(
连, 作者, 作品, 冻结范围, 内容, 运行, 引用解析, 基准
) -> tuple[list[依赖锁定], list[str]]:
锁定: list[依赖锁定] = []
变化项: list[str] = []
目标章 = 冻结范围.get("target_plan")
if isinstance(目标章, dict):
try:
记录 = 读取细纲投影(
连,
作者,
作品,
目标章["chapter_id"],
内容用途=内容,
运行用途=运行,
引用解析=引用解析,
)
except (作品错误, 事实错误) as 错误:
# 细纲不可用、已撤回或其本书引用失去当前依据都算锁定失效,不向外泄漏
变化项.append(f"目标章 {目标章['chapter_id']} 细纲已不可用或引用失效:{错误.说明}")
else:
if (
记录["plan_id"],
记录["revision"],
记录["content_hash"],
) != (
目标章["plan_id"],
目标章["revision"],
目标章["content_hash"],
):
变化项.append(
f"目标章 {目标章['chapter_id']} 细纲已从版本 {目标章['revision']} 漂移到 "
f"{记录['revision']};来源锁定失效"
)
else:
基 = 基准.get(记录["plan_id"])
if 基 is not None and (
基.数据版本,
基.结构哈希,
基.投影版本,
) != (
str(记录["revision"]),
记录["content_hash"],
记录["projection_version"],
):
变化项.append(
f"来源 {记录['plan_id']} 与冻结消费绑定的版本、结构或投影版本不一致;"
"来源锁定失效"
)
else:
锁定.append(
依赖锁定(
记录["plan_id"],
"plan",
str(记录["revision"]),
记录["content_hash"],
记录["projection_version"],
)
)
return 锁定, 变化项
def _核对事实(连, 作者, 作品, 冻结范围, 投影) -> tuple[list[依赖锁定], list[str]]:
锁定: list[依赖锁定] = []
变化项: list[str] = []
事实节 = 冻结范围.get("facts")
if isinstance(事实节, dict):
try:
当前版本 = 读取当前事实系统版本(连, 作者, 作品)
except 事实错误 as 错误:
变化项.append(f"事实系统已不可读取:{错误.说明}")
else:
if 当前版本 != 事实节["system_revision"]:
变化项.append(
f"事实系统版本已从 {事实节['system_revision']} 前进到 {当前版本};来源锁定失效"
)
else:
锁定.append(依赖锁定(f"{作品}:事实系统", "facts", str(当前版本), "", 投影))
return 锁定, 变化项
def 核对依赖(
连: Connection,
冻结范围: dict,
引用解析=None,
基准绑定: tuple[来源绑定, ...] = (),
) -> 依赖状态:
"""在调用方事务内重查冻结来源;返回当前有效性与逐来源锁定依据。"""
if not isinstance(冻结范围, dict):
raise 上下文错误("CONTEXT_SCOPE_INVALID", "依赖核对需要任务冻结范围")
基准: dict[str, 来源绑定] = {}
for 绑 in 基准绑定:
if not isinstance(绑, 来源绑定) or 绑.来源ID in 基准:
raise 上下文错误("CONTEXT_INPUT_INVALID", "基准来源绑定必须是未重复的来源绑定对象")
基准[绑.来源ID] = 绑
作者 = 冻结范围.get("authorized_by")
作品 = 冻结范围.get("work_id")
if not isinstance(作者, str) or not 作者 or not isinstance(作品, str) or not 作品:
raise 上下文错误("CONTEXT_SCOPE_INVALID", "依赖核对需要冻结范围的作者与作品绑定")
变化项: list[str] = []
锁定: list[依赖锁定] = []
try:
目录 = 读取作品范围(连, 作者, 作品)
except Muse错误 as 错误:
return 依赖状态(False, (), (f"作品或授权已不可用:{错误.说明}",))
运行 = 冻结范围.get("run_purpose")
内容 = 冻结范围.get("content_purpose")
用途有效 = (
isinstance(运行, str)
and 运行 in 允许执行用途
and isinstance(内容, str)
and 内容 in 允许内容用途
)
if not 用途有效:
变化项.append(f"冻结范围的执行用途 {运行!r} 或内容用途 {内容!r} 已不被接受;来源锁定失效")
运行 = 运行 if isinstance(运行, str) else ""
内容 = 内容 if isinstance(内容, str) else ""
投影 = 可见投影版本(运行, 内容) if 用途有效 else ""
if 目录["revision"] != 冻结范围["directory_revision"]:
变化项.append(
f"作品目录已从版本 {冻结范围['directory_revision']} 前进到 {目录['revision']};"
"来源锁定失效"
)
else:
目录源 = 冻结范围.get("directory_source")
哈希 = 固定哈希(目录) if 目录源 else ""
if 目录源 and 目录源["content_hash"] != 哈希:
变化项.append("目录内容与冻结哈希不一致;来源锁定失效")
else:
锁定.append(
依赖锁定(
f"{作品}:目录",
"directory",
str(目录["revision"]),
哈希,
"work-directory-v1" if 目录源 else "",
)
)
当前锁定, 当前变化 = _核对文稿(连, 作者, 冻结范围, 目录, 基准, 投影)
锁定.extend(当前锁定)
变化项.extend(当前变化)
当前锁定, 当前变化 = _核对细纲(连, 作者, 作品, 冻结范围, 内容, 运行, 引用解析, 基准)
锁定.extend(当前锁定)
变化项.extend(当前变化)
当前锁定, 当前变化 = _核对事实(连, 作者, 作品, 冻结范围, 投影)
锁定.extend(当前锁定)
变化项.extend(当前变化)
try:
声音 = 核对范围声音(连, 冻结范围)
except 上下文错误 as 错误:
变化项.append(错误.说明)
else:
if 声音 is not None:
锁定.append(
依赖锁定(
声音["version_id"],
"voice",
str(声音["revision"]),
声音["schema_hash"],
声音["projection_version"],
)
)
绑 = 基准.get(声音["version_id"])
if 绑 is not None and (绑.数据版本, 绑.结构哈希, 绑.投影版本) != (
str(声音["revision"]),
声音["schema_hash"],
声音["projection_version"],
):
变化项.append("声音来源绑定与当前投影不一致")
from muse.上下文.任务范围 import 核对范围方法
from muse.知识方法.接口 import 方法错误
try:
方法 = 核对范围方法(连, 冻结范围)
except 方法错误 as 错误:
变化项.append(f"方法授权或绑定已失效:{错误.说明}")
else:
for 条 in 方法:
锁定.append(
依赖锁定(
条["binding_id"],
"method",
str(条["version"]),
条["schema_hash"],
条["projection_version"],
)
)
try:
偏好 = 核对范围偏好(连, 冻结范围)
except 上下文错误 as 错误:
变化项.append(错误.说明)
else:
for 条 in 偏好:
锁定.append(
依赖锁定(条["preference_id"], "preference", str(条["revision"]), 偏好结构, 偏好投影)
)
from muse.上下文.参考材料 import 核对范围参考
try:
参考 = 核对范围参考(连, 冻结范围)
except 上下文错误 as 错误:
变化项.append(错误.说明)
else:
for 条 in 参考:
if 条.get("载体") == "slice":
锁定.append(
依赖锁定(
条["slice_id"],
"reference-slice",
str(条["revision"]),
条["内容哈希"],
"reference-slice-v1",
)
)
else:
锁定.append(
依赖锁定(
条["version_id"],
"reference",
str(条["version"]),
条["schema_hash"],
条["projection_version"],
)
)
return 依赖状态(有效=not 变化项, 锁定依据=tuple(锁定), 变化项=tuple(变化项))
def 快照漂移说明(状态: 依赖状态) -> str | None:
"""快照对外失效说明:变化项只描述差异,还要给出下一步,漂移范围必须重新授权。"""
if 状态.有效:
return None
return ";".join(状态.变化项) + ";需重新授权"
def 依赖重验检查(服务: 上下文服务) -> Callable[[任务快照], None]:
"""S02 恢复检查的真实实现:授权、来源、结构与版本重验;漂移即阻断。"""
def 检查(任务: 任务快照) -> None:
范围 = 服务.恢复冻结范围(任务)
with 服务.数据库.连接(只读=True) as 连:
状态 = 核对依赖(连, 范围, 服务.规划引用解析)
if not 状态.有效:
raise 上下文错误("SOURCE_STALE", "恢复重验未通过:" + ";".join(状态.变化项))
return 检查
__all__ = ["依赖重验检查", "核对依赖", "快照漂移说明"]