Files
XingHuo/app/config/settings.py
T
GaoYiYuan_0626 fe4801bc0a feat: C4 FR-8 持仓集中度预警(RISK-006)
依据《实现方案-风控追加需求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)。
2026-09-07 19:05:02 +08:00

84 lines
3.3 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.
"""环境配置(从 .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()