阅读原文
建议阅读原文,始终查看最新文档版本,获得最佳阅读体验:《基于知识库的AI客服系统 - 项目总结报告》
执行摘要(Executive Summary):基于知识库的AI客服系统
本报告概述了智能AI客服系统的成功开发与实施,该系统旨在显著提升用户体验和运营效率,尤其是在IT服务台领域。针对用户重复咨询的常见挑战,该系统提供7x24小时自动化支持,并无缝集成人工客服以处理复杂问题。
主要成就:
智能化问答:
系统以RAGFlow作为核心知识库引擎,能够快速准确地回答常见的内部问题,例如密码重置和账号申请,从而大大减轻IT和运维人员的负担。
无缝人机协作:
用户可以轻松地从AI对话切换到Rocket.Chat上的人工客服,确保所有问题最终都能得到解决。用户(通过Chainlit前端)与人工客服(通过Rocket.Chat)之间的实时、双向消息同步是核心功能。
稳健且可扩展的架构:
系统采用模块化设计,前端使用Chainlit,知识管理使用RAGFlow,人工支持使用Rocket.Chat,消息队列使用Redis,Web服务使用FastAPI,使其具有高度的可扩展性和可靠性。Keycloak提供安全的统一用户身份认证服务,并与现有企业LDAP对接。
聚焦问题解决:
项目成功克服了包括异步通信、多服务组件间的会话状态持久化以及安全的API交互等重大技术挑战,主要是通过利用Redis作为中央消息队列和状态管理工具。
战略影响:
该AI客服系统是一项战略资产,其应用范围不仅限于IT支持,还可以革新研发部门的知识获取方式,简化行政职能,并增强售前/售后支持。通过自动化常规查询并促进高效的人工干预,它提高了生产力,提升了用户满意度,并使宝贵的人力资源能够专注于更复杂的工作。
未来展望:
未来的增强功能包括基于用户意图分析的智能路由、更强大的人工客服工作台、多渠道接入(例如微信、钉钉、飞书、Microsoft teams、slack),以及对话历史的持久存储、用户反馈机制以及支持图片和各种类型文件的收发。此外,还计划进一步提高系统的高可用性和增强安全协议。该项目为企业内部智能客户支持的持续创新奠定了坚实基础。
项目概述
项目背景与目标
我在工作过程中,发现用户的咨询或提问,很多都是重复的,例如:怎么该办公系统或业务系统的密码、怎么申请sap的账号、用户入职后如何申请电脑以及使用电脑的注意事项等等,其实这些问题都有标准的解决方案(Q&A文档)的,但传统的办法就是不厌其烦的回答用户的提问。此方法效率低下,而且有时候用户在下班时间也会提问,往往回复不及时,影响用户体验。
为了提升用户体验,也为了提高运维人员的工作效率,我开发了一个智能AI客服系统,能很好地解决上述问题,本文是对此项目的总结报告,代码也已经开源。欢迎大家共同交流。
本项目旨在开发一个高效、智能的AI客服系统。系统以RAGFlow作为核心知识库引擎,为用户提供快速、精准的自动化问答服务。同时,为了处理复杂和个性化问题,系统无缝集成了Rocket.Chat人工客服,实现了AI与人工的协同工作。
项目的主要目标如下:
智能化问答: 基于企业内部知识库,提供7x24小时的自动化客户支持。当前主要是面向企业员工,解答常见的办公问题。
人机协作: 用户可随时从AI对话切换至人工客服,确保问题得到最终解决。当前主要是桌面运维工程师充当人工客服。
双向通信: 实现用户(通过Chainlit前端)与人工客服(通过Rocket.Chat)之间的实时、双向消息同步。
高可扩展性: 系统采用模块化设计,便于未来功能扩展和与其他系统集成。
视频演示
请至钉钉文档查看附件《基于本地知识库的AI客服系统(支持转人工客服).mp4》
截图
一般提问:
英文提问
转人工客服演示截图:
技术选型
关于技术选型,我花了不少时间上网查找能满足我需求的开源软件,然而我发现github上又很多成熟的聊天机器人框架,但没有找到完全开源的支持转人工客服的框架,有些软件虽然支持,但是需要付费,最终我还是决定自己开发,组合当前主流的开源软件,以满足实际需求。
为实现上述目标,我选择了以下技术栈:
前端与应用框架: Chainlit
一个用于快速构建聊天应用的Python框架,提供了用户认证、会话管理和友好的UI。其支持实时渲染,比如,消息中如果有代码,则会自动显示代码块,方便阅读。
知识库与问答引擎: RAGFlow
领先的检索增强生成(RAG)平台,负责知识的存储、检索和智能问答。其还支持多语言、MCP、agent等。
人工客服平台: Rocket.Chat
一个功能强大的开源团队沟通协作平台,用作人工客服的工作台。
消息队列: Redis
利用其发布/订阅和Stream数据结构,实现各服务间的异步消息通信,确保系统的响应速度和稳定性。
Web服务: FastAPI
用于构建接收Rocket.Chat消息的Webhook服务,性能卓越。
身份认证: Keycloak
通过OAuth 2.0协议,为Chainlit提供统一、安全的用户身份认证服务。keycloak对接的是企业LDAP,因此用户可以直接通过现有的域账号登录AI客服系统。
典型应用场景
IT服务台(IT技术支持)
桌面运维工程师每天遇到的问题可以说是最多的,也是与终端用户沟通最为频繁的,可以说是在一线的,本项目主要针对的就是这个场景
知识积累与高效获取
企业的研发部门往往积累了大量的资料,无论是新员工还是老员工,想要查找这些资料是非常不方便的,非常低效的,完全可以利用我开发的AI客服(这时,叫超级AI助理更加合适)高效快速获取知识。提高研发效率。
行政后勤
虽然我是干IT的,但是多年工作经验,我发现很多公司的很多制度文件、工作流程等等,都比较分散,员工往往很难找到自己想要的信息,比如某个具体的制度、考勤问题、办公用品维修找谁、薪资问题及大量日常的问题,这些问题其实本身不难,就是查找资料费劲,其实完全可以用我开发的AI客服实现自动化的回答员工的各种提问,这对提升用户体验,提升企业内部的行政效率是明显有帮助的。
产品售前售后
这个场景,其实目前已经有案例了,可以说是AI技术和rag技术应用得比较早的领域了。
系统架构设计
本系统的核心设计思想是“人机协同”与“松耦合”,各个组件各司其职,通过消息队列和API调用进行高效协作。
总体架构图(architecture diagram)
系统架构图
graph TD
subgraph "用户端"
A[用户浏览器]
end
subgraph "应用服务层"
B[Chainlit应用]
C[FastAPI Webhook服务]
end
subgraph "核心引擎与中间件"
D[RAGFlow 知识库]
E[Redis 消息队列]
F[Keycloak 认证服务]
end
subgraph "人工客服端"
G[Rocket.Chat]
end
A -- HTTPS --> B
B -- OAuth 2.0 --> F
B -- RESTful API --> D
B -- "转人工" --> G
B -- 读/写消息 --> E
C -- 接收Webhook --> G
C -- 写消息 --> E
G -- 登录认证 --> F
架构解析:
用户交互: 用户通过浏览器访问Chainlit应用。Keycloak负责用户登录认证(LDAP),确保会话安全。
AI问答流程:
用户在Chainlit界面提出问题。
chainlit_ragflow_streaming.py
中的RAGFlowClient
模块调用RAGFlow API。
RAGFlow在知识库中检索相关信息,生成答案,并通过流式(Streaming)方式返回给Chainlit,实现答案的实时显示。
转人工流程:
用户点击“转人工”按钮。
chainlit_ragflow_streaming.py
记录当前会话状态,并调用Rocket.Chat的API,将用户的历史对话记录发送给指定的人工客服。
系统根据.env
文件中的WEEKDAY_USERS
配置,实现客服的智能排班。
双向消息同步:
用户 -> 人工客服: 用户在转人工后发送的消息,会直接通过Rocket.Chat API发送给客服。
人工客服 -> 用户: 客服在Rocket.Chat中回复消息,Rocket.Chat触发Webhook,调用我们的webhook_server.py
服务。该服务将消息写入Redis的特定消息队列中。Chainlit应用中的后台任务 (process_messages
) 实时监听此队列,并将消息推送给相应的用户。
数据流图/时序图(sequence diagram)
核心数据流图
sequenceDiagram
participant User as 用户 (Chainlit)
participant App as Chainlit应用
participant RAGFlow
participant Redis
participant RocketChat as 人工客服
participant Webhook
User->>App: 提问
App->>RAGFlow: 请求回答 (stream)
RAGFlow-->>App: 流式返回答案
App-->>User: 实时展示答案
User->>App: 点击"转人工"
App->>RocketChat: 发送历史对话
App->>Redis: 更新会话状态 (转人工)
RocketChat-->>User: (通过App)提示已转接
User->>App: 发送消息给人工
App->>RocketChat: 推送消息
RocketChat->>Webhook: 回复消息 (Webhook)
Webhook->>Redis: 将消息写入队列
Redis-->>App: (后台任务监听)
App-->>User: 推送人工客服消息
ragflow系统架构图(system architecture)
ragflow是本项目的核心之一,企业内部的各种文档都是由ragflow处理的,正因为有了ragflow这个强大的rag系统,AI客服才能给出准确的回答(大大减少幻觉)。
源代码
代码已开源,github项目网址为:iamtornado/AI-Assistant
基于知识库的AI客服系统开发和实施文档
使用手册
核心模块分析
本项目代码结构清晰,各模块功能明确。
主应用 (**chainlit_ragflow_streaming.py**
)
这是系统的核心,负责处理用户交互和业务逻辑编排。
用户认证 (@cl.oauth_callback
): 与.env
中配置的Keycloak服务集成,所有用户必须登录后才能使用。
会话管理 (@cl.on_chat_start
, @cl.on_chat_end
):
会话开始时,初始化RAGFlowClient
(通过ragflow http api与ragflow建立会话),并在Redis中为该会话创建元数据记录(如用户ID、chainlit会话ID、创建时间等)。
启动一个后台任务process_messages
,专门用于监听来自人工客服的消息。
会话结束时,清理后台任务和Redis中的相关数据。注意:不会删除消息队列,只删除redis中存放元数据的队列
AI与人工模式切换 (@cl.on_message
):
通过检查会话状态is_human_session
来判断用户当前处于AI模式还是人工模式。
AI模式: 调用rag_client.stream_chat_completion
方法,与RAGFlow交互。
人工模式: 将用户消息直接通过Rocket.Chat API发送给客服。
转人工功能 (@cl.action_callback("转人工")
):
这是人机协作的关键。当用户触发此操作时,系统会:
使用.env
配置的LDAP_PASSWORD
等信息,代表用户(通过API)登录Rocket.Chat。
根据WEEKDAY_USERS
确定当前值班客服。您可以利用.ENV文件自行排班。
将完整的对话历史发送给该客服。
在Redis中更新会话状态,标记为“人工会话”,并记录下room_id
等关键信息,为后续消息路由做准备。
RAGFlow客户端 (**ragflow_client.py**
)
该模块封装了与RAGFlow服务的所有API交互,使主应用逻辑更清晰。
__init__
: 初始化时配置好API密钥和基础URL(均来自.env
)。
get_chat_id
: 在系统启动时,根据.env
中配置的RAGFLOW_ASSISTANT_NAME
获取对应的AI助手ID,验证配置的有效性。
stream_chat_completion
: 核心方法,负责向RAGFlow发送用户问题并以流式方式处理返回的答案。同时,它还能解析RAGFlow返回的引用文档信息,并在Chainlit前端以文件链接的形式展示,增强了答案的可信度和追溯性。
Webhook服务 (**webhook_server.py**
)
这是一个独立的FastAPI应用,作为Rocket.Chat与AI应用系统之间的桥梁。
/rocketchat-webhook
(POST): 这是暴露给Rocket.Chat的唯一端点。
安全验证: 首先会验证请求中是否包含与.env
中ROCKETCHAT_WEBHOOK_TOKEN
一致的令牌,防止非法请求。
消息解析: 解析Webhook发来的JSON数据,提取出消息内容、发送者、房间ID等。
消息过滤: 过滤掉由系统自动发出的消息(如对话历史),避免无限循环。
消息入队: 将格式化后的人工客服消息,通过redis_queue.enqueue_stream
方法存入一个唯一的Redis Stream。队列的名称是动态生成的,格式为:{环境}:{平台}:{客服名}:{房间ID}:messages_queue
,例如 dev:rocket.chat_session:bob:XYZ123:messages_queue
。这种设计确保了消息能被准确路由到正确的用户会话。而且便于开发者进行调试诊断。
消息队列 (**message_queue.py**
)
这个模块是对Redis操作的封装,提供了标准的消息队列接口。
环境隔离: 在初始化时,会根据.env
文件中的IT_ENVIRONMENT
变量(dev
或test
)来选择连接到不同的Redis数据库(REDIS_DB_INDEX_DEV
或REDIS_DB_INDEX_TEST
),实现了开发与测试环境的隔离。(已变更,原先我是打算将不同环境中的数据存放在redis中的不同数据库中的,后来发现官方不建议这样做,建议直接存放在相同的数据库中,通过业务前缀等方式标识。所以后来我就采用环境来标识消息队列所处的环境了,也很简单,就是在消息队列名称最开头加上前缀,如果是开发环境就是dev,如果是测试环境就是test,以此类推)
核心方法(method):
enqueue_stream
: 使用Redis Stream XADD
命令,将消息添加到流中。Stream比List更适合做消息队列,因为它支持消费者组、消息持久化和更灵活的消费模式。
stream_peek_latest
: 使用XREAD
命令从Stream中读取最新的消息。Chainlit端的process_messages
任务使用此方法来消费消息。
qsize
: 智能判断Redis Key的类型(List或Stream),返回队列长度。
配置与日志
.env文件: 集中管理了所有敏感信息和环境配置,如API密钥、服务器地址、数据库密码等。这是现代应用开发的最佳实践,实现了配置与代码的分离。
logger_config.py
: 提供了一个标准化的日志设置函数setup_logger
。所有模块都使用它来初始化日志记录器,将日志同时输出到控制台和app.log
文件。日志级别和传播行为可以通过.env
文件进行配置,便于在不同环境下进行调试和监控。
遇到的挑战与解决方案
在项目开发过程中,我遇到了一些挑战,并通过以下方式成功克服:
挑战1: 异步通信与消息路由的复杂性
参考资料:https://blog.gitcode.com/a56467fe9ae6fcd17807ed17b77b181f.html
-
问题描述: Chainlit (基于 FastAPI 的异步框架) 与 Rocket.Chat (通过 Webhook 进行通信) 之间需要实时、双向的消息传递。尤其是在用户转接人工客服后,Rocket.Chat 人工客服的回复需要准确无误地回流到对应的 Chainlit 会话。如何确保消息不丢失、不混乱,并路由到正确的用户界面,是一个核心挑战。这个问题困扰我许久,我在开发时一直遇到
**Chainlit context not found**
报错,这是因为chainlit有其自己的上下文,如果直接在fastapi中调用cl.user_session等chainlit中的函数就会报错,以下是解决方案: -
解决方案: 引入 Redis 作为消息队列的中心枢纽。
-
当用户点击“转人工”时,Chainlit 会将 Rocket.Chat 生成的
room_id
与当前的 Chainlitsession_id
关联起来,并将此映射及会话状态 (is_human_session
设为True
) 存储在 Redis 的会话元数据 (chainlit_session:{username}:{chainlit_session_id}:metadata
) 中。 -
Rocket.Chat Webhook Server 接收到人工客服的回复后,会解析消息中的
channel_id
(即room_id
),并使用此room_id
作为队列名称 ({os.getenv("IT_ENVIRONMENT")}:rocket.chat_session:{sender_username}:{room_id}:messages_queue
),将回复消息推入 Redis。 -
Chainlit 应用中运行的
process_messages
异步任务会动态地监听这个{os.getenv("IT_ENVIRONMENT")}:rocket.chat_session:{sender_username}:{room_id}:messages_queue
队列,一旦有新消息,便会立即展示给用户。这种基于room_id
的消息队列和动态监听机制,确保了消息的精确路由。而且也便于开发者和运维人员诊断问题。
-
挑战2: 会话状态在多服务组件间的持久化与同步
-
问题描述: 系统的不同组件(Chainlit 应用本身、Webhook Server)需要共享和维护用户会话的实时状态,例如当前是 AI 问答模式还是人工会话模式,以及人工会话的目标客服和 Rocket.Chat 频道 ID。如何高效且可靠地同步这些状态,防止数据不一致,是一个关键问题。
-
解决方案: 利用 Redis 的哈希表 (
**HSET**
) 功能进行会话状态存储。-
每个 Chainlit 会话在 Redis 中都有一个独立的哈希表键
chainlit_session:{username}:{chainlit_session_id}:metadata
。 -
此哈希表存储了会话的所有关键元数据,包括
user_id
、created_at
、status
(active
/human_chat
)以及 Rocket.Chat 的room_id
。 -
Chainlit 在转人工时会更新这个哈希表,Webhook Server 在处理 Rocket.Chat 回复时也会查询这个哈希表(后来我改为从chainlit用户会话中保存的状态数据中读取room_id的值)。这种集中式的状态管理方式,确保了会话状态的一致性和持久性,即使在服务重启后也能恢复部分会话信息。同时,为会话元数据设置过期时间(如
expire(..., 86400)
)有助于自动清理长时间不活跃的会话数据。消息队列也设置了保存的数量,如我将每个消息队列(stream)设置为最多保存最近1000条消息,如此可以有效防止内存溢出,导致redis崩溃。
-
挑战3: Rocket.Chat API 交互与身份验证
-
问题描述: 与 Rocket.Chat 进行程序化交互,特别是登录验证和消息发送,需要正确处理其 API 协议和认证机制。
-
解决方案: 使用
**rocketchat_API.rocketchat**
库。-
该 Python 库封装了 Rocket.Chat 的 REST API,极大地简化了开发工作。通过环境变量(
ROCKETCHAT_SERVER_URL
和LDAP_PASSWORD
)动态初始化RocketChat
客户端,实现了以当前用户身份自动化登录和消息发送。 -
rocket.me().json()
用于验证登录状态,rocket.chat_post_message()
用于发送消息到指定频道或用户。
-
挑战4: 异步编程中的阻塞操作
-
问题描述: Redis 的
dequeue
操作和xread
操作(尤其是阻塞模式的取出消息队列中的消息操作)是同步的。在 Chainlit 的异步环境(基于asyncio
)中直接调用同步操作会导致事件循环阻塞,影响系统响应性。 -
解决方案: 利用
**asyncio.to_thread**
。- 在
process_messages
任务中,所有对redis_queue.stream_peek_latest
等同步 Redis 操作的调用都通过await asyncio.to_thread(...)
进行。这会将同步操作转移到一个单独的线程池中执行,从而避免阻塞主事件循环,保证 Chainlit 应用的响应性和并发处理能力。
- 在
挑战5: 日志调试与追踪
-
问题描述: 在一个包含多个异步组件和外部依赖的系统中,追踪消息流和诊断问题可能会非常困难。
-
解决方案: 实现统一且详细的日志记录。
-
创建了独立的
logger_config.py
模块,提供标准化的日志配置,支持文件和控制台输出,并可根据环境变量设置日志级别。 -
在
chainlit_ragflow_streaming.py
和webhook_server.py
等核心文件中,广泛使用了logger.debug
和logger.info
语句,在关键的代码路径、数据传输、会话状态变更等地方打印详细信息,极大地提高了问题排查的效率。
-
挑战6:rocket.chat outgoing webhook
-
问题描述: rocket.chat上的人工客服回复消息后,如何将回复的消息立即发送给相应的用户(chainlit UI)
-
解决方案: 利用rocket.chat中的integration:outgoing webhook,实现指定事件触发后,自动将消息发送给目标url
- rocket.chat本身就有webhook功能,其中的outgoing webhook很好地解决了这个问题,只需要创建一个outgoing webhook,然后指定某个rocket.chat用户收到消息后,就触发webhook,rocket.chat会自动将消息发送到目标url(即本项目中的fastapi),fastapi解析数据后,再将消息发送到redis中,chainlit应用实时从redis中读取数据,就实现了用户与人工客服实时对话。
总结与展望
项目成果
本项目成功地构建了一个功能完备、架构稳健的AI客服系统,达到了预期的设计目标:
功能完整: 实现了从AI自动问答到无缝转接人工客服的全流程。
技术先进: 采用了RAG、流式响应、Webhook、消息队列等现代技术,保证了系统的性能和用户体验。
高可靠性: 通过异步消息队列解耦了各个服务,提升了系统的容错能力和稳定性。
配置灵活: 所有关键参数均通过环境变量配置,便于部署和维护。
环境隔离: 通过Redis队列名前缀区分开发和测试环境等,保证了开发流程的规范性。
存在的问题与改进建议
高可用性: 当前Webhook服务是单点。在生产环境中,可以考虑将其部署为多个实例,并使用负载均衡器来提高可用性。ragflow、rocket.chat、redis同样如此。生产环境最好将服务部署在k8s集群上。
会话状态管理: 目前会话状态主要依赖Redis。可以考虑引入更完善的分布式会话管理机制,或者将部分状态持久化到数据库中,以便进行更复杂的数据分析。
前端交互优化: 可以在用户等待人工客服回复时,增加更丰富的提示,如预计等待时间、当前排队位置等。
安全:当前没有使用https协议,生成环境应该用SSL证书加密。
未来展望(roadmap)
智能路由(用户意图分析):
未来可以根据用户问题的内容,通过引入NLU (自然语言理解) 模型技术,将问题自动分配给最擅长该领域的客服专家;
还可以通过NLU技术,分析用户的意图,将有些需要人工处理的事物,自动化完成,比如,如果用户有个excel表格被加密了,想要解密,AI客服要能意识到用户的需求,然后自动解密文件,并发回给用户。这个我正在研究,我想或许可以利用MCP实现;
在用户输入时即时识别其意图,例如“抱怨”、“查询订单”、“技术支持”等,并根据意图自动转接给对应技能组的人工客服;
情绪感知转接: 结合情感分析技术,当检测到用户情绪负面或焦躁时,即使 AI 能够回答,也优先转接人工,以提升用户体验和满意度。
客服工作台优化:
可以为人工客服开发一个更强大的工作台,集成更多用户信息(如历史提问记录、用户画像等),辅助客服更高效地解决问题。
多渠道接入:
当前系统(用户侧)只支持Web端。未来可以扩展至微信、钉钉等其他即时通讯平台,为用户提供更广泛的服务渠道,在国内,用开源软件作为企业即时通讯平台的不多。PS:rocket.chat是支持全平台的,其有专门的PC客户端,macos客户端、Linux客户端、IOS、Android
持久存储
当前chainlit应用并不会保存用户的会话历史,如果用户点击了“新建对话”按钮,则之前的对话历史就会立即被清空,chainlit本身是支持持久存储的,跟数据库对接即可。
用户反馈机制(feedback)
在对话页面加入用户评价按钮,就像腾讯元宝、chatGPT那样,如此可以收集用户反馈,以便更好地改进程序,提供更佳使用体验。
支持收发图片和其它文件
当前,是不支持收发图片和文件的,用户虽然可以上传附件,但是ragflow和rocket.chat其实是无法接收到的,反之亦然。后续可以实现此功能,实现更好地用户体验,能解决更多的用户咨询。
用户权限管理(RBAC)
当前,所有用户都可以询问所有ragflow知识库中的内容,但实际的企业生产环境中,有些知识库只能给某些用户查看,所以有必要进行权限管控,让某些用户只能基于某些知识库提问。确保信息安全。
数据分析与业务洞察:
-
收集和分析用户交互数据、AI 回答的准确率、人工转接率、平均处理时长等关键指标。
-
利用这些数据进行报表生成和可视化,为业务运营提供决策支持,发现知识库盲区和客服瓶颈。