NL 线(含其并入的袁聪场外/推广域)。唯一冲突是 .gitignore —— 双方都往同一区域加了 .workdir/,取对方版本(他的更完整,含 .tmp/ 与说明),顺带修掉我之前用 Add-Content -Encoding utf8 造成的编码混合(read 工具当时报 invalid UTF-8)。 合并后修的问题 —— 都不是"改别人业务逻辑",是让门禁能绿: 1. 缺运行依赖 python-docx。document_parser.py 解析 .docx 用它,但 requirements.txt 与 pyproject.toml 都没声明 —— 别人环境跑知识入库会直接 ModuleNotFoundError: No module named 'docx'。已补声明。 2. ruff 7 项:其中 tests/conftest.py 的 F821 Undefined name 'Path'(他的 tmp_path 修复 写了字符串注解 "Path" 却漏 import,运行时不求值所以没炸,但 mypy/ruff 会抓)、 tools/publish_customer_service_config.py 的 F841 inherited_keys 死变量(他改同 key 覆盖、换成 inherited_only 后忘删旧的)、3 处 E501,另 2 项 ruff --fix 自动修复。 3. 合规基线种子未跑:integration 的 test_compliance_seed_mysql 4 个用例要求 agent_negative_word 有 7 条 active 且已复核、agent_reply_template 覆盖 6 场景。 跑 tools/seed_compliance_baseline.py(11 条 active 规则 / 6 个场景模板)后 80 passed。 验证:ruff 干净 / mypy 180 文件 0 错 / unit+contract 1140 passed / integration 80 passed / 表数 68(alembic 已在 20260911_merge_risk_heads)。 唯一失败 tests/unit/repository/test_fund_readonly_contract.py 是双方一致的既有缺陷: 它断言 Base.metadata 里的 fin_* 表集合,而实测为空集 —— 即该测试依赖别的测试先导入模型的 副作用,单独跑必失败。NL 方也明确"不修不报",此处照办,仅记录。
52 lines
2.2 KiB
Python
52 lines
2.2 KiB
Python
"""治理审计落库的字段契约(`agent_type` + 治理是否改写过输出)。
|
||
|
||
**为什么需要这个文件**:治理层(`PlatformGovernance.review`)会**改写对外输出** ——
|
||
追加固定免责声明(门禁 F5),命中禁用词时还会把整条回复替换成安全话术。
|
||
架构师评审明确要求:"治理层改写了对外输出,事后必须能追溯到是哪个 Agent 触发的。"
|
||
|
||
所以 `agent.run_completed` 那条审计的 `detail` 里必须带 `agent_type`;
|
||
本文件锁住这个契约,避免以后有人"顺手精简"掉它。
|
||
"""
|
||
|
||
from app.core.contracts import AgentResult, CoreResult
|
||
from app.service.agent_persistence_service import _governance_rewrote
|
||
|
||
|
||
def _result(text: str, *, transfer_required: bool = False) -> AgentResult:
|
||
return AgentResult(
|
||
run_id="r",
|
||
result=CoreResult(text=text, transfer_required=transfer_required),
|
||
)
|
||
|
||
|
||
def test_plain_answer_without_disclaimer_counts_as_rewritten() -> None:
|
||
"""客服出口的正文**不带**免责声明(话术由治理层统一注入)。
|
||
|
||
所以"Agent 产出的纯粹正文"在治理前后的差异就是那句被追加的话术——
|
||
见到治理后的正文(含话术)即说明治理改写过。
|
||
"""
|
||
assert _governance_rewrote(
|
||
_result(
|
||
"交易日 15:00 前提交,T+1 日确认份额。\n\n"
|
||
"本内容仅为投资分析参考,不构成任何直接投资建议。"
|
||
)
|
||
) is True
|
||
|
||
|
||
def test_transfer_required_counts_as_rewritten() -> None:
|
||
"""拦截分支:治理层把回复替换成"该内容需要人工核实"并置 `transfer_required`。"""
|
||
assert _governance_rewrote(
|
||
_result("该内容需要人工核实。基金投资存在风险,本系统不代客交易。",
|
||
transfer_required=True)
|
||
) is True
|
||
|
||
|
||
def test_untouched_text_is_not_marked_as_rewritten() -> None:
|
||
"""没有任何治理痕迹时不标记(避免"全部记 True"让这个字段失去信息量)。"""
|
||
assert _governance_rewrote(_result("交易日 15:00 前提交,T+1 日确认份额。")) is False
|
||
|
||
|
||
def test_empty_text_does_not_crash() -> None:
|
||
"""空正文(异常路径)不得让审计写入抛异常。"""
|
||
assert _governance_rewrote(_result("")) is False
|