AI客服RAG落地:从数据飞轮到检索优化的完整工程实践
半年人工喂出来的AI客服:从0到1打磨生产级RAG系统,越用越聪明
这是一篇极具实操价值的RAG落地复盘,不仅给出了技术栈组合,更揭示了数据清洗、意图识别中的具体陷阱与解决方案,独立开发者可直接复用其架构思路。
一个语言学习 App 上线后靠创始人轮流当客服,每天要花两三个小时回答重复问题,这种状态持续了半年多,也顺带攒下了真实语料。落到工程上,团队在低代码平台和自建代码之间选了后者:知识库检索要求高,召回策略、排序逻辑和上下文拼接都得自己调。
之前我们说过23年是模型初年,主要行业主要着眼于模型,史称百模大战;
然后很快大家发现确实卷不动,于是开始冷静下来,找切实的落地场景,所以24年都在做应用,AI应用三巨头依次是:AIGC(窗口生成文案)、工作流AI、然后就是AI客服了。
而后25年在AI视觉这块需求变多、成熟的AI Coding异军突起,但复杂的AI客服依旧有很大的需求,AI客服这个也被认为是RAG类的应用,看上去简单,只不过很多公司却做不好。
所以,我们这里以一个生产案例作为课件,带大家深入浅出的了解一个AI客服是怎么打磨好的:
一、案例说明
这个AI客服案例来源于我们去年AI 2C的创业项目:空气小猪。他是一款基于社交的语言学习App,从上线之后一直是我们自己在做客服工作,每天都会消耗2-3小时的时间,这种状态已经持续了半年多了,占用了大量时间。
这期间,我们也尝试找过兼职客服做过一段时间,发现效果很差。一方面,兼职人员不太上心;
另一方面,由于对产品理念和定位理解不够深入,往往无法准确、有效地回应用户问题,整体服务质量难以保障。
其实用户的问题基本都是重复的,高频问题就那些,每天去人工重复回答这些问题确实挺费时间的,既低效也不具备可持续性。所以关于简单AI客服的系统,我们之前就形成了一套方法论:
简单场景照着做就行(虽然最后证明其他同学会在其他地方掉坑里);复杂场景的话又有另外一套方法论,由此我们也几乎可以得出一个结论:
客服工作,无论简单复杂,在未来大概率会被AI替换
在线客服工作本身具有较强的重复性,因此“降本增效”始终是客服系统演进的核心目标。基于大模型的AI客服,可同时显著提升产品能力和用户体验,还能大幅降低运维成本。
随着 AI Agent 的成熟应用,AI客服系统在成本、效率和可扩展性方面进一步优化,逐渐会成为最优解。
那么这里又有同学会有疑问:为什么不一开始就上AI客服,还要每天花几小时去自己做?
答案很简单:我们也需要积累原始数据啊…
在这半年多的时间里,创始人与用户之间沉淀了大量真实、高质量的对话语料,这为我们提供了宝贵的数据基础。在基础问题开始不断重复的时候:就是构建一套 AI 客服系统的时机了。
这里目标就简单了:把我们从每天2小时的人工客服时间释放出来!这也是整体项目背景了,下面我们将详细的介绍空气小猪AI客服从0到1 的整个实践过程,以及落地的关键步骤。
二、技术选型
在实现环节我们面临一个选择:是采用智能体低代码开发平台,还是直接进行工程化代码开发?
从技术角度来看,这两种方式都可以实现目标,但真的要做肯定是直接选择工程化代码方案啊,好处很多,最重要的是对这块有更深入的技术实践,所有这一切都会体现在自主可控性这点,但也可以将选型过程简单说下,各位可以考虑看看:
在智能体开发平台的选型中,主要考虑的是Dify,前面我们介绍过coze、dify、fastgpt、n8n的技术选型。
整体来看,Dify的能力算是这些平台中最均衡的,没有明显的短板,尤其适合企业场景下的智能体开发、工作流的编排以及知识库驱动型应用。
但是最终没有选择Dify,主要是基于下面几个方面考虑:
- 我们对知识库检索能力有较高要求,希望在召回策略、排序逻辑、上下文拼接等环节进行深度优化。这部分是 AI 客服的核心能力,我们更希望逻辑完全自主可控。
- 私有化部署也需要额外的服务器的成本和运维成本,对我们当前阶段而言是没有必要的(真实使用你们才知道开源版本会少了什么)。
- 在实际测试中,我们发现 Dify 的执行链路相对较长,尤其在知识检索节点,整体响应时间偏长,不完全符合我们对实时性的要求。
- 即使使用Dify,实际落地过程中依然会涉及系统对接、数据适配和定制开发等工作,并非零成本集成。
综合这些因素,我们最终决定自己写代码,从底层能力开始搭建,虽然前期投入更高,但在可控性、性能优化空间以及长期演进能力方面更有优势。
并且自己写代码,事实上成本也并不高…
解决了第一个问题,很多同学第二个问题也就出现了:那么我们的AI客服要不要直接用Agent模式呢?
Workflow还是Agent?
项目开始阶段,除了要决定使用Dify还是工程化代码,开发模式也需要在Workflow和Agent中做出选择。
Agent是具有高度自主性的智能体,但是结果的确定性、性能等都不占优势,通常客服场景会选择比较保守的做法:先按照Workflow的方式,搭建稳定的流程,来追求确定的、能控制的结果。
从工作流程入手,逐步引入并解决每一个遇到的问题,同时兼顾效果、成本和可控制程度。
至于后期要不要“升级”Agent版本,完全看业务需要,业务上几乎不追求这些时髦的东西。
基础流程设计
在确定基础技术选型后,就可以进行流程设计了,在第一个版本中,我们把意图分为产品咨询和闲聊,大致的流程如下:
但是很快我们发现,用户除了咨询常见的产品功能问题,还会反馈一些产品建议以及程序故障等问题。
而这一部分内容的处理逻辑与单纯的产品咨询其实是不太一样的,用户反馈的问题是需要存入数据库,并且根据问题的严重程度划分等级,方便我们及时跟进修复处理。
在初版的基础上面我们增加了优化建议、故障反馈的意图处理流程。具体流程如下:
这也就是我们方法论中所述的整理大表了,在基础意图整理差不多后就可以进入最重要的知识梳理环节了:
三、知识整理
要做AI知识库的同学一定要注意:数据才是灵魂!如果有人说做AI项目不深度聊数据,那么他大概率是没做过的…
产品知识是AI客服的基础(这也是为什么我们非要等半年才做客服的原因),只有提供优质的知识才能让AI输出优质的回答,否则就是垃圾进垃圾出。
在数据不全的情况下,不管做再多的工程化优化都是无用功,所以说RAG的本质是个数据工程,大家一定要去理解这到底是在说撒。
具体在做AI客服知识梳理时,理论上来说要求是很高的,它最好来自这个产品最有解释权的人,但是知识的完备性需要持续的迭代更新才能覆盖完全。
真实情况是:所有知识库应用初始阶段都存在类似的情况,所以允许知识覆盖不全,在迭代中逐步补齐知识,并且后面用数据飞轮的策略来自动化的完善知识不足的问题变成了主流。
具体到这个项目,我们的知识来源于两部分内容:一部分内容是人工整理的,另外一部分是历史客服数据。
在进行知识整理时,就需要考虑如何组织知识才能有更好的检索效果,而检索的效果很大程度是取决于数据处理是否正确,比如每个分块的语义是否完整独立。
因此输出的知识一定要能够单独成块,可以利用Markdown格式进行结构的化层级进行整理,在分块时,可以根据标题进行分割,知识内容大致形式如下:
另一方面,我们导出了所有历史客服对话数据,这里主要根据会话维度进行分析,因为数据较多,不太可能人工整理,可借助AI分析提取每个会话中有效的问答记录。
当然这里也不能一次性把所有的数据给到大模型整理,有两个原因:
一是数据太多会超出大模型上下文限制;
二是数据太多,幻觉增加,准确率会降低;
处理的策略是拆分为多个批次进行整理,比如每10个会话为一个批次,我们借助Coze搭建了一个自动化处理工作流,流程如下:
PS:这里也可以看出来,我们其实也使用Coze,但一般是来做一些自动化小操作,用完就走
从数据库导出会话记录时,每10个会话导出为一个会话记录文件,最终会导出若干个会话记录文件,然后把这些文件上传到coze工作流上进行批量处理,生成的问答对内容都会被写入飞书多维表格中。
最终表格中的内容必定会存在重复的内容,再把表格中整个内容给到大模型进行分析,把内容去重处理,形成最终的问答对。
通过这两种方式,我们就得到了最终的产品知识数据,这一步非常重要,也非常耗费精力,并且需要真正懂这个产品的人来做才能取得好的效果。
可以认为:这一步没做好,那么AI客服的效果一定会差!
在数据整理结束后,就进入了知识库构建环节:
四、知识库构建
这里技术实现:Python + FAISS + MYSQL + qwen(text-embedding-v4)
向量库选择Meta公司开源的FAISS,非常轻量,嵌入模型使用千问的text-embedding-v4(1024维度),它在中文场景下表现更好。
数据存储的结构
整个知识库由库、集合和数据 3 部分组成。
集合可以简单理解为一个文件,一个库中可以包含多个集合,一个集合中可以包含多组数据;
最小的搜索单位是库,也就是说,知识库搜索时,是对整个库进行搜索,而集合仅是为了对数据进行分类管理,与搜索效果无关。
向量存储的结构
使用 Faiss 作为向量检索引擎,使用 MySQL 作为业务数据存储数据库,实现知识入库与向量召回的分离式架构设计。
这里Faiss只负责向量存储与相似度检索,MySQL负责原始知识数据与业务字段存储,向量检索引擎可独立替换为其它向量数据库(比如Milvus、Weaviate等),不影响业务数据库结构。
而这两者间的关系通过向量ID进行关联,在MySQL的dataset.datas表中,会存储向量原数据的信息,同时有一个vectorId字段,会记录其对应的向量ID。
知识入库是一个离线过程,大致流程如下:
- 先加载知识文档,对文档进行分块
- 然后把分块存入到mysql业务数据库中,此时向量化状态为待向量化,向量ID字段为null
- 然后通过异步事件驱动把每一个分块chunk使用向量模型进行向量化,得到向量坐标之后,存入Faiss向量库中
- 存入向量库完成后,会生成向量ID,然后在把这个向量ID存入到mysql对应的chunk中的vectorId字段,并且将向量化状态更新为已向量化
知识检索流程,如下:
- 使用向量模型对用户的问题进行向量化,得到向量
- 然后使用 Faiss 执行相似度搜索,召回TopK个向量ID,同时得到相似度得分
- 拿到召回结果后,根据向量ID去MySQL数据库中查询该向量ID对应的原始文档chunk数据
知识入库
在知识整理完之后,使用代码对markdown格式的知识,按照结构层级进行分块,得到结构化的JSON数据,然后存入业务数据库和向量库中。
把一级标题设置为category字段,把最后一级标题设置为questions,段落内容设置为answer字段,如果存在三级以上的层级,把1到n级标题通过“-”链接设置为category字段,n级标题设置为questions字段。我们得到的分块内容如下结构,这里只列举核心字段:
{
“category”: “产品概念”,
“questions”: [
“空气小猪解决的核心问题是什么?”
],
“answer”: “很多用户长期学外语遇到的真实问题包括:\n外语只存在于“学习时间”,无法进入日常生活\n输入和输出被割裂,学到的语言很难迁移到真实使用\n听力和阅读材料与个人生活无关,难以长期坚持\n空气小猪并不通过增加学习任务来解决这些问题,而是通过重用已经发生的聊天内容,为用户建立一个长期、低成本、真实相关的外语环境。”,
“keywords”: []
}
得到若干上述分块结构之后,按照前面的流程把每个分块存入mysql数据库,并存入向量库中。
需要注意的是,向量化的文档内容并不是直接把这个JSON进行向量化,这里JSON结构主要是为了拿到文档分块的结构化信息,方便元信息获取及存储。
向量化的真正文本内容为category+quesions+answer的拼接内容。
在这一步我们最初犯了一个错误,我们把生成的原始chunk使用大模型针对问题进行了泛化处理,泛化后的结构为:
{
“category”: “产品概念”,
“questions”: [
“空气小猪解决的核心问题是什么?”,
“空气小猪主要解决了什么问题?”,
“空气小猪的核心价值是什么?”
],
“answer”: “很多用户长期学外语遇到的真实问题包括:\n外语只存在于“学习时间”,无法进入日常生活\n输入和输出被割裂,学到的语言很难迁移到真实使用\n听力和阅读材料与个人生活无关,难以长期坚持\n空气小猪并不通过增加学习任务来解决这些问题,而是通过重用已经发生的聊天内容,为用户建立一个长期、低成本、真实相关的外语环境。”,
“keywords”: []
}
也就是把一个问题泛化为多个可能的问题,以提升召回命中率,并且在存储时按照一问一答的结构进行储存,也就是把问题1 + 答案、问题2 + 答案、问题3 + 答案 分别进行存储。
最后在知识召回时就出现问题了,同一个问题很大概率会召回相同的文档信息,并且这部分的得分还比较高,于是取Top-K时,召回的数据都是一样的,只是问题不一样,这就导致其它可能更相关的内容被挤掉。
接下来是检索流程:
五、检索过程
RAG系统在检索这里,比较重要的是意图识别。
所以,前置的意图分类这一步就至关重要,真实用户的问题是非常发散的,第一步就需要对用户的意图进行收敛,收敛的目的是为了匹配后续的流程以及更好的匹配到我们知识库中的知识。
我们在把第一级大类意图拆分为 产品咨询、故障反馈、闲聊,然后通过代码进行路由分发,分别走不同的处理流程;
但是这里要让大模型准确判断是哪一个意图,需要继续细化用户的二级意图,比如什么情况下应该匹配到产品咨询上,什么情况匹配到故障反馈上;
你需要给大模型一个明确的评价标准,这个二级意图就是用来辅助大模型判断一级意图的标准。否则在意图识别这一步很可能就直接出错,后续的回答肯定就不准确了。
这里整理的思路也跟大家简单分享一下:
一是从产品本身梳理(我们提供了什么、主要解决什么问题);
二是从用户的角度进行梳理(我能得到什么、对我英语有没有提升,怎么用),然后对两者进行融合;
通过客服历史问答数据,我们提炼了用户的高频问题,这里给了非常大的参考价值。下面是我们梳理的一些高频问题类别:
这里再摘抄部分意图识别的提示词:
角色
你是 空气小猪 App 智能客服系统的专业意图识别器。
你的输出将被直接用于自动路由和客服决策,请严格遵循规则,不得自由发挥。
总体目标
基于【对话历史】和【用户最新消息】,完成以下 两个任务:
1. 指代消解(必须先完成)
2. 意图识别(基于指代消解后的问题)
任务一:指代消解
目标:
结合【对话历史】,对【用户最新消息】中的所有指代性表达进行消解,你需要生成一条单句、语义完整、无任何模糊指代的用户问题
处理规则:
1.必须结合【对话历史】进行还原
2.如果用户问题本身已经明确,不要强行改写
3.不得引入对话中不存在的新信息
4.输出必须是一个完整问题句
输出字段:
resolved_question
任务二:意图识别
基于 resolved_question,结合【对话历史】信息进行理解,将其准确分类到以下三个意图之一,并且按照顺序依次判断:
1. 产品咨询(SUPPORT) , 判断标准为 {
空气小猪小猪相关问题
产品功能介绍/产品价值/能解决什么问题
使用方法/操作步骤/使用教程
下载安装/注册登录/账号相关
产品定价/会员/付费
与竞品的比较
翻译/语言支持/语言学习相关
播放/播客/朗读
聊天/加好友/社交/翻译对话相关
任何“如何用空气小猪做某事”的问题
}
2. 问题反馈(FEEDBACK), 判断标准为 {
用户明确提出对空气小猪产品功能优化建议、希望增加功能、优化建议、故障反馈,比如:功能异常、报错、打不开、点不动、卡顿、无法使用等
}
3. 闲聊(CHAT),判断标准为 {
当不属于以上两类时,一律归为 CHAT,例如问候、寒暄、玩笑、调侃、情感倾诉、模糊无意义输入
}
输入信息
对话历史
{
%s
}
用户最新消息
{
%s
}
输出要求
1. 用户输入的翻译,翻译为%s
2. **必须**使用以下JSON格式返回,不得包含任何其他文本:
{
“confidence”: 0.0-1.0″,
“intent”: “SUPPORT” | “FEEDBACK” | “CHAT”,
“resolved_question”: “指代消解后的明确问题”,
“translation”:”用户输入的翻译”,
“reason”: “简要分类理由(说明上下文如何影响判断)”
}
从提示词的设计可以看出,我们不仅对用户的提问进行了意图识别,还在这之前增加了指代消解的处理步骤。也就是说,在完成指代消解之后,才进入意图识别阶段。
PS:事实上也可以将这个提示词再拆分,以符合提示词原子性的要求,我们这里因为是做案例讲解就不麻烦了
至于之所以需要进行指代消解,是因为在实际使用过程中,我们发现用户往往会将一个完整的问题拆分成两句话,或者在后续提问中使用上下文中的指代词;
例如,用户先问“空气小猪是干什么的?”,接着又问“它怎么用?”如果将第二句话直接用于检索,很可能无法获得有效结果;而真正符合用户意图的检索语句应该是“空气小猪怎么使用?”。
因此,我们借助大模型结合上下文语境,对用户最新的问题进行指代消解,将其中的指代词还原为具体对象。这样可以提高意图识别、以及检索的准确性,使整体流程更加连贯和高效。
在意图识别这个环节中,模型经历了从GPT-4.1到Qwen-plus-latest、Qwen-max-latest,再到Qwen3-max、Qwen-plus的选择过程。
GPT-4.1作为效果标杆,Qwen系列则需要通过提示词优化或节点拆分来提升表现。值得一提的是,Qwen-plus在未修改提示词的情况下即有提升,整体效果还可以。
我们这里场景不算复杂,为了均衡成本和效果,我们模型最终选择了Qwen-plus, 如果需要更好的效果可以考虑Qwen3-max或GPT4.x等模型。
模型的选择可以根据项目阶段来确定,项目初期选择参数较大的模型进行验证并快速上线;后期则在保证意图识别准确率不下降的前提下,逐步替换为成本更低的模型,以实现效果与成本的平衡。
总之,我们每个项目做的时候都会用最好的模型,后续实际上线,都是各种成本考虑…
问题泛化
在正式进入检索阶段之前,需要先对用户问题进行一系列语义预处理,最大程度提升后续检索的召回率与覆盖面。
首先,通过指代消解技术,对问题中的代词、模糊指称和上下文依赖表达进行还原。
例如,将“他”“这个”“那个”等模糊表达,结合上下文明确为具体的名称,从而避免检索阶段因语义歧义而遗漏关键信息。
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力