Files
X-Financial/server/src/app/services/risk_rule_generation_interpreter.py
caoxiaozhu 7989f3a159 feat: 新增风险图谱算法与系统仪表盘及操作反馈体系
后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
2026-05-30 15:46:51 +08:00

76 lines
2.6 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.
from __future__ import annotations
from typing import Any
COMPOSITE_RULE_TEMPLATE_KEY = "composite_rule_v1"
COMPOSITE_RULE_OPERATORS = {
"exists_any",
"exists_all",
"all_present",
"in_scope",
"not_in_scope",
"not_in_set",
"overlap",
"not_overlap",
"date_outside_range",
"numeric_compare",
"duplicate_value",
"contains_any",
"not_contains_any",
}
def build_dsl_from_semantic_plan(semantic_plan: dict[str, Any]) -> dict[str, Any]:
"""把模型语义计划转换成可交给 validator 继续规范化的 DSL 草稿。"""
if not isinstance(semantic_plan, dict):
return {}
text_parts = _semantic_text_parts(semantic_plan)
field_keys = _semantic_field_keys(semantic_plan)
if not text_parts and not field_keys:
return {}
return {
"template_key": COMPOSITE_RULE_TEMPLATE_KEY,
"field_keys": field_keys,
"description": str(semantic_plan.get("rule_intent") or "").strip(),
"condition_summary": "".join(text_parts)[:800],
"keywords": [],
"rule_ir": {
"facts": field_keys,
"conditions": text_parts,
"hit_logic": "由 DSL validator 根据字段本体和语义步骤生成受控条件",
},
}
def _semantic_text_parts(semantic_plan: dict[str, Any]) -> list[str]:
parts: list[str] = []
for key in ("rule_intent", "scope", "judgment_steps", "exception_conditions", "risk_action"):
parts.extend(_flatten_semantic_text(semantic_plan.get(key)))
return [item for index, item in enumerate(parts) if item and item not in parts[:index]]
def _semantic_field_keys(semantic_plan: dict[str, Any]) -> list[str]:
keys: list[str] = []
for value in (semantic_plan.get("required_fields"), semantic_plan.get("fields")):
for item in value if isinstance(value, list) else []:
key = item if isinstance(item, str) else next(
(item.get(name) for name in ("field", "key", "field_key") if isinstance(item, dict) and item.get(name)),
"",
)
text = str(key or "").strip()
if "." in text and text not in keys:
keys.append(text)
return keys
def _flatten_semantic_text(value: Any) -> list[str]:
if isinstance(value, str):
return [value.strip()] if value.strip() else []
if isinstance(value, list):
return [item for value_item in value for item in _flatten_semantic_text(value_item)]
if isinstance(value, dict):
return [item for value_item in value.values() for item in _flatten_semantic_text(value_item)]
return []