瞬维AI技术拆解:AI获客智能体的双层漏斗算法原理

AI获客智能体的核心挑战是:每天处理数百条消息,如何准确筛选出高意向线索,同时不漏接潜在客户?我们采用了双层漏斗算法,本文详细拆解其技术原理。

一、问题定义

AI获客智能体部署在抖音、快手、小红书、视频号等平台的评论区和私信中,7x24小时监听用户消息。单台智能体日触达可达数百条,但其中大部分是闲聊、表情、无关评论。

如果智能体不能准确筛选,就会出现两个问题:

  1. 漏接:高意向咨询被当成闲聊,客户流失
  2. 误报:闲聊被当成高意向,销售被无效线索淹没

传统的单层分类(是/否高意向)很难同时保证准确率和召回率。双层漏斗算法通过两级筛选,在保证准确率的同时最大化召回率。

二、双层漏斗算法架构

复制代码
用户消息
    │
    ▼
┌─────────────────────┐
│ 第一层:意图分类      │  宽进严出,高召回
│ 6大类 x 3-5子类     │  准确率要求:85%+
│ 召回率要求:95%+     │
└─────────┬───────────┘
          │ 高意向类消息(咨询/价格/联系方式)
          ▼
┌─────────────────────┐
│ 第二层:意向评分      │  精准筛选,高准确
│ 0-100分综合评分      │  准确率要求:90%+
│ 阈值:60分           │
└─────────┬───────────┘
          │ 评分>=60分
          ▼
    推送给销售

下面是双层漏斗算法的整体流程图:
#mermaid-svg-BsrKr0kpmAoJWDXO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BsrKr0kpmAoJWDXO .error-icon{fill:#552222;}#mermaid-svg-BsrKr0kpmAoJWDXO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BsrKr0kpmAoJWDXO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BsrKr0kpmAoJWDXO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BsrKr0kpmAoJWDXO .marker.cross{stroke:#333333;}#mermaid-svg-BsrKr0kpmAoJWDXO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BsrKr0kpmAoJWDXO p{margin:0;}#mermaid-svg-BsrKr0kpmAoJWDXO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BsrKr0kpmAoJWDXO .cluster-label text{fill:#333;}#mermaid-svg-BsrKr0kpmAoJWDXO .cluster-label span{color:#333;}#mermaid-svg-BsrKr0kpmAoJWDXO .cluster-label span p{background-color:transparent;}#mermaid-svg-BsrKr0kpmAoJWDXO .label text,#mermaid-svg-BsrKr0kpmAoJWDXO span{fill:#333;color:#333;}#mermaid-svg-BsrKr0kpmAoJWDXO .node rect,#mermaid-svg-BsrKr0kpmAoJWDXO .node circle,#mermaid-svg-BsrKr0kpmAoJWDXO .node ellipse,#mermaid-svg-BsrKr0kpmAoJWDXO .node polygon,#mermaid-svg-BsrKr0kpmAoJWDXO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BsrKr0kpmAoJWDXO .rough-node .label text,#mermaid-svg-BsrKr0kpmAoJWDXO .node .label text,#mermaid-svg-BsrKr0kpmAoJWDXO .image-shape .label,#mermaid-svg-BsrKr0kpmAoJWDXO .icon-shape .label{text-anchor:middle;}#mermaid-svg-BsrKr0kpmAoJWDXO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BsrKr0kpmAoJWDXO .rough-node .label,#mermaid-svg-BsrKr0kpmAoJWDXO .node .label,#mermaid-svg-BsrKr0kpmAoJWDXO .image-shape .label,#mermaid-svg-BsrKr0kpmAoJWDXO .icon-shape .label{text-align:center;}#mermaid-svg-BsrKr0kpmAoJWDXO .node.clickable{cursor:pointer;}#mermaid-svg-BsrKr0kpmAoJWDXO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BsrKr0kpmAoJWDXO .arrowheadPath{fill:#333333;}#mermaid-svg-BsrKr0kpmAoJWDXO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BsrKr0kpmAoJWDXO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BsrKr0kpmAoJWDXO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BsrKr0kpmAoJWDXO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BsrKr0kpmAoJWDXO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BsrKr0kpmAoJWDXO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BsrKr0kpmAoJWDXO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BsrKr0kpmAoJWDXO .cluster text{fill:#333;}#mermaid-svg-BsrKr0kpmAoJWDXO .cluster span{color:#333;}#mermaid-svg-BsrKr0kpmAoJWDXO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BsrKr0kpmAoJWDXO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BsrKr0kpmAoJWDXO rect.text{fill:none;stroke-width:0;}#mermaid-svg-BsrKr0kpmAoJWDXO .icon-shape,#mermaid-svg-BsrKr0kpmAoJWDXO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BsrKr0kpmAoJWDXO .icon-shape p,#mermaid-svg-BsrKr0kpmAoJWDXO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BsrKr0kpmAoJWDXO .icon-shape .label rect,#mermaid-svg-BsrKr0kpmAoJWDXO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BsrKr0kpmAoJWDXO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BsrKr0kpmAoJWDXO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BsrKr0kpmAoJWDXO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否(闲聊/投诉/其他)
是(咨询产品/价格/联系方式)
是
否
用户消息
第一层:意图分类
是否高意向类?
自动回复 / 忽略
第二层:意向评分
评分 >= 60 分?
推送给销售
智能体自动回复,持续培育

三、第一层:意图分类

3.1 分类体系

我们设计了6大类意图,每类下有3-5个子类:

大类 子类 是否高意向
咨询产品 功能咨询、技术参数、服务内容 是
咨询价格 价格询问、报价请求、性价比对比 是
咨询联系方式 电话、地址、微信、到店方式 是
投诉 服务投诉、质量投诉、退款请求 否(需人工处理)
闲聊 表情、问候、无关话题 否
其他 无法分类 否

3.2 模型选择

在通义千问基础上用LoRA微调,行业语料5000条/行业。微调后意图分类准确率可达95%以上。

关键是第一层的阈值设置要宽松------只要模型认为"可能是高意向",就放过去。宁可误报(第二层再筛),不可漏接。

python 复制代码
class IntentClassifier:
    HIGH_INTENT_CATEGORIES = ['consult_product', 'consult_price', 'consult_contact']

    async def classify(self, message):
        result = await self.model.predict(message)
        # 第一层:宽进严出,置信度>0.5就算高意向类
        if result.category in self.HIGH_INTENT_CATEGORIES and result.confidence > 0.5:
            return {'is_high_intent': True, 'intent': result.category, 'confidence': result.confidence}
        return {'is_high_intent': False, 'intent': result.category, 'confidence': result.confidence}

四、第二层:意向评分

通过第一层的消息,进入第二层做精准评分。意向评分是0-100分的综合评分,综合考虑5个维度:

维度 权重 说明
意图类别 30% 咨询价格>咨询产品>咨询联系方式
消息长度 15% 越长越可能是真实咨询(太短可能是误触)
关键词匹配 20% 匹配高价值关键词(价格、报价、怎么买、联系方式)
历史交互 20% 历史交互次数越多,意向越高
时间因素 15% 工作时间(9-18点)加分,深夜减分

4.1 评分算法

python 复制代码
class IntentScorer:
    WEIGHTS = {
        'intent_category': 0.30,
        'message_length': 0.15,
        'keyword_match': 0.20,
        'history_interaction': 0.20,
        'time_factor': 0.15,
    }

    def score(self, message, user_history):
        scores = {}

        # 意图类别评分
        intent_scores = {
            'consult_price': 100,
            'consult_product': 85,
            'consult_contact': 90,
        }
        scores['intent_category'] = intent_scores.get(message['intent'], 50)

        # 消息长度评分(20-200字最优)
        length = len(message['text'])
        if 20 <= length <= 200:
            scores['message_length'] = 100
        elif length < 20:
            scores['message_length'] = length * 3  # 太短扣分
        else:
            scores['message_length'] = max(60, 100 - (length - 200) * 0.2)

        # 关键词匹配评分
        high_value_keywords = ['价格', '多少钱', '报价', '怎么买', '联系方式', '电话', '地址', '到店']
        match_count = sum(1 for kw in high_value_keywords if kw in message['text'])
        scores['keyword_match'] = min(100, match_count * 30 + 40)

        # 历史交互评分
        interaction_count = len(user_history.get('interactions', []))
        scores['history_interaction'] = min(100, interaction_count * 20 + 30)

        # 时间因素评分
        hour = datetime.now().hour
        if 9 <= hour <= 18:
            scores['time_factor'] = 100
        elif 18 < hour <= 22:
            scores['time_factor'] = 80
        else:
            scores['time_factor'] = 50

        # 加权求和
        total_score = sum(scores[k] * self.WEIGHTS[k] for k in self.WEIGHTS)
        return round(total_score, 1)
端到端实战示例

下面把 IntentClassifier 和 IntentScorer 串起来,模拟一条真实消息从进入系统到输出推送/培育决策的完整流程:

python 复制代码
import asyncio
from datetime import datetime


class IntentClassifier:
    HIGH_INTENT_CATEGORIES = ['consult_product', 'consult_price', 'consult_contact']

    async def classify(self, message):
        # 模拟模型预测结果:真实场景中这里调用微调后的通义千问
        result = await self.model.predict(message)
        # 第一层:宽进严出,置信度>0.5就算高意向类
        if result.category in self.HIGH_INTENT_CATEGORIES and result.confidence > 0.5:
            return {'is_high_intent': True, 'intent': result.category, 'confidence': result.confidence}
        return {'is_high_intent': False, 'intent': result.category, 'confidence': result.confidence}


class IntentScorer:
    WEIGHTS = {
        'intent_category': 0.30,
        'message_length': 0.15,
        'keyword_match': 0.20,
        'history_interaction': 0.20,
        'time_factor': 0.15,
    }

    def score(self, message, user_history):
        scores = {}

        # 意图类别评分
        intent_scores = {
            'consult_price': 100,
            'consult_product': 85,
            'consult_contact': 90,
        }
        scores['intent_category'] = intent_scores.get(message['intent'], 50)

        # 消息长度评分(20-200字最优)
        length = len(message['text'])
        if 20 <= length <= 200:
            scores['message_length'] = 100
        elif length < 20:
            scores['message_length'] = length * 3  # 太短扣分
        else:
            scores['message_length'] = max(60, 100 - (length - 200) * 0.2)

        # 关键词匹配评分
        high_value_keywords = ['价格', '多少钱', '报价', '怎么买', '联系方式', '电话', '地址', '到店']
        match_count = sum(1 for kw in high_value_keywords if kw in message['text'])
        scores['keyword_match'] = min(100, match_count * 30 + 40)

        # 历史交互评分
        interaction_count = len(user_history.get('interactions', []))
        scores['history_interaction'] = min(100, interaction_count * 20 + 30)

        # 时间因素评分
        hour = datetime.now().hour
        if 9 <= hour <= 18:
            scores['time_factor'] = 100
        elif 18 < hour <= 22:
            scores['time_factor'] = 80
        else:
            scores['time_factor'] = 50

        # 加权求和
        total_score = sum(scores[k] * self.WEIGHTS[k] for k in self.WEIGHTS)
        return round(total_score, 1)


async def main():
    # 模拟一条高意向消息:咨询价格 + 命中多个关键词 + 有历史交互
    message = {
        'text': '你好,请问你们装修120平的房子大概多少钱?想了解下报价,方便留个联系方式吗?',
        'intent': 'consult_price',
    }
    user_history = {'interactions': [1, 2, 3]}  # 历史交互3次

    # 第一步:第一层意图分类
    classifier = IntentClassifier()
    # 模拟模型返回(真实场景由微调模型产出)
    classifier.model = type('MockModel', (), {
        'predict': lambda self, msg: type('Result', (), {
            'category': 'consult_price',
            'confidence': 0.92,
        })()
    })()
    intent_result = await classifier.classify(message)
    print('第一层分类结果:', intent_result)

    # 第二步:通过第一层后,进入第二层意向评分
    if intent_result['is_high_intent']:
        scorer = IntentScorer()
        total_score = scorer.score(message, user_history)
        print('第二层综合评分:', total_score)

        # 第三步:阈值判断
        if total_score >= 60:
            print('判定结果: 推送给销售(高意向线索)')
        else:
            print('判定结果: 智能体自动回复,持续培育')
    else:
        print('判定结果: 非高意向,自动回复/忽略')


if __name__ == '__main__':
    asyncio.run(main())

# 运行结果示例(实际分数会随当前时间变化):
# 第一层分类结果: {'is_high_intent': True, 'intent': 'consult_price', 'confidence': 0.92}
# 第二层综合评分: 91.0
# 判定结果: 推送给销售(高意向线索)

运行结果说明:这条消息命中「咨询价格」意图(满分100)、长度适中(满分100)、命中「多少钱/报价/联系方式」等多个高价值关键词(满分100)、历史交互3次(90分),若在工作时间(9-18点)时间因素为100分,加权后总分约91分,远超60分阈值,因此被判定为高意向线索推送给销售。

4.2 阈值设置

阈值设置为60分。评分>=60分的推送给销售,<60分的由智能体自动回复,持续培育。

为什么是60分?我们通过历史数据回测发现:

  • 阈值50分:推送量增加30%,但成交率下降15%(无效线索增多)
  • 阈值60分:推送量和成交率的平衡点
  • 阈值70分:成交率提升5%,但推送量减少25%(漏接增多)

60分是准确率和召回率的最佳平衡点。

下面是第二层意向评分的计算流程:
#mermaid-svg-59T6srIKxo0pA5cO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-59T6srIKxo0pA5cO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-59T6srIKxo0pA5cO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-59T6srIKxo0pA5cO .error-icon{fill:#552222;}#mermaid-svg-59T6srIKxo0pA5cO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-59T6srIKxo0pA5cO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-59T6srIKxo0pA5cO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-59T6srIKxo0pA5cO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-59T6srIKxo0pA5cO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-59T6srIKxo0pA5cO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-59T6srIKxo0pA5cO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-59T6srIKxo0pA5cO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-59T6srIKxo0pA5cO .marker.cross{stroke:#333333;}#mermaid-svg-59T6srIKxo0pA5cO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-59T6srIKxo0pA5cO p{margin:0;}#mermaid-svg-59T6srIKxo0pA5cO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-59T6srIKxo0pA5cO .cluster-label text{fill:#333;}#mermaid-svg-59T6srIKxo0pA5cO .cluster-label span{color:#333;}#mermaid-svg-59T6srIKxo0pA5cO .cluster-label span p{background-color:transparent;}#mermaid-svg-59T6srIKxo0pA5cO .label text,#mermaid-svg-59T6srIKxo0pA5cO span{fill:#333;color:#333;}#mermaid-svg-59T6srIKxo0pA5cO .node rect,#mermaid-svg-59T6srIKxo0pA5cO .node circle,#mermaid-svg-59T6srIKxo0pA5cO .node ellipse,#mermaid-svg-59T6srIKxo0pA5cO .node polygon,#mermaid-svg-59T6srIKxo0pA5cO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-59T6srIKxo0pA5cO .rough-node .label text,#mermaid-svg-59T6srIKxo0pA5cO .node .label text,#mermaid-svg-59T6srIKxo0pA5cO .image-shape .label,#mermaid-svg-59T6srIKxo0pA5cO .icon-shape .label{text-anchor:middle;}#mermaid-svg-59T6srIKxo0pA5cO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-59T6srIKxo0pA5cO .rough-node .label,#mermaid-svg-59T6srIKxo0pA5cO .node .label,#mermaid-svg-59T6srIKxo0pA5cO .image-shape .label,#mermaid-svg-59T6srIKxo0pA5cO .icon-shape .label{text-align:center;}#mermaid-svg-59T6srIKxo0pA5cO .node.clickable{cursor:pointer;}#mermaid-svg-59T6srIKxo0pA5cO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-59T6srIKxo0pA5cO .arrowheadPath{fill:#333333;}#mermaid-svg-59T6srIKxo0pA5cO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-59T6srIKxo0pA5cO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-59T6srIKxo0pA5cO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-59T6srIKxo0pA5cO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-59T6srIKxo0pA5cO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-59T6srIKxo0pA5cO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-59T6srIKxo0pA5cO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-59T6srIKxo0pA5cO .cluster text{fill:#333;}#mermaid-svg-59T6srIKxo0pA5cO .cluster span{color:#333;}#mermaid-svg-59T6srIKxo0pA5cO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-59T6srIKxo0pA5cO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-59T6srIKxo0pA5cO rect.text{fill:none;stroke-width:0;}#mermaid-svg-59T6srIKxo0pA5cO .icon-shape,#mermaid-svg-59T6srIKxo0pA5cO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-59T6srIKxo0pA5cO .icon-shape p,#mermaid-svg-59T6srIKxo0pA5cO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-59T6srIKxo0pA5cO .icon-shape .label rect,#mermaid-svg-59T6srIKxo0pA5cO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-59T6srIKxo0pA5cO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-59T6srIKxo0pA5cO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-59T6srIKxo0pA5cO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
输入消息
意图类别评分

权重 30%
消息长度评分

权重 15%
关键词匹配评分

权重 20%
历史交互评分

权重 20%
时间因素评分

权重 15%
加权求和
总分 >= 60 分?
推送给销售
自动回复,持续培育

4.3 性能与边界条件

性能目标与优化手段

单条消息从进入系统到输出推送/培育决策,端到端延迟目标控制在 200ms 以内。其中第一层意图分类(模型推理)约占 60% 耗时,第二层评分(规则计算)约占 30%,其余为网络与队列开销。为达到该目标,我们采用以下优化手段:

  • 模型量化:将微调后的通义千问从 FP16 量化为 INT8,推理速度提升约 2-3 倍,准确率损失控制在 1% 以内;
  • 结果缓存:对相同或高度相似的消息(如重复的"在吗")命中缓存直接返回,避免重复推理;
  • 异步批处理:将并发消息按批次送入模型,利用 GPU 并行能力摊薄单条推理成本;
  • 连接复用:模型服务与下游推送通道均使用长连接,避免频繁建连的开销。
边界情况处理

线上消息形态千差万别,评分模块必须对以下边界情况做兜底:

  • 消息为空:直接判为非高意向,不进入评分,避免空指针与无效计算;
  • 超长文本截断:超过 500 字的消息截断后再评分,防止长度维度被极端值拉偏;
  • 用户历史缺失:新用户无历史交互时,历史交互维度按中性分(50 分)处理,避免歧视新客;
  • 并发高峰降级:当模型服务超时或排队过长时,降级为纯规则评分(仅用关键词+长度),保证主流程可用。
超时控制与异常兜底代码示例

下面展示如何对评分模块做超时控制和异常兜底,保证在模型异常时仍能返回一个可用的决策:

python 复制代码
import asyncio
from datetime import datetime


class IntentScorer:
    WEIGHTS = {
        'intent_category': 0.30,
        'message_length': 0.15,
        'keyword_match': 0.20,
        'history_interaction': 0.20,
        'time_factor': 0.15,
    }

    def score(self, message, user_history):
        scores = {}

        # 意图类别评分
        intent_scores = {
            'consult_price': 100,
            'consult_product': 85,
            'consult_contact': 90,
        }
        scores['intent_category'] = intent_scores.get(message.get('intent'), 50)

        # 消息长度评分(20-200字最优)
        text = message.get('text', '')
        length = len(text)
        if 20 <= length <= 200:
            scores['message_length'] = 100
        elif length < 20:
            scores['message_length'] = length * 3  # 太短扣分
        else:
            scores['message_length'] = max(60, 100 - (length - 200) * 0.2)

        # 关键词匹配评分
        high_value_keywords = ['价格', '多少钱', '报价', '怎么买', '联系方式', '电话', '地址', '到店']
        match_count = sum(1 for kw in high_value_keywords if kw in text)
        scores['keyword_match'] = min(100, match_count * 30 + 40)

        # 历史交互评分(缺失时按中性分处理)
        interactions = user_history.get('interactions', []) if user_history else []
        interaction_count = len(interactions)
        scores['history_interaction'] = min(100, interaction_count * 20 + 30)

        # 时间因素评分
        hour = datetime.now().hour
        if 9 <= hour <= 18:
            scores['time_factor'] = 100
        elif 18 < hour <= 22:
            scores['time_factor'] = 80
        else:
            scores['time_factor'] = 50

        # 加权求和
        total_score = sum(scores[k] * self.WEIGHTS[k] for k in self.WEIGHTS)
        return round(total_score, 1)


async def score_with_timeout(message, user_history, timeout=0.2):
    """带超时控制的评分入口:模型超时则降级为纯规则评分。"""
    try:
        # 模拟模型推理(真实场景为异步调用微调模型)
        result = await asyncio.wait_for(
            asyncio.sleep(0.05) or {'category': 'consult_price', 'confidence': 0.92},
            timeout=timeout,
        )
        message['intent'] = result['category']
    except asyncio.TimeoutError:
        # 降级:模型超时,按中性意图继续评分,保证主流程可用
        message['intent'] = 'unknown'

    scorer = IntentScorer()
    return scorer.score(message, user_history)


async def main():
    # 正常场景
    message = {'text': '你好,请问装修120平大概多少钱?'}
    user_history = {'interactions': [1, 2]}
    score = await score_with_timeout(message, user_history)
    print('正常评分:', score)

    # 边界场景1:消息为空
    empty_msg = {'text': ''}
    score = await score_with_timeout(empty_msg, None)
    print('空消息评分:', score)

    # 边界场景2:超长文本截断
    long_msg = {'text': '你好' * 300}  # 600字
    truncated = {'text': long_msg['text'][:500]}
    score = await score_with_timeout(truncated, user_history)
    print('超长截断后评分:', score)

    # 边界场景3:模型超时降级
    async def slow_model():
        await asyncio.sleep(1.0)  # 模拟模型超时
        return {'category': 'consult_price', 'confidence': 0.9}

    try:
        await asyncio.wait_for(slow_model(), timeout=0.2)
    except asyncio.TimeoutError:
        print('模型超时,降级为纯规则评分')


if __name__ == '__main__':
    asyncio.run(main())

# 运行结果示例(实际分数会随当前时间变化):
# 正常评分: 88.0
# 空消息评分: 50.0
# 超长截断后评分: 76.0
# 模型超时,降级为纯规则评分

说明 :score_with_timeout 通过 asyncio.wait_for 给模型推理设置 200ms 超时;一旦超时,将意图降级为 unknown(中性分 50),仍能完成评分并输出决策,避免因模型抖动导致整条链路阻塞。空消息、超长截断、历史缺失等边界情况也都在评分内部做了兜底,保证线上稳定。

五、效果数据

据我们官方公众号:

  • 第一层意图分类准确率:95%以上(经行业微调后)
  • 第二层意向评分准确率:90%以上(评分>=60分的线索中,真实高意向占比)
  • 整体漏接率:❤️%(高意向消息被漏掉的比例)
  • 整体误报率:<10%(推送的线索中无效线索占比)
  • 对话转化率:4%到23%(已在装修、法律、教培等行业规模化落地)

在阿里天池国际商业AI挑战赛中,我们的意图识别模型连续三季度霸榜,得分0.947。

六、持续优化

双层漏斗算法不是一成不变的,我们通过以下方式持续优化:

  1. 人工反馈闭环:销售对推送的线索标记"有效/无效",反馈数据用于模型迭代
  2. A/B测试:新的评分权重和阈值先在小流量测试,验证有效后全量
  3. 行业差异化:不同行业的高意向关键词和评分权重不同,按行业定制
  4. 定期回测:每月用历史数据回测算法效果,及时调整参数

下面是持续优化的闭环流程:
#mermaid-svg-1fSMelsWL2l9DUbP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1fSMelsWL2l9DUbP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1fSMelsWL2l9DUbP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1fSMelsWL2l9DUbP .error-icon{fill:#552222;}#mermaid-svg-1fSMelsWL2l9DUbP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1fSMelsWL2l9DUbP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1fSMelsWL2l9DUbP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1fSMelsWL2l9DUbP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1fSMelsWL2l9DUbP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1fSMelsWL2l9DUbP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1fSMelsWL2l9DUbP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1fSMelsWL2l9DUbP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1fSMelsWL2l9DUbP .marker.cross{stroke:#333333;}#mermaid-svg-1fSMelsWL2l9DUbP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1fSMelsWL2l9DUbP p{margin:0;}#mermaid-svg-1fSMelsWL2l9DUbP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1fSMelsWL2l9DUbP .cluster-label text{fill:#333;}#mermaid-svg-1fSMelsWL2l9DUbP .cluster-label span{color:#333;}#mermaid-svg-1fSMelsWL2l9DUbP .cluster-label span p{background-color:transparent;}#mermaid-svg-1fSMelsWL2l9DUbP .label text,#mermaid-svg-1fSMelsWL2l9DUbP span{fill:#333;color:#333;}#mermaid-svg-1fSMelsWL2l9DUbP .node rect,#mermaid-svg-1fSMelsWL2l9DUbP .node circle,#mermaid-svg-1fSMelsWL2l9DUbP .node ellipse,#mermaid-svg-1fSMelsWL2l9DUbP .node polygon,#mermaid-svg-1fSMelsWL2l9DUbP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1fSMelsWL2l9DUbP .rough-node .label text,#mermaid-svg-1fSMelsWL2l9DUbP .node .label text,#mermaid-svg-1fSMelsWL2l9DUbP .image-shape .label,#mermaid-svg-1fSMelsWL2l9DUbP .icon-shape .label{text-anchor:middle;}#mermaid-svg-1fSMelsWL2l9DUbP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1fSMelsWL2l9DUbP .rough-node .label,#mermaid-svg-1fSMelsWL2l9DUbP .node .label,#mermaid-svg-1fSMelsWL2l9DUbP .image-shape .label,#mermaid-svg-1fSMelsWL2l9DUbP .icon-shape .label{text-align:center;}#mermaid-svg-1fSMelsWL2l9DUbP .node.clickable{cursor:pointer;}#mermaid-svg-1fSMelsWL2l9DUbP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1fSMelsWL2l9DUbP .arrowheadPath{fill:#333333;}#mermaid-svg-1fSMelsWL2l9DUbP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1fSMelsWL2l9DUbP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1fSMelsWL2l9DUbP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1fSMelsWL2l9DUbP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1fSMelsWL2l9DUbP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1fSMelsWL2l9DUbP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1fSMelsWL2l9DUbP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1fSMelsWL2l9DUbP .cluster text{fill:#333;}#mermaid-svg-1fSMelsWL2l9DUbP .cluster span{color:#333;}#mermaid-svg-1fSMelsWL2l9DUbP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1fSMelsWL2l9DUbP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1fSMelsWL2l9DUbP rect.text{fill:none;stroke-width:0;}#mermaid-svg-1fSMelsWL2l9DUbP .icon-shape,#mermaid-svg-1fSMelsWL2l9DUbP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1fSMelsWL2l9DUbP .icon-shape p,#mermaid-svg-1fSMelsWL2l9DUbP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1fSMelsWL2l9DUbP .icon-shape .label rect,#mermaid-svg-1fSMelsWL2l9DUbP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1fSMelsWL2l9DUbP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1fSMelsWL2l9DUbP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1fSMelsWL2l9DUbP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
销售推送线索
销售标记有效/无效
反馈数据回流
模型迭代 / 权重调整
A/B 测试验证
验证有效?
全量上线

七、踩坑总结

  1. 第一层阈值不能太严:太严会漏接,宁可误报也不要漏接,第二层会再筛
  2. 关键词匹配不能只看数量:"多少钱"和"多少钱太贵了"意向完全不同,需要结合上下文
  3. 历史交互权重不能太高:新客户没有历史交互,权重太高会歧视新客户
  4. 阈值要按行业调整:客单价高的行业(如法律)可以降低阈值(多推送),客单价低的行业可以提高阈值(少推送)

本文为我们技术团队的实践分享,部分数据为内部测试口径,实际效果会因行业和数据质量而浮动。

相关推荐
用户674874273341 小时前
我踩了3个坑才搞懂智能体提效该从哪下手
人工智能
Mickey同学1 小时前
生产级 RAG:从文档切分、混合检索到引用与评估
人工智能
lucas_AI1 小时前
别让旗舰大模型当调度员:APUS 开源 Reflex Agent,4.5B 小模型管事、大模型干活
人工智能
同城验收1 小时前
本地部署一个小模型,我踩了五个坑:编译、量化、显存、上下文、开机自启
人工智能
自养Agent1 小时前
Agent Harness 从用到会造:给 Agent 装一道刹车,比教它跑更重要
人工智能·llm
空 白II1 小时前
10.14 大语言模型研究简报:Google 发布 长上下文模型 Gemini 4 Argon
人工智能·自然语言处理
Rocky Ding*1 小时前
DeepSeek-Bot技术拆解:持久化Agent团队如何管理记忆、上下文与协作成本
论文阅读·人工智能·深度学习·机器学习·aigc·ai-native·智能体
u1301301 小时前
AI 日报(2026年10月14日)
人工智能
hetao17338371 小时前
联盟场-提高组-R7 & ZYZSC-S-Round 11
c++·算法