“三高买什么医疗险”和“高血压高血脂高血糖怎么买医疗险”,说法不同,推品需求却接近。把前一句里的“医疗险”改成“重疾险”,只换了两个字,系统就需要重新选择产品。
在保险快查里,QQ Matching 的一个匹配结果会带出整组产品。我们需要它识别那些可以复用选品结果的需求,也要分清那些文字相似、却会改变推荐范围的条件。
把复杂选品留在离线,线上找回对应的产品池
面对复杂需求,Agentic Search 要拆解条件、调用搜索工具、整理候选产品。我们把这段工作放到离线执行,将查询及对应产品池一起写入 Elasticsearch。线上请求到来后,先找已有结果中能否复用的一组。
离线:推品 Query → Agentic Search → 产品池 → 写入 Elasticsearch
线上:用户 Query → Embedding → 最相近的离线 Query → 取回产品池查询和候选查询分别变成向量,Elasticsearch 返回相似度最高的一个离线 Query。只有最高分超过阈值,才复用它的产品池;我们没有把多个相近查询的产品池混在一起。
这条链路只服务复杂 Query。产品名搜索、明确的单条件筛选等简单请求,仍走原有的 HA3 easy。实际请求中,复杂度判断、QQ Matching 和原有搜索从开始就并发执行。复杂需求没有匹配上时,提交已经在途的原搜索结果。RAG 工程实践:QQ 产品召回与 QA 文档导航 记录了完整时序与评测口径。
Embedding 在这里比较的是两个推品问题。产品责任和适用条件仍然由产品数据与搜索工具提供;向量匹配决定是否能借用一次已有的选品结果。
把保险业务里的“同一个需求”做成训练样本
既有链路已经使用经过保险领域语料微调的 Qwen-Embedding-4B。面向 Qwen3-Embedding 的新数据方案聚焦推品意图,从 Top1000 Query 开始构造查询间的正负关系。
| 角色 | Query | 要保留或区分的需求 |
|---|---|---|
| 原始问题 | 三高买什么医疗险 | 三高人群购买医疗险 |
| 正例 | 高血压高血脂高血糖怎么买医疗险 | 展开疾病表达,保留推品意图 |
| 难负例 | 三高买什么重疾险 | 人群不变,改变保险赛道 |
这组三条查询导出成 MS-SWIFT 的 InfoNCE 样本后,对应下面的结构。示例只保留一个正例和一个难负例:
{
"messages": [
{"role": "user", "content": "三高买什么医疗险"}
],
"positive_messages": [
[
{"role": "user", "content": "高血压高血脂高血糖怎么买医疗险"}
]
],
"negative_messages": [
[
{"role": "user", "content": "三高买什么重疾险"}
]
]
}messages 放原始问题,也就是 Anchor;positive_messages 和 negative_messages 的外层列表分别容纳正例和负例,每个候选又是一组消息。这里每组只有一条 user 消息,content 只保存查询文本。监督关系由字段位置表达:疾病表达展开后的问法放进正例,改变保险赛道的问法放进负例。业务标注需要在导出前完成。Qwen 训练说明
为确定一条查询应该放在哪个字段,原始池先清洗全半角、标点和重复项,再用 prod_mapper、规则和受限 LLM 标明查询里的产品、人群、疾病、赛道与硬约束。主数据中的 query_categories 以查询原文为键,保存标签列表;生成前还要抽取意图、实体和不可改变的核心语义。对这条原始问题,正例必须保留“三高人群”和“购买医疗险”,这些制作依据不拼入训练文本。
带有年龄、免赔额、健告、续保或保障期限要求的查询,还要把对应条件写入槽位,正例不能遗漏或新增这些条件。多标签不只覆盖险种和人群,还包括产品名与保司、疾病、保障属性、旅行养老等场景,以及产品名、属性选品、产品对比、口语简称等表达类型。这些标签约束改写范围,也用于按需求类别检查匹配错误。
prod_mapper 提供标准产品名、别名、保司和一至三级赛道,约束哪些实体能替换、哪些不能替换。已有实体必须映射到标准值,模型不能自由创造赛道,也不能把同属一个赛道的两款产品当作别名。这里的产品数据用于约束制样,训练候选侧仍然只放 Query。
产品名需要先核对身份。“好医保·长期医疗(旗舰版2025)”与“好医保长期医疗旗舰版2025”经 Mapper 确认后可归到同一标准名称;若清洗后已经相同,就不再算一条有新表达的正例。简称、旧称、版本省略写法和渠道副本,要确认仍指向同一个产品才可替换,查询中明确的渠道条件也须保留。
“某产品的门诊能报销吗”可能是在问产品责任,“门诊保障选哪款”才是在表达选品需求。scope 把推品、产品知识、投保操作、理赔咨询、非保险、脏数据和无法判断分开。
每条原始问题计划生成 2~4 条正例。除了主例中的疾病表达展开,也覆盖稳定别名、句式和口语变化,例如把“适合买什么”换成“应该选哪种”。允许少量错别字、简称和标点省略,但保险赛道、人群、疾病、年龄、场景和硬约束保持一致,不能为了让句子更完整就补出原问题没有的条件。
负例计划为每条 4~7 条,主要选择表面相近、却改变推荐结果的查询。可以只换险种,也可以把“60 岁买什么意外险”改成“6 岁买什么意外险”,或只替换免赔额要求。一次尽量改一个关键槽位,让样本能够解释到底是哪项差异导致不匹配。少量跨赛道随机负例可以补充,不能用一批显而易见的不相关问题替代难负例。
把“老人”改成“父母”,未必保留年龄范围;把“保证续保 20 年”与“保障期 1 年”直接配成负例,又混淆了两个不同字段。这些地方需要按业务含义复核,不能只靠替换词表生成标签。
包含关系同样容易制造假负例。“医疗险/百万医疗险”“低免赔/零免赔”“三高/高血压”不能只看标签不同就判负。两条查询当前筛出的产品集合高度重合,也应触发复核;重合只能作为线索,不能替代对关键条件的检查,产品池本身也可能漏掉约束。
合成样本之外,方案还用原始 Qwen3-Embedding 对 Top1000 池做近邻召回。先找到高相似查询,再把关键标签冲突的项交给 LLM 判断是否确实会改变推品意图,明确不同才收为负例;意图相同的真实问法则补入正例。真实查询能补足生成模板没有覆盖的表达。
所有生成结果再经过一次意图核对,去掉语义漂移、重复、正负交叉和过于机械的模板。判断不清的样本留给人工,不为了凑足条数硬塞进训练集。正负例保存 sample_logics,记录改了哪个实体、为什么认为推品意图相同或不同。出现错误匹配时,可以据此回查样本的标注依据。
这些样本怎样改变模型的匹配结果
训练时,原始问题、正例和负例分别经过同一个编码器产生向量。训练程序计算向量间的相似度,再通过反向传播更新编码器参数。更新后的参数也会影响没见过的问法;能否正确匹配,要用独立样本检验。
假设原始问题为 ,正例为 ,难负例为 。常用的 InfoNCE 损失可以写成:
比较的是编码后向量的余弦相似度, 控制分数差异在损失中的敏感程度。若“三高买什么重疾险”比正确改写得分还高,训练就会惩罚这种排序。正例与负例共同告诉模型,疾病词的展开应该被容忍,险种变化需要被识别。对比损失说明
领域微调的起点已经具备通用语言和表示能力。以公开的 Qwen3 Embedding 为例,它先继承 Qwen3 语言模型底座,再做大规模相关文本对的训练。语言模型预训练主要预测下一个 token;Embedding 的对比预训练则让文本向量适合比较相关性。领域微调在已有能力上继续调整业务所需的相关关系。
公开配方使用 Qwen3-32B 合成约 1.5 亿对多任务弱监督文本,再通过高质量数据微调,其中包含筛出的约 1200 万对合成样本。前后两阶段都使用对比学习目标。通用模型在这些数据上学习广泛的文本关系,推品样本则用于进一步训练它区分会改变推荐范围的保险需求。Qwen3 技术报告 §3
MS-SWIFT 使用 task_type=embedding 和 loss_type=infonce 配置这类训练。虽然样本沿用消息结构,三侧的 user 文本都会被编码为向量,无须增加 assistant 答案。在上面这个单正例示例中,messages 中的原始问题对应公式中的 ,positive_messages 中的候选对应 ,negative_messages 中的候选对应 。
富信息主数据还保留分类、生成方式和置信度;每个正负例消息在 role、content 同级保存 sample_logics,记录改写或判负依据,原始查询消息不需要该字段。导出时明确筛出上例中的三个消息字段,并仅保留消息内的 role 与 content,不能假定框架会自动忽略自定义元数据。
目标模型限定为固定的推品 QQ 匹配,训练、评估与线上推理都不增加任务指令,也不拼接 Instruct: ...\nQuery: ...。两侧查询采用相同的输入约定,不能训练时是纯问题,推理封装却自动补上提示。以后若让同一模型承担产品文档或知识检索,再重新考虑多任务指令;当前方案不把那些内容混进候选侧。
使用批内负样本的 InfoNCE 会把同批其他样本也纳入比较,但同一问题的两条正例不能因此互相排斥。主数据保留多个正例,训练端的采样与屏蔽方式需要按所用版本核对,不能仅靠行内标签正确就认为整批没有假负例。
采用全参数微调还是 LoRA,决定哪些参数可以更新,正负例提供的学习目标不因此改变。LoRA 冻结底座、训练低秩增量,能减少可训练参数和显存需求;启用增量后仍会改变文本向量,冻结底座并不保证原有匹配能力不下降。LoRA 论文 方案尚未确定使用哪种参数更新方式,也没有提供训练效果对照。
最后还是看用户拿到了哪些产品
一条查询的改写如果同时出现在训练集与评测集里,模型容易得到很好看的分数。本阶段先保留完整数据,不按导出行随机切分。后续划分时,同一原始问题及其所有正负改写放在同一集合,同义查询组和同一产品别名组也不能跨集合。同一模板只换一个词的样本,还要检查是否借着不同的原始问题编号泄漏出去。
分组之后,再尽量平衡一级、二级赛道、人群、疾病、表达类型与高频约束。三级赛道只有样本量足够时才参与分层,不能为了让比例整齐就拆开同义组。评测集优先保留真实查询对和人工确认样本,用分组结果观察哪些人群或赛道还容易匹配错。
真实近邻被多个原始问题引用时,也要追踪身份,避免评测问题以另一条训练样本的正例或负例再次出现。切分后检查跨集合引用,移除或重新选择冲突样本;同一句查询可能有多个来源,仅检查 query_id 重复还不够。
查询对评测检查模型能否区分推品意图,线上验收仍落在产品上。QQ Matching 的核心指标是前三个产品的 Precision@3,即其中满足需求的产品数除以三;不足三个时,分母仍按三个计算。一个匹配分数更高的模型,如果带回了不合适的产品池,就没有解决推品问题。
错误也不全来自模型。离线查询库可能缺少对应需求,产品池可能过期,候选排序也可能把合适产品挤到后面。取回产品池后,渠道、上下架和硬条件仍由确定性代码校验。AgenticOne 产品召回:JSON 分发与内存全量过滤 记录了这些约束怎样落到候选产品上。
新模型接入时,离线查询库要重新生成向量,模型与索引成套切换。即使候选排序改善,相似度分布也可能随训练改变,原来的命中阈值仍需重新验证。阈值决定这次请求能否复用产品池,没过线就继续交给原有搜索。