营销预测里有一个很现实的问题:新活动还没上线,怎么估计它可能带来的表现?Target 的做法是把“找相似历史活动”这件事从规则表和人工判断,升级成基于 embedding、向量检索和 LLM 排序的语义匹配系统。它不是让大模型直接拍脑袋预测结果,而是先把历史案例找准,再把这些案例喂给后续预测流程。
从规则匹配到语义检索
传统营销活动匹配通常依赖人工维护的规则:渠道相同、品类相同、预算区间接近、时间窗口类似……这些条件清晰,但容易漏掉“语义上相似、字段上不完全一致”的活动。
Target 的系统核心变化是:
- 把营销活动描述、目标、渠道、受众、优惠形式等信息编码成向量;
- 用向量搜索召回相似历史活动;
- 再用 LLM 对候选活动做排序和解释;
- 将活动实际结果作为反馈,继续修正检索与排序质量。
这类架构的好处是,匹配逻辑不再只依赖硬编码字段,而能理解“返校季文具促销”和“学生用品开学活动”这类表达差异。
为什么不是只用向量搜索
向量检索适合快速召回,但它不总是适合做最终判断。营销活动的相似性往往有多层含义:受众是否一致、促销机制是否相似、业务目标是否接近、投放季节是否可比。
因此,一个更稳妥的流程是两段式:
- 向量数据库先召回 top-k 候选,控制成本和延迟;
- LLM 阅读新活动和候选活动摘要,给出排序、理由和置信度。
Target 报告的评估结果显示,该系统在 top-1 上有 75% 覆盖率,在 top-3 上达到 100% 覆盖率。这个数字的意义不只是“模型很准”,更重要的是它说明:即使第一名不总是完美,前三个候选通常足够让预测流程和分析人员继续工作。
可以这样实践:一个最小语义匹配原型
下面示例用 Python 做一个本地可运行的简化版本:用句向量模型生成 embedding,用余弦相似度召回历史活动。实际生产中可以把内存数组替换成向量数据库,并在召回后接入 LLM reranker。
运行前安装依赖:
pip install sentence-transformers numpy
保存为 campaign_match.py 后运行:
from sentence_transformers import SentenceTransformer
import numpy as np
campaigns = [
{
"id": "C001",
"text": "Back-to-school promotion for notebooks, backpacks, and student supplies. Audience: parents and students. Channel: email and app push.",
"outcome": "high conversion, strong app engagement",
},
{
"id": "C002",
"text": "Holiday discount campaign for toys and gifts. Audience: families. Channel: paid search and social ads.",
"outcome": "high revenue, medium conversion",
},
{
"id": "C003",
"text": "Weekly grocery coupon campaign for household essentials. Audience: loyalty members. Channel: app push.",
"outcome": "steady repeat purchases",
},
{
"id": "C004",
"text": "Dorm room essentials campaign for college students, including bedding, lamps, and storage. Channel: email and social.",
"outcome": "strong seasonal lift",
},
]
new_campaign = "Campaign for college move-in season selling dorm storage, bedding, and study supplies to students through email and app notifications."
model = SentenceTransformer("all-MiniLM-L6-v2")
texts = [item["text"] for item in campaigns]
embeddings = model.encode(texts, normalize_embeddings=True)
query_embedding = model.encode([new_campaign], normalize_embeddings=True)[0]
scores = embeddings @ query_embedding
ranked_indexes = np.argsort(scores)[::-1]
for rank, index in enumerate(ranked_indexes[:3], start=1):
item = campaigns[index]
print(f"#{rank} {item['id']} score={scores[index]:.3f}")
print(f" text: {item['text']}")
print(f" historical outcome: {item['outcome']}")
你可以改动三类内容来贴近自己的业务:
- 把
text扩展为结构化拼接,例如目标、渠道、预算、品类、时间、受众; - 把
outcome记录为真实指标,例如转化率、销售额、增量 lift; - 把 top-3 候选交给 LLM,让它输出“最相似活动、相似原因、不适合作比较的风险”。
一个简单的 rerank prompt 可以这样写:
你是营销预测系统的候选活动排序器。
新活动:
{new_campaign}
候选历史活动:
{candidate_campaigns}
请按预测可参考价值排序,返回 JSON:
[
{
"campaign_id": "...",
"rank": 1,
"reason": "说明受众、渠道、品类或季节性为什么相似",
"risk": "说明哪里不可比"
}
]
反馈闭环比一次性匹配更关键
语义匹配系统真正变强,靠的不是一次 embedding 训练完就结束,而是把活动结果放回系统里。比如,某个候选活动在语义上相似,但实际表现差异很大,系统就需要记录原因:季节不同、折扣力度不同、库存约束不同,还是渠道组合不同。
这会影响后续两件事:
- 检索层:哪些字段应该提高权重,哪些文本描述需要补充;
- 排序层:LLM 在解释相似性时,是否应更重视业务结果而不是文案相似。
换句话说,反馈数据让系统从“看起来像”逐步变成“预测上有参考价值”。
落地时的取舍清单
如果要在自己的营销或增长系统里采用类似方案,可以从下面几个问题开始:
- 历史活动是否有足够干净的描述、标签和结果指标?
- top-k 召回错了以后,LLM 是否还有机会修正?
- 排序结果是否需要给业务人员解释,而不是只返回分数?
- 新活动上线后的真实结果是否会回流到样本库?
- 是否监控不同品类、渠道、季节下的匹配偏差?
这类系统的价值不在于炫技地“用 LLM 做预测”,而在于把过去分散在表格、经验和会议里的相似案例,稳定地送到预测流水线前面。对于营销团队来说,这通常意味着更少的人工检索、更一致的判断标准,以及更容易复盘的预测过程。