153 lines
5.6 KiB
Python
153 lines
5.6 KiB
Python
|
|
import streamlit as st
|
|||
|
|
import pandas as pd
|
|||
|
|
import joblib
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
# 添加项目根目录到 Path 以便导入 src
|
|||
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|||
|
|
|
|||
|
|
from src.agent_app import MarketingAgent, CustomerFeatures
|
|||
|
|
|
|||
|
|
# 配置页面
|
|||
|
|
st.set_page_config(
|
|||
|
|
page_title="智能银行营销助手",
|
|||
|
|
page_icon="🤖",
|
|||
|
|
layout="wide"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 侧边栏:项目信息
|
|||
|
|
with st.sidebar:
|
|||
|
|
st.title("🏦 智能营销系统")
|
|||
|
|
st.markdown("---")
|
|||
|
|
st.info("**Day 5 演示版**")
|
|||
|
|
st.markdown("""
|
|||
|
|
**核心能力:**
|
|||
|
|
1. 📊 **LightGBM** 客户购买预测
|
|||
|
|
2. 🧠 **Agent** 策略生成
|
|||
|
|
3. 📝 **Pydantic** 结构化输出
|
|||
|
|
""")
|
|||
|
|
st.markdown("---")
|
|||
|
|
st.caption("由第 X 组开发")
|
|||
|
|
|
|||
|
|
# 主界面
|
|||
|
|
st.title("🤖 客户意向预测与决策系统")
|
|||
|
|
|
|||
|
|
# 1. 模拟客户输入
|
|||
|
|
st.header("1. 录入客户信息")
|
|||
|
|
|
|||
|
|
col1, col2, col3 = st.columns(3)
|
|||
|
|
|
|||
|
|
# 映射字典
|
|||
|
|
job_map = {
|
|||
|
|
"management": "管理人员", "technician": "技术人员", "entrepreneur": "企业家",
|
|||
|
|
"blue-collar": "蓝领", "unknown": "未知", "retired": "退休人员",
|
|||
|
|
"admin.": "行政人员", "services": "服务业", "self-employed": "自雇人士",
|
|||
|
|
"unemployed": "失业", "maid": "家政", "student": "学生"
|
|||
|
|
}
|
|||
|
|
education_map = {"tertiary": "高等教育", "secondary": "中等教育", "primary": "初等教育", "unknown": "未知"}
|
|||
|
|
marital_map = {"married": "已婚", "single": "单身", "divorced": "离异"}
|
|||
|
|
binary_map = {"yes": "是", "no": "否"}
|
|||
|
|
contact_map = {"cellular": "手机", "telephone": "座机", "unknown": "未知"}
|
|||
|
|
month_map = {
|
|||
|
|
"jan": "1月", "feb": "2月", "mar": "3月", "apr": "4月", "may": "5月", "jun": "6月",
|
|||
|
|
"jul": "7月", "aug": "8月", "sep": "9月", "oct": "10月", "nov": "11月", "dec": "12月"
|
|||
|
|
}
|
|||
|
|
poutcome_map = {"unknown": "未知", "failure": "失败", "other": "其他", "success": "成功"}
|
|||
|
|
|
|||
|
|
# 辅助函数:反向查找 key
|
|||
|
|
def get_key(val, my_dict):
|
|||
|
|
for key, value in my_dict.items():
|
|||
|
|
if val == value: return key
|
|||
|
|
return val
|
|||
|
|
|
|||
|
|
with col1:
|
|||
|
|
age = st.number_input("年龄", 18, 100, 30)
|
|||
|
|
job_display = st.selectbox("职业", list(job_map.values()))
|
|||
|
|
job = get_key(job_display, job_map)
|
|||
|
|
|
|||
|
|
education_display = st.selectbox("教育", list(education_map.values()))
|
|||
|
|
education = get_key(education_display, education_map)
|
|||
|
|
|
|||
|
|
balance = st.number_input("账户余额 (欧元)", -1000, 100000, 1500)
|
|||
|
|
|
|||
|
|
with col2:
|
|||
|
|
marital_display = st.selectbox("婚姻", list(marital_map.values()))
|
|||
|
|
marital = get_key(marital_display, marital_map)
|
|||
|
|
|
|||
|
|
housing_display = st.selectbox("是否有房贷", list(binary_map.values()))
|
|||
|
|
housing = get_key(housing_display, binary_map)
|
|||
|
|
|
|||
|
|
loan_display = st.selectbox("是否有个人贷", list(binary_map.values()))
|
|||
|
|
loan = get_key(loan_display, binary_map)
|
|||
|
|
|
|||
|
|
default_display = st.selectbox("是否有违约记录", list(binary_map.values()))
|
|||
|
|
default = get_key(default_display, binary_map)
|
|||
|
|
|
|||
|
|
with col3:
|
|||
|
|
contact_display = st.selectbox("联系方式", list(contact_map.values()))
|
|||
|
|
contact = get_key(contact_display, contact_map)
|
|||
|
|
|
|||
|
|
month_display = st.selectbox("最后联系月份", list(month_map.values()))
|
|||
|
|
month = get_key(month_display, month_map)
|
|||
|
|
|
|||
|
|
day = st.slider("最后联系日", 1, 31, 15)
|
|||
|
|
|
|||
|
|
poutcome_display = st.selectbox("上次活动结果", list(poutcome_map.values()))
|
|||
|
|
poutcome = get_key(poutcome_display, poutcome_map)
|
|||
|
|
|
|||
|
|
# 隐藏的高级特征
|
|||
|
|
with st.expander("高级营销特征 (可选)"):
|
|||
|
|
campaign = st.number_input("本次活动联系次数", 1, 50, 1)
|
|||
|
|
pdays = st.number_input("距离上次联系天数 (-1代表无)", -1, 999, -1)
|
|||
|
|
previous = st.number_input("活动前联系次数", 0, 100, 0)
|
|||
|
|
|
|||
|
|
# 2. 触发 Agent
|
|||
|
|
if st.button("🚀 开始分析与决策", type="primary"):
|
|||
|
|
try:
|
|||
|
|
# 构造 Pydantic 对象
|
|||
|
|
customer = CustomerFeatures(
|
|||
|
|
age=age, job=job, marital=marital, education=education,
|
|||
|
|
default=default, balance=balance, housing=housing, loan=loan,
|
|||
|
|
contact=contact, day=day, month=month,
|
|||
|
|
campaign=campaign, pdays=pdays, previous=previous, poutcome=poutcome
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 初始化 Agent
|
|||
|
|
with st.spinner("Agent 正在加载模型并思考..."):
|
|||
|
|
agent = MarketingAgent()
|
|||
|
|
decision = agent.run(customer)
|
|||
|
|
|
|||
|
|
# 3. 展示结果
|
|||
|
|
st.divider()
|
|||
|
|
st.header("2. 智能分析报告")
|
|||
|
|
|
|||
|
|
# 结果看板
|
|||
|
|
res_col1, res_col2 = st.columns([1, 2])
|
|||
|
|
|
|||
|
|
with res_col1:
|
|||
|
|
st.metric("预测购买概率", f"{decision.risk_score:.1%}")
|
|||
|
|
if decision.risk_score > 0.6:
|
|||
|
|
st.success(f"分群:{decision.customer_segment}")
|
|||
|
|
elif decision.risk_score > 0.3:
|
|||
|
|
st.warning(f"分群:{decision.customer_segment}")
|
|||
|
|
else:
|
|||
|
|
st.error(f"分群:{decision.customer_segment}")
|
|||
|
|
|
|||
|
|
with res_col2:
|
|||
|
|
st.subheader("💡 决策建议")
|
|||
|
|
st.info(decision.decision)
|
|||
|
|
st.markdown(f"**决策依据:** {decision.rationale}")
|
|||
|
|
|
|||
|
|
# 行动清单
|
|||
|
|
st.subheader("📝 执行清单")
|
|||
|
|
for i, action in enumerate(decision.actions, 1):
|
|||
|
|
st.write(f"{i}. {action}")
|
|||
|
|
|
|||
|
|
# JSON 视图
|
|||
|
|
with st.expander("查看原始 JSON 输出 (Traceable)"):
|
|||
|
|
st.json(decision.model_dump())
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
st.error(f"发生错误: {str(e)}")
|