feat: 新增风险图谱算法与系统仪表盘及操作反馈体系

后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-30 15:46:51 +08:00
parent 4c59941ec6
commit 7989f3a159
314 changed files with 30073 additions and 20626 deletions

View File

@@ -432,7 +432,8 @@ class AgentAssetRiskRuleTestingMixin:
case: AgentAssetRiskRuleSampleCase,
) -> dict[str, Any]:
claim, contexts = self._build_synthetic_claim(case.values, manifest)
result = RiskRuleTemplateExecutor().evaluate(manifest, claim=claim, contexts=contexts)
execution = RiskRuleTemplateExecutor().evaluate_with_trace(manifest, claim=claim, contexts=contexts)
result = execution["result"]
actual_hit = result is not None
actual_severity = (
str((manifest.get("outcomes") or {}).get("fail", {}).get("severity") or "").strip()
@@ -455,11 +456,13 @@ class AgentAssetRiskRuleTestingMixin:
"passed": passed,
"message": str(result.get("message") or "") if isinstance(result, dict) else "",
"evidence": result.get("evidence") if isinstance(result, dict) else {},
"trace": execution["trace"] if isinstance(execution.get("trace"), dict) else {},
}
def _run_claim_scenario(self, manifest: dict[str, Any], claim: ExpenseClaim) -> dict[str, Any]:
contexts = ExpenseClaimService(self.db)._build_claim_attachment_contexts(claim)
result = RiskRuleTemplateExecutor().evaluate(manifest, claim=claim, contexts=contexts)
execution = RiskRuleTemplateExecutor().evaluate_with_trace(manifest, claim=claim, contexts=contexts)
result = execution["result"]
hit = result is not None
return {
"claim_id": claim.id,
@@ -476,6 +479,7 @@ class AgentAssetRiskRuleTestingMixin:
else "none",
"message": str(result.get("message") or "") if isinstance(result, dict) else "",
"evidence": result.get("evidence") if isinstance(result, dict) else {},
"trace": execution["trace"] if isinstance(execution.get("trace"), dict) else {},
}
def _build_synthetic_claim(
@@ -617,6 +621,9 @@ class AgentAssetRiskRuleTestingMixin:
template_key = str(manifest.get("template_key") or "").strip()
params = manifest.get("params") if isinstance(manifest.get("params"), dict) else {}
if template_key == "field_compare_v1":
if str(params.get("semantic_type") or "").strip() in {"travel_city_consistency", "travel_route_city_consistency"}:
values.update({"attachment.hotel_city": "上海" if hit else "北京", "attachment.route_cities": ["上海"] if hit else ["北京"], "claim.location": "北京", "item.item_location": "北京", "employee.location": "北京"})
return values
condition = next(
(item for item in params.get("conditions", []) if isinstance(item, dict)),
{},