fix(上下文): 审查修复——混合载体授权崩溃、前端片段落账领域、检索当前版本过滤与构建行序稳定

- _核对材料片段跳过片段载体条目:dict.get默认值急切求值在无version_id条目上KeyError致整轮修订回滚
- 前端片段落账 domain 用当前选定领域实值:空领域被请求模型拒绝致落账必败
- 场景检索加当前版本子查询过滤:来源推进新版本后旧切片退出候选(对齐B09承诺)
- 构建查询 ORDER BY 稳定指纹行序;读取新鲜切片索引返回单值;B03构建命令措辞对齐(仅HTTP)
- 新增混合载体用例a10038:方法+片段同轮整轮完成、消费只登记方法
This commit is contained in:
zizi 2026-09-20 00:31:08 +08:00
parent c777c89e4a
commit 41faed9f31
7 changed files with 102 additions and 10 deletions

View File

@ -90,7 +90,7 @@ S02检查点是执行证据而非第二分析读取路径。运行中的完成
参考书原文可确定性切片入检索池,为参考改写提供场景原文语料。切片是纯函数:按段落边界聚合至目标窗长(1200~2400 字符,不足并入邻窗),切片参数与窗口清单记入窗计划哈希;同一来源版本只认一个窗计划,重算不一致即拒绝为陈旧。切片与索引是可全量重建的派生缓存(原文以 `muse_source_version` 为权威),禁原地改写,重建走整批替换;代次守卫随事务变更递增。
片段向量索引与方法索引同构:哈希 bigram embedder(零模型成本、确定性)、代次台账 active 唯一、检索时对不上即拒绝为陈旧。索引构建是显式命令(CLI/HTTP),不耦合导入入口;构建入口按当前具备 `reference_reading` 用途的来源版本全量对账,来源版本更新或用途撤回后重算。切片身份与授权解耦:授权变化只影响检索候选与材料读取,不删历史切片。
片段向量索引与方法索引同构:哈希 bigram embedder(零模型成本、确定性)、代次台账 active 唯一、检索时对不上即拒绝为陈旧。索引构建是显式命令(HTTP),不耦合导入入口;构建入口按当前具备 `reference_reading` 用途的来源版本全量对账,来源版本更新或用途撤回后重算。切片身份与授权解耦:授权变化只影响检索候选与材料读取,不删历史切片。
读取授权:只有来源当前具备 `reference_reading` 用途,其切片才进入检索候选与参考材料读取;B03 经既有 `核对来源授权` 核验。片段材料必须逐字取自冻结时声明的来源版本内容哈希;版本漂移即失效,不默换新版本。

View File

@ -106,9 +106,17 @@ def _核对材料片段(行: dict, 快照ID: str, 方法版本ID: str, 材料哈
实际 = 按键读取材料(装配)
版本绑定 = {条["source_id"]: 条 for 条 in 行["source_bindings"]}
# 方法节与参考节同样校验:方法节经作品绑定关联,参考节无绑定,以版本身份直接关联。
冻结条目 = [*行["frozen_scope"].get("methods", []), *行["frozen_scope"].get("references", [])]
# 片段载体不是方法版本,与方法消费核对无关,直接跳过。
冻结条目 = [
条
for 条 in [
*行["frozen_scope"].get("methods", []),
*行["frozen_scope"].get("references", []),
]
if 条.get("载体") != "slice" and "version_id" in 条
]
for 条 in 冻结条目:
绑定键 = 条.get("binding_id", 条["version_id"])
绑定键 = 条.get("binding_id") or 条["version_id"]
绑 = 版本绑定.get(绑定键)
if 条["version_id"] != 方法版本ID or 条["材料键"] not in 实际 or 绑 is None:
continue

View File

@ -17,8 +17,8 @@ from muse.基础设施.模型.嵌入 import 特征哈希嵌入器
切片数量上限 = 50
def 读取新鲜切片索引(连) -> tuple[dict[str, Any], list[dict[str, Any]]]:
"""代次与索引一致才可用;对不上即拒绝为陈旧,不默重建。"""
def 读取新鲜切片索引(连) -> dict[str, Any]:
"""读取代次台账与当前有效索引身份;尚未构建即拒绝为陈旧,不默重建。"""
from psycopg.rows import dict_row
行 = (
@ -33,7 +33,7 @@ def 读取新鲜切片索引(连) -> tuple[dict[str, Any], list[dict[str, Any]]]
)
if 行 is None or 行["ledger_id"] is None:
raise 上下文错误("CONTEXT_INDEX_STALE", "场景切片索引尚未构建")
return dict(行), []
return dict(行)
def 场景检索(连, 查询: str, *, 数量: int = 10) -> dict[str, Any]:
@ -44,7 +44,7 @@ def 场景检索(连, 查询: str, *, 数量: int = 10) -> dict[str, Any]:
raise 上下文错误("CONTEXT_INPUT_INVALID", "场景检索需要明确的非空查询")
if type(数量) is not int or not 1 <= 数量 <= 切片数量上限:
raise 上下文错误("CONTEXT_INPUT_INVALID", f"场景检索数量必须在 1 到 {切片数量上限} 之间")
台账, _ = 读取新鲜切片索引(连)
台账 = 读取新鲜切片索引(连)
匹配 = 台账["embedder"].rsplit("-", 2)
维度 = int(匹配[1]) if len(匹配) == 3 and 匹配[1].isdigit() else None
if 维度 is None:
@ -61,7 +61,9 @@ def 场景检索(连, 查询: str, *, 数量: int = 10) -> dict[str, Any]:
"JOIN public.muse_source_slice_index i ON i.slice_id=x.slice_id "
"JOIN public.muse_source s ON s.source_id=x.source_id "
"JOIN muse_source_version v ON v.source_id=x.source_id AND v.revision=x.revision "
"WHERE s.authorized_uses ? 'reference_reading' AND i.embedder=%s",
"WHERE s.authorized_uses ? 'reference_reading' AND i.embedder=%s "
"AND x.revision = (SELECT MAX(v2.revision) FROM muse_source_version v2 "
"WHERE v2.source_id=x.source_id)",
(台账["embedder"],),
)
.fetchall()

View File

@ -109,7 +109,7 @@ def 构建场景索引(连, 作者: str, *, 嵌入器: 特征哈希嵌入器 | N
"(ARRAY_AGG(v.import_result->>'导入者' ORDER BY v.revision DESC))[1] AS author_id "
"FROM muse_source s JOIN muse_source_version v ON v.source_id=s.source_id "
"WHERE s.authorized_uses ? 'reference_reading' "
"GROUP BY s.source_id, s.title"
"GROUP BY s.source_id, s.title ORDER BY s.source_id"
).fetchall()
切片行: list[dict[str, Any]] = []
来源数 = 0

View File

@ -27814,6 +27814,41 @@
"数据库"
]
},
{
"case_id": "NC-w32-a10038",
"environment": "隔离PG与合成HTTP提供方",
"given": "同一选段授权同时携带方法参考与片段参考",
"when": "发起并执行受控修订流程至候选交付",
"then": [
"混合载体授权整轮完成不崩溃;方法消费正常登记,片段不登记",
"两类材料同时入选装配并绑定进快照"
],
"contract": "docs/系统架构/新版设计/模块设计/B06-审校修订.md#参考改写选段改写链路扩展",
"file": "tests/集成/test_参考改写.py",
"symbol": "test_混合载体参考授权整轮完成__a10038",
"parameter_ids": [],
"node_ids": [
"tests/集成/test_参考改写.py::test_混合载体参考授权整轮完成__a10038"
],
"fixtures": [
"request",
"tmp_path",
"tmp_path_factory",
"内置种子方案",
"内置结构测试库",
"参考环境",
"数据库底座",
"测试资源接缝",
"源码资源",
"片段环境",
"生成环境",
"离线防护",
"隔离数据库URL"
],
"markers": [
"数据库"
]
},
{
"case_id": "NC-w33-a10025",
"environment": "离线纯函数",

View File

@ -378,3 +378,50 @@ def test_片段来源漂移使快照失效__a10036(片段环境):
快照 = env["装配"].要求上下文().读取快照(快照ID)
assert 快照.失效说明 and "参考片段" in 快照.失效说明
assert "需重新授权" in 快照.失效说明
@pytest.mark.case_id(
"NC-w32-a10038",
environment="隔离PG与合成HTTP提供方",
given="同一选段授权同时携带方法参考与片段参考",
when="发起并执行受控修订流程至候选交付",
then=[
"混合载体授权整轮完成不崩溃;方法消费正常登记,片段不登记",
"两类材料同时入选装配并绑定进快照",
],
contract="docs/系统架构/新版设计/模块设计/B06-审校修订.md#参考改写选段改写链路扩展",
)
def test_混合载体参考授权整轮完成__a10038(片段环境):
env = 片段环境
# 片段环境默认清空了方法参考;此处恢复混合:方法 + 片段同轮。
env["授权"] = replace(env["授权"], references=(env["参考版本"],))
tid = 修订测试.发起(env, command="mix-revision-1")
env["剧本"].append(
{
"类型": "文本",
"文本": {
"edits": [
{
"selection_id": env["选段"].selection_id,
"action": "replace",
"replacement": "刀锋擦过装甲,反震顺着掌心爬上来。",
"reason": "合成混合参考改写",
}
]
},
}
)
task = 修订测试.跑(env, tid)
assert task.状态 == 任务状态.已完成
快照 = env["装配"].要求上下文().读取快照(_快照ID(task))
assert "参考方法:" + env["参考版本"] in 快照.装配["入选"]
assert "参考片段:" + env["片段"]["slice_id"] in 快照.装配["入选"]
assert {str(绑.来源ID) for 绑 in 快照.来源绑定} >= {env["参考版本"], env["片段"]["slice_id"]}
with env["装配"].要求数据库().连接(只读=True) as 连:
rows = 连.execute(
"SELECT method_version_id FROM muse_method_usage WHERE task_id=%s",
(tid,),
).fetchall()
assert [str(r[0]) for r in rows] == [env["参考版本"]]

View File

@ -198,7 +198,7 @@ export function Ui选段改写({
command_id: refCommandId.current || crypto.randomUUID(),
selection: {
query: refQuery.trim(),
domain: "",
domain: refDomain,
work_id: current.work_id,
quote: "",
index_generation: 0,