- 场景检索.py:切片语料两路等权RRF(键带slice:前缀),授权join过滤、代次陈旧拒绝、结果带书名预览双路得分;RRF抽公共融合入基础设施 - 参考材料双载体:references节判别式(方法/片段),片段冻结身份带来源内容哈希哨兵,现读核当前版本一致否则SOURCE_STALE - 选段授权slice_references→范围授权→校验→依赖复检→装配→快照绑定全链贯通;片段不登记方法消费 - 范围校验source_ids基集、服务包装、依赖锁定键分派同步;B03接口增读取来源当前指纹(架构门禁禁私表) - 集成a10032-a10036:检索知情候选/未建拒绝/撤回空/片段进快照/来源漂移失效
186 lines
7.3 KiB
Python
186 lines
7.3 KiB
Python
"""场景切片构建与读取:授权来源入池,用途撤回即退出候选;切片可全量重建。"""
|
|
|
|
import pytest
|
|
import test_模型修订任务 as 修订测试
|
|
|
|
from muse.资料研究.场景切片 import 计算切片
|
|
from muse.资料研究.接口 import (
|
|
导入请求,
|
|
构建场景索引,
|
|
读取场景切片,
|
|
资料服务,
|
|
)
|
|
|
|
pytestmark = pytest.mark.数据库
|
|
生成环境 = 修订测试.生成环境
|
|
|
|
|
|
def _导入(env, 连, *, 标题: str, origin: str, 用途: tuple[str, ...]) -> str:
|
|
来源 = 资料服务().导入(
|
|
连,
|
|
env["作者"].作者,
|
|
导入请求(
|
|
kind="reference",
|
|
title=标题,
|
|
origin=origin,
|
|
content="刀锋擦过装甲,反震顺着掌心爬上来。\n火花在真空中凝成细线。\n" * 400,
|
|
authorized_uses=用途,
|
|
),
|
|
)
|
|
return str(来源.source_id)
|
|
|
|
|
|
@pytest.fixture
|
|
def 切片环境(生成环境):
|
|
env = 生成环境
|
|
with env["装配"].要求数据库().连接() as 连:
|
|
env["授权来源"] = _导入(
|
|
env, 连, 标题="机甲打斗参考", origin="seed://slice-ref", 用途=("reference_reading",)
|
|
)
|
|
env["未授权来源"] = _导入(
|
|
env, 连, 标题="分析专用资料", origin="seed://slice-analysis", 用途=("analysis",)
|
|
)
|
|
return env
|
|
|
|
|
|
@pytest.mark.case_id(
|
|
"NC-w33-a10029",
|
|
environment="隔离PG与生成环境",
|
|
given="一个 reference_reading 授权来源与一个仅 analysis 来源",
|
|
when="构建场景索引并读取切片",
|
|
then=[
|
|
"仅授权来源入池,切片数与确定性计算一致",
|
|
"切片行携带书名、定位与来源版本内容哈希",
|
|
"重复构建为幂等重建,索引身份一致",
|
|
],
|
|
contract="docs/系统架构/新版设计/模块设计/B03-资料研究.md#场景片段切片与索引",
|
|
)
|
|
def test_构建场景索引只纳入授权来源__a10029(切片环境):
|
|
env = 切片环境
|
|
库 = env["装配"].要求数据库()
|
|
with 库.连接() as 连:
|
|
回执 = 构建场景索引(连, env["作者"].作者)
|
|
assert 回执["来源数"] == 1
|
|
assert 回执["切片数"] > 0
|
|
assert 回执["embedder"].startswith("hash-bigram-")
|
|
再次 = 构建场景索引(连, env["作者"].作者)
|
|
assert 再次["切片数"] == 回执["切片数"]
|
|
assert 再次["索引身份"] == 回执["索引身份"]
|
|
|
|
raw = 连.execute("SELECT slice_id FROM muse_source_slice").fetchall()
|
|
切片集 = [str(r[0]) for r in raw]
|
|
明细 = 读取场景切片(连, env["作者"].作者, 切片集)
|
|
assert len(明细) == 回执["切片数"]
|
|
assert all(条["书名"] == "机甲打斗参考" for 条 in 明细)
|
|
assert all(条["内容哈希"] for 条 in 明细)
|
|
# 未授权来源切片不出现。
|
|
assert all(条["source_id"] == env["授权来源"] for 条 in 明细)
|
|
|
|
|
|
@pytest.mark.case_id(
|
|
"NC-w33-a10030",
|
|
environment="隔离PG与生成环境",
|
|
given="已入池的授权来源",
|
|
when="撤回 reference_reading 用途后读取切片",
|
|
then=["读取返回空,切片行保留(派生缓存不删历史),重建后来源数归零"],
|
|
contract="docs/系统架构/新版设计/模块设计/B03-资料研究.md#场景片段切片与索引",
|
|
)
|
|
def test_用途撤回后切片退出候选__a10030(切片环境):
|
|
env = 切片环境
|
|
库 = env["装配"].要求数据库()
|
|
with 库.连接() as 连:
|
|
构建场景索引(连, env["作者"].作者)
|
|
当前 = 连.execute("SELECT slice_id FROM muse_source_slice LIMIT 1").fetchone()
|
|
# 测试关注读侧授权过滤,直接置空用途模拟撤回后的来源状态。
|
|
连.execute(
|
|
"UPDATE muse_source SET authorized_uses='[]'::jsonb WHERE source_id=%s",
|
|
(env["授权来源"],),
|
|
)
|
|
明细 = 读取场景切片(连, env["作者"].作者, [str(当前[0])])
|
|
assert 明细 == []
|
|
保留 = 连.execute("SELECT COUNT(*) FROM muse_source_slice").fetchone()[0]
|
|
assert 保留 > 0
|
|
回执 = 构建场景索引(连, env["作者"].作者)
|
|
assert 回执["来源数"] == 0 and 回执["切片数"] == 0
|
|
|
|
|
|
@pytest.mark.case_id(
|
|
"NC-w33-a10031",
|
|
environment="隔离PG与生成环境",
|
|
given="他人导入的授权来源",
|
|
when="以当前作者构建场景索引",
|
|
then=["来源不计入切片池:切片只对导入者视角构建"],
|
|
contract="docs/系统架构/新版设计/模块设计/B03-资料研究.md#场景片段切片与索引",
|
|
)
|
|
def test_非导入者来源不入池__a10031(切片环境):
|
|
env = 切片环境
|
|
with env["装配"].要求数据库().连接() as 连:
|
|
回执 = 构建场景索引(连, "other-author")
|
|
assert 回执["来源数"] == 0 and 回执["切片数"] == 0
|
|
assert 计算切片("有字。\n", env["授权来源"], 1)
|
|
|
|
|
|
@pytest.mark.case_id(
|
|
"NC-w33-a10032",
|
|
environment="隔离PG与生成环境",
|
|
given="已构建的授权来源切片索引",
|
|
when="以场景相关查询执行场景检索",
|
|
then=["两路融合返回切片:带书名、预览与双路得分;授权来源独占结果"],
|
|
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md#场景片段检索",
|
|
)
|
|
def test_场景检索返回知情候选__a10032(切片环境):
|
|
env = 切片环境
|
|
库 = env["装配"].要求数据库()
|
|
from muse.上下文.接口 import 场景检索
|
|
|
|
with 库.连接() as 连:
|
|
构建场景索引(连, env["作者"].作者)
|
|
回执 = 场景检索(连, "刀锋擦过装甲 反震", 数量=3)
|
|
assert 回执["empty_reason"] is None
|
|
assert 1 <= len(回执["结果"]) <= 3
|
|
首 = 回执["结果"][0]
|
|
assert 首["书名"] == "机甲打斗参考"
|
|
assert "刀锋" in 首["预览"]
|
|
assert "关键词得分" in 首 and "向量得分" in 首 and 首["融合得分"] > 0
|
|
|
|
|
|
@pytest.mark.case_id(
|
|
"NC-w33-a10033",
|
|
environment="隔离PG与生成环境",
|
|
given="尚未构建切片索引的库",
|
|
when="执行场景检索",
|
|
then=["拒绝为索引陈旧,不静默返回空结果"],
|
|
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md#场景片段检索",
|
|
)
|
|
def test_未建索引拒绝__a10033(生成环境):
|
|
env = 生成环境
|
|
from muse.上下文.接口 import 场景检索
|
|
from muse.上下文.模型 import 上下文错误
|
|
|
|
with env["装配"].要求数据库().连接(只读=True) as 连:
|
|
with pytest.raises(上下文错误):
|
|
场景检索(连, "任意查询")
|
|
|
|
|
|
@pytest.mark.case_id(
|
|
"NC-w33-a10034",
|
|
environment="隔离PG与生成环境",
|
|
given="授权来源用途被撤回后的切片索引",
|
|
when="执行场景检索",
|
|
then=["结果为空且说明可读,不扩大为索引不可用"],
|
|
contract="docs/系统架构/新版设计/模块设计/B09-上下文.md#场景片段检索",
|
|
)
|
|
def test_用途撤回后检索为空__a10034(切片环境):
|
|
env = 切片环境
|
|
库 = env["装配"].要求数据库()
|
|
from muse.上下文.接口 import 场景检索
|
|
|
|
with 库.连接() as 连:
|
|
构建场景索引(连, env["作者"].作者)
|
|
连.execute(
|
|
"UPDATE muse_source SET authorized_uses='[]'::jsonb WHERE source_id=%s",
|
|
(env["授权来源"],),
|
|
)
|
|
回执 = 场景检索(连, "刀锋", 数量=3)
|
|
assert 回执["结果"] == []
|