muse-agent-example/tests/单元/test_上下文读取上界.py
zizi d909d1bd1b 后端实现与用例身份:19 包集成落地并修复收尾缺陷
实现侧:
- 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。
- 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。
- 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。
- 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。
- 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。
- 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。
- 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。
- 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。

用例侧:
- 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存;
- 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
2026-09-18 01:15:00 +08:00

481 lines
19 KiB
Python

"""应用读取前拒绝、阶段用量恢复和版本化回放的合成行为检查。"""
from contextlib import nullcontext
from dataclasses import replace
from datetime import UTC, datetime
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
import pytest
from muse.上下文.任务范围 import 上下文服务
from muse.上下文.分页 import 解码游标
from muse.上下文.存储 import 上下文存储
from muse.上下文.来源读取 import _片段游标, _片段起点, 读取历史正文
from muse.上下文.模型 import 上下文错误, 依赖状态
from muse.任务运行.上下文预算 import (
上下文预算错误,
核对请求预算,
读取用量,
默认读取预算,
)
from muse.任务运行.工具调用 import 只读工具集, 工具定义, 工具来源, 工具结果, 工具范围
from muse.任务运行.执行合同 import 工具请求
from muse.共享.调用身份 import 用途
from muse.正文写作.存储 import 正文存储
def 策略(**变化):
return {"context_policy_version": 2, "context_read_budget": {**默认读取预算, **变化}}
def 来源范围(**变化):
return {
**策略(),
"authorized_by": "author",
"work_id": "work",
"directory_revision": 1,
"run_purpose": "production",
"content_purpose": "generation",
"source_ids": ["doc"],
"documents": [
{
"chapter_id": "ch",
"branch_id": "main",
"revision": 1,
"position": 1,
"document_id": "doc",
"document_hash": "hash",
}
],
**变化,
}
def 工具域(用量=None):
return 工具范围("task", "work", ("doc",), 1, "production", "generation", 用量)
@pytest.mark.case_id("NC-O07-001")
def test_超长来源在取得正文前拒绝():
连 = MagicMock()
连.transaction.return_value = nullcontext()
数据库 = SimpleNamespace(连接=lambda **_: nullcontext(连))
with (
patch(
"muse.上下文.来源读取._核对目录",
return_value={"chapters": [{"chapter_id": "ch", "position": 1}]},
),
patch(
"muse.上下文.来源读取.读取正文元数据",
return_value={"current_revision": 1, "document_hash": "hash", "payload_bytes": 262145},
),
patch("muse.上下文.来源读取.读取正文依据") as 正文,
):
with pytest.raises(上下文预算错误) as 错:
读取历史正文(数据库, 来源范围(), 工具域(), {"chapter_id": "ch", "revision": 1})
assert 错.value.上下文["requested"] == 262145
正文.assert_not_called()
@pytest.mark.case_id("NC-O07-002")
def test_重启从持久消费量继续而非清零():
事件 = []
一 = 读取用量(策略(source_reads=1), 记录=事件.append)
一.预留来源(25)
二 = 读取用量(策略(source_reads=1), 事件[-1])
with pytest.raises(上下文预算错误) as 错:
二.预留来源(25)
assert 错.value.上下文["consumed"]["source_bytes"] == 25
assert 错.value.上下文["dimension"] == "source_reads"
@pytest.mark.case_id("NC-O07-003")
def test_预算预留未落盘时禁止来源读取():
def 拒写(_):
raise RuntimeError("event unavailable")
量 = 读取用量(策略(), 记录=拒写)
with pytest.raises(RuntimeError):
量.预留来源(32)
assert 量.已用["source_reads"] == 0
@pytest.mark.case_id("NC-O07-004")
def test_单工具和累计历史在返回前分别拒绝():
单 = 读取用量(策略(single_tool_bytes=10))
with pytest.raises(上下文预算错误):
单.接收工具(11)
累 = 读取用量(策略(tool_bytes=30))
for _ in range(3):
累.接收工具(10)
with pytest.raises(上下文预算错误) as 错:
累.接收工具(1)
assert 错.value.上下文["consumed"]["tool_bytes"] == 30
@pytest.mark.case_id("NC-O07-005")
def test_单次与累计耗时独立限制():
量 = 读取用量(策略(single_read_milliseconds=20, read_milliseconds=25))
with patch("muse.任务运行.上下文预算.monotonic", return_value=0.015):
量.完成读取(0)
with patch("muse.任务运行.上下文预算.monotonic", return_value=0.015):
with pytest.raises(上下文预算错误) as 错:
量.完成读取(0)
assert 错.value.上下文["dimension"] == "read_milliseconds"
with patch("muse.任务运行.上下文预算.monotonic", return_value=0.03):
with pytest.raises(上下文预算错误) as 错:
读取用量(策略(single_read_milliseconds=20)).完成读取(0)
assert 错.value.上下文["dimension"] == "single_read_milliseconds"
@pytest.mark.case_id("NC-O07-006")
def test_旧任务超部署上界要求新授权且不改原冻结输入():
旧 = {"legacy": True}
with pytest.raises(上下文预算错误) as 错:
核对请求预算(旧, b"x" * 262145)
assert 错.value.错误码 == "CONTEXT_POLICY_REAUTH_REQUIRED"
assert 旧 == {"legacy": True}
with pytest.raises(上下文预算错误) as 错:
核对请求预算(策略(request_bytes=32), b"x" * 33)
assert 错.value.错误码 == "CONTEXT_BUDGET_EXCEEDED"
@pytest.mark.case_id("NC-O07-007")
def test_正文游标不可跨任务版本或投影():
冻结 = 来源范围()
键 = ("ch", "main", 1)
域 = 工具域()
游标 = _片段游标(域, 冻结, 键, 2048)
assert _片段起点(域, 冻结, 键, {"cursor": 游标}) == 2048
for 域2, 冻结2, 键2 in [
(replace(域, 任务ID="other"), 冻结, 键),
(域, {**冻结, "directory_revision": 2}, 键),
(域, 冻结, ("ch", "main", 2)),
(replace(域, 内容用途="planning"), 冻结, 键),
]:
with pytest.raises(上下文错误):
_片段起点(域2, 冻结2, 键2, {"cursor": 游标})
@pytest.mark.case_id("NC-O07-008")
def test_窄依据只查版本哈希与数据库侧大小():
连 = MagicMock()
正文存储(连).读取元数据("doc", 1)
sql, params = 连.cursor.return_value.execute.call_args.args
assert "v.document," not in sql and "v.document " not in sql
assert "octet_length(v.document::text) AS payload_bytes" in sql
assert params == (1, "doc")
@pytest.mark.case_id("NC-O07-009")
def test_快照摘要页不载入装配正文且请求有上界():
存储 = 上下文存储(object(), 用途.生产)
with patch.object(存储, "查询") as 查询:
存储.按任务读取摘要页("task", 20, None)
sql, params = 查询.call_args.args
assert "SELECT *" not in sql and "assembly->'文本'" not in sql
assert params[-1] == 21
@pytest.mark.case_id("NC-O07-010")
def test_快照页同范围只做一遍依赖核验并保留下一页位置():
行 = {
"snapshot_id": "00000000-0000-0000-0000-000000000001",
"task_id": "task",
"work_id": "work",
"input_hash": "hash",
"frozen_scope": 来源范围(),
"source_bindings": [],
"assembly": {},
"created_at": datetime(2026, 1, 1, tzinfo=UTC),
}
连 = MagicMock()
连.transaction.return_value = nullcontext()
服务 = 上下文服务(SimpleNamespace(用途=用途.生产, 连接=lambda **_: nullcontext(连)), None)
with (
patch("muse.上下文.存储.上下文存储.按任务读取摘要页", return_value=[行] * 21),
patch("muse.上下文.依赖校验.核对依赖", return_value=依赖状态(True, (), ())) as 核对,
):
页 = 服务.读取任务快照页("task")
assert len(页["items"]) == 20 and 页["has_more"]
核对.assert_called_once()
assert (
解码游标(页["next_cursor"], {"task_id": "task", "purpose": "production"})[1]
== 行["snapshot_id"]
)
with pytest.raises(上下文错误):
解码游标(页["next_cursor"], {"task_id": "other", "purpose": "production"})
@pytest.mark.case_id("NC-O07-011")
def test_工具结果超限不进入历史也不产生成功读取记录():
记录 = []
量 = 读取用量(策略(single_tool_bytes=20))
定义 = 工具定义(
"read",
"读",
{"type": "object"},
lambda *_: 工具结果("x" * 50, (工具来源("doc", "1", "hash", "v1"),)),
)
集 = 只读工具集((定义,), ("read",), 工具域(量), 记录.append)
with pytest.raises(上下文预算错误):
集.调用(工具请求("call", "read", {}))
assert len(记录) == 1 and 记录[0]["outcome"] == "blocked"
assert 记录[0]["reason_code"] == "CONTEXT_BUDGET_EXCEEDED"
@pytest.mark.case_id("NC-O07-012")
def test_多文稿依赖复检只读元数据并保留版本失效():
from muse.上下文.依赖校验 import 核对依赖
文稿 = [
{
"chapter_id": f"ch{i}",
"document_id": f"d{i}",
"revision": 1,
"position": i + 1,
"document_hash": "hash",
"branch_id": "main",
}
for i in range(16)
]
冻结 = 来源范围(documents=文稿)
目录 = {
"revision": 1,
"chapters": [{"chapter_id": d["chapter_id"], "position": d["position"]} for d in 文稿],
}
def 元数据(_连, _作者, 章, _版, **_):
return {
"document_id": "d" + 章[2:],
"document_hash": "hash",
"current_revision": 2 if 章 == "ch15" else 1,
}
with (
patch("muse.上下文.依赖校验.读取作品范围", return_value=目录),
patch("muse.上下文.依赖校验.读取正文元数据", side_effect=元数据) as 元,
patch("muse.正文写作.存储.正文存储.恢复草稿", side_effect=AssertionError("no payload")),
):
状态 = 核对依赖(object(), 冻结)
assert 元.call_count == 16 and not 状态.有效
assert len(状态.锁定依据) == 16 # 目录和十五个未漂移正文
assert "ch15" in 状态.变化项[0]
@pytest.mark.case_id("NC-O07-013")
def test_v2默认正文片段明确续页而v1回放内容不变():
连 = MagicMock()
连.transaction.return_value = nullcontext()
数据库 = SimpleNamespace(连接=lambda **_: nullcontext(连))
依据 = {"document": object(), "document_id": "doc", "document_hash": "hash"}
with (
patch("muse.上下文.来源读取._核对并读取", return_value=依据),
patch("muse.上下文.来源读取.可见文本", return_value="a" * 3000),
patch("muse.上下文.来源读取.可见文本哈希", return_value="hash"),
):
新 = 读取历史正文(数据库, 来源范围(), 工具域(), {"chapter_id": "ch", "revision": 1})
旧 = 来源范围()
del 旧["context_policy_version"], 旧["context_read_budget"]
老 = 读取历史正文(数据库, 旧, 工具域(), {"chapter_id": "ch", "revision": 1})
assert len(新.内容["text"]) == 2048 and 新.内容["has_more"]
assert 新.内容["next_cursor"] and 新.内容["omitted_reason"] == "bounded_fragment"
assert len(老.内容["text"]) == 3000 and "has_more" not in 老.内容
@pytest.mark.case_id("NC-O07-014")
def test_事实全量超限在时点投影前拒绝():
from muse.上下文.来源读取 import 读取时点事实
连 = MagicMock()
连.transaction.return_value = nullcontext()
数据库 = SimpleNamespace(连接=lambda **_: nullcontext(连))
with (
patch("muse.上下文.来源读取._核对目录"),
patch("muse.上下文.来源读取.读取当前事实系统版本", return_value=1),
patch(
"muse.上下文.来源读取.读取事实预算元数据",
return_value={"source_headers": 5, "payload_bytes": 262145},
),
patch("muse.上下文.来源读取.读取时点事实投影") as 投影,
):
with pytest.raises(上下文预算错误):
读取时点事实(数据库, 来源范围(facts={"system_revision": 1}), 工具域(), {})
投影.assert_not_called()
@pytest.mark.case_id("NC-O07-015")
def test_细纲超限在正文投影前拒绝():
from muse.上下文.来源读取 import 读取目标细纲
连 = MagicMock()
连.transaction.return_value = nullcontext()
数据库 = SimpleNamespace(连接=lambda **_: nullcontext(连))
with (
patch("muse.上下文.来源读取._核对目录"),
patch("muse.上下文.来源读取.读取细纲元数据", return_value={"payload_bytes": 262145}),
patch("muse.上下文.来源读取.读取细纲投影") as 投影,
):
with pytest.raises(上下文预算错误):
读取目标细纲(数据库, 来源范围(target_plan={"chapter_id": "ch"}), 工具域(), {})
投影.assert_not_called()
@pytest.mark.case_id("NC-O07-016")
def test_事实点名集合越出冻结范围建联前拒绝():
from muse.上下文.来源读取 import 读取时点事实
数据库 = MagicMock()
with pytest.raises(上下文错误) as 错:
读取时点事实(
数据库, 来源范围(facts={"system_revision": 1}), 工具域(), {"object_ids": ["secret"]}
)
assert 错.value.错误码 == "SCOPE_DENIED"
数据库.连接.assert_not_called()
@pytest.mark.case_id("NC-O07-017")
def test_篡改v2预算不能放大部署上界():
for 冻结 in [
{"context_policy_version": 2},
策略(request_bytes=262145),
策略(source_reads=True),
]:
with pytest.raises(上下文预算错误):
读取用量(冻结)
@pytest.mark.case_id("NC-O07-018")
def test_单阶段失败预检耗时也保存消费量():
from muse.上下文.来源读取 import _读取阶段
事件 = []
量 = 读取用量(策略(), 记录=事件.append)
数据库 = MagicMock()
数据库.连接.side_effect = RuntimeError("connection failed")
with (
pytest.raises(RuntimeError),
patch("muse.上下文.来源读取.monotonic", return_value=1),
patch("muse.任务运行.上下文预算.monotonic", return_value=1.01),
):
with _读取阶段(数据库, 来源范围(), 工具域(量)):
raise AssertionError("unreachable")
assert 事件[-1]["read_milliseconds"] >= 9
@pytest.mark.case_id("NC-O07-019")
def test_快照部分失效不将声音偏好方法误报为事实状态():
from muse.上下文.模型 import 上下文快照, 依赖锁定, 来源绑定
from muse.接入.http.路由.上下文 import 呈现快照
冻结 = 来源范围(
source_ids=["doc", "voice", "pref", "method", "fact"],
voice={"version_id": "voice"},
preferences=[{"preference_id": "pref"}],
methods=[{"binding_id": "method"}],
facts={"system_revision": 1},
)
快照 = 上下文快照(
"snap",
"task",
"work",
"hash",
冻结,
{},
tuple(来源绑定(k, "1", "hash", "v1") for k in ["voice", "pref", "method", "fact"]),
"SOURCE_STALE",
"voice changed",
)
状态 = 依赖状态(
False,
(依赖锁定("work:事实系统", "facts", "1", ""), 依赖锁定("method", "method", "1", "")),
("voice changed",),
)
视图 = 呈现快照(快照, 状态)
assert {r.source_id: r.current_state for r in 视图.sources} == {
"voice": "SOURCE_STALE",
"pref": "SOURCE_STALE",
"method": "有效",
"fact": "有效",
}
状态 = 依赖状态(False, (依赖锁定("voice", "voice", "1", ""),), ("fact changed",))
assert 呈现快照(快照, 状态).sources[0].current_state == "有效"
@pytest.mark.case_id("NC-O07-020")
def test_HTTP快照页先核任务作者再取摘要():
from muse.接入.http.路由.上下文 import 列出任务快照页
请求 = MagicMock()
with patch(
"muse.接入.http.路由.上下文.读取作者任务",
side_effect=上下文错误("SCOPE_DENIED", "wrong author"),
):
with pytest.raises(上下文错误):
列出任务快照页("task", 请求, "author", limit=20, cursor=None)
请求.app.state.装配.要求上下文.assert_not_called()
@pytest.mark.case_id("NC-O07-021")
def test_实验臂纯组装不改变原文码点且必读超限失败():
from muse.上下文.回放材料 import _组装实验臂, _选取足额补充
原 = [{"text": "甲🙂乙", "document_id": "doc"}]
片段 = _选取足额补充(原, 2)
assert 片段[0]["text"] == "甲🙂" and 原[0]["text"] == "甲🙂乙"
with pytest.raises(上下文错误):
_选取足额补充(原, 4)
with pytest.raises(上下文错误):
_组装实验臂({"content": {"必读": "保留"}}, 原, [], [], [], 1)
@pytest.mark.case_id("NC-O07-022")
def test_并发工具实例不能覆盖已消费累计量():
from muse.任务运行.接口 import 任务服务
from muse.任务运行.模型 import 状态冲突
最新 = {}
存储 = MagicMock()
存储.读取上下文用量.side_effect = lambda *_: dict(最新)
存储.追加事件.side_effect = lambda _任务, _类型, payload, **_: 最新.update(
payload["context_usage"]
)
服务 = 任务服务.__new__(任务服务)
服务._存储 = lambda: nullcontext(存储)
领取 = SimpleNamespace(任务ID="task", 步骤ID="step", 尝试ID="try")
一 = 读取用量(策略())
二 = 读取用量(策略())
一.记录 = lambda 新: 服务.登记上下文用量(领取, 一.已用, 新, "a")
二.记录 = lambda 新: 服务.登记上下文用量(领取, 二.已用, 新, "b")
with patch("muse.任务运行.接口.校验领取") as 锁:
一.预留来源(10)
with pytest.raises(状态冲突):
二.预留来源(11)
assert 最新["source_bytes"] == 10 and 存储.追加事件.call_count == 1
assert 锁.call_count == 2 and 二.已用["source_reads"] == 0
@pytest.mark.case_id("NC-O07-023")
def test_旧任务工具描述不被v2参数扩展改写():
from muse.上下文.来源读取 import 事实工具, 历史正文工具
新 = 来源范围(facts={"system_revision": 1})
旧 = {k: v for k, v in 新.items() if k not in {"context_policy_version", "context_read_budget"}}
assert "cursor" in 历史正文工具(None, 新).参数合同["properties"]
assert "cursor" not in 历史正文工具(None, 旧).参数合同["properties"]
assert "object_ids" in 事实工具(None, 新).参数合同["properties"]
assert "object_ids" not in 事实工具(None, 旧).参数合同["properties"]
assert "context_policy_version" not in 旧
@pytest.mark.case_id("NC-O07-024")
def test_循环读取在下一载荷前检查剩余耗时():
量 = 读取用量(策略(single_read_milliseconds=10))
量.当前开始 = 0
with patch("muse.任务运行.上下文预算.monotonic", return_value=0.011):
with pytest.raises(上下文预算错误) as 错:
量.预留来源(8)
assert 错.value.上下文["dimension"] == "single_read_milliseconds"
assert 量.已用["source_reads"] == 0