
AI 个人理财助手(下文简称 IPA)通过对话式界面帮助用户完成「记账-预算-投资」闭环。我们希望回答:
北极星指标:30 日留存率(Day30 Retention)。拆解公式:
Day30 = f(首次激活完成度, 功能使用深度, 理财教育完成度, 推送触达效果)
事件类型 | 关键属性 | 示例 |
|---|---|---|
app_launch | channel, os | 小米商店、Android |
onboarding_step | step_id, duration | step_3, 15.2s |
bill_add | bill_type, amount, auto_tag | 餐饮, 28.5, False |
budget_set | category, amount | 餐饮, 1200 |
invest_view | product_id, risk_level | 0001, R3 |
push_receive | campaign_id, msg_type | 2025q3_edu, 图文 |
ODS → DWD(事件明细)→ DWS(用户-日汇总)→ ADS(分析主题宽表)
以 React-Native 为例,只展示 bill_add:
// track.js
import { Analytics } from 'mobile-analytics-sdk';
export const trackBillAdd = (bill) => {
Analytics.track('bill_add', {
bill_type: bill.type,
amount: bill.amount,
auto_tag: bill.autoTag,
ts: Date.now(),
user_id: global.userId,
});
};使用 FastAPI + Kafka:
# logger_service.py
from fastapi import FastAPI, Request
from aiokafka import AIOKafkaProducer
import json, time, uvicorn
app = FastAPI()
producer = AIOKafkaProducer(bootstrap_servers='kafka:9092')
@app.post("/collect")
async def collect(request: Request):
payload = await request.json()
payload['server_ts'] = int(time.time()*1000)
await producer.send("user_events", json.dumps(payload).encode())
return {"status": "ok"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)# etl_stream.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import *
spark = SparkSession.builder.appName("IPA_ETL").getOrCreate()
df = (spark.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "kafka:9092")
.option("subscribe", "user_events")
.load()
.selectExpr("CAST(value AS STRING) as json"))
schema = spark.read.json(df.rdd.map(lambda r: r.json)).schema
parsed = (df
.select(from_json(col("json"), schema).alias("e"))
.select("e.*")
.withColumn("dt", to_date(from_unixtime(col("ts")/1000)))
.withWatermark("ts", "10 minutes"))
query = (parsed.writeStream
.format("parquet")
.option("path", "/warehouse/ods/events")
.option("checkpointLocation", "/chk/events")
.partitionBy("dt")
.trigger(processingTime='1 minute')
.start())
query.awaitTermination()# user_tag.py
from pyspark.sql import functions as F
from pyspark.ml.feature import Bucketizer
df = spark.table("dws.user_daily")
# 计算「理财成熟度」
df = df.withColumn("maturity_score",
F.when(F.col("invest_view_cnt_30d") > 5, 3)
.when(F.col("budget_set_cnt_7d") > 0, 2)
.otherwise(1))
# 金额分桶
bucketizer = Bucketizer(splits=[0, 500, 2000, 10000, float("inf")],
inputCol="avg_bill_amount_30d",
outputCol="amount_level")
df = bucketizer.transform(df)
df.select("user_id", "maturity_score", "amount_level") \
.write.mode("overwrite").saveAsTable("ads.user_tag")-- retention.sql
WITH first_day AS (
SELECT user_id, min(dt) AS first_dt
FROM ods.events
WHERE event='app_launch'
GROUP BY 1
),
retention AS (
SELECT f.user_id,
datediff(e.dt, f.first_dt) AS delta
FROM first_day f
JOIN ods.events e
ON f.user_id = e.user_id
)
SELECT delta, count(distinct user_id) as users
FROM retention
WHERE delta BETWEEN 0 AND 30
GROUP BY delta
ORDER BY delta;用 Python 画留存曲线:
import pandas as pd, seaborn as sns, matplotlib.pyplot as plt
df = pd.read_sql("SELECT * FROM retention ORDER BY delta", conn)
sns.lineplot(x='delta', y='users', data=df)
plt.title("Cohort Retention")
plt.show()使用 retentioneering 库:
import retentioneering as re
re.config.update_config(col_event='event',
col_user_id='user_id',
col_time='ts')
df = pd.read_parquet("/warehouse/ods/events")
dataset = re.Dataset(df)
dataset.rete.plot_graph(
weight_col='user_id',
thresh=0.03,
targets=['invest_view'],
width=800, height=600
)from lifetimes import GammaGammaFitter, BetaGeoFitter
df_rfm = spark.sql("""
SELECT user_id,
datediff(max(dt), min(dt)) as T,
count(distinct dt) as frequency,
sum(amount) as monetary_value
FROM ods.events
WHERE event='bill_add'
GROUP BY 1
""").toPandas()
bgf = BetaGeoFitter(penalizer_coef=0.01)
bgf.fit(df_rfm['frequency'], df_rfm['T'])
ggf = GammaGammaFitter(penalizer_coef=0.01)
ggf.fit(df_rfm['frequency'], df_rfm['monetary_value'])
df_rfm['predicted_clv'] = ggf.customer_lifetime_value(
bgf,
df_rfm['frequency'],
df_rfm['T'],
df_rfm['monetary_value'],
time=12, # 12 个月
freq_unit='D'
)状态:用户成熟度、预算超支率、最近 7 日活跃度
动作:{教育卡片、基金推荐、记账提醒、沉默唤醒}
奖励:用户 3 日内活跃且投资转化率
使用 Stable-Baselines3 的 PPO:
import gym, numpy as np
from stable_baselines3 import PPO
class FinancePushEnv(gym.Env):
def __init__(self, user_features):
super().__init__()
self.user = user_features
self.action_space = gym.spaces.Discrete(4)
self.observation_space = gym.spaces.Box(low=0, high=1, shape=(10,), dtype=np.float32)
def step(self, action):
reward = simulate_reward(self.user, action)
next_state = self.user.next_features()
done = True
return next_state, reward, done, {}
def reset(self):
return self.user.to_vec()
env = FinancePushEnv(user_features)
model = PPO("MlpPolicy", env, verbose=1).learn(total_timesteps=10000)# ab_test.py
import statsmodels.api as sm
def evaluate_exp(df):
treat = df[df.variant=='push_v2']['converted']
ctrl = df[df.variant=='push_v1']['converted']
return sm.stats.proportion_ztest([treat.sum(), ctrl.sum()],
[len(treat), len(ctrl)])
p_value = evaluate_exp(spark.table("exp.push_exp").toPandas())
print("p-value:", p_value)原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。