# -*- coding: utf-8 -*- """ 💰 动态定价与核保页面 保险产品选择 → 费率计算器 → 核保报告生成 """ import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import streamlit as st import plotly.graph_objects as go from collectors.financial_collector import get_all_companies, get_company_by_code from risk_engine.risk_scorer import calculate_six_dimension_scores from risk_engine.dynamic_pricing import ( calculate_premium, calculate_all_products, get_enterprise_scale, INDUSTRY_RISK_FACTORS, ) from risk_engine.report_generator import generate_report, format_report_markdown from config import INSURANCE_PRODUCTS st.set_page_config(page_title="动态定价与核保", page_icon="💰", layout="wide") st.markdown("# 💰 科创特有风险综合险 · 动态定价引擎") st.markdown("基于六维风险评分的“千企千面”精准核保与定价。") from utils.session_helper import render_company_selector, render_sidebar_global_company_selector with st.sidebar: render_sidebar_global_company_selector() st.markdown("---") # 企业选择(全局同步) company = render_company_selector("🏢 选择目标企业", key_suffix="pricing_page") stock_code = company["stock_code"] if company else "688256" if company: # 计算风险评分 risk_result = calculate_six_dimension_scores(company) scores = risk_result["scores"] comprehensive = risk_result["comprehensive_score"] level_info = risk_result["risk_level"] revenue = company.get("financials", {}).get("revenue_2024", 0) sector = company.get("sector", "") scale = get_enterprise_scale(revenue) st.markdown("---") # ============================================================ # 风险概要 # ============================================================ col_info, col_score = st.columns([2, 1]) with col_info: st.markdown(f"### {company['short_name']}") st.markdown(f"**行业**: {company['industry']} | **领域**: {sector} | **规模**: {scale}") st.markdown(f"**营收**: ¥{revenue/1e8:.1f}亿 | **行业风险系数**: {INDUSTRY_RISK_FACTORS.get(sector, 1.0):.2f}") with col_score: color = level_info["color"] st.markdown( f"
" f"
{comprehensive}
" f"
综合风险评分 {level_info['emoji']} {level_info['level']}
" f"
", unsafe_allow_html=True, ) st.markdown("---") # ============================================================ # 保险产品费率计算 # ============================================================ st.markdown("### 📊 保险产品费率方案") pricing_results = calculate_all_products( comprehensive, scores, sector, revenue ) # 三列展示三个险种 cols = st.columns(3) for i, pricing in enumerate(pricing_results): with cols[i]: product = INSURANCE_PRODUCTS[pricing["product_key"]] is_insurable = pricing["is_insurable"] if is_insurable: border_color = "#4CAF50" if comprehensive < 40 else ("#FF9800" if comprehensive < 70 else "#F44336") else: border_color = "#B71C1C" st.markdown( f"
" f"

{product['name']}

" f"

{product['description']}

" f"
", unsafe_allow_html=True, ) if is_insurable: st.metric("基础保费", f"¥{pricing['base_premium']:,.0f}") st.metric("最终保费", f"¥{pricing['final_premium']:,.0f}", delta=f"×{pricing['risk_multiplier']:.2f}", delta_color="inverse" if pricing['risk_multiplier'] > 1.2 else "normal") st.metric("保额", f"¥{pricing['adjusted_coverage']:,.0f}") st.metric("免赔率", f"{pricing['deductible_rate']:.0%}") with st.expander("📐 定价明细"): st.markdown(f"- 风险系数: {pricing['risk_multiplier']:.3f}") st.markdown(f"- 行业调整: {pricing['industry_factor']:.3f}") st.markdown(f"- 规模折扣: {pricing['scale_factor']:.3f}") st.markdown(f"- 维度调整: {pricing['dimension_adjustment']:.3f}") st.markdown(f"- **计算公式**: {pricing['pricing_breakdown']}") else: st.error("⛔ 风险过高,建议拒保") # ============================================================ # 费率对比图 # ============================================================ st.markdown("---") st.markdown("### 📈 费率构成分析") col_chart1, col_chart2 = st.columns(2) with col_chart1: # 基础保费 vs 最终保费对比 product_names = [p["product_name"] for p in pricing_results if p["is_insurable"]] base_premiums = [p["base_premium"] for p in pricing_results if p["is_insurable"]] final_premiums = [p["final_premium"] for p in pricing_results if p["is_insurable"]] max_val = max(max(base_premiums, default=100000), max(final_premiums, default=100000)) fig = go.Figure(data=[ go.Bar( name="基础保费", x=product_names, y=base_premiums, marker_color="#3B82F6", text=[f"¥{v:,.0f}" for v in base_premiums], textposition="outside", textfont=dict(size=11, color="#93C5FD") ), go.Bar( name="调整后保费", x=product_names, y=final_premiums, marker_color="#EF4444", text=[f"¥{v:,.0f}" for v in final_premiums], textposition="outside", textfont=dict(size=11, color="#FCA5A5") ), ]) fig.update_layout( title=dict( text="📊 基础保费 vs 调整后保费对比", font=dict(size=15, color="#F8FAFC"), x=0.02, y=0.96 ), barmode="group", height=380, margin=dict(l=20, r=20, t=75, b=30), paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font=dict(color="white"), legend=dict( x=0.02, y=0.88, orientation="h", bgcolor="rgba(0,0,0,0)", font=dict(color="#CBD5E1", size=12) ), xaxis=dict(gridcolor="rgba(255,255,255,0.05)"), yaxis=dict( gridcolor="rgba(255,255,255,0.08)", range=[0, max_val * 1.3] ), ) st.plotly_chart(fig, use_container_width=True) with col_chart2: # 定价因子贡献瀑布图(选第一个可投保的产品) insurable = [p for p in pricing_results if p["is_insurable"]] if insurable: p = insurable[0] factors = ["基础保费", "风险系数", "行业调整", "规模折扣", "维度调整", "最终保费"] values = [ p["base_premium"], p["base_premium"] * (p["risk_multiplier"] - 1), p["base_premium"] * p["risk_multiplier"] * (p["industry_factor"] - 1), p["base_premium"] * p["risk_multiplier"] * p["industry_factor"] * (p["scale_factor"] - 1), p["base_premium"] * p["risk_multiplier"] * p["industry_factor"] * p["scale_factor"] * (p["dimension_adjustment"] - 1), p["final_premium"], ] measures = ["absolute", "relative", "relative", "relative", "relative", "total"] fig2 = go.Figure(go.Waterfall( name=p["product_name"], orientation="v", measure=measures, x=factors, y=values, text=[f"¥{v:,.0f}" for v in values], textposition="outside", connector={"line": {"color": "rgba(255,255,255,0.3)"}}, increasing={"marker": {"color": "#EF4444"}}, decreasing={"marker": {"color": "#10B981"}}, totals={"marker": {"color": "#3B82F6"}}, )) fig2.update_layout( title=dict( text=f"📉 {p['product_name']} — 定价因子分解", font=dict(size=15, color="#F8FAFC"), x=0.02, y=0.96 ), height=380, margin=dict(l=20, r=20, t=75, b=30), paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font=dict(color="white"), xaxis=dict(gridcolor="rgba(255,255,255,0.05)"), yaxis=dict(gridcolor="rgba(255,255,255,0.08)"), ) st.plotly_chart(fig2, use_container_width=True) # ============================================================ # 核保报告生成 # ============================================================ st.markdown("---") st.markdown("### 📋 核保决策报告") # 检查 session_state 中是否有该企业的真实大模型辩论结果 has_real_debate = False debate_res_in_session = st.session_state.get("debate_results") if debate_res_in_session and debate_res_in_session.get("company", {}).get("stock_code") == stock_code: has_real_debate = True st.success("🤖 **已成功链接页面 3 的【大模型多智能体辩论】真实研判与穿透凭据!**") else: st.info("💡 **提示**:建议先至 **【⚖️ 多智能体辩论诊断】** 页面为该企业发起大模型辩论,本报告将自动整合最深度的 AI 审查凭据。") if st.button("📄 生成完整核保报告", type="primary", use_container_width=True): if has_real_debate: debate_result = debate_res_in_session else: # 若尚未发起辩论,则回退到离线规则引擎评估 from agents.law_agent import LawAgent from agents.tech_agent import TechAgent from agents.finance_agent import FinanceAgent from agents.judge_agent import JudgeAgent law_result = LawAgent()._rule_based_evaluation(company) tech_result = TechAgent()._rule_based_evaluation(company) fin_result = FinanceAgent()._rule_based_evaluation(company) judge_result = JudgeAgent()._rule_based_evaluation( company, law_result, tech_result, fin_result ) debate_result = { "law_result": law_result, "tech_result": tech_result, "finance_result": fin_result, "judge_result": judge_result, "conflicts": [], } report = generate_report(company, debate_result, pricing_results) markdown_report = format_report_markdown(report) st.markdown(markdown_report) # 下载按钮 st.download_button( label="📥 下载核保报告 (Markdown 格式)", data=markdown_report, file_name=f"核保报告_{company['short_name']}_{report['report_id']}.md", mime="text/markdown", use_container_width=True, )