fix(上下文): 审查修复——混合载体授权崩溃、前端片段落账领域、检索当前版本过滤与构建行序稳定
- _核对材料片段跳过片段载体条目:dict.get默认值急切求值在无version_id条目上KeyError致整轮修订回滚 - 前端片段落账 domain 用当前选定领域实值:空领域被请求模型拒绝致落账必败 - 场景检索加当前版本子查询过滤:来源推进新版本后旧切片退出候选(对齐B09承诺) - 构建查询 ORDER BY 稳定指纹行序;读取新鲜切片索引返回单值;B03构建命令措辞对齐(仅HTTP) - 新增混合载体用例a10038:方法+片段同轮整轮完成、消费只登记方法
This commit is contained in:
parent
c777c89e4a
commit
41faed9f31
@ -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 经既有 `核对来源授权` 核验。片段材料必须逐字取自冻结时声明的来源版本内容哈希;版本漂移即失效,不默换新版本。
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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": "离线纯函数",
|
||||
|
||||
@ -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["参考版本"]]
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user