依据《实现方案-风控追加需求v1.1-C4C6.md》§2;不改表结构(alert_type/status 复用 payload 承载,audit_log.event_type 为 VARCHAR 可直接扩)。 1. settings.py + .env.example:一次性加齐风控追加 v1.1 共 11 项配置(C4~C6 共用)。 2. core_ro.concentration_profile(customer_id, limit=500):一次 SQL 取明细 (LIMIT limit+1 探测截断)+ Python 端按 min_risk_code in (R4,R5) 聚合; 收口挂账 #1(PRD 字面为 list_holdings,改聚合封装,docstring 注明偏离)。 3. rules.py:RULE_SCORES/RULE_ALERT_TYPES 加 RISK-006=60/pattern;RuleHit 加 alert_subtype;RiskThresholds 加 concentration_threshold 且 from_settings 必须补读(评审 P1-2:漏读会让 conftest monkeypatch 失效打穿现有断言); 新增纯函数 rule_concentration——空仓不触发、截断视同达标(保守告警)、 阈值边界 79.9% 不触发 / 80% 触发、R4+R5 为 0 不触发。 4. engine.process_trade_event:run_rules 之后、record_trade_alerts 之前并入 集中度命中(不动 run_rules 签名);命中后 L3 打 high_risk_concentration 标签 + 写 risk_concentration 审计(金额只落合计与前 5 条摘要)。 5. risk_repository:find_pending_event_alert 改候选 LIMIT 50 + Python 过滤掉 payload.alert_subtype 含 agent_behavior 的单(评审 P0-1:代理人维度行为链单 不得充当客户维度事件单的聚合锚点);append_alert_event 加 extra_subtypes 合并进 payload.alert_subtype(不传时行为与原先一致,向后兼容)。 6. alert_service:subtypes 集合维护(空集不注入 payload,评审 P2-3); 追加时 alert_type 按「老单规则 ∪ 本批规则」重算(评审 P1-3,修掉既有 large_amount 单被本批仅 RISK-006(60) 翻转为 pattern 的缺陷); _publish_alert 加 notify_role/extra 可选参数(C5/C6 复用)。 7. 对话线:chat_tools.customer_context 加 profile(concentration_ratio/ r45_value/total_value/holdings_truncated),tool_service.summarize 加 「高风险持仓占比 X%(仅供参考)」;不新增意图词。 8. 02-redis-keys.md 增补 alert_subtype / escalation_level 附加推送字段。 测试:conftest 加 autouse _disable_concentration_rule(阈值推 1.01 做回归隔离, 现有用例断言零改动);test_risk_rules 加 RISK-006 纯函数 6 例;新建 tests/test_concentration_c4.py 11 例(与 RISK-001 同单聚合、score max=70、 L3 tag、risk_concentration 审计、仅集中度也出单、subtype 合并、P0-1 回归、 alert_type 不翻转、对话线 ratio)。全量 453 绿(436 + 17)。
84 lines
3.3 KiB
Python
84 lines
3.3 KiB
Python
"""环境配置(从 .env 读取,见 .env.example)。"""
|
||
|
||
from decimal import Decimal
|
||
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
||
|
||
app_env: str = "development"
|
||
|
||
mysql_host: str = "127.0.0.1"
|
||
mysql_port: int = 3306
|
||
mysql_database: str = "jinrong_agent"
|
||
mysql_core_database: str = "jinrong_core"
|
||
mysql_user: str = "root"
|
||
mysql_password: str = ""
|
||
|
||
redis_url: str = "redis://127.0.0.1:6379/0"
|
||
|
||
neo4j_uri: str = "bolt://localhost:7687"
|
||
neo4j_user: str = "neo4j"
|
||
neo4j_password: str = ""
|
||
|
||
milvus_uri: str = "./data/milvus.db"
|
||
|
||
ollama_base_url: str = "http://127.0.0.1:11434"
|
||
embed_model: str = "bge-m3"
|
||
# 向量维度(必须与 Ollama bge-m3 输出一致;Milvus Collection 建集合时同源引用)
|
||
embed_dim: int = 1024
|
||
# Ollama embedding 单次请求超时(秒);本地推理首次加载模型可能较慢
|
||
embed_timeout_seconds: float = 60.0
|
||
|
||
deepseek_api_key: str = ""
|
||
deepseek_base_url: str = "https://api.deepseek.com"
|
||
|
||
# ===== JWT(T-01 · JWT 手册 §4/§11)=====
|
||
# RS256 公钥路径(生产,私钥仅在 IdP);为空时用 HS256 + jwt_dev_secret(仅 development)
|
||
jwt_public_key_path: str = ""
|
||
jwt_dev_secret: str = "change-me-in-dev-only"
|
||
jwt_issuer: str = "https://idp.jinrong.internal"
|
||
jwt_audience: str = "agent-gateway"
|
||
|
||
# ===== Risk 阈值(默认值=冻结规则 · docs/PRD/附-风控规则表.md)=====
|
||
# 注:risk_assessment_valid_days 已随 AL-05(对齐 main 基准)退役——
|
||
# 风评有效期改由 core_customer_risk.expires_at 数据驱动(FM-03),不再可配。
|
||
risk_large_amount: Decimal = Decimal("500000")
|
||
risk_daily_total: Decimal = Decimal("500000")
|
||
risk_freq_count: int = 3
|
||
risk_probe_window_minutes: int = 5
|
||
risk_probe_count: int = 3
|
||
risk_probe_amount: Decimal = Decimal("400000")
|
||
risk_small_amount: Decimal = Decimal("10000")
|
||
risk_small_count: int = 3
|
||
risk_aml_default_threshold: Decimal = Decimal("0.85")
|
||
|
||
# ===== 风控追加 v1.1(FR-8/9/10 · PRD §4A · C4~C6 共用,一次性加齐)=====
|
||
# FR-8 RISK-006 集中度:R4+R5 市值占比阈值(≥ 即命中)
|
||
risk_concentration_threshold: float = 0.80
|
||
# FR-9 RISK-007 时效升级:扫描周期(脚本侧参考)与两级超时小时数
|
||
risk_escalation_scan_minutes: int = 15
|
||
risk_escalation_l1_hours: int = 4
|
||
risk_escalation_l2_hours: int = 24
|
||
# AML 单走短通道(1h/4h),与普通单分开计
|
||
risk_escalation_aml_l1_hours: int = 1
|
||
risk_escalation_aml_l2_hours: int = 4
|
||
# FR-10 RISK-008 代理人行为链:扫描周期与 A/B/C 三条件窗口与次数
|
||
risk_agent_behavior_scan_minutes: int = 30
|
||
risk_agent_behavior_a_window_hours: int = 24
|
||
risk_agent_behavior_a_count: int = 3
|
||
risk_agent_behavior_b_window_hours: int = 72
|
||
risk_agent_behavior_b_count: int = 5
|
||
risk_agent_behavior_c_window_hours: int = 24
|
||
risk_agent_behavior_c_count: int = 10
|
||
|
||
# ===== 输入防护(T-03 · F-03)=====
|
||
# 对话限流:actor 级固定窗口(拍板 2026-09-07:30 次/分钟,Redis 异常 fail-open)
|
||
guard_rate_limit_max: int = 30
|
||
guard_rate_limit_window_seconds: int = 60
|
||
|
||
|
||
settings = Settings()
|