feat(platform): close AI expense value loop

Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
caoxiaozhu
2026-07-17 14:14:08 +08:00
parent 242d68c36f
commit 787bc3a481
507 changed files with 82072 additions and 6344 deletions

View File

@@ -1,9 +1,6 @@
from __future__ import annotations
from collections.abc import Generator
from datetime import datetime
from decimal import Decimal
from unittest.mock import MagicMock, patch
from unittest.mock import patch
import pytest
from sqlalchemy import create_engine
@@ -12,11 +9,8 @@ from sqlalchemy.pool import StaticPool
from app.db.base import Base
from app.models.agent_asset import AgentAsset, AgentAssetTestRun
from app.models.employee import Employee
from app.models.financial_record import ExpenseClaim
from app.models.golden_case import GoldenCase
from app.services.risk_rule_golden_evaluator import (
GoldenEvalReport,
RiskRuleGoldenEvaluator,
_aggregate,
_run_single_case,
@@ -141,19 +135,36 @@ def test_aggregate_with_failure() -> None:
results = [
GoldenCaseResult("1", "a", True, True, "high", "high", True),
GoldenCaseResult("2", "b", True, False, "high", "none", False), # FP
GoldenCaseResult("2", "b", True, False, "high", "none", False), # FN
]
report = _aggregate(results)
assert report.passed_count == 1
assert report.failed_count == 1
assert report.accuracy == 0.5
assert report.all_passed is False
assert report.precision == 0.5 # 1/(1+1)
assert report.precision == 1.0
assert report.recall == 0.5 # 1/(1+1)
def test_aggregate_false_positive_reduces_precision_not_recall() -> None:
from app.services.risk_rule_golden_evaluator import GoldenCaseResult
report = _aggregate(
[
GoldenCaseResult("1", "a", True, True, "high", "high", True),
GoldenCaseResult("2", "b", False, True, "", "high", False),
]
)
assert report.precision == 0.5
assert report.recall == 1.0
def test_evaluate_for_rule_empty_returns_passed() -> None:
with _build_session() as db:
report = RiskRuleGoldenEvaluator().evaluate_for_rule(db, _keyword_manifest(), "risk.test.keyword")
report = RiskRuleGoldenEvaluator().evaluate_for_rule(
db, _keyword_manifest(), "risk.test.keyword"
)
assert report.total == 0
assert report.all_passed is True
@@ -163,7 +174,9 @@ def test_evaluate_for_rule_all_pass() -> None:
db.add(_golden_case("g1", reason="虚假发票", expected_hit=True))
db.add(_golden_case("g2", reason="正常报销", expected_hit=False))
db.commit()
report = RiskRuleGoldenEvaluator().evaluate_for_rule(db, _keyword_manifest(), "risk.test.keyword")
report = RiskRuleGoldenEvaluator().evaluate_for_rule(
db, _keyword_manifest(), "risk.test.keyword"
)
assert report.total == 2
assert report.all_passed is True
assert report.accuracy == 1.0
@@ -172,9 +185,11 @@ def test_evaluate_for_rule_all_pass() -> None:
def test_evaluate_for_rule_with_failure() -> None:
with _build_session() as db:
db.add(_golden_case("g1", reason="虚假发票", expected_hit=False)) # 期望不命中但实际命中
db.add(_golden_case("g2", reason="正常报销", expected_hit=True)) # 期望命中但实际不命中
db.add(_golden_case("g2", reason="正常报销", expected_hit=True)) # 期望命中但实际不命中
db.commit()
report = RiskRuleGoldenEvaluator().evaluate_for_rule(db, _keyword_manifest(), "risk.test.keyword")
report = RiskRuleGoldenEvaluator().evaluate_for_rule(
db, _keyword_manifest(), "risk.test.keyword"
)
assert report.total == 2
assert report.all_passed is False
assert report.failed_count == 2
@@ -206,6 +221,7 @@ def test_require_pass_passes_when_all_green() -> None:
# 应写一条 test_type='golden' 记录
run = db.query(AgentAssetTestRun).filter_by(asset_id="a1", test_type="golden").one()
assert run.passed is True
assert run.status == "passed"
def test_require_pass_raises_on_failure() -> None:
@@ -220,18 +236,22 @@ def test_require_pass_raises_on_failure() -> None:
)
run = db.query(AgentAssetTestRun).filter_by(asset_id="a2", test_type="golden").one()
assert run.passed is False
assert run.status == "failed"
assert run.result_json["failure_reason"] == "golden_case_regression"
def test_require_pass_empty_golden_set_passes() -> None:
def test_require_pass_empty_golden_set_fails_closed() -> None:
with _build_session() as db:
asset = _asset("a3", "R3")
db.add(asset)
db.commit()
report = RiskRuleGoldenEvaluator().require_pass(
db, asset, "v1", _keyword_manifest(), "risk.test.keyword", actor="tester"
)
assert report.total == 0
assert report.all_passed is True
with pytest.raises(PermissionError, match="缺少必要"):
RiskRuleGoldenEvaluator().require_pass(
db, asset, "v1", _keyword_manifest(), "risk.test.keyword", actor="tester"
)
run = db.query(AgentAssetTestRun).filter_by(asset_id="a3", test_type="golden").one()
assert run.passed is False
assert run.result_json["failure_reason"] == "missing_active_golden_cases"
def test_require_pass_respects_feature_flag(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -246,17 +266,45 @@ def test_require_pass_respects_feature_flag(monkeypatch: pytest.MonkeyPatch) ->
db, asset, "v1", _keyword_manifest(), "risk.test.keyword", actor="tester"
)
assert report.total == 0
assert report.gate_status == "skipped"
run = db.query(AgentAssetTestRun).filter_by(asset_id="a4", test_type="golden").one()
assert run.status == "skipped"
assert run.result_json["failure_reason"] == "gate_explicitly_disabled"
def test_require_pass_swallows_evaluator_exception() -> None:
def test_require_pass_records_and_blocks_precondition_error() -> None:
with _build_session() as db:
asset = _asset("a-precondition", "R-PRECONDITION")
db.add(asset)
db.commit()
with pytest.raises(PermissionError, match="前置条件失败"):
RiskRuleGoldenEvaluator().require_pass(
db,
asset,
"v1",
{},
"",
actor="tester",
precondition_error="missing_rule_document",
)
run = db.query(AgentAssetTestRun).filter_by(asset_id=asset.id).one()
assert run.status == "failed"
assert run.result_json["failure_reason"] == "missing_rule_document"
def test_require_pass_fails_closed_on_evaluator_exception() -> None:
with _build_session() as db:
asset = _asset("a5", "R5")
db.add(asset)
db.commit()
evaluator = RiskRuleGoldenEvaluator()
with patch.object(evaluator, "evaluate_for_rule", side_effect=RuntimeError("boom")):
report = evaluator.require_pass(
db, asset, "v1", _keyword_manifest(), "risk.test.keyword", actor="tester"
)
assert report.total == 0
assert report.all_passed is True # 降级放行
with pytest.raises(PermissionError, match="fail-closed"):
evaluator.require_pass(
db, asset, "v1", _keyword_manifest(), "risk.test.keyword", actor="tester"
)
run = db.query(AgentAssetTestRun).filter_by(asset_id="a5", test_type="golden").one()
assert run.passed is False
assert run.result_json["failure_reason"] == "evaluation_error:RuntimeError"