实现侧: - 上下文:任务范围拆分为 范围校验/范围授权;索引按可发现口径重建、索引新鲜度改对称差;依赖校验统一快照漂移说明。 - 知识方法:方法与材料读取口径统一;超限方法材料按可选省略,核对路径不再二次计费;删除无合同的读时重算。 - 任务运行:新增 context.usage/tool.denied 事件类型;连接池常驻并在装配生命周期内开关;调用结算与核对分列。 - 效果评测/审校修订/交付连载/作者经验/作品规划:凭据冻结、标定消费、导出补证、事实引文核对等收尾修复。 - 资源加载:能力正文不再夹带索引用的导航注记(该注记此前进入角色与技能的模型提示)。 - 元数据:受保护骨架与代码保护属性对齐;字段校验与内置结构口径同步。 - 基础设施:环境预检进入装配生命周期;数据库连接运行期字段不参与相等比较;索引指纹归一化 jsonb 浮点。 - 删除被替代实现:7 份旧提示词模板与空壳 资料来源 读取器。 用例侧: - 用例身份与导航元信息迁移;夹具补生命周期、同库暴露与模板封存; - 本轮定向修复:方法材料省略、事实引文、迁移回执、额度与暂停用例、慢用例超时预算等。
227 lines
8.7 KiB
Python
227 lines
8.7 KiB
Python
"""运行配置角色探针:证据结构、身份核对与失败关闭;宿主为替身,不证明真实链路。"""
|
||
|
||
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="草案端点"):
|
||
探针模块.核对探针请求(执行器, 上下文, 请求)
|