Files
group_xinghuo_jinrong/scripts/seed/import_compliance_rules.py
T
zhanghongyu_0626 70aa861983 feat(advisor-agent): Introduce advisor agent functionalities with compliance, KYC, and script templates
- Added new modules for advisor compliance, KYC sessions, and script templates, enhancing the advisor agent's capabilities.
- Implemented a comprehensive API structure under the `/api/advisor-agent` prefix, ensuring clear organization and access to new features.
- Established database models and repositories for compliance rules and KYC sessions, facilitating robust data management.
- Integrated exception handling and response models to improve error management and user feedback.
- Updated settings to include new configurations for compliance and KYC features, ensuring flexibility and adaptability.

This update significantly expands the advisor agent's functionality, providing essential tools for compliance and customer interaction while maintaining a structured API design.
2026-09-12 16:33:07 +08:00

179 lines
5.4 KiB
Python

"""Import compliance rules from the Sprint 1 Markdown dataset."""
from __future__ import annotations
import sys
from dataclasses import dataclass
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from app.model.entities_advisor import ComplianceRule
DEFAULT_DATASET = ROOT / "docs" / "开发文档" / "20-Sprint1首批合规规则数据集.md"
ALLOWED_RULE_TYPES = {"keyword", "regex", "semantic"}
ALLOWED_SEVERITIES = {"block", "warn", "info"}
REQUIRED_HEADERS = [
"序号",
"rule_type",
"pattern",
"severity",
"category",
"suggestion",
"is_active",
"priority",
"审核状态",
"备注",
]
@dataclass(frozen=True)
class SeedComplianceRule:
rule_code: str
rule_type: str
pattern: str
severity: str
category: str
suggestion: str | None
is_active: bool
priority: int
review_status: str
note: str
@dataclass(frozen=True)
class ImportResult:
total: int
created: int
updated: int
def load_rules_from_markdown(path: Path = DEFAULT_DATASET) -> list[SeedComplianceRule]:
rows = _read_markdown_table(path)
rules: list[SeedComplianceRule] = []
for row in rows:
sequence = int(row["序号"])
rule_type = row["rule_type"].lower()
severity = row["severity"].lower()
priority = int(row["priority"])
pattern = row["pattern"].strip()
category = row["category"].strip()
suggestion = row["suggestion"].strip() or None
note = row["备注"].strip()
if rule_type not in ALLOWED_RULE_TYPES:
raise ValueError(f"Unsupported rule_type at row {sequence}: {rule_type}")
if severity not in ALLOWED_SEVERITIES:
raise ValueError(f"Unsupported severity at row {sequence}: {severity}")
if not pattern:
raise ValueError(f"Missing pattern at row {sequence}")
if not category:
raise ValueError(f"Missing category at row {sequence}")
if priority < 1:
raise ValueError(f"Priority must be positive at row {sequence}")
rules.append(
SeedComplianceRule(
rule_code=f"CR-TEST-{sequence:03d}",
rule_type=rule_type,
pattern=pattern,
severity=severity,
category=category,
suggestion=suggestion,
is_active=_parse_bool(row["is_active"]),
priority=priority,
review_status=_normalize_review_status(row["审核状态"], note),
note=note,
)
)
return rules
def import_rules_from_markdown(
path: Path = DEFAULT_DATASET,
*,
actor_id: str = "seed:test_data",
) -> ImportResult:
from app.advisor_db import AgentSessionLocal
rules = load_rules_from_markdown(path)
created = 0
updated = 0
with AgentSessionLocal() as session:
for seed_rule in rules:
existing = session.query(ComplianceRule).filter(ComplianceRule.rule_code == seed_rule.rule_code).one_or_none()
values = {
"rule_type": seed_rule.rule_type,
"pattern": seed_rule.pattern,
"severity": seed_rule.severity,
"category": seed_rule.category,
"suggestion": seed_rule.suggestion,
"is_active": seed_rule.is_active,
"priority": seed_rule.priority,
"updated_by": actor_id,
}
if existing is None:
session.add(ComplianceRule(rule_code=seed_rule.rule_code, created_by=actor_id, **values))
created += 1
else:
for key, value in values.items():
setattr(existing, key, value)
updated += 1
session.commit()
return ImportResult(total=len(rules), created=created, updated=updated)
def _read_markdown_table(path: Path) -> list[dict[str, str]]:
if not path.exists():
raise FileNotFoundError(path)
table_lines = [
line.strip()
for line in path.read_text(encoding="utf-8").splitlines()
if line.strip().startswith("|") and line.strip().endswith("|")
]
if len(table_lines) < 3:
raise ValueError("Markdown table is missing or empty")
headers = _split_markdown_row(table_lines[0])
if headers != REQUIRED_HEADERS:
raise ValueError(f"Unexpected table headers: {headers}")
rows: list[dict[str, str]] = []
for line in table_lines[2:]:
cells = _split_markdown_row(line)
if len(cells) != len(headers):
raise ValueError(f"Column count mismatch: {line}")
rows.append(dict(zip(headers, cells, strict=True)))
return rows
def _split_markdown_row(line: str) -> list[str]:
return [cell.strip() for cell in line.strip().strip("|").split("|")]
def _parse_bool(value: str) -> bool:
normalized = value.strip().lower()
if normalized == "true":
return True
if normalized == "false":
return False
raise ValueError(f"Expected true/false, got: {value}")
def _normalize_review_status(value: str, note: str) -> str:
if "已审核" in value:
return "approved"
if "测试数据" in value or "测试数据" in note:
return "test_data"
return "pending"
if __name__ == "__main__":
result = import_rules_from_markdown()
print(f"Imported compliance rules: total={result.total}, created={result.created}, updated={result.updated}")