文章目录
- 每日一句正能量
- 1. 背景与问题
- 2. 环境与数据
- 2.1 客户关系表
- 2.2 文档画像和向量表
- 2.3 客户行为时序表
- 3. 复现过程
- 3.1 只有关系筛选的局限
- 3.2 只有向量检索的风险
- 3.3 复现过滤后置问题
- 4. 方案实施
- 4.1 先定义强约束
- 4.2 关系与向量联合查询
- 4.3 组合 JSONB 标签
- 4.4 使用时序行为重排
- 4.5 性能观察
- 4.6 部署与验证
- 5. 结果对比
- 5.1 性能与效果可视化分析
- 6. 风险与复盘
- 6.1 常见风险
- 6.2 RTO 与 RPO
- 6.3 复盘结论
每日一句正能量
“最美的蜕变常常包裹着最难熬的磨砺。”
最高价值的获得,往往与最艰难的付出成正比。
不抗拒命运,但始终选择看见光亮;不否认黑暗,但坚信根在生长。在日常中活出——一种清醒的、优美的、属于你自己的“活着”。
1. 背景与问题
客户画像系统通常已经积累了大量结构化信息:客户等级、注册时间、所属区域、会员状态、消费金额、标签、最近活跃时间、投诉记录等。这些数据适合用关系数据库进行精确筛选。
例如,运营人员可以找到“近 90 天未消费、累计消费超过 5000 元、位于华东区域的客户”:
SELECTcustomer_id,customer_name,total_spendFROMcustomerWHEREregion_codeIN('CN-SH','CN-JS','CN-ZJ')ANDtotal_spend>=5000ANDlast_purchase_at<now()-interval'90 days';但实际运营问题通常不止于精确筛选。运营人员还会提出语义型需求:
找出与近期频繁询问价格保护、续费优惠、竞品对比的高价值沉默客户相似的人群。这类问题无法只靠LIKE、枚举标签或固定规则解决。客户咨询摘要、客服会话、工单说明和运营备注中包含大量自然语言信息,同样的意图可能有不同表达:
续费太贵,准备换其他平台。 现在有没有保价活动? 别家的方案价格低很多。 会员到期后暂时不考虑继续购买。如果只使用关系条件,无法识别这些语义相近但词面不同的客户;如果只使用向量检索,又可能返回不符合客户等级、区域、授权范围和营销规则的人群。
因此,客户画像检索应采用多模联合方式:
关系数据:客户等级、区域、消费、会员状态、合规状态; 文档数据:标签、会话摘要、工单内容和动态属性; 时序数据:访问、咨询、购买、退款和投诉变化; 向量数据:客户意图、相似问题和语义特征。核心原则是:
关系条件定义“哪些客户可以进入候选集”; 向量检索定义“候选客户与目标意图是否相似”; 时序行为定义“客户当前是否值得触达”; 文档属性补充“为什么被选中”的解释。2. 环境与数据
实验环境:
| 组件 | 用途 |
|---|---|
| PostgreSQL 15 | 客户主数据、权限、运营任务 |
| pgvector | 会话摘要和客户意图向量 |
| JSONB | 兴趣标签、渠道、偏好和扩展属性 |
| TimescaleDB | 浏览、咨询、订单、退款等行为事件 |
| Embedding 服务 | 将客户会话摘要转换为向量 |
| 查询 API | 条件校验、联合检索、审计和结果解释 |
2.1 客户关系表
CREATETABLEcustomer(customer_id UUIDPRIMARYKEY,tenant_idTEXTNOTNULL,customer_nameTEXTNOTNULL,customer_levelTEXTNOTNULL,region_codeTEXTNOTNULL,member_statusTEXTNOTNULL,total_spendNUMERIC(14,2)NOTNULLDEFAULT0,last_purchase_at TIMESTAMPTZ,marketing_consentBOOLEANNOTNULLDEFAULTFALSE,statusTEXTNOTNULLDEFAULT'active',created_at TIMESTAMPTZNOTNULLDEFAULTnow());CREATEINDEXidx_customer_segmentONcustomer(tenant_id,status,marketing_consent,customer_level,region_code,total_spend);marketing_consent是强约束字段。无论向量相似度多高,未授权营销触达的客户都不能进入运营名单。
2.2 文档画像和向量表
CREATEEXTENSIONIFNOTEXISTSvector;CREATETABLEcustomer_profile_document(customer_id UUIDPRIMARYKEYREFERENCEScustomer(customer_id),tenant_idTEXTNOTNULL,profile_summaryTEXTNOTNULL,tags JSONBNOTNULLDEFAULT'{}'::jsonb,updated_at TIMESTAMPTZNOTNULLDEFAULTnow());CREATETABLEcustomer_intent_embedding(embedding_id UUIDPRIMARYKEY,customer_id UUIDNOTNULLREFERENCEScustomer(customer_id),tenant_idTEXTNOTNULL,source_typeTEXTNOTNULL,source_time TIMESTAMPTZNOTNULL,summaryTEXTNOTNULL,embedding_modelTEXTNOTNULL,embedding VECTOR(768)NOTNULL,metadata JSONBNOTNULLDEFAULT'{}'::jsonb,statusTEXTNOTNULLDEFAULT'active');CREATEINDEXidx_customer_embedding_hnswONcustomer_intent_embeddingUSINGhnsw(embedding vector_cosine_ops);CREATEINDEXidx_customer_embedding_filterONcustomer_intent_embedding(tenant_id,status,customer_id);CREATEINDEXidx_customer_profile_tagsONcustomer_profile_documentUSINGGIN(tags);source_type可以用于区分向量来源:
service_chat ticket_summary sales_note survey_feedback complaint2.3 客户行为时序表
CREATEEXTENSIONIFNOTEXISTStimescaledb;CREATETABLEcustomer_event(ts TIMESTAMPTZNOTNULL,tenant_idTEXTNOTNULL,customer_id UUIDNOTNULL,event_typeTEXTNOTNULL,event_valueNUMERIC(14,2),attributes JSONBNOTNULLDEFAULT'{}'::jsonb);SELECTcreate_hypertable('customer_event',by_range('ts'),if_not_exists=>TRUE);CREATEINDEXidx_customer_event_lookupONcustomer_event(tenant_id,customer_id,event_type,tsDESC);事件类型示例:
page_view consultation purchase refund complaint campaign_click coupon_claim3. 复现过程
3.1 只有关系筛选的局限
下面 SQL 可以找出高价值沉默客户:
SELECTcustomer_id,customer_name,total_spend,last_purchase_atFROMcustomerWHEREtenant_id='tenant_a'ANDstatus='active'ANDmarketing_consent=TRUEANDcustomer_levelIN('gold','platinum')ANDtotal_spend>=5000ANDlast_purchase_at<now()-interval'90 days';但这条查询无法判断客户沉默的原因。客户可能是满意但暂时没有需求,也可能是对价格、服务、竞品或产品功能存在不满。对所有人使用同一种召回活动,转化率通常不高。
3.2 只有向量检索的风险
错误方式是全量寻找“价格敏感”客户:
SELECTcustomer_id,summary,1-(embedding<=>$1::vector)ASsimilarityFROMcustomer_intent_embeddingWHEREtenant_id='tenant_a'ANDstatus='active'ORDERBYembedding<=>$1::vectorLIMIT100;这会包含:
- 已注销客户。
- 未授权营销的客户。
- 消费能力不匹配的客户。
- 不在当前活动区域的客户。
- 已投诉且不适合触达的客户。
向量相似度只能说明文本意图接近,不能替代业务资格判断。
3.3 复现过滤后置问题
WITHvector_topAS(SELECTcustomer_id,summary,1-(embedding<=>$1::vector)ASsimilarityFROMcustomer_intent_embeddingWHEREtenant_id='tenant_a'ORDERBYembedding<=>$1::vectorLIMIT100)SELECTv.customer_id,v.summary,v.similarityFROMvector_top vJOINcustomer cONc.customer_id=v.customer_idWHEREc.marketing_consent=TRUEANDc.customer_levelIN('gold','platinum')ANDc.status='active'ORDERBYv.similarityDESCLIMIT20;如果全局 Top-100 中大部分客户不符合营销资格,过滤后可能只剩少量结果。更严重的是,无资格客户已进入应用候选集、日志或后续模型上下文。
4. 方案实施
4.1 先定义强约束
客户画像检索必须区分强约束和弱排序。
| 类型 | 字段示例 | 处理方式 |
|---|---|---|
| 强约束 | 租户、客户状态、营销授权、区域、黑名单 | 必须在 SQL 中过滤 |
| 业务筛选 | 客户等级、累计消费、会员状态、最近消费时间 | 通常在 SQL 中过滤 |
| 弱排序 | 意图相似度、近期浏览、活动点击、购买趋势 | 用于候选重排 |
| 解释信息 | 标签、会话摘要、客服备注 | 用于结果说明 |
4.2 关系与向量联合查询
目标是找到“高价值、已授权、超过 90 天未消费、且与价格敏感咨询语义相似”的客户。
WITHeligible_customerAS(SELECTc.customer_id,c.customer_name,c.customer_level,c.region_code,c.total_spend,c.last_purchase_atFROMcustomer cWHEREc.tenant_id=$2ANDc.status='active'ANDc.marketing_consent=TRUEANDc.customer_levelIN('gold','platinum')ANDc.total_spend>=5000ANDc.last_purchase_at<now()-interval'90 days'),vector_candidatesAS(SELECTe.customer_id,e.summary,e.source_time,1-(e.embedding<=>$1::vector)ASsemantic_scoreFROMcustomer_intent_embedding eJOINeligible_customer cONc.customer_id=e.customer_idWHEREe.tenant_id=$2ANDe.status='active'ANDe.source_typeIN('service_chat','ticket_summary')ORDERBYe.embedding<=>$1::vectorLIMIT200)SELECTc.customer_id,c.customer_name,c.customer_level,c.region_code,c.total_spend,v.summary,v.source_time,v.semantic_scoreFROMvector_candidates vJOINeligible_customer cONc.customer_id=v.customer_idORDERBYv.semantic_scoreDESCLIMIT20;该查询先使用关系模型限制业务边界,再使用向量模型排序。最终结果既符合营销规则,也保留语义相关性。
4.3 组合 JSONB 标签
客户画像往往有动态标签,例如:
{"preferred_channel":"wechat","product_interest":["database","monitoring"],"price_sensitivity":"high","industry":"retail"}查询“偏好微信触达、对数据库产品感兴趣”的客户:
SELECTc.customer_id,c.customer_name,p.tags,1-(e.embedding<=>$1::vector)ASsemantic_scoreFROMcustomer cJOINcustomer_profile_document pONp.customer_id=c.customer_idJOINcustomer_intent_embedding eONe.customer_id=c.customer_idWHEREc.tenant_id=$2ANDc.status='active'ANDc.marketing_consent=TRUEANDp.tags @>'{"preferred_channel":"wechat"}'ANDp.tags->'product_interest'?'database'ANDe.status='active'ORDERBYe.embedding<=>$1::vectorLIMIT20;高频筛选项应保留在普通列中。JSONB 更适合承载变化快、维度多、难以预先穷举的画像属性。
4.4 使用时序行为重排
向量结果相似不代表客户当前值得触达。可以按近 30 天行为进行重排:
WITHbehaviorAS(SELECTcustomer_id,count(*)FILTER(WHEREevent_type='campaign_click')AScampaign_clicks_30d,count(*)FILTER(WHEREevent_type='consultation')ASconsultations_30d,max(ts)ASlast_active_atFROMcustomer_eventWHEREtenant_id=$2ANDts>=now()-interval'30 days'GROUPBYcustomer_id),semantic_candidatesAS(SELECTe.customer_id,max(1-(e.embedding<=>$1::vector))ASsemantic_scoreFROMcustomer_intent_embedding eJOINcustomer cONc.customer_id=e.customer_idWHEREe.tenant_id=$2ANDc.status='active'ANDc.marketing_consent=TRUEANDc.total_spend>=5000ANDe.status='active'GROUPBYe.customer_idORDERBYmax(1-(e.embedding<=>$1::vector))DESCLIMIT200)SELECTs.customer_id,s.semantic_score,coalesce(b.campaign_clicks_30d,0)AScampaign_clicks_30d,coalesce(b.consultations_30d,0)ASconsultations_30d,s.semantic_score*0.75+ln(1+coalesce(b.campaign_clicks_30d,0))*0.15+ln(1+coalesce(b.consultations_30d,0))*0.10ASfinal_scoreFROMsemantic_candidates sLEFTJOINbehavior bONb.customer_id=s.customer_idORDERBYfinal_scoreDESCLIMIT20;这里的时序信号只用于排序,不能覆盖营销授权、黑名单、客户状态等强约束。
4.5 性能观察
使用以下 SQL 查看联合查询计划:
EXPLAIN(ANALYZE,BUFFERS)SELECTe.customer_id,1-(e.embedding<=>$1::vector)ASsemantic_scoreFROMcustomer_intent_embedding eJOINcustomer cONc.customer_id=e.customer_idWHEREe.tenant_id='tenant_a'ANDe.status='active'ANDc.status='active'ANDc.marketing_consent=TRUEANDc.total_spend>=5000ORDERBYe.embedding<=>$1::vectorLIMIT20;重点检查:
是否先过滤租户和授权; Rows Removed by Filter 是否过高; 向量索引是否真正参与查询; Buffers 是否出现异常增长; 不同客户分群下 P95 延迟是否稳定; 过滤后 Top-K 是否仍能返回足够候选。4.6 部署与验证
建议上线前执行以下验证:
[ ] 未授权营销客户不会进入任何候选集。 [ ] 已注销、黑名单和冻结客户不会被向量召回。 [ ] 同一客户多条会话不会重复占满 Top-K。 [ ] 画像标签更新后,查询结果能够及时反映。 [ ] 向量生成失败时,旧向量是否仍可控使用。 [ ] 时序行为服务异常时,系统能回退到语义排序。 [ ] 查询日志不记录完整敏感会话内容。5. 结果对比
以下是用于说明评估方法的示例结果,正式发布应替换为真实压测与业务统计数据。
| 方案 | 客户资格合规率 | Top-20 语义相关率 | P95 延迟 | 人群重复率 |
|---|---|---|---|---|
| 仅关系筛选 | 100% | 58% | 35 ms | 低 |
| 仅向量检索 | 74% | 84% | 72 ms | 高 |
| 向量后置过滤 | 100% | 69% | 88 ms | 中 |
| 关系过滤下推 + 向量排序 | 100% | 82% | 96 ms | 低 |
| 联合查询 + 时序重排 | 100% | 85% | 118 ms | 低 |
从结果可以看出:
- 只用关系筛选,合规但难理解客户当前意图。
- 只用向量检索,语义较强但容易违反业务规则。
- 后置过滤会造成候选浪费和 Top-K 不足。
- 关系过滤下推后,语义结果和业务资格可以同时满足。
- 时序行为重排能提升“当前可触达性”,但应严格控制权重。
建议持续监控:
授权过滤拦截数; Top-K 不足率; 客户重复出现率; 向量召回耗时; 时序行为聚合耗时; 活动触达后的点击、咨询和转化; 投诉率与退订率。5.1 性能与效果可视化分析
为了更直观地对比各方案的差异,下面基于表格数据绘制两张 ASCII 图表。
图 1:客户资格合规率与 Top-20 语义相关率对比
合规率 / 相关率 (%) 100 | ●───────────────●───────────────●───────────────● | │ │ │ │ 90 | │ │ │ │ | │ │ │ │ 80 | │ │ ●───● │ ●───● │ | │ │ │ │ │ │ │ │ 70 | │ │ ●───┤ │ │ │ │ │ | │ │ │ │ │ │ │ │ │ 60 | │ ●───● │ │ │ │ │ │ │ │ | │ │ │ │ │ │ │ │ │ │ │ 50 | └───┴───┴───────┴───┴───┴───┴───┴───────┴───┴───┴──▶ | 仅关系筛选 仅向量检索 向量后置过滤 关系过滤下推 联合查询 | + 向量排序 + 时序重排 | | ● = 客户资格合规率 ■ = Top-20 语义相关率图 2:P95 延迟递增趋势
P95 延迟 (ms) 120 | ●───────────────● | │ │ 100 | ●───────────────●───┤ │ | │ │ │ │ 80 | ●───────────┤ │ │ │ | │ │ │ │ │ 60 | ●────┤ │ │ │ │ | │ │ │ │ │ │ 40 | │ │ │ │ │ │ | │ │ │ │ │ │ 20 | └────┴───────────┴───────────────┴───┴───────────────┴──▶ | 仅关系筛选 仅向量检索 向量后置过滤 关系过滤下推 联合查询 | + 向量排序 + 时序重排从两张图可以清晰看出:「关系过滤下推 + 向量排序」是性价比最优的方案。它在保持 100% 客户资格合规率的同时,将 Top-20 语义相关率从「仅关系筛选」的 58% 大幅提升至 82%,而 P95 延迟仅从 35ms 增加到 96ms,相比「仅向量检索」的 72ms 也只多了 24ms,却换来了合规率从 74% 到 100% 的质变,彻底消除了业务违规风险。相比之下,「联合查询 + 时序重排」虽然把语义相关率进一步推高到 85%,但代价是延迟从 96ms 增至 118ms,增加了 22ms。这 3% 的相关率提升是否值得,取决于业务场景:对于客单价高、转化周期长的精细化运营场景,多花 22ms 换取更精准的触达名单、减少无效打扰,通常是划算的;但对于对延迟极度敏感、且已有足够候选量的场景,这 3% 的边际收益可能并不明显,此时「关系过滤下推 + 向量排序」已能覆盖绝大多数需求。
6. 风险与复盘
6.1 常见风险
| 风险 | 表现 | 应对措施 |
|---|---|---|
| 只做向量检索 | 返回不符合资格的客户 | 强约束下推到 SQL |
| 过滤条件后置 | Top-K 过滤后不足 | 在候选前过滤 |
| 画像数据过期 | 触达策略失效 | 设置更新时间和有效期 |
| 标签滥用 JSONB | 高频查询变慢 | 高频字段列化并建立索引 |
| 多会话重复召回 | 单客户占据候选列表 | 按 customer_id 聚合或去重 |
| 时序数据滞后 | 热度排序偏差 | 标记数据延迟并设置回退 |
| 营销授权遗漏 | 合规风险 | 授权作为强制条件 |
| 会话摘要泄露 | 敏感信息暴露 | 脱敏、最小化展示与审计 |
| 向量模型混用 | 分数无法比较 | 记录模型版本并分批重建 |
6.2 RTO 与 RPO
| 项目 | 建议目标 |
|---|---|
| 客户主数据 RPO | 0 |
| 营销授权变更生效 | 1 分钟内 |
| 客户画像更新到可检索 | 10 分钟内 |
| 向量索引故障 RTO | 30 分钟内降级或恢复 |
| 行为重排故障 RTO | 5 分钟内回退至语义排序 |
| 越权或违规触达容忍度 | 0 |
6.3 复盘结论
关系数据与向量数据联合查询,不是把两张表简单JOIN起来,而是为不同数据能力划定职责:
关系数据负责资格、权限、状态和业务边界; 文档数据负责标签、偏好、来源和解释信息; 时序数据负责活跃度、趋势和触达时机; 向量数据负责自然语言意图和相似客户发现。推荐的生产查询路径是:
校验租户、权限和营销授权 -> 关系筛选客户资格 -> 文档标签过滤画像范围 -> 向量检索相似意图 -> 客户维度去重 -> 时序行为重排 -> 输出可解释的触达名单 -> 记录审计与效果反馈最终验收不能只关注“是否找到相似客户”,还必须确认:
- 客户是否满足业务和合规条件。
- 相似意图是否有客服摘要或标签作为解释。
- 近期时序行为是否支持当前触达决策。
- 查询延迟、候选数量和重复率是否可控。
- 用户反馈、退订率和投诉率是否被持续监控。
转载自:https://blog.csdn.net/u014727709/article/details/164884529
欢迎 👍点赞✍评论⭐收藏,欢迎指正