AI智能客服开发
一、需求背景
刚开始我们项目使用了 WebSocket 技术搭建人工客服系统,但是人工客服无法做到 7 * 24 小时在线。用户咨询客服的时间不固定,有些用户可能在半夜咨询,这时候人工客服不在线,用户体验就不太好。
后面甲方问我们有没有更好的解决方案。刚好当时 AI 应用比较火,我们就给客户推荐开发一个 AI 智能客服模块,让用户可以随时访问 AI 客服。
AI 客服主要解决两个问题:
- 用户可以随时咨询,不受人工客服在线时间限制。
- 常见问题由 AI 自动回答,减少人工客服压力。
当然,有些 AI 回答不了的问题,或者涉及复杂业务处理的问题,系统也支持直接转人工客服。
二、框架及模型选型
1. AI 框架选型
项目里我们先做了 AI 框架选型。当时主要考虑了两个方案:
| 框架 | 特点 |
|---|---|
| LangChain4j | 出现比较早,生态比较丰富;不是 Spring 项目也能接入;JDK 8 以上即可使用 |
| Spring AI Alibaba | 和 Spring 项目集成比较方便,适合 Spring Boot 项目 |
我们项目本身使用的是:
- JDK 17
- Spring Boot 3.3 以上版本
所以最终选择了 Spring AI Alibaba。它和 Spring Boot 集成更自然,项目里接入成本也比较低。
2. 大模型选型
大模型这块主要有两种方案:
- 本地部署大模型
- 使用第三方大模型平台
本地部署的好处是数据可控,但是服务器成本、运维成本和模型维护成本都比较高。客户希望尽可能减少成本,所以我们最终选择了第三方大模型平台。
我们接入的是阿里百炼平台的通义千问大模型。当时选择它主要有几个原因:
- 价格相对便宜。
- 和阿里生态组件集成比较方便。
- 支持 Function Calling。
- 支持多模态能力。
当时评估的价格大概是:
| 类型 | 价格 |
|---|---|
| 文本输入 | 0.0018 元 / 千 Tokens |
| 文本输出 | 0.0069 元 / 千 Tokens |
| 图片、视频输入 | 0.0033 元 / 千 Tokens |
| 图片、视频输出 | 0.0626 元 / 千 Tokens |
三、整体实现思路
我们主要从几个方面实现 AI 智能客服能力。
1. 给 AI 赋予角色
这部分主要通过 Prompt 提示词工程来实现。
我们会在提示词里告诉 AI:
- 它是什么角色。
- 它能回答什么问题。
- 它不能回答什么问题。
- 回答时应该使用什么语气。
- 没有依据时不能乱编。
- 遇到复杂问题时需要建议转人工。
比如智能客服的定位是:
你是一个电商系统的 AI 智能客服。
你需要根据知识库内容、用户上下文和业务查询结果回答用户问题。
不能编造订单状态、退款结果、物流信息。
如果无法确定答案,请提示用户转人工客服。2. 做上下文记忆
AI 客服不是只回答单轮问题,用户经常会连续追问。
比如用户第一句问:
我的订单为什么还没发货?第二句继续问:
那还能取消吗?第二句里面没有明确说是哪一个订单,所以后端需要把前面的对话上下文带给模型,让模型知道用户还在问上一个订单。
所以我们做了会话存储和消息存储,让 AI 能结合上下文回答问题。
3. 使用 Function Calling / MCP 调用业务能力
有些问题不是模型自己能回答的,需要调用我们系统里的业务接口。
比如:
- 查询订单状态。
- 查询物流信息。
- 查询退款进度。
- 查询售后单状态。
- 判断是否支持退货。
这些数据都在我们自己的业务系统里,模型本身不知道。所以我们通过 Function Calling / MCP 机制,把一些安全的只读业务能力封装成工具,让模型在需要时调用。
这里有一个原则:高风险操作不能直接交给 AI 自动执行。比如退款、取消订单、修改地址这类操作,必须走明确的业务校验和用户确认,不能让模型一句话就直接执行。
4. Embedding 和向量数据库基础
RAG 检索依赖 Embedding 和向量数据库,这里先简单说一下这两个东西是干嘛的。
4.1 什么是 Embedding
Embedding 说白了就是把文字变成一串数字。
比如“怎么申请退款”这句话,经过 Embedding 模型后会变成一个几百维的向量。语义相近的话,向量在空间里就比较近。
这样计算机不需要真正“读懂”中文,只要算两个向量之间的距离,就能判断两句话意思像不像。
那向量具体是怎么算出来的呢?大致分三步:
- 分词:先把句子拆成一个个 token,比如“怎么申请退款”变成 ["怎么", "申请", "退款"]。
- 过神经网络:把这些 token 丢进预训练好的 Embedding 模型里。这个模型在大量文本上学过“哪些词意思相近、哪些句子经常一起出现”。
- 取向量:模型最后一层输出一串固定长度的数字,比如 768 维或 1024 维,这就是这句话的向量。
判断两句话像不像,通常用余弦相似度。两个向量夹角越小,距离越近,语义就越相近。
4.2 向量数据库是干嘛的
向量数据库就是专门存这些数字向量的数据库。
普通数据库是按字段条件查,比如 status = 1。向量数据库是按语义相似度查:把用户问题也变成向量,然后找离它最近的那些文档向量。
我们项目用的是 Qdrant,主要因为开源、部署简单,和 Spring AI Alibaba 集成也比较方便。同类工具还有 Milvus、Weaviate,Elasticsearch 现在也能做向量检索。
4.3 Embedding 模型怎么选
Embedding 模型有很多,面试里常见的有这几个:
- OpenAI text-embedding 系列:比如 text-embedding-3-small、text-embedding-3-large,英文效果好,中文场景不一定最优。
- 阿里通义千问 Embedding:比如 text-embedding-v1、v2、v3,中文语义理解比较好,和我们项目里用的大模型同生态。
- BGE:智源开源的中文 Embedding 模型,效果也不错,适合本地部署。
- M3E:中文社区常用的开源 Embedding 模型。
- Sentence-BERT:比较老的开源方案,现在用得少了。
我们项目里用的是 阿里百炼平台的通义千问 Embedding API,主要考虑这几点:
- 客服场景中文为主,中文语义理解更重要。
- 和大模型同一个平台,接入维护方便。
- 按调用量付费,不用自己部署模型服务,省服务器成本。
- Spring AI Alibaba 配置起来比较自然。
如果数据敏感、不能走公网,也可以考虑本地部署 BGE 这类开源模型。
5. 使用 RAG 减少模型幻觉
为了减少模型胡编,我们做了 RAG,也就是信息检索增强。
我们准备了一些知识库文档,主要包括:
- 常见问题 FAQ。
- 售后政策。
- 退换货规则。
- 订单说明。
- 平台使用说明。
- 客服标准话术。
5.1 RAG 处理流程
文档格式主要是 PDF。处理流程是:
PDF 文档
↓
文档加载器加载到内存
↓
文档分割器按段落切分
↓
Embedding 模型生成向量
↓
写入 Qdrant 向量数据库
↓
用户提问时检索相关片段
↓
把检索结果和用户问题一起交给大模型回答5.2 文档切分策略
我们这边业务不算特别复杂,所以第一版文档切分没有做得太细,主要是按段落进行分割。后面根据效果再继续调优。
四、Function Calling 和 MCP 概念梳理
在 AI 智能客服项目里,Function Calling 和 MCP 都是为了让大模型具备“调用外部能力”的能力。简单说,大模型本身只擅长理解和生成文本,但是它不知道我们系统里的实时订单状态、物流信息、退款进度,也不能自己直接操作数据库。所以需要通过工具调用机制,把我们后端已有的业务能力提供给模型使用。
1. Function Calling 是什么
Function Calling 可以理解为:我们提前告诉模型系统里有哪些函数可以用,每个函数叫什么、需要哪些参数、能做什么。用户提问时,模型判断自己是否需要调用某个函数。
比如用户问:
我的订单 202406080001 到哪里了?模型自己不知道这个订单状态,它会判断需要调用查询订单接口,然后生成类似下面的调用意图:
{
"functionName": "queryOrderStatus",
"arguments": {
"orderNo": "202406080001"
}
}后端收到这个结果后,不是让模型直接查数据库,而是由 Java 后端去调用真实的订单查询接口。查询到结果后,再把订单状态返回给模型,让模型组织成用户能听懂的话。
整个流程大概是:
用户提问
↓
模型判断需要调用工具
↓
模型返回函数名和参数
↓
Java 后端校验参数和权限
↓
Java 后端调用真实业务接口
↓
把业务结果交给模型整理
↓
返回用户Function Calling 的重点是:模型只负责判断要不要调用、调用哪个函数、参数是什么;真正执行动作的一定是后端代码。
2. Function Calling 适合解决什么问题
在智能客服里,Function Calling 比较适合处理实时业务数据查询,比如:
- 查询订单状态。
- 查询物流信息。
- 查询退款进度。
- 查询售后单状态。
- 查询用户优惠券。
- 判断订单是否支持取消。
- 判断商品是否支持退货。
这些问题不能只靠知识库回答,因为知识库里只有规则,没有用户自己的实时数据。
3. MCP 是什么
MCP 全称是 Model Context Protocol,可以理解为一种更标准化的“模型连接外部工具和数据源的协议”。
Function Calling 更像是某个模型平台提供的一种函数调用能力,而 MCP 更像是一套通用协议。它把外部系统封装成 MCP Server,让模型或智能体通过统一方式访问工具、资源和上下文。
通俗一点说:
- Function Calling 更像是“我在代码里给模型注册几个函数”。
- MCP 更像是“我把一个系统封装成标准工具服务,模型可以按协议去调用”。
比如我们可以把订单系统、物流系统、知识库系统分别封装成 MCP Server:
AI 智能客服
↓
MCP Client
↓
订单 MCP Server / 物流 MCP Server / 知识库 MCP Server
↓
真实业务系统这样智能体不用关心底层系统具体怎么实现,只需要按 MCP 协议调用对应工具。
4. MCP 主要包含哪些概念
MCP 里常见的几个概念可以这样理解:
| 概念 | 通俗理解 |
|---|---|
| MCP Host | 使用 MCP 能力的宿主,比如 AI 客服应用 |
| MCP Client | 负责和 MCP Server 通信的客户端 |
| MCP Server | 对外提供工具、资源和能力的服务 |
| Tools | 可被模型调用的工具,比如查询订单、查询物流 |
| Resources | 可被读取的数据资源,比如文档、配置、文件 |
| Prompts | 可复用的提示词模板 |
在项目里,AI 智能客服本身可以看作 MCP Host,后端集成 MCP Client,订单查询、物流查询、知识库查询这些能力可以封装成 MCP Server 提供的 Tools。
5. Function Calling 和 MCP 的区别
| 对比项 | Function Calling | MCP |
|---|---|---|
| 定位 | 模型调用函数的能力 | 模型连接外部工具和数据源的协议 |
| 使用方式 | 在模型请求里声明函数 | 通过 MCP Server 暴露工具和资源 |
| 适用范围 | 比较适合少量工具调用 | 更适合多个系统、多个工具统一接入 |
| 标准化程度 | 更依赖具体模型平台 | 更强调统一协议 |
| 项目复杂度 | 接入较简单 | 架构更清晰,但接入成本更高 |
项目早期功能不复杂时,Function Calling 接入更快。后面工具越来越多,比如订单、物流、售后、工单、知识库都要接入时,用 MCP 统一管理会更清晰。
6. 在智能客服项目里我是怎么用的
我们在第一版里主要使用 Function Calling,因为项目初期工具数量不算太多,主要是订单查询、物流查询、退款进度查询和售后状态查询。
每个工具都会定义清楚:
- 工具名称。
- 工具用途。
- 入参字段。
- 出参字段。
- 权限要求。
- 超时时间。
- 失败兜底。
比如订单状态查询工具:
工具名称:queryOrderStatus
工具作用:根据订单号查询订单状态
入参:orderNo
出参:orderStatus、payStatus、deliveryStatus、canCancel
限制:只能查询当前登录用户自己的订单后端执行工具调用前,一定会做权限校验。比如用户只能查询自己的订单,不能随便传一个订单号就查到别人的订单。
后面工具数量变多以后,我们也评估过把这些能力进一步 MCP 化。比如订单服务提供订单 MCP Server,物流服务提供物流 MCP Server,AI 客服统一通过 MCP Client 调用。这样后续其他 AI 应用也能复用这些工具能力。
7. 工具调用的安全边界
无论是 Function Calling 还是 MCP,都不能让模型绕过后端业务规则。
我在项目里主要做了这些限制:
- 只开放必要工具,不把所有接口都暴露给模型。
- 第一版只开放查询类工具,写操作必须人工确认。
- 工具调用前必须校验用户身份和权限。
- 参数必须由 Java 后端再次校验,不能直接相信模型生成的参数。
- 工具调用要设置超时时间,避免一直阻塞。
- 工具调用失败时要返回明确兜底。
- 敏感字段返回给模型前要脱敏。
- 所有工具调用都要记录日志,方便排查。
比如退款这种操作,模型最多只能判断“是否满足申请退款条件”,然后引导用户去页面确认,不能直接替用户发起退款。
这块我的理解是:模型可以做助手,但不能做最终执行者。真正改变业务状态的操作,必须由后端规则、用户确认和权限系统共同控制。
五、优化设计
调用大模型会耗时,也会产生费用。所以在真正调用模型前,我们做了几层优化。
1. 敏感词过滤
在调用模型前,先做敏感词过滤。
我们接入了一个叫 Sensitive Word 的敏感词组件,它本身自带一些敏感词规则。但默认词库不够,所以我们又通过数据库管理了一套自定义敏感词库。
处理方式是:
- 数据库存储自定义敏感词。
- 后台支持新增、修改、删除敏感词。
- 敏感词变更后同步到 Sensitive Word 组件。
- 用户输入命中敏感词时,直接拦截或提示修改。
这样可以避免一些不合规内容直接进入模型,也能减少无效调用。
2. FAQ 优先匹配
很多用户问的问题是高频问题,比如:
- 怎么申请退款?
- 订单多久发货?
- 如何修改收货地址?
- 退货运费谁承担?
这些问题没有必要每次都调用大模型。我们做了一套 FAQ 管理功能,把常见问题和标准答案维护在数据库中。
为了让用户输入能更准确地匹配 FAQ,我们没有只做关键词匹配,而是结合了向量数据库。
FAQ 写入时:
FAQ 问题和答案
↓
保存到业务数据库
↓
问题文本向量化
↓
写入 Qdrant用户提问时:
用户问题
↓
先做向量化
↓
到 Qdrant 匹配相似 FAQ
↓
命中 FAQ 时直接返回标准答案
↓
没有命中 FAQ 时再走 RAG 和模型调用这样可以减少频繁调用大模型的问题,既提升响应速度,也节省成本。
六、模型上下文存储
为了让模型调用时能记住上下文,Spring AI Alibaba 提供了几种存储方式:
- 内存存储
- 数据库存储
- Redis 存储
我在项目里使用的是 Redis 存储。Redis 主要负责保存当前会话的短期上下文,让模型在连续对话时能拿到最近几轮消息。
同时,为了支持用户下次进入系统后还能看到历史对话,我们又把完整会话记录落到了数据库中。
也就是说:
- Redis:保存短期上下文,服务模型多轮对话。
- MySQL:保存完整聊天历史,服务前端历史记录展示。
七、多轮对话存储
这个项目里,多轮对话是单独设计的,不是简单把所有聊天内容都临时放在内存里。因为用户下次进入系统时,需要能看到之前的会话记录;同一个会话里继续提问时,模型也需要知道前面聊过什么。
我把会话存储拆成两张核心表。
1. 会话表
会话表主要记录一次对话主题。
| 字段 | 说明 |
|---|---|
| id | 会话 ID |
| user_id | 用户 ID |
| title | 会话标题 |
| status | 会话状态 |
| created_at | 创建时间 |
| updated_at | 更新时间 |
2. 消息表
消息表记录每一轮用户和 AI 的消息。
| 字段 | 说明 |
|---|---|
| id | 消息 ID |
| conversation_id | 会话 ID |
| user_id | 用户 ID |
| role | 消息角色,比如 user、assistant、system |
| content | 消息内容 |
| references | 引用的知识库来源 |
| input_tokens | 输入 Token |
| output_tokens | 输出 Token |
| latency_ms | 本轮耗时 |
| created_at | 创建时间 |
前端每次新建聊天时,后端会先创建一条会话记录,并返回 conversationId。后续用户继续在这个聊天窗口里提问,前端都会带上这个 conversationId,后端就能知道这次问题属于哪一段会话。
每轮对话处理时,我会先保存用户问题,再调用 RAG 和大模型。模型返回后,再保存 AI 回答。这样即使模型调用中间失败,也能看到用户问过什么,以及失败原因。
用户下次打开系统时,前端会先调用会话列表接口:
GET /api/conversations后端按 user_id 查询这个用户自己的会话列表,返回标题、最后更新时间和最近一条消息。
用户点击某个会话后,再调用详情接口:
GET /api/conversations/{conversationId}/messages后端根据 conversationId 查询消息表,把历史对话按时间顺序返回给前端展示。
这里必须校验 conversationId 是否属于当前登录用户,不能只凭会话 ID 查询,否则会有越权风险。
3. 多轮上下文怎么传给模型
多轮上下文传给模型时,我没有把全部历史消息都塞进去。因为历史越长,Token 成本越高,响应也越慢。
我的处理方式是:
- 只取最近几轮关键对话。
- 对更早的历史做摘要。
- 当前问题永远优先级最高。
- RAG 检索结果优先级高于历史闲聊内容。
- 超过 Token 限制时,优先丢弃低价值历史。
比如用户第一轮问:
支付回调失败怎么排查?第二轮继续问:
那 401 这种情况呢?第二轮的问题本身不完整。后端会加载上一轮上下文,知道这里的 401 还是在支付回调排查场景下,然后再去检索接口鉴权和支付回调相关文档。
这里我遇到过一个问题:历史上下文带太多时,模型容易被前面的问题带偏。后来我做了上下文选择规则,只保留和当前问题相关的最近几轮,同时提示词里明确要求优先回答当前用户问题。这样多轮对话既能连续,又不容易跑题。
八、RAG 调优
1. 文档切片太大
早期一个片段接近两三千字,模型输入 Token 很多,响应慢,而且回答经常抓不到重点。
后来改成按 300 到 800 字左右切片,并保留一小段上下文重叠,效果明显好一些。
2. 文档切片太碎
有些制度条款被切开后,模型只看到半句话,回答不完整。
后来我们按标题层级和段落边界切分,尽量保证一个片段表达完整含义。
3. 错误码和接口名命中不好
只用向量检索时,错误码、接口名这类内容命中效果不稳定。
比如用户问某个错误码,向量相似度不一定高。后来加了关键词检索和标签过滤,把向量检索和关键词检索结合起来,错误码、接口名、订单状态这类问题命中率提升比较明显。
4. 检索结果太多会干扰模型
一开始为了怕漏资料,会塞很多片段给模型,结果模型反而容易混淆。
后来控制 Top K 数量,并设置相似度阈值,只传最相关的几段。
5. 召回率不够高
RAG 里有一个很重要的指标叫召回率,面试里经常会被问到。
召回率可以理解为:用户这个问题,知识库里其实能回答的片段,我们有没有把它们尽量找回来。
比如用户问“退货运费谁承担”,知识库里可能有三处相关:售后政策、FAQ、运费规则。如果只召回了一个,另外两段没召回来,模型就可能回答不完整,甚至说不知道。
召回率低,最直观的现象就是模型经常说“当前知识库没有相关内容”,但实际上文档里是有答案的。
6. 怎么提升召回率
我们在项目里主要做了这几件事。
第一,混合检索。
只用向量检索有时候不够稳,尤其是对错误码、接口名、政策编号、专业术语这些,关键词匹配反而更准。
我们的做法是向量检索和关键词检索一起做,最后把两路结果合并去重,再交给重排序模型排一次,召回率提升很明显。
第二,查询改写。
用户口语化和文档书面语不一定一致。比如用户问“怎么退钱”,文档里写的是“申请退款流程”。
我们会在检索前对问题做改写,补一些同义词或扩展表达,比如把“退钱”扩展成“退款、退货、退运费”,再去检索,能召回更多相关片段。
第三,优化文档切分。
如果切得太碎,一个完整知识点被拆成多个小片段,向量检索时可能只命中其中一部分。
我们后来按语义段落切分,尽量让一段表达一个完整意思;同时在片段头部保留标题或上级标题信息,让模型知道这个片段属于哪个模块。
第四,适当增加 Top K。
一开始只取 Top 3,发现漏召回的情况比较多。后来先取 Top 10 到 Top 20,再通过相似度阈值和重排序精排,最终只把最相关的 3 到 5 段给模型。
这样既能保证召回率,又不会让模型被干扰。
第五,引入重排序模型。
向量检索阶段更关注“语义相似”,但不一定懂“哪个更相关”。我们用了一个轻量级的重排序模型,对召回结果再做一次精排,把真正相关的片段排到前面。
第六,做反馈闭环。
我们会记录哪些问题没有召回、哪些回答被用户点了不满意。定期把这些 bad case 拿出来分析,是切分问题、检索问题,还是知识库本身缺失,然后针对性优化。
简单总结就是:召回率看的是“有没有把该找的文档找回来”。优化方向主要是多路召回、查询改写、合理切分、扩大候选、重排序精排、持续复盘 bad case。
九、回答准确性怎么评估
判断 AI 客服回答准不准,我们主要看三条线:和原始资料对不对得上、和业务数据对不对得上、用户满不满意。
1. 对答案
- 知识库问题:把模型回答和检索出来的文档片段对比,看有没有乱编、漏条件。
- 业务数据问题:比如订单状态、物流信息,直接和数据库真实数据对比。
- 规则问题:比如退款条件、运费谁承担,对照售后政策原文看有没有说错。
2. 自动化指标
用 RAGAS 这类工具跑几个指标辅助判断:
| 指标 | 大白话 |
|---|---|
| Faithfulness | 回答是不是照着检索内容说的 |
| Answer Relevance | 回答有没有答非所问 |
| Context Precision | 找回来的资料里有多少是相关的 |
| Context Recall | 该找的资料有没有找回来 |
这些指标只能参考,最终还得人工看。
3. 看用户反馈
- 点踩、转人工、反复问同一个问题,都是回答不准的信号。
- 客服工单里标注“AI 答错了”的,必须复盘。
4. 人工抽检
定期抽一些回答,让业务专家打标签:正确、部分正确、错误。重点看退款、售后、投诉这些高风险场景。
5. 一致性检查
同一个问题多问几次,看答案是不是一样。如果答案前后矛盾,说明模型不稳定。
6. 边界行为
不懂的时候不乱答,也是准确。比如知识库没依据时能不能提示转人工,涉及权限外的数据能不能拒绝。
7. 面试一句话
判断 AI 客服回答准不准,核心看三点:有没有依据、和业务数据一不一致、用户满不满意。
十、模型响应时间优化
上线测试时,最明显的问题是响应慢。慢的地方主要有三个:
- RAG 检索。
- 业务工具调用。
- 大模型生成。
我做了几类优化。
1. 接入流式响应
前端使用 SSE 接收模型输出,后端边拿到模型内容边推给前端。
虽然总耗时没有完全消失,但用户能更快看到首字,体验提升比较明显。
2. 控制提示词长度
RAG 只传最相关的几个片段,不再把整篇文档塞进去。
多轮会话也不直接拼接全部历史,而是只保留最近几轮和必要摘要。
3. 控制输出长度
普通知识问答要求简洁回答,复杂排障问题才允许输出更长步骤。
这样既降低耗时,也减少 Token 消耗。
4. 高频问题做缓存
像报销标准、请假流程、接口鉴权说明这类高频问题,会把相似问题和答案缓存起来。
命中缓存时不再调用模型。
5. 模型分层
简单意图识别、问题分类用成本更低的小模型或规则判断。
真正需要总结、生成、归纳时,再调用主模型。
6. 异步化耗时操作
用户问题涉及多个业务查询时,比如同时查订单、物流、售后状态,把这些工具调用改成异步并行执行,等结果都回来再交给模型总结,比串行调用省很多时间。
另外一些不阻塞用户的离线任务,比如会话摘要生成、FAQ 批量 Embedding,也用异步处理,避免影响主流程响应。
十一、成本控制
AI 应用成本主要来自 Token。输入越长、输出越长、调用次数越多,费用越高。
我当时做了几个控制点。
1. 限制输入长度
用户问题超过长度会提示用户缩短,避免一次请求带入大量无关内容。
2. 限制输出长度
提示词里要求普通回答控制在一定长度内,排障类问题才允许分步骤详细说明。
3. 减少上下文
多轮对话不会无限拼接历史记录,而是保留最近对话和摘要。
4. 缓存高频问题
缓存命中后可以直接返回,不走模型调用。
5. 记录成本明细
每次调用都会记录:
- 用户。
- 会话。
- 模型。
- 输入 Token。
- 输出 Token。
- 耗时。
- 是否成功。
后面可以按用户、部门、功能统计消耗。
6. 确定性逻辑不用模型
权限判断、参数校验、路由判断、是否为空、是否超长,这些全部用 Java 代码处理。
模型只负责理解、总结和生成。
十二、边界状态和兜底
AI 项目最怕模型一本正经地答错,所以我对边界状态做了很多兜底。
| 边界情况 | 处理方式 |
|---|---|
| 用户问题为空 | 直接返回“请输入要咨询的问题”,不调用模型 |
| 用户问题过长 | 提示用户缩短问题,避免浪费 Token |
| 知识库没有命中可靠资料 | 返回“当前知识库中没有找到明确依据” |
| 检索结果相似度低 | 不进入大模型回答流程,或者提示用户换个问法 |
| 模型接口超时 | 返回友好提示,并记录超时日志 |
| 模型输出 JSON 格式错误 | 先做一次格式修复重试,失败后走兜底 |
| 用户访问无权限知识库 | 直接拦截,相关文档不会进入检索范围 |
| 工具调用失败 | 明确提示“业务数据查询失败” |
| 返回内容包含敏感字段 | 后端做脱敏处理 |
| 模型回答不了 | 直接转人工客服 |
敏感字段包括:
- 手机号
- 身份证号
- Token
- 密钥
- 用户隐私信息
十三、面试中可以这样总结
我做的这个 AI 智能客服项目,核心不是简单调用大模型接口,而是围绕真实客服场景做了一套完整的智能体流程。
整体上,用户提问后,系统会先做敏感词过滤和 FAQ 匹配。FAQ 命中时直接返回标准答案;FAQ 没命中时,再走 RAG 检索知识库,把相关文档片段和用户问题一起交给大模型生成回答。
对于需要查询业务数据的问题,比如订单状态、物流信息、退款进度,我通过 Function Calling / MCP 把业务只读接口封装成工具,让模型在需要时调用。
为了支持多轮对话,我用 Redis 保存短期上下文,用 MySQL 保存完整历史会话。用户下次进入系统时,可以看到上次的聊天记录;同一个会话里继续追问时,模型也能结合上下文回答。
项目中我重点解决了几个问题:模型幻觉、RAG 命中不准、响应慢、Token 成本高、模型输出格式不稳定和人工客服兜底。最终通过 RAG、FAQ 缓存、敏感词过滤、流式响应、上下文摘要、成本统计和转人工机制,让这个 AI 客服系统能够比较稳定地落地。