muse-agent-example/humanization/tests/test_humanization_v2.py
zizi 061010ba1b 框架: 共享运行时装成可安装包,切断 Skill 之间的 sys.path 互指
被多个 Skill 或看板消费的连接、模型、嵌入、Claude 运行时与声音账入口
各只保留一份实现;Skill 只留 CLI/落库,看板只读 muse-db,门禁锁死跨域注入。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 00:45:20 +08:00

213 lines
10 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.

#!/usr/bin/env python3
"""人感 v2 共享骨架与技能 5 生命周期的离线负路。"""
from __future__ import annotations
import copy
import importlib.util
import json
import pathlib
import sys
import tempfile
import unittest
ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from deai import evaluation, gates, load # noqa: E402
from deai.baseline import BaselineContractError, draft_ledger, profile_text, run_voice_gate, validate_ledger # noqa: E402
from deai.carriers import carrier_at, carrier_ranges # noqa: E402
from deai.diagnose import run_deterministic_rules # noqa: E402
def load_script(name: str, path: pathlib.Path):
spec = importlib.util.spec_from_file_location(name, path)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
return module
MINE = load_script(
"mine_ai_flavor_v2",
ROOT.parent / ".claude/skills/capture-ai-flavor-cases/scripts/mine_ai_flavor.py",
)
CAPTURE = load_script(
"capture_cases_v2",
ROOT.parent / ".claude/skills/capture-ai-flavor-cases/scripts/capture_cases.py",
)
class HumanizationV2Test(unittest.TestCase):
def test_baseline_draft_exposes_sampling_and_unknowns(self):
text = "".join(f"第{i}句,门外的雨还在落下,青石路上没有一个人。\n" for i in range(1, 65))
ledger = draft_ledger("synthetic:demo", [{"source_ref": "demo.txt", "text": text}])
self.assertEqual(ledger["status"], "candidate")
self.assertTrue(ledger["sampling"]["sample_sufficient"])
self.assertIn("characters", ledger["unknown_fields"])
self.assertTrue(ledger["narrator"]["metrics"]["sentence_length"]["median"] > 0)
def test_baseline_rejects_source_hash_drift(self):
with self.assertRaisesRegex(ValueError, "source_sha256"):
draft_ledger(
"synthetic:demo",
[{"source_ref": "demo.txt", "source_sha256": "0" * 64, "text": "甲走进门。"}],
)
def test_baseline_profile_is_deterministic(self):
text = "甲走进门。乙关上窗。雨落下来。"
self.assertEqual(profile_text(text), profile_text(text))
def test_baseline_duplicate_voice_ownership_is_rejected_by_skill_validator(self):
ledger = {
"schema_version": "voice-baseline-v1",
"work_ref": "synthetic:demo",
"narrator": {"sentence_habits": [], "punctuation_habits": []},
"characters": {
"甲": {"verbal_tics": ["嗯"], "sample_lines": ["嗯"]},
"乙": {"verbal_tics": ["嗯"], "sample_lines": ["嗯"]},
},
"untouchable_verbal_tics": {"甲": ["嗯"], "乙": ["嗯"]},
"protected_spans": [], "blacklist": [],
}
with self.assertRaisesRegex(BaselineContractError, "同时归属"):
validate_ledger(ledger, work_ref="synthetic:demo")
def test_carrier_scope_masks_dialogue_and_marks_carve_out(self):
samples = load.load_samples()
rules = load.load_rules(samples=samples)
text = "「值得注意的是,这里是对白。」值得注意的是,这里是叙述。"
artifact = run_deterministic_rules(text, [rules["l002"]], "v-test", "Audit")
self.assertEqual(len(artifact["findings"]), 1)
self.assertEqual(artifact["findings"][0]["carrier"], "narration")
self.assertEqual(artifact["findings"][0]["decision_proposal"], "ask")
self.assertEqual(carrier_at(text, 1, 7, carrier_ranges(text)), "dialogue")
def test_deterministic_rules_have_contract_replay(self):
"""确定性触发器规则(regex/handler/density)都要能回放四类夹具。
blocking 类机械规则的默认建议就是 repair,属于其合同本身;
safe_default 约束只对非 blocking 规则断言。
"""
samples = load.load_samples()
rules = load.load_rules(samples=samples)
deterministic = [rule for rule in rules.values() if rule["trigger"]["type"] != "model_judgment"]
self.assertGreaterEqual(len(deterministic), 2)
for rule in deterministic:
if rule["default_disposition"] == "blocking":
continue
report = evaluation.evaluate_rule_contract(rule, samples)
self.assertTrue(report["contract_pass"], report)
self.assertEqual(report["dataset_kind"], "shipped_contract_samples")
def test_modality_and_time_fact_gates_block_known_regressions(self):
modality = gates.modality_attribution([{
"original_exact": "多半背景不凡", "replacement": "背景都不凡",
}])
temporal = gates.temporal_fact_delta([{
"original_exact": "谁也不知道", "replacement": "那年秋分的夜里,谁也不知道",
}])
numeric_temporal = gates.temporal_fact_delta([{
"original_exact": "谁也不知道", "replacement": "2026年3天后的夜里,谁也不知道",
}])
self.assertFalse(modality["pass"])
self.assertFalse(temporal["pass"])
self.assertFalse(numeric_temporal["pass"])
def test_voice_gate_does_not_turn_missing_or_candidate_baseline_into_pass(self):
result = run_voice_gate("甲走进门。乙关上窗。", "甲走进门。乙关上窗。", None)
candidate = run_voice_gate(
"甲走进门。乙关上窗。", "甲走进门。乙关上窗。", {"status": "candidate"}
)
self.assertIsNone(result["pass"])
self.assertEqual(result["status"], "unverified")
self.assertIsNone(candidate["pass"])
self.assertEqual(candidate["status"], "unverified")
def test_rule_activation_requires_holdout_and_approver(self):
samples = load.load_samples()
rules = load.load_rules(samples=samples)
# s002 已 active;激活门行为用它的 candidate 副本演练,不依赖库里必须留有候选。
candidate = {**rules["s002"], "status": "candidate"}
version = candidate["version"]
contract = evaluation.evaluate_rule_contract(candidate, samples)
with self.assertRaises(evaluation.EvaluationError):
evaluation.activate_rule(candidate, contract_report=contract, holdout_report=None, approver="qingse")
with self.assertRaises(evaluation.EvaluationError):
evaluation.activate_rule(
candidate,
contract_report={**contract, "rule_id": "s002", "rule_version": version},
holdout_report={"rule_id": "s002", "eligible": True},
approver="qingse",
)
holdout = evaluation.evaluate_holdout(candidate, {
"dataset_id": "holdout-s002-v1",
"sf_total": 5,
"sf_hit": 5,
"snf_total": 5,
"snf_false_repair": 0,
"boundary_total": 2,
"boundary_false_repair": 0,
"regression_total": 2,
"regression_safe": 2,
})
full_holdout = {
**contract,
"rule_id": "s002", "rule_version": version,
"eligible": True, "holdout": holdout,
}
with self.assertRaises(evaluation.EvaluationError):
evaluation.activate_rule(
candidate, contract_report={**contract, "rule_id": "s002", "rule_version": version},
holdout_report={**full_holdout, "rule_version": version + 1}, approver="qingse"
)
active = evaluation.activate_rule(
candidate, contract_report={**contract, "rule_id": "s002", "rule_version": version},
holdout_report=full_holdout, approver="qingse"
)
self.assertEqual(active["status"], "active")
self.assertEqual(active["activation"]["approved_by"], "qingse")
def test_mining_extra_sample_requires_canonical_verified_projection(self):
with tempfile.TemporaryDirectory() as tmp:
root = pathlib.Path(tmp)
source = root / "source.txt"
source.write_text("值得注意的是,门外下起了雨。", encoding="utf-8")
shadow = CAPTURE.capture_file(source, work_ref="synthetic:work", source_license="owned")[0]
annotated = CAPTURE.annotate_card(shadow, label="sf", carrier="narration")
verification = {"cards": [CAPTURE.revalidate_card(annotated, source_path=source)]}
canonical = CAPTURE.confirm_card(
annotated, reviewer="human", note="功能已确认", verification=verification
)
sample = CAPTURE.project_sample(canonical, verification=verification)
sample_path = root / "samples.json"
sample_path.write_text(json.dumps({"samples": [sample]}, ensure_ascii=False), encoding="utf-8")
with self.assertRaises(MINE.MiningError):
MINE._load_extra_samples([sample_path])
loaded = MINE._load_extra_samples(
[sample_path], cards={canonical["id"]: canonical}, verification=verification
)
self.assertEqual(loaded[sample["id"]]["case_card_id"], canonical["id"])
tampered = dict(sample, text="篡改后的样例")
sample_path.write_text(json.dumps({"samples": [tampered]}, ensure_ascii=False), encoding="utf-8")
with self.assertRaises(MINE.MiningError):
MINE._load_extra_samples(
[sample_path], cards={canonical["id"]: canonical}, verification=verification
)
def test_mining_evaluate_cli_is_offline_by_explicit_flag(self):
rule_path = ROOT / "rules/structural/s002.yaml"
with tempfile.TemporaryDirectory() as tmp:
output = pathlib.Path(tmp) / "evaluation.json"
code = MINE.main([
"evaluate-rule", "--rule", str(rule_path), "--output", str(output), "--offline",
])
self.assertEqual(code, 0)
report = json.loads(output.read_text(encoding="utf-8"))
self.assertTrue(report["contract_pass"])
self.assertFalse(report["eligible"])
self.assertEqual(report["persistence"]["status"], "offline")
if __name__ == "__main__":
unittest.main()