muse-agent-example/tests/集成/test_参考挑选接入.py
zizi c777c89e4a feat(接入): 场景检索与片段挑选接入——HTTP端点、前端语料源切换与反馈语料类型
- 资料研究路由:scene-slices/search 检索与 reindex 构建端点
- 参考挑选反馈请求增语料类型(method/slice,缺省method存量兼容);信号聚合与明细只计方法语料,片段挑选只落账留痕
- 选段改写页:语料源切换(方法卡/场景片段),片段卡显示书名+预览,挑选进 slice_references,落账按源分派
- 集成a10037:片段挑选HTTP落账不进方法信号轨;场景检索端点真实接缝
2026-09-20 00:17:11 +08:00

190 lines
7.4 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.

"""参考挑选反馈的真实 CLI/HTTP 接缝:落账、信号聚合读回;不经浏览器。"""
import json
import subprocess
import sys
import pytest
import test_参考改写 as 改写测试
from fastapi.testclient import TestClient
from muse.接入.http.应用 import 创建应用
from muse.资源加载 import 加载清单
from muse.配置 import 读取配置
pytestmark = pytest.mark.数据库
生成环境 = 改写测试.生成环境
参考环境 = 改写测试.参考环境
片段环境 = 改写测试.片段环境
def _配置(env, tmp_path):
secret = tmp_path / "author-password"
secret.write_text("synthetic-evaluation-only")
secret.chmod(0o600)
pool = env["库"]
config = tmp_path / f"{pool.用途.value}.toml"
config.write_text(
'["数据库"]\n"取值方式"="受控存储"\n"位置"='
+ json.dumps(pool.引用.位置)
+ '\n["运行"]\n"用途"='
+ json.dumps(pool.用途.value)
+ '\n["资源"]\n"发布身份"="test"\n["HTTP"]\n"作者ID"="eval-author"'
+ '\n"口令文件"='
+ json.dumps(str(secret))
+ '\n"公开地址"="http://testserver"\n"允许来源"=["http://testserver"]\n'
)
path = config
path.write_text(
path.read_text()
.replace("eval-author", env["作者"].作者)
.replace(
'"发布身份"="test"',
'"发布身份"=' + json.dumps(加载清单()["构建身份"]),
)
)
return path
def _cli(tmp_path, config, 动作, *args):
response = subprocess.run(
[sys.executable, "-I", "-m", "muse", "经验", str(config), 动作, *map(str, args)],
cwd=tmp_path,
capture_output=True,
text=True,
timeout=30,
)
assert response.returncode == 0, response.stderr
return json.loads(response.stdout)
@pytest.mark.case_id(
"NC-w32-a10013",
environment="隔离PG、实际CLI子进程与合成HTTP,不经浏览器",
given="真实作者、已确认方法版本与检索回执形状的候选清单",
when="经HTTP保存参考挑选反馈并按领域读回信号,再经CLI读同一信号",
then=["反馈只追加落账;HTTP与CLI读回一致的选用/拒绝计数"],
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#偏好双轨关系",
)
def test_HTTP与CLI读回一致的挑选信号__a10013(参考环境, tmp_path):
env = 参考环境
config = _配置(env, tmp_path)
版本 = env["参考版本"]
body = {
"command_id": "http-ref-sel-1",
"selection": {
"query": "机甲近身刀法",
"domain": "combat",
"work_id": "gen-work",
"quote": "",
"index_generation": 0,
"种类": "参考挑选",
"candidates": [{"method_version_id": 版本, "rank": 1, "score": 0.031}],
"selected": [版本],
"rejected": [],
},
}
with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client:
assert (
client.post(
"/api/v1/session", json={"password": "synthetic-evaluation-only"}
).status_code
== 200
)
saved = client.post("/api/v1/reference-selections", json=body)
assert saved.status_code == 201, saved.text
assert saved.json()["payload"]["种类"] == "参考挑选"
# 幂等重放:同命令同内容返回原记录
replay = client.post("/api/v1/reference-selections", json=body)
assert replay.status_code == 201
assert replay.json()["feedback_id"] == saved.json()["feedback_id"]
signals = client.get("/api/v1/reference-selection-signals", params={"domain": "combat"})
assert signals.status_code == 200
rows = {行["method_version_id"]: 行 for 行 in signals.json()}
assert rows[版本]["选用次数"] == 1
assert rows[版本]["拒绝次数"] == 0
assert rows[版本]["选中位次"] == [1]
cli = _cli(tmp_path, config, "挑选信号", "combat")
assert {行["method_version_id"]: 行["选用次数"] for 行 in cli} == {版本: 1}
@pytest.mark.case_id(
"NC-w32-a10014",
environment="隔离PG与合成HTTP,不经浏览器",
given="选中与拒绝重叠的非法请求",
when="经HTTP保存参考挑选反馈",
then=["边界校验拒绝,不落账"],
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#偏好双轨关系",
)
def test_非法挑选请求在边界拒绝__a10014(参考环境, tmp_path):
env = 参考环境
config = _配置(env, tmp_path)
版本 = env["参考版本"]
body = {
"command_id": "http-ref-sel-bad",
"selection": {
"query": "机甲近身刀法",
"domain": "combat",
"candidates": [{"method_version_id": 版本, "rank": 1, "score": 0.03}],
"selected": [版本],
"rejected": [版本],
},
}
with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client:
assert (
client.post(
"/api/v1/session", json={"password": "synthetic-evaluation-only"}
).status_code
== 200
)
response = client.post("/api/v1/reference-selections", json=body)
assert response.status_code >= 400
signals = client.get("/api/v1/reference-selection-signals")
assert all(行["method_version_id"] != 版本 for 行 in signals.json())
@pytest.mark.case_id(
"NC-w33-a10037",
environment="隔离PG与合成HTTP,不经浏览器",
given="已构建的场景切片索引与片段语料挑选",
when="经HTTP落账片段挑选反馈再读方法信号",
then=["片段挑选落账成功;不进入方法信号轨(语料类型过滤)"],
contract="docs/系统架构/新版设计/模块设计/B07-作者经验.md#偏好双轨关系",
)
def test_片段语料挑选不进方法信号__a10037(片段环境, tmp_path):
env = 片段环境
config = _配置(env, tmp_path)
片段 = env["片段"]
body = {
"command_id": "http-slice-sel-1",
"selection": {
"query": "刀锋划过装甲",
"domain": "other",
"work_id": "gen-work",
"quote": "",
"index_generation": 0,
"语料类型": "slice",
"种类": "参考挑选",
"candidates": [{"method_version_id": 片段["slice_id"], "rank": 1, "score": 0.0}],
"selected": [片段["slice_id"]],
"rejected": [],
},
}
with TestClient(创建应用(读取配置(config)), headers={"Origin": "http://testserver"}) as client:
assert (
client.post(
"/api/v1/session", json={"password": "synthetic-evaluation-only"}
).status_code
== 200
)
saved = client.post("/api/v1/reference-selections", json=body)
assert saved.status_code == 201, saved.text
assert saved.json()["payload"]["语料类型"] == "slice"
检索 = client.get("/api/v1/scene-slices/search", params={"q": "刀锋", "limit": 3})
assert 检索.status_code == 200, 检索.text
信号 = client.get("/api/v1/reference-selection-signals", params={"domain": ""})
assert 信号.status_code == 200, 信号.text
行集 = 信号.json()
assert all(r["method_version_id"] != 片段["slice_id"] for r in 行集)