Files
AIPortPilot/backend/app/services/evaluation_engine.py
T

314 lines
16 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.
"""评价权重计算引擎。
6 轴动态权重合并 + 归一化:
1. 基金类型 × 存续期 → 基础权重模板(32 个预设)
2. 企业阶段 → 阶段系数调整(5 个预设)
3. 产业赛道 → 维度裁剪 + 专属指标注入(6 个预设)
4. 投资策略 → ±5% 微调(4 个预设)
5. 归一化 — 裁剪后剩余维度权重自动归一化到 100%
6. 修饰因子叠加 — LP 附加指标 + 地域基准校准
"""
import logging
from typing import Any
logger = logging.getLogger(__name__)
# --- 基础 14 维度权重(来自 health_calculator.py ---
BASE_WEIGHTS: dict[str, float] = {
"financial": 0.15,
"operational": 0.10,
"ai_commercial": 0.10,
"ai_cost": 0.05,
"org_talent": 0.10,
"product_tech": 0.10,
"market_compete": 0.10,
"governance": 0.05,
"financing": 0.05,
"synergy": 0.05,
"ai_model_product": 0.05,
"data_compliance": 0.05,
"team_tech": 0.03,
"customer_success": 0.07,
}
# 维度 key 映射:权重 key → 评分字段 key
DIMENSION_KEY_MAP: dict[str, str] = {
"financial": "financial_score",
"operational": "operational_score",
"ai_commercial": "ai_commercial_score",
"ai_cost": "ai_cost_score",
"org_talent": "org_talent_score",
"product_tech": "product_tech_score",
"market_compete": "market_compete_score",
"governance": "governance_score",
"financing": "financing_score",
"synergy": "synergy_score",
"ai_model_product": "ai_model_product_score",
"data_compliance": "data_compliance_score",
"team_tech": "team_tech_score",
"customer_success": "customer_success_score",
}
# --- 轴 5:基金类型 × 存续期基础权重系数(32 个预设) ---
FUND_LIFECYCLE_MULTIPLIERS: dict[tuple[str, str], dict[str, float]] = {
("angel", "investment"): {"financial": 0.5, "product_tech": 1.8, "org_talent": 1.5, "market": 1.2},
("angel", "growth"): {"financial": 0.7, "product_tech": 1.5, "org_talent": 1.3, "market": 1.2},
("angel", "exit_preparation"): {"financial": 1.0, "product_tech": 1.2, "org_talent": 1.0, "financing": 1.5},
("angel", "liquidation"): {"financial": 1.5, "financing": 2.0, "product_tech": 0.8},
("early_vc", "investment"): {"financial": 0.7, "product_tech": 1.5, "market": 1.2, "customer_success": 0.8},
("early_vc", "growth"): {"financial": 0.9, "product_tech": 1.3, "market": 1.2, "customer_success": 1.0},
("early_vc", "exit_preparation"): {"financial": 1.3, "market": 1.0, "customer_success": 1.2, "financing": 1.5},
("early_vc", "liquidation"): {"financial": 1.8, "financing": 2.0, "product_tech": 0.6},
("growth_vc", "investment"): {"financial": 1.0, "market": 1.3, "customer_success": 1.2},
("growth_vc", "growth"): {"financial": 1.2, "market": 1.2, "customer_success": 1.3},
("growth_vc", "exit_preparation"): {"financial": 1.5, "market": 1.0, "customer_success": 1.2, "financing": 1.5},
("growth_vc", "liquidation"): {"financial": 2.0, "financing": 2.0, "market": 0.8},
("pe", "investment"): {"financial": 1.8, "governance": 1.5, "customer_success": 1.3, "product_tech": 0.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("pe", "growth"): {"financial": 2.0, "governance": 1.5, "customer_success": 1.3, "product_tech": 0.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("pe", "exit_preparation"): {"financial": 2.5, "governance": 1.8, "financing": 1.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("pe", "liquidation"): {"financial": 3.0, "financing": 2.0, "governance": 1.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("cvc", "investment"): {"synergy": 2.0, "market": 1.3, "product_tech": 1.2, "financial": 0.7},
("cvc", "growth"): {"synergy": 1.8, "market": 1.2, "product_tech": 1.2, "financial": 0.8},
("cvc", "exit_preparation"): {"synergy": 1.5, "market": 1.0, "financial": 1.2, "financing": 1.3},
("cvc", "liquidation"): {"financial": 1.5, "financing": 1.5, "synergy": 1.0},
("distress", "investment"): {"financial": 2.5, "governance": 1.5, "product_tech": 0.5, "market": 0.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("distress", "growth"): {"financial": 2.5, "governance": 1.5, "product_tech": 0.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("distress", "exit_preparation"): {"financial": 3.0, "governance": 1.5, "financing": 1.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("distress", "liquidation"): {"financial": 3.0, "financing": 2.0, "governance": 1.5, "ai_commercial": 0.3, "ai_cost": 0.3, "ai_model_product": 0.3},
("esg", "investment"): {"governance": 1.8, "data_compliance": 1.5, "product_tech": 1.2, "financial": 0.8},
("esg", "growth"): {"governance": 1.5, "data_compliance": 1.3, "customer_success": 1.2},
("esg", "exit_preparation"): {"governance": 1.8, "financial": 1.3, "financing": 1.3},
("esg", "liquidation"): {"financial": 1.5, "governance": 1.5, "financing": 1.5},
# FOF 不直接评价单企业,使用默认权重
("fof", "investment"): {},
("fof", "growth"): {},
("fof", "exit_preparation"): {},
("fof", "liquidation"): {},
}
# --- 轴 2:企业阶段系数(5 个预设) ---
STAGE_MULTIPLIERS: dict[str, dict[str, float]] = {
"seed": {"financial": 0.6, "product_tech": 1.8, "org_talent": 1.5, "market_compete": 0.5, "governance": 0.6, "customer_success": 0.5},
"a": {"financial": 0.8, "product_tech": 1.5, "org_talent": 1.2, "market_compete": 1.0, "governance": 0.7, "customer_success": 0.8},
"b": {"financial": 1.2, "market_compete": 1.3, "customer_success": 1.3, "product_tech": 1.0, "governance": 1.0},
"c": {"financial": 1.5, "market_compete": 1.3, "governance": 1.3, "customer_success": 1.3, "product_tech": 0.8},
"pre_ipo": {"financial": 1.5, "governance": 1.8, "customer_success": 1.3, "market_compete": 1.0, "product_tech": 0.8},
}
# --- 轴 3:产业赛道配置(6 个预设) ---
INDUSTRY_CONFIGS: dict[str, dict[str, Any]] = {
"ai": {
"enabled": list(DIMENSION_KEY_MAP.keys()),
"disabled": [],
"custom_metrics": [
{"key": "model_accuracy", "label": "模型精度", "description": "AI 模型准确率/召回率"},
{"key": "inference_cost", "label": "推理成本", "description": "单次推理成本趋势"},
{"key": "api_call_volume", "label": "API 调用量", "description": "月度 API 调用次数"},
{"key": "poc_conversion_rate", "label": "PoC 转化率", "description": "PoC 到付费转化率"},
],
},
"saas": {
"enabled": ["financial", "operational", "ai_commercial", "org_talent", "product_tech",
"market_compete", "governance", "financing", "synergy", "data_compliance",
"team_tech", "customer_success"],
"disabled": ["ai_cost", "ai_model_product"],
"custom_metrics": [
{"key": "arr_growth", "label": "ARR 增长率", "description": "年度经常性收入增长率"},
{"key": "net_revenue_retention", "label": "NRR", "description": "净收入留存率"},
{"key": "cac_payback", "label": "CAC 回收期", "description": "获客成本回收月数"},
{"key": "rule_of_40", "label": "Rule of 40", "description": "增长率 + 利润率"},
],
},
"hardware": {
"enabled": ["financial", "operational", "org_talent", "product_tech", "market_compete",
"governance", "financing", "synergy", "data_compliance",
"team_tech", "customer_success"],
"disabled": ["ai_commercial", "ai_cost", "ai_model_product"],
"custom_metrics": [
{"key": "patent_count", "label": "专利数", "description": "累计授权专利数量"},
{"key": "tape_out_progress", "label": "流片进度", "description": "芯片流片里程碑进展"},
{"key": "yield_rate", "label": "良率", "description": "产品良率"},
{"key": "rd_investment_ratio", "label": "研发投入比", "description": "研发投入占营收比例"},
],
},
"biotech": {
"enabled": ["financial", "operational", "org_talent", "product_tech", "governance",
"financing", "synergy", "data_compliance", "team_tech"],
"disabled": ["ai_commercial", "ai_cost", "ai_model_product", "market_compete", "customer_success"],
"custom_metrics": [
{"key": "clinical_stage", "label": "临床阶段", "description": "当前临床试验阶段"},
{"key": "pipeline_progress", "label": "管线进度", "description": "在研管线推进情况"},
{"key": "regulatory_milestone", "label": "审批节点", "description": "监管审批里程碑"},
{"key": "patent_landscape", "label": "专利布局", "description": "核心专利布局覆盖度"},
],
},
"consumer": {
"enabled": ["financial", "operational", "org_talent", "product_tech", "market_compete",
"governance", "financing", "synergy", "team_tech", "customer_success"],
"disabled": ["ai_commercial", "ai_cost", "ai_model_product", "data_compliance"],
"custom_metrics": [
{"key": "gmv", "label": "GMV", "description": "月度交易总额"},
{"key": "repurchase_rate", "label": "复购率", "description": "客户复购率"},
{"key": "brand_index", "label": "品牌指数", "description": "品牌知名度/美誉度"},
{"key": "channel_coverage", "label": "渠道覆盖率", "description": "销售渠道覆盖广度"},
],
},
"fintech": {
"enabled": ["financial", "operational", "ai_commercial", "org_talent", "product_tech",
"market_compete", "governance", "financing", "synergy", "ai_model_product",
"data_compliance", "team_tech", "customer_success"],
"disabled": ["ai_cost"],
"custom_metrics": [
{"key": "license_progress", "label": "牌照进度", "description": "金融牌照获取进展"},
{"key": "risk_control_score", "label": "风控指标", "description": "风控模型评分"},
{"key": "compliance_events", "label": "合规事件", "description": "合规事件数量"},
{"key": "npl_ratio", "label": "坏账率", "description": "不良贷款率"},
],
},
"manufacturing": {
"enabled": ["financial", "operational", "org_talent", "product_tech", "market_compete",
"governance", "financing", "synergy", "data_compliance",
"team_tech", "customer_success"],
"disabled": ["ai_commercial", "ai_cost", "ai_model_product"],
"custom_metrics": [
{"key": "capacity_utilization", "label": "产能利用率", "description": "实际产能/设计产能"},
{"key": "delivery_cycle", "label": "交付周期", "description": "订单交付周期天数"},
{"key": "supply_chain_stability", "label": "供应链稳定性", "description": "供应链中断风险评分"},
{"key": "rd_investment_ratio", "label": "研发投入比", "description": "研发投入占营收比例"},
],
},
}
# --- 轴 4:投资策略微调(4 个预设,±5%) ---
STRATEGY_ADJUSTMENTS: dict[str, dict[str, float]] = {
"growth": {"market_compete": 5, "product_tech": 5, "customer_success": 5, "financial": -5, "governance": -5},
"value": {"financial": 5, "governance": 5, "customer_success": 5, "market_compete": -5, "product_tech": -5},
"empowerment": {"synergy": 5, "org_talent": 5, "product_tech": 5, "financial": -5, "market_compete": -5},
"turnaround": {"financial": 5, "governance": 5, "market_compete": -5, "product_tech": -5},
}
def compute_weights(
fund_type: str,
fund_lifecycle: str,
company_stage: str,
industry: str,
strategy: str = "growth",
investor_type: str = "investor",
) -> dict[str, Any]:
"""计算 6 轴动态权重。
Args:
fund_type: 基金类型 — angel/early_vc/growth_vc/pe/cvc/fof/distress/esg
fund_lifecycle: 存续期阶段 — investment/growth/exit_preparation/liquidation
company_stage: 企业阶段 — seed/a/b/c/pre_ipo
industry: 产业赛道 — ai/saas/hardware/biotech/consumer/fintech/manufacturing
strategy: 投资策略 — growth/value/empowerment/turnaround
investor_type: 投资人类型 — gp/post_invest_lead/investor(不影响权重)
Returns:
包含 weights、enabled_dimensions、disabled_dimensions、custom_metrics 的字典
"""
# Step 1:复制基础权重
weights = dict(BASE_WEIGHTS)
# Step 2:基金类型 × 存续期系数
fund_key = (fund_type, fund_lifecycle)
fund_multipliers = FUND_LIFECYCLE_MULTIPLIERS.get(fund_key, {})
for dim, multiplier in fund_multipliers.items():
if dim in weights:
weights[dim] *= multiplier
# Step 3:企业阶段系数
stage_multipliers = STAGE_MULTIPLIERS.get(company_stage, {})
for dim, multiplier in stage_multipliers.items():
if dim in weights:
weights[dim] *= multiplier
# Step 4:产业赛道 — 维度裁剪
industry_config = INDUSTRY_CONFIGS.get(industry, INDUSTRY_CONFIGS["ai"])
enabled_dims = industry_config["enabled"]
disabled_dims = industry_config["disabled"]
# 禁用的维度权重置零
for dim in disabled_dims:
if dim in weights:
weights[dim] = 0.0
# Step 5:投资策略微调(±5%,基于百分比点)
strategy_adj = STRATEGY_ADJUSTMENTS.get(strategy, {})
for dim, adjustment in strategy_adj.items():
if dim in weights and weights[dim] > 0:
# 将百分比点转换为权重调整量
weights[dim] += adjustment / 100.0
# 确保非负
for dim in weights:
weights[dim] = max(0.0, weights[dim])
# Step 6:归一化 — 只对启用维度归一化到 1.0
enabled_weights = {dim: weights[dim] for dim in enabled_dims if dim in weights}
total = sum(enabled_weights.values())
if total > 0:
normalized = {dim: w / total for dim, w in enabled_weights.items()}
else:
# 极端情况:所有权重为零,均分
count = len(enabled_dims) if enabled_dims else 1
normalized = {dim: 1.0 / count for dim in enabled_dims}
# 转换为百分比格式(保留 4 位小数)
final_weights = {dim: round(w * 100, 2) for dim, w in normalized.items()}
logger.info(
"权重计算完成: fund_type=%s, lifecycle=%s, stage=%s, industry=%s, strategy=%s%s",
fund_type, fund_lifecycle, company_stage, industry, strategy, final_weights,
)
return {
"weights": final_weights,
"enabled_dimensions": enabled_dims,
"disabled_dimensions": disabled_dims,
"custom_metrics": industry_config.get("custom_metrics", []),
}
def get_dimension_score_key(weight_key: str) -> str:
"""将权重 key 转换为评分字段 key。"""
return DIMENSION_KEY_MAP.get(weight_key, f"{weight_key}_score")
def calculate_weighted_score(
dimension_scores: dict[str, float],
weights: dict[str, float],
) -> float:
"""根据维度评分和权重计算加权总分。
Args:
dimension_scores: 各维度评分(0-100),key 为权重 key(如 financial
weights: 各维度权重(百分比),key 为权重 key
Returns:
加权总分(0-100
"""
total = 0.0
weight_sum = 0.0
for dim, weight in weights.items():
score = dimension_scores.get(dim)
if score is not None and weight > 0:
total += score * weight
weight_sum += weight
if weight_sum > 0:
return round(total / weight_sum, 1)
return 0.0