feat: 初始提交 - 科创企业特有风险的识别与管理 (数智风控系统)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""风险评估引擎模块"""
|
||||
@@ -0,0 +1,184 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
动态保险定价模型
|
||||
基于精算原理,结合六维风险评分实现"千企千面"费率计算
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 将项目根目录添加到 Python 路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from config import INSURANCE_PRODUCTS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# 行业风险系数
|
||||
INDUSTRY_RISK_FACTORS = {
|
||||
"芯片": 1.35, # 地缘风险+技术迭代双高
|
||||
"AI": 1.30, # 合规风险+技术竞争
|
||||
"软件": 1.05, # 相对成熟
|
||||
"医疗器械": 0.95, # 技术壁垒高但风险较稳
|
||||
"新能源": 1.15, # 技术路线之争+贸易摩擦
|
||||
"消费电子": 1.10, # 供应链风险
|
||||
}
|
||||
|
||||
# 企业规模折扣(大企业风险分散能力更强)
|
||||
SCALE_DISCOUNT = {
|
||||
"超大型": 0.85, # 营收 > 500亿
|
||||
"大型": 0.90, # 100-500亿
|
||||
"中型": 1.00, # 10-100亿
|
||||
"小型": 1.15, # 1-10亿
|
||||
"微型": 1.30, # < 1亿
|
||||
}
|
||||
|
||||
|
||||
def get_enterprise_scale(revenue: float) -> str:
|
||||
"""根据营收判断企业规模"""
|
||||
if revenue >= 50_000_000_000:
|
||||
return "超大型"
|
||||
elif revenue >= 10_000_000_000:
|
||||
return "大型"
|
||||
elif revenue >= 1_000_000_000:
|
||||
return "中型"
|
||||
elif revenue >= 100_000_000:
|
||||
return "小型"
|
||||
return "微型"
|
||||
|
||||
|
||||
def calculate_premium(
|
||||
product_key: str,
|
||||
comprehensive_risk_score: int,
|
||||
six_dimension_scores: dict,
|
||||
sector: str = "",
|
||||
revenue: float = 0,
|
||||
) -> dict:
|
||||
"""
|
||||
动态费率计算器
|
||||
|
||||
定价公式:
|
||||
实际保费 = 基础保费 × 风险系数 × 行业调整 × 规模折扣
|
||||
|
||||
风险系数由综合评分决定(分段线性):
|
||||
- [0, 30) → 0.7 ~ 0.9(优质折扣)
|
||||
- [30, 50) → 0.9 ~ 1.2(标准浮动)
|
||||
- [50, 70) → 1.2 ~ 1.8(风险上浮)
|
||||
- [70, 90) → 1.8 ~ 2.5(惩罚性费率)
|
||||
- [90,100] → 拒保或附加极高免赔额
|
||||
"""
|
||||
product = INSURANCE_PRODUCTS.get(product_key)
|
||||
if not product:
|
||||
return {"error": f"未知保险产品: {product_key}"}
|
||||
|
||||
base_premium = product["base_premium"]
|
||||
base_coverage = product["base_coverage"]
|
||||
|
||||
# 1. 计算风险系数
|
||||
risk_multiplier = _calculate_risk_multiplier(comprehensive_risk_score)
|
||||
|
||||
# 2. 行业调整系数
|
||||
industry_factor = INDUSTRY_RISK_FACTORS.get(sector, 1.0)
|
||||
|
||||
# 3. 规模折扣
|
||||
scale = get_enterprise_scale(revenue)
|
||||
scale_factor = SCALE_DISCOUNT[scale]
|
||||
|
||||
# 4. 特定险种的维度调整
|
||||
dimension_adjustment = _get_dimension_adjustment(product_key, six_dimension_scores)
|
||||
|
||||
# 5. 最终保费
|
||||
final_premium = base_premium * risk_multiplier * industry_factor * scale_factor * dimension_adjustment
|
||||
|
||||
# 6. 核保条件
|
||||
deductible_rate = _calculate_deductible(comprehensive_risk_score)
|
||||
adjusted_coverage = base_coverage * (1.0 if comprehensive_risk_score < 70 else 0.7)
|
||||
|
||||
return {
|
||||
"product_name": product["name"],
|
||||
"product_key": product_key,
|
||||
"base_premium": base_premium,
|
||||
"base_coverage": base_coverage,
|
||||
"risk_multiplier": round(risk_multiplier, 3),
|
||||
"industry_factor": round(industry_factor, 3),
|
||||
"scale_factor": round(scale_factor, 3),
|
||||
"scale_label": scale,
|
||||
"dimension_adjustment": round(dimension_adjustment, 3),
|
||||
"final_premium": round(final_premium, 2),
|
||||
"adjusted_coverage": round(adjusted_coverage, 2),
|
||||
"deductible_rate": round(deductible_rate, 3),
|
||||
"deductible_amount": round(adjusted_coverage * deductible_rate, 2),
|
||||
"comprehensive_risk_score": comprehensive_risk_score,
|
||||
"is_insurable": comprehensive_risk_score < 90,
|
||||
"pricing_breakdown": (
|
||||
f"¥{base_premium:,.0f} × {risk_multiplier:.2f}(风险) "
|
||||
f"× {industry_factor:.2f}(行业) × {scale_factor:.2f}(规模) "
|
||||
f"× {dimension_adjustment:.2f}(维度) = ¥{final_premium:,.0f}"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _calculate_risk_multiplier(score: int) -> float:
|
||||
"""分段线性风险系数计算"""
|
||||
if score < 30:
|
||||
return 0.7 + (score / 30) * 0.2 # 0.7 ~ 0.9
|
||||
elif score < 50:
|
||||
return 0.9 + ((score - 30) / 20) * 0.3 # 0.9 ~ 1.2
|
||||
elif score < 70:
|
||||
return 1.2 + ((score - 50) / 20) * 0.6 # 1.2 ~ 1.8
|
||||
elif score < 90:
|
||||
return 1.8 + ((score - 70) / 20) * 0.7 # 1.8 ~ 2.5
|
||||
else:
|
||||
return 3.0 # 拒保级别
|
||||
|
||||
|
||||
def _get_dimension_adjustment(product_key: str, scores: dict) -> float:
|
||||
"""针对特定险种,根据相关维度得分进行微调"""
|
||||
if product_key == "ip_lawsuit":
|
||||
# 知识产权被诉险:重点看技术路线和专利
|
||||
tech_score = scores.get("tech_disruption", 50)
|
||||
return 0.8 + (tech_score / 100) * 0.4 # 0.8 ~ 1.2
|
||||
|
||||
elif product_key == "exec_departure":
|
||||
# 高管离职险:重点看人员流失风险
|
||||
talent_score = scores.get("talent_loss", 50)
|
||||
return 0.7 + (talent_score / 100) * 0.6 # 0.7 ~ 1.3
|
||||
|
||||
elif product_key == "data_compliance":
|
||||
# 数据合规险:重点看合规风险
|
||||
compliance_score = scores.get("algo_compliance", 50)
|
||||
return 0.8 + (compliance_score / 100) * 0.4 # 0.8 ~ 1.2
|
||||
|
||||
return 1.0
|
||||
|
||||
|
||||
def _calculate_deductible(score: int) -> float:
|
||||
"""计算免赔率"""
|
||||
if score < 30:
|
||||
return 0.05 # 5%
|
||||
elif score < 50:
|
||||
return 0.10 # 10%
|
||||
elif score < 70:
|
||||
return 0.15 # 15%
|
||||
elif score < 90:
|
||||
return 0.25 # 25%
|
||||
else:
|
||||
return 0.50 # 50%(惩罚性高免赔)
|
||||
|
||||
|
||||
def calculate_all_products(
|
||||
comprehensive_risk_score: int,
|
||||
six_dimension_scores: dict,
|
||||
sector: str = "",
|
||||
revenue: float = 0,
|
||||
) -> list:
|
||||
"""计算所有险种的保费"""
|
||||
results = []
|
||||
for product_key in INSURANCE_PRODUCTS:
|
||||
result = calculate_premium(
|
||||
product_key, comprehensive_risk_score,
|
||||
six_dimension_scores, sector, revenue,
|
||||
)
|
||||
results.append(result)
|
||||
return results
|
||||
@@ -0,0 +1,146 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
核保决策报告生成器
|
||||
生成结构化的风险评估报告
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def generate_report(
|
||||
company_data: dict,
|
||||
debate_result: dict,
|
||||
pricing_results: list,
|
||||
) -> dict:
|
||||
"""
|
||||
生成完整的核保决策报告
|
||||
"""
|
||||
company_name = company_data.get("short_name", "未知企业")
|
||||
judge = debate_result.get("judge_result", {})
|
||||
scores = judge.get("six_dimension_scores", {})
|
||||
comprehensive = judge.get("comprehensive_score", 50)
|
||||
decision = judge.get("underwriting_decision", "标准承保")
|
||||
|
||||
report = {
|
||||
"title": f"科创企业智能风控核保报告 —— {company_name}",
|
||||
"report_id": f"RPT-{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
||||
"generate_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"company_profile": {
|
||||
"name": company_name,
|
||||
"stock_code": company_data.get("stock_code", ""),
|
||||
"industry": company_data.get("industry", ""),
|
||||
"sector": company_data.get("sector", ""),
|
||||
"description": company_data.get("description", ""),
|
||||
},
|
||||
"risk_assessment": {
|
||||
"comprehensive_score": comprehensive,
|
||||
"risk_level": judge.get("risk_level", "中"),
|
||||
"six_dimension_scores": scores,
|
||||
"key_risks": judge.get("key_risks", []),
|
||||
},
|
||||
"debate_summary": {
|
||||
"law_findings": debate_result.get("law_result", {}).get("key_findings", []),
|
||||
"tech_findings": debate_result.get("tech_result", {}).get("key_findings", []),
|
||||
"fin_findings": debate_result.get("finance_result", {}).get("key_findings", []),
|
||||
"conflicts": debate_result.get("conflicts", []),
|
||||
"resolution": judge.get("conflict_resolution", ""),
|
||||
},
|
||||
"underwriting_decision": {
|
||||
"decision": decision,
|
||||
"conditions": judge.get("underwriting_conditions", []),
|
||||
"summary": judge.get("summary", ""),
|
||||
},
|
||||
"pricing": pricing_results,
|
||||
}
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def format_report_markdown(report: dict) -> str:
|
||||
"""将报告格式化为 Markdown"""
|
||||
lines = []
|
||||
lines.append(f"# {report['title']}")
|
||||
lines.append(f"\n> 报告编号: {report['report_id']} | 生成时间: {report['generate_time']}")
|
||||
|
||||
# 企业概况
|
||||
profile = report["company_profile"]
|
||||
lines.append("\n## 一、企业概况")
|
||||
lines.append(f"- **企业名称**: {profile['name']}")
|
||||
lines.append(f"- **股票代码**: {profile['stock_code']}")
|
||||
lines.append(f"- **所属行业**: {profile['industry']}")
|
||||
lines.append(f"- **企业描述**: {profile['description']}")
|
||||
|
||||
# 风险评估
|
||||
risk = report["risk_assessment"]
|
||||
lines.append("\n## 二、风险评估")
|
||||
lines.append(f"- **综合风险评分**: {risk['comprehensive_score']} 分")
|
||||
lines.append(f"- **风险等级**: {risk['risk_level']}")
|
||||
|
||||
lines.append("\n### 六维风险评分")
|
||||
dim_names = {
|
||||
"tech_disruption": "技术路线颠覆",
|
||||
"talent_loss": "核心人员流失",
|
||||
"algo_compliance": "算法/数据合规",
|
||||
"geopolitical": "地缘政治/出口管制",
|
||||
"rd_capitalization": "研发资本化操纵",
|
||||
"concentration": "客户/供应商集中",
|
||||
}
|
||||
lines.append("| 风险维度 | 评分 | 等级 |")
|
||||
lines.append("|---------|------|------|")
|
||||
for dim, score in risk.get("six_dimension_scores", {}).items():
|
||||
name = dim_names.get(dim, dim)
|
||||
level = "高" if score >= 70 else ("中" if score >= 40 else "低")
|
||||
lines.append(f"| {name} | {score} | {level} |")
|
||||
|
||||
# 辩论摘要
|
||||
debate = report["debate_summary"]
|
||||
lines.append("\n## 三、多智能体交叉验证摘要")
|
||||
|
||||
lines.append("\n### 👩⚖️ 法务风控节点")
|
||||
for f in debate.get("law_findings", []):
|
||||
lines.append(f"- {f}")
|
||||
|
||||
lines.append("\n### 👨🔬 技术风控节点")
|
||||
for f in debate.get("tech_findings", []):
|
||||
lines.append(f"- {f}")
|
||||
|
||||
lines.append("\n### 👔 财务风控节点")
|
||||
for f in debate.get("fin_findings", []):
|
||||
lines.append(f"- {f}")
|
||||
|
||||
if debate.get("conflicts"):
|
||||
lines.append("\n### ⚠️ 冲突点")
|
||||
for c in debate["conflicts"]:
|
||||
lines.append(f"- {c}")
|
||||
|
||||
# 核保决策
|
||||
uw = report["underwriting_decision"]
|
||||
lines.append(f"\n## 四、核保决策")
|
||||
lines.append(f"\n**决策**: 【{uw['decision']}】")
|
||||
lines.append(f"\n{uw.get('summary', '')}")
|
||||
|
||||
if uw.get("conditions"):
|
||||
lines.append("\n### 核保附加条件")
|
||||
for c in uw["conditions"]:
|
||||
lines.append(f"- {c}")
|
||||
|
||||
# 保费方案
|
||||
lines.append("\n## 五、保险产品费率方案")
|
||||
if report.get("pricing"):
|
||||
lines.append("| 险种 | 基础保费 | 最终保费 | 保额 | 免赔率 |")
|
||||
lines.append("|------|---------|---------|------|--------|")
|
||||
for p in report["pricing"]:
|
||||
if p.get("is_insurable"):
|
||||
lines.append(
|
||||
f"| {p['product_name']} | ¥{p['base_premium']:,.0f} | "
|
||||
f"¥{p['final_premium']:,.0f} | ¥{p['adjusted_coverage']:,.0f} | "
|
||||
f"{p['deductible_rate']:.0%} |"
|
||||
)
|
||||
else:
|
||||
lines.append(f"| {p['product_name']} | - | **拒保** | - | - |")
|
||||
|
||||
return "\n".join(lines)
|
||||
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
六维风险评分器
|
||||
整合所有数据源和分析结果,生成结构化六维风险画像
|
||||
"""
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# 风险等级映射
|
||||
RISK_LEVELS = {
|
||||
(0, 30): {"level": "低", "color": "#4CAF50", "emoji": "🟢"},
|
||||
(30, 50): {"level": "中低", "color": "#8BC34A", "emoji": "🟡"},
|
||||
(50, 70): {"level": "中高", "color": "#FF9800", "emoji": "🟠"},
|
||||
(70, 90): {"level": "高", "color": "#F44336", "emoji": "🔴"},
|
||||
(90, 101): {"level": "极高", "color": "#B71C1C", "emoji": "⛔"},
|
||||
}
|
||||
|
||||
|
||||
def get_risk_level(score: int) -> dict:
|
||||
"""根据分数获取风险等级详情"""
|
||||
for (low, high), info in RISK_LEVELS.items():
|
||||
if low <= score < high:
|
||||
return info
|
||||
return {"level": "未知", "color": "#9E9E9E", "emoji": "❓"}
|
||||
|
||||
|
||||
def calculate_six_dimension_scores(company_data: dict) -> dict:
|
||||
"""
|
||||
基于企业数据直接计算六维风险评分(不依赖 LLM)
|
||||
用于快速预览和规则引擎降级场景
|
||||
"""
|
||||
financials = company_data.get("financials", {})
|
||||
compliance = company_data.get("compliance", {})
|
||||
tech_route = company_data.get("tech_route", {})
|
||||
personnel = company_data.get("core_tech_personnel", [])
|
||||
supply_chain = company_data.get("supply_chain", {})
|
||||
|
||||
scores = {}
|
||||
|
||||
# 1. 技术路线颠覆风险
|
||||
competing = len(tech_route.get("competing_techs", []))
|
||||
moat = tech_route.get("tech_moat", "")
|
||||
tech_score = min(25 + competing * 15, 85)
|
||||
if "差距" in moat or "受制" in moat or "威胁" in moat:
|
||||
tech_score = min(tech_score + 15, 95)
|
||||
if "领先" in moat or "第一" in moat:
|
||||
tech_score = max(tech_score - 10, 10)
|
||||
scores["tech_disruption"] = tech_score
|
||||
|
||||
# 2. 核心人员流失风险
|
||||
departed = [p for p in personnel if "离职" in p.get("status", "")]
|
||||
high_imp = [p for p in personnel if p.get("importance") == "极高"]
|
||||
if departed:
|
||||
scores["talent_loss"] = 80
|
||||
elif len(high_imp) <= 1 and len(personnel) <= 2:
|
||||
scores["talent_loss"] = 55
|
||||
else:
|
||||
scores["talent_loss"] = 25
|
||||
|
||||
# 3. 算法/数据合规风险
|
||||
algo_status = compliance.get("algo_filing_status", "")
|
||||
data_risk = compliance.get("data_export_risk", "低")
|
||||
algo_score = 20
|
||||
if "未" in algo_status:
|
||||
algo_score = 75
|
||||
elif data_risk == "高":
|
||||
algo_score = 65
|
||||
elif data_risk == "中":
|
||||
algo_score = 40
|
||||
elif "已备案" in algo_status:
|
||||
algo_score = 15
|
||||
scores["algo_compliance"] = algo_score
|
||||
|
||||
# 4. 地缘政治/出口管制风险
|
||||
entity_status = compliance.get("entity_list_status", "")
|
||||
if "被列入" in entity_status:
|
||||
scores["geopolitical"] = 92
|
||||
elif supply_chain.get("supplier_concentration_risk") == "极高":
|
||||
scores["geopolitical"] = 68
|
||||
elif supply_chain.get("supplier_concentration_risk") == "高":
|
||||
scores["geopolitical"] = 50
|
||||
else:
|
||||
scores["geopolitical"] = 18
|
||||
|
||||
# 5. 研发资本化操纵风险
|
||||
cap_rate = financials.get("rd_capitalization_rate", 0)
|
||||
if cap_rate >= 0.4:
|
||||
scores["rd_capitalization"] = 90
|
||||
elif cap_rate >= 0.3:
|
||||
scores["rd_capitalization"] = 72
|
||||
elif cap_rate >= 0.15:
|
||||
scores["rd_capitalization"] = 48
|
||||
elif cap_rate > 0:
|
||||
scores["rd_capitalization"] = 25
|
||||
else:
|
||||
scores["rd_capitalization"] = 10
|
||||
|
||||
# 6. 客户/供应商集中风险
|
||||
cust_ratio = financials.get("top5_customer_ratio", 0)
|
||||
supp_ratio = financials.get("top5_supplier_ratio", 0)
|
||||
max_conc = max(cust_ratio, supp_ratio)
|
||||
if max_conc >= 0.8:
|
||||
scores["concentration"] = 88
|
||||
elif max_conc >= 0.6:
|
||||
scores["concentration"] = 68
|
||||
elif max_conc >= 0.4:
|
||||
scores["concentration"] = 42
|
||||
else:
|
||||
scores["concentration"] = 18
|
||||
|
||||
# 加权综合
|
||||
weights = {
|
||||
"tech_disruption": 0.20,
|
||||
"talent_loss": 0.15,
|
||||
"algo_compliance": 0.15,
|
||||
"geopolitical": 0.20,
|
||||
"rd_capitalization": 0.15,
|
||||
"concentration": 0.15,
|
||||
}
|
||||
comprehensive = int(sum(scores[k] * weights[k] for k in scores))
|
||||
|
||||
# 维度中文名映射
|
||||
dim_names = {
|
||||
"tech_disruption": "技术路线颠覆",
|
||||
"talent_loss": "核心人员流失",
|
||||
"algo_compliance": "算法/数据合规",
|
||||
"geopolitical": "地缘政治/出口管制",
|
||||
"rd_capitalization": "研发资本化操纵",
|
||||
"concentration": "客户/供应商集中",
|
||||
}
|
||||
|
||||
return {
|
||||
"scores": scores,
|
||||
"comprehensive_score": comprehensive,
|
||||
"risk_level": get_risk_level(comprehensive),
|
||||
"dimension_details": {
|
||||
k: {
|
||||
"name": dim_names[k],
|
||||
"score": v,
|
||||
"level": get_risk_level(v),
|
||||
"weight": weights[k],
|
||||
}
|
||||
for k, v in scores.items()
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user