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

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

227 lines
8.7 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.

"""运行配置角色探针:证据结构、身份核对与失败关闭;宿主为替身,不证明真实链路。"""
from pathlib import Path
import pytest
import yaml
from muse.任务运行.接口 import (
凭据引用,
提供方配置,
模型用量,
模型结果,
角色策略目录,
运行配置内容,
)
from muse.共享.错误 import Muse错误
from muse.基础设施.模型.角色探针 import 角色运行探针
仓库根 = Path(__file__).resolve().parents[2]
发布身份 = "探针资源-1"
def _策略() -> 角色策略目录:
定义 = yaml.safe_load((仓库根 / "配置/角色策略.yaml").read_text(encoding="utf-8"))
return 角色策略目录(定义, 资源发布身份=发布身份)
def _配置(角色="extractor", model="claude-opus-4-8") -> 运行配置内容:
策略 = _策略()
return 运行配置内容(
"direct",
"1",
策略.定义["version"],
发布身份,
"账户-1",
{角色: {"provider": "网关", "model": model, "thinking": "off"}},
(凭据引用("key", "受控存储", "/不存在/凭据.txt"),),
(提供方配置("网关", "chat-completions", "http://127.0.0.1:1/v1/chat/completions", "key"),),
"费率-1",
)
class _假已备:
def __init__(self, 宿主: "_假宿主") -> None:
self.宿主 = 宿主
async def 调用(self, 收到片段=None) -> 模型结果:
return self.宿主.结果
class _假宿主:
def __init__(self, 结果: 模型结果) -> None:
self.结果 = 结果
self.请求 = None
def 名称(self) -> str:
return "direct"
def 准备(self, 请求):
self.请求 = 请求
return _假已备(self)
def _成功结果(实际模型="claude-opus-4-8", 文本='{"ok": true}') -> 模型结果:
return 模型结果(
"completed",
文本,
实际模型,
模型用量(5, 7),
"探针响应-1",
回放协议="chat-completions",
回放内容=(),
)
@pytest.mark.case_id(
"NC-r2-da0001",
environment="离线单元(注入宿主)",
given="运行配置声明合成单角色探针与受控凭据",
when="执行运行配置验证(固定探针,冻结请求,不外发)",
then=["逐角色仅组装并判定固定探针请求,费用与证据走统一治理账;不因配置草案验证取得业务执行权"],
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
)
def test_合成单角色探针冻结请求且禁止绕过治理__da0001() -> None:
宿主 = _假宿主(_成功结果())
探针 = 角色运行探针(_策略(), 宿主工厂=lambda 提供方, 凭据: 宿主)
请求 = 探针.请求("task-1", "extractor", _配置())
assert 请求 == 探针.请求("task-1", "extractor", _配置())
assert 请求.调用ID != 探针.请求("task-2", "extractor", _配置()).调用ID
assert 请求.工具 == () and 请求.最大输出token == 64 and 请求.总期限秒 == 60
assert 探针.核对(请求, _成功结果()) == {"ok": True}
with pytest.raises(Muse错误, match="具名命令"):
探针.验证(_配置(), _用途())
assert 宿主.请求 is None
@pytest.mark.case_id(
"NC-r2-da0002",
environment="离线单元(注入宿主)",
given="模型不在角色策略或探针输出不确认或未完成",
when="执行运行配置验证",
then=["拒绝生成验证证据"],
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
)
def test_探针在模型不在策略或输出不确认时拒绝__da0002() -> None:
策略 = _策略()
不在策略 = 角色运行探针(策略, 宿主工厂=lambda 提供方, 凭据: _假宿主(_成功结果("other-model")))
with pytest.raises(Muse错误):
不在策略.请求("task", "extractor", _配置(model="glm-9.9"))
不确认 = 角色运行探针(
策略, 宿主工厂=lambda 提供方, 凭据: _假宿主(_成功结果(文本='{"ok": false}'))
)
with pytest.raises(Muse错误, match="未返回可用确认"):
不确认.核对(不确认.请求("task", "extractor", _配置()), _成功结果(文本='{"ok": false}'))
未完成 = 角色运行探针(
策略,
宿主工厂=lambda 提供方, 凭据: _假宿主(
模型结果("incomplete", "", "claude-opus-4-8", 模型用量(5, 0), None)
),
)
with pytest.raises(Muse错误):
未完成.核对(
未完成.请求("task", "extractor", _配置()),
模型结果("incomplete", "", "claude-opus-4-8", None),
)
@pytest.mark.case_id(
"NC-r2-da0004",
environment="离线单元(注入宿主)",
given="运行配置声明 thinking=off,提供方回包仍带推理内容",
when="执行运行配置验证",
then=["在验证阶段拒绝,不把无法关闭推理的协议当成通过"],
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
)
def test_探针拒绝声明关闭推理却仍返回推理的提供方__da0004() -> None:
推理结果 = 模型结果(
"completed",
'{"ok": true}',
"claude-opus-4-8",
模型用量(5, 9),
"探针响应-2",
回放协议="chat-completions",
回放内容=(),
推理字符数=128,
)
探针 = 角色运行探针(_策略(), 宿主工厂=lambda 提供方, 凭据: _假宿主(推理结果))
with pytest.raises(Muse错误, match="thinking=off"):
探针.核对(探针.请求("task", "extractor", _配置()), 推理结果)
@pytest.mark.case_id(
"NC-r2-da0003",
environment="离线单元(注入宿主)",
given="配置固定的资源发布身份与当前安装不一致",
when="执行运行配置验证",
then=["按资源发布身份拒绝"],
contract="docs/系统架构/新版设计/模块设计/S02-任务运行.md",
)
def test_探针核对资源与角色策略版本__da0003() -> None:
策略 = _策略()
探针 = 角色运行探针(策略, 宿主工厂=lambda 提供方, 凭据: _假宿主(_成功结果()))
其他资源 = 运行配置内容(
"direct",
"1",
策略.定义["version"],
"别的发布身份",
"账户-1",
{"extractor": {"provider": "网关", "model": "claude-opus-4-8", "thinking": "off"}},
(凭据引用("key", "受控存储", "/不存在/凭据.txt"),),
(提供方配置("网关", "chat-completions", "http://127.0.0.1:1/v1/chat/completions", "key"),),
"费率-1",
)
with pytest.raises(Muse错误, match="资源或角色策略"):
探针.请求("task", "extractor", 其他资源)
def _用途():
from muse.共享.调用身份 import 用途
return 用途.生产
@pytest.mark.case_id("NC-O02-PROBE-CAPABILITY")
def test_验证任务只接受固定草案且拒绝工具与业务输入(monkeypatch):
from contextlib import nullcontext
from dataclasses import replace
from types import SimpleNamespace
import muse.任务运行.任务领取 as 领取模块
import muse.任务运行.配置探针 as 探针模块
from muse.任务运行.接口 import 内容哈希
from muse.任务运行.配置版本 import 配置快照
内容 = _配置()
配置 = 配置快照("draft", "1", 内容哈希(内容.冻结()), 内容)
任务 = SimpleNamespace(任务ID="task")
领取 = SimpleNamespace(任务ID="task", 步骤ID="extractor")
连 = SimpleNamespace(transaction=lambda: nullcontext())
数据库 = SimpleNamespace(用途=_用途(), 连接=lambda: nullcontext(连))
运行 = SimpleNamespace(
数据库=数据库,
重验执行范围=lambda _: None,
读取任务=lambda _: 任务,
)
执行器 = SimpleNamespace(
运行=运行, 角色=_策略(), 固定配置=配置, 计价=SimpleNamespace(版本=内容.计价版本)
)
上下文 = SimpleNamespace(任务=任务, 领取=领取)
monkeypatch.setattr(领取模块, "校验领取", lambda *_: None)
monkeypatch.setattr(探针模块, "读取探针绑定于", lambda *_: 配置)
请求 = 探针模块.探针请求("task", "extractor", 内容, _策略())
assert 探针模块.核对探针请求(执行器, 上下文, 请求)[0] == 请求
for 篡改 in (
replace(请求, 用户输入="读取作者正文"),
replace(请求, provider="other-endpoint"),
replace(请求, 工具=({"name": "write_canonical"},)),
replace(请求, 最大输出token=65),
):
with pytest.raises(Muse错误, match="零工具请求"):
探针模块.核对探针请求(执行器, 上下文, 篡改)
执行器.固定配置 = replace(配置, 版本="other")
with pytest.raises(Muse错误, match="草案端点"):
探针模块.核对探针请求(执行器, 上下文, 请求)