feat(backend): 6-axis dynamic evaluation system with weight engine and template management

This commit is contained in:
selfrelease
2026-07-19 20:59:31 +08:00
parent f4ddcab2ca
commit de53a252e4
11 changed files with 2145 additions and 1 deletions
+313
View File
@@ -0,0 +1,313 @@
"""评价权重计算引擎。
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
+154
View File
@@ -0,0 +1,154 @@
"""评价模板预设配置种子数据。
启动时自动初始化 50 个预设配置单元到数据库。
"""
import logging
from typing import Any
from app.services.evaluation_engine import compute_weights
logger = logging.getLogger(__name__)
# 基金类型列表
FUND_TYPES = ["angel", "early_vc", "growth_vc", "pe", "cvc", "fof", "distress", "esg"]
# 存续期阶段列表
FUND_LIFECYCLES = ["investment", "growth", "exit_preparation", "liquidation"]
# 企业阶段列表
COMPANY_STAGES = ["seed", "a", "b", "c", "pre_ipo"]
# 产业赛道列表
INDUSTRIES = ["ai", "saas", "hardware", "biotech", "consumer", "fintech", "manufacturing"]
# 投资策略列表
STRATEGIES = ["growth", "value", "empowerment", "turnaround"]
# 投资人类型列表
INVESTOR_TYPES = ["gp", "post_invest_lead", "investor"]
# 模板名称中文名
FUND_TYPE_LABELS = {
"angel": "天使/种子基金",
"early_vc": "早期VC",
"growth_vc": "成长期VC",
"pe": "PE/并购基金",
"cvc": "产业基金",
"fof": "母基金",
"distress": "困境/特殊机会基金",
"esg": "ESG/影响力基金",
}
LIFECYCLE_LABELS = {
"investment": "投资期",
"growth": "成长期",
"exit_preparation": "退出准备期",
"liquidation": "清算期",
}
STAGE_LABELS = {
"seed": "种子/天使",
"a": "A轮",
"b": "B轮",
"c": "C轮+",
"pre_ipo": "Pre-IPO",
}
INDUSTRY_LABELS = {
"ai": "AI/SaaS",
"saas": "企业服务",
"hardware": "硬科技/芯片",
"biotech": "生物医药",
"consumer": "消费品牌",
"fintech": "金融科技",
"manufacturing": "新能源/先进制造",
}
STRATEGY_LABELS = {
"growth": "成长型",
"value": "价值型",
"empowerment": "投后赋能型",
"turnaround": "困境反转型",
}
def generate_preset_templates() -> list[dict[str, Any]]:
"""生成全部预设模板配置。
策略:为每个 (fund_type, lifecycle, stage, industry) 组合生成默认模板,
strategy 默认使用 growthinvestor_type 默认使用 investor。
用户可以在前端创建自定义策略/投资人类型的模板。
Returns:
模板配置字典列表
"""
templates: list[dict[str, Any]] = []
for fund_type in FUND_TYPES:
for lifecycle in FUND_LIFECYCLES:
for stage in COMPANY_STAGES:
for industry in INDUSTRIES:
# FOF 不生成单企业模板
if fund_type == "fof":
continue
result = compute_weights(
fund_type=fund_type,
fund_lifecycle=lifecycle,
company_stage=stage,
industry=industry,
strategy="growth",
investor_type="investor",
)
name = (
f"{FUND_TYPE_LABELS[fund_type]}-{LIFECYCLE_LABELS[lifecycle]}-"
f"{STAGE_LABELS[stage]}-{INDUSTRY_LABELS[industry]}"
)
templates.append({
"name": name,
"fund_type": fund_type,
"fund_lifecycle": lifecycle,
"company_stage": stage,
"industry": industry,
"strategy": "growth",
"investor_type": "investor",
"weights_json": result["weights"],
"enabled_dimensions": result["enabled_dimensions"],
"disabled_dimensions": result["disabled_dimensions"],
"custom_metrics_json": {"metrics": result["custom_metrics"]},
"is_default": True,
"is_active": True,
"version": 1,
})
logger.info("生成预设模板 %d", len(templates))
return templates
async def seed_evaluation_templates(db_session) -> None:
"""将预设模板写入数据库(仅当表为空时)。
Args:
db_session: 异步数据库会话
"""
from sqlalchemy import select
from app.models.evaluation_template import EvaluationTemplate
# 检查是否已有数据
result = await db_session.execute(select(EvaluationTemplate).limit(1))
existing = result.scalar_one_or_none()
if existing:
logger.info("评价模板表已有数据,跳过种子初始化")
return
templates = generate_preset_templates()
for tmpl_data in templates:
tmpl = EvaluationTemplate(**tmpl_data)
db_session.add(tmpl)
await db_session.flush()
logger.info("预设评价模板已写入数据库: %d", len(templates))