18 KiB
智能客服API接口文档
一、接口说明
本接口用于连接微信小程序与Dify聊天机器人,实现智能客服功能。支持流式和非流式两种响应模式,具备完整的事务保护、数据一致性和状态管理功能。
二、配置说明
1. 安装插件
在ThinkPHP后台的插件管理页面中,找到智能客服插件(aikefu)并点击安装按钮。
2. 配置插件
- 进入智能客服配置页面
- 输入从Dify平台获取的API密钥
- 配置API基础地址(默认:https://api.dify.ai/v1)
- 配置聊天接口端点(默认:/chat-messages)
- 启用智能客服功能
3. 获取Dify API密钥
- 登录Dify平台
- 进入工作台
- 选择您的聊天机器人项目
- 点击"发布"按钮
- 在API访问页面获取API密钥
三、接口列表
1. 系统健康检查
接口地址:/api/kefu/health
请求方式:POST
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| uniacid | int | 是 | 站点ID |
| check_type | string | 否 | 检查类型:full(完整)、basic(基础)、ai_service(AI服务),默认full |
响应示例:
{
"code": 0,
"message": "success",
"data": {
"status": "healthy",
"check_id": "health_56789",
"timestamp": "2023-12-25 10:30:45",
"total_checks": 2,
"passed_checks": 2,
"failed_checks": 0,
"response_time_ms": 170,
"components": {
"ai_service": {
"status": "healthy",
"message": "AI服务正常",
"response_time_ms": 150
},
"database": {
"status": "healthy",
"message": "数据库连接正常",
"response_time_ms": 20
}
},
"warnings": [],
"errors": []
}
}
2. 获取服务配置信息
接口地址:/api/kefu/info
请求方式:POST
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| uniacid | int | 是 | 站点ID |
| member_id | int | 否 | 会员ID |
| token | string | 否 | 访问令牌 |
响应示例:
{
"code": 0,
"message": "success",
"data": {
"enabled": true,
"status": "enabled"
}
}
3. 智能客服聊天接口
接口地址:/api/kefu/chat
请求方式:POST 或 GET(流式模式支持EventSource)
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| query | string | 是 | 用户输入的消息内容 |
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
| conversation_id | string | 否 | 会话ID,第一次聊天可不传,系统会自动创建 |
| stream | bool | 否 | 是否使用流式响应,默认false |
| response_mode | string | 否 | 响应模式:streaming(流式)、blocking(阻塞),默认streaming |
| uniacid | int | 是 | 站点ID |
响应示例:
非流式响应(stream=false 或 response_mode=blocking)
{
"code": 0,
"message": "success",
"data": {
"conversation_id": "conv_123456789",
"answer": "您好,我是智能客服,有什么可以帮助您的?",
"message_id": "msg_123456789",
"finish_reason": "stop",
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}
}
流式响应(stream=true 或 response_mode=streaming)
响应格式:Server-Sent Events (SSE)
响应示例:
data: {"event":"message","answer":"您好","conversation_id":"conv_123456789","message_id":"msg_123456789"}
data: {"event":"message","answer":",我是智能客服,","conversation_id":"conv_123456789","message_id":"msg_123456789"}
data: {"event":"message","answer":"有什么可以帮助您的?","conversation_id":"conv_123456789","message_id":"msg_123456789"}
data: {"event":"message_end","conversation_id":"conv_123456789","message_id":"msg_123456789"}
data: {"event":"done","data":{"conversation_id":"conv_123456789","message_id":"msg_123456789","content":"您好,我是智能客服,有什么可以帮助您的?"}}
data: {"event":"close","data":{"conversation_id":"conv_123456789","message_id":"msg_123456789"}}
4. 获取会话历史
接口地址:/api/kefu/getHistory
请求方式:POST
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| uniacid | int | 是 | 站点ID |
| conversation_id | string | 是 | 会话ID |
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
| limit | int | 否 | 每页条数,默认20 |
| offset | int | 否 | 偏移量,默认0 |
| member_id | int | 否 | 会员ID |
| token | string | 否 | 访问令牌 |
响应示例:
{
"code": 0,
"message": "success",
"data": {
"messages": [
{
"id": "msg_123456789",
"role": "user",
"content": "您好",
"create_time": 1703505845
},
{
"id": "msg_123456790",
"role": "assistant",
"content": "您好,我是智能客服,有什么可以帮助您的?",
"create_time": 1703505846
}
],
"total": 2,
"page_info": {
"limit": 20,
"offset": 0
}
}
}
5. 清除会话历史
接口地址:/api/kefu/clearConversation
请求方式:POST
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| uniacid | int | 是 | 站点ID |
| conversation_id | string | 否(与user_id二选一) | 会话ID(与user_id二选一) |
| user_id | string | 否(与conversation_id二选一) | 用户ID,默认使用当前登录会员ID(与conversation_id二选一) |
| member_id | int | 否 | 会员ID |
| token | string | 否 | 访问令牌 |
响应示例:
{
"code": 0,
"message": "success",
"data": {}
}
四、前端调用示例
1. 非流式聊天(Fetch API)
// 非流式聊天
async function chatWithAI(message, conversationId = '') {
try {
const formData = new FormData();
formData.append('query', message);
formData.append('uniacid', '1');
formData.append('stream', 'false');
formData.append('response_mode', 'blocking');
if (conversationId) {
formData.append('conversation_id', conversationId);
}
const response = await fetch('/api/kefu/chat', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.code === 0) {
return result.data;
} else {
console.error('聊天失败:', result.message);
return null;
}
} catch (error) {
console.error('聊天请求失败:', error);
return null;
}
}
2. 流式聊天(EventSource)
// EventSource 流式聊天
function chatWithAIEventSource(message, conversationId = '', onMessage, onComplete, onError) {
// 关闭之前的连接
if (window.currentEventSource) {
window.currentEventSource.close();
}
// 构建请求参数
const params = new URLSearchParams({
uniacid: '1',
user_id: '123456',
query: message,
conversation_id: conversationId || '',
stream: 'true'
});
const url = `/api/kefu/chat?${params.toString()}`;
try {
const eventSource = new EventSource(url);
window.currentEventSource = eventSource;
let aiMessage = '';
// 监听消息事件
eventSource.addEventListener('message', (event) => {
try {
const data = JSON.parse(event.data);
if (data.event === 'message') {
// 更新 AI 消息
aiMessage += data.answer || '';
if (onMessage) onMessage(data.answer || '');
}
if (data.event === 'message_end') {
// 对话完成
if (onComplete) onComplete({
conversation_id: data.conversation_id,
message: aiMessage
});
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
} catch (error) {
console.error('解析消息失败:', error);
}
});
// 监听完成事件
eventSource.addEventListener('done', (event) => {
try {
const data = JSON.parse(event.data);
if (onComplete) onComplete(data);
} catch (error) {
console.error('解析完成事件失败:', error);
}
});
// 监听关闭事件
eventSource.addEventListener('close', (event) => {
try {
const data = JSON.parse(event.data);
console.log('连接正常结束:', data);
} catch (error) {
console.error('解析关闭事件失败:', error);
}
window.currentEventSource = null;
});
// 监听错误事件
eventSource.addEventListener('error', (error) => {
console.error('EventSource错误:', error);
if (onError) onError({ error: 'EventSource连接错误' });
window.currentEventSource = null;
});
return eventSource;
} catch (error) {
console.error('创建EventSource失败:', error);
if (onError) onError({ error: error.message });
return null;
}
}
3. 流式聊天(Fetch API)
// Fetch API 流式聊天
async function chatWithAIFetchStream(message, conversationId = '', onMessage, onComplete, onError) {
try {
// 构建请求体
const formData = new FormData();
formData.append('uniacid', '1');
formData.append('user_id', '123456');
formData.append('query', message);
formData.append('conversation_id', conversationId || '');
formData.append('stream', 'true');
const response = await fetch('/api/kefu/chat', {
method: 'POST',
body: formData,
headers: {
'Accept': 'text/event-stream'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
if (!response.body) {
throw new Error('响应体不可用');
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let aiMessage = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 解码新接收的数据
buffer += decoder.decode(value, { stream: true });
// 处理缓冲的数据,按行分割
let lineEnd;
while ((lineEnd = buffer.indexOf('\n')) !== -1) {
const line = buffer.substring(0, lineEnd);
buffer = buffer.substring(lineEnd + 1);
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.substring(6));
if (data.event === 'message') {
aiMessage += data.answer || '';
if (onMessage) onMessage(data.answer || '');
} else if (data.event === 'message_end') {
if (onComplete) onComplete({
conversation_id: data.conversation_id,
message: aiMessage
});
} else if (data.event === 'done' && onComplete) {
onComplete(data);
} else if (data.event === 'error' && onError) {
onError(data);
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
} catch (e) {
console.warn('解析流式数据失败:', e);
}
}
}
}
// 处理剩余的缓冲数据
if (buffer.startsWith('data: ')) {
try {
const data = JSON.parse(buffer.substring(6));
if (data.event === 'message' && onMessage) {
onMessage(data.answer || '');
} else if (data.event === 'done' && onComplete) {
onComplete(data);
}
} catch (e) {
console.warn('解析剩余数据失败:', e);
}
}
} catch (error) {
console.error('Fetch流式聊天请求失败:', error);
if (onError) onError({ error: error.message });
}
}
4. Uniapp调用示例
// Uniapp 非流式调用
async function chatWithAI(message, conversationId = '') {
try {
const res = await uni.request({
url: '/api/kefu/chat',
method: 'POST',
data: {
query: message,
uniacid: 1,
conversation_id: conversationId,
response_mode: 'blocking'
}
});
if (res[1].data.code === 0) {
return res[1].data.data;
} else {
console.error('聊天失败:', res[1].data.message);
return null;
}
} catch (error) {
console.error('聊天请求失败:', error);
return null;
}
}
// Uniapp 获取历史记录
async function getChatHistory(conversationId, limit = 20, offset = 0) {
try {
const res = await uni.request({
url: '/api/kefu/getHistory',
method: 'POST',
data: {
uniacid: 1,
conversation_id: conversationId,
limit: limit,
offset: offset
}
});
if (res[1].data.code === 0) {
return res[1].data.data;
} else {
console.error('获取历史记录失败:', res[1].data.message);
return null;
}
} catch (error) {
console.error('获取历史记录请求失败:', error);
return null;
}
}
// Uniapp 健康检查
async function checkHealth(checkType = 'full') {
try {
const res = await uni.request({
url: '/api/kefu/health',
method: 'POST',
data: {
uniacid: 1,
check_type: checkType
}
});
if (res[1].data.code === 0) {
return res[1].data.data;
} else {
console.error('健康检查失败:', res[1].data.message);
return null;
}
} catch (error) {
console.error('健康检查请求失败:', error);
return null;
}
}
五、使用流程
- 初始化检查:小程序端启动时,调用
health和info接口检查服务状态 - 获取会话:进入客服页面时,系统会自动创建或使用已有会话
- 发送消息:用户输入消息后,调用
chat接口发送消息,获取机器人回复 - 显示消息:将用户消息和机器人回复显示在聊天界面
- 加载历史记录:需要时调用
getHistory接口加载历史消息 - 维护会话:保持会话ID,用于后续消息交流
- 清理数据:根据用户需求调用
clearConversation接口清理历史数据
六、数据存储机制
1. 存储状态
| 状态值 | 含义 | 说明 |
|---|---|---|
streaming |
流式中 | 正在进行流式输出的临时数据 |
completed |
已完成 | 正常完成的对话数据 |
failed |
失败 | 流式过程中发生失败的数据 |
2. 事务保护
- 流式对话:使用临时会话ID机制,失败时自动回滚
- 非流式对话:完整的事务保护,确保数据一致性
- 重复检查:避免重复存储相同消息
3. 数据一致性
- 用户消息和助手消息通过
conversation_id关联 - 会话状态实时更新,便于管理和监控
- 详细的日志记录,便于问题排查
七、注意事项
- 必填参数:所有接口都需要
uniacid(站点ID)参数 - 参数更新:新版本使用
query替代message,使用uniacid替代site_id - 事件驱动:后端采用事件驱动架构,所有业务逻辑通过事件处理器执行
- 安全性:请确保Dify API密钥的安全性,不要泄露给前端
- 用户标识:建议对用户ID进行加密处理,避免直接使用敏感信息
- 流式体验:推荐使用
stream: true参数获得更好的用户体验 - 会话管理:建议实现会话管理机制,定期清理过期会话
- 频率限制:建议添加请求频率限制,防止恶意请求
- 生产环境:在生产环境中,建议关闭DEBUG模式
- 数据完整性:系统已内置事务保护和重复检查机制
八、测试建议
- 基础检查:首先调用
health接口检查系统状态 - 配置验证:调用
info接口验证配置信息 - 接口测试:使用Postman或类似工具测试各个API接口
- 流式测试:测试
chat接口的流式响应功能 - 完整流程:在小程序端集成并测试完整流程
- 边界测试:模拟不同场景下的用户输入,测试机器人回复效果
- 压力测试:测试接口在高并发情况下的表现
- 数据验证:检查非流式对话的存储完整性和一致性
九、常见问题
1. 接口返回400错误
原因:缺少必填参数uniacid或参数格式错误
解决方法:确保请求中包含有效的站点ID,参数名已更新为uniacid
2. 健康检查返回503错误
原因:AI服务配置不完整或服务异常 解决方法:检查插件配置和Dify API服务状态
3. 接口返回401错误
原因:Dify API密钥无效或过期 解决方法:重新获取有效的API密钥并更新插件配置
4. 接口返回500错误
原因:后端服务器错误或Dify API服务异常 解决方法:查看服务器日志,检查Dify API服务状态
5. 机器人回复为空
原因:Dify聊天机器人配置问题或请求参数错误 解决方法:检查Dify机器人配置,验证请求参数是否正确
6. 流式响应无法解析
原因:客户端不支持SSE或解析方式错误 解决方法:使用正确的方式解析Server-Sent Events格式,参考前端示例代码
7. 会话ID无效
原因:会话已过期或不存在 解决方法:创建新会话,获取新的会话ID
8. 参数不匹配
原因:使用了旧版本的参数名称
解决方法:更新参数:message → query,site_id → uniacid
十、性能优化建议
- 缓存配置:可对
info接口返回的配置信息进行客户端缓存 - 连接复用:HTTP请求使用连接池,减少建立连接的开销
- 压缩传输:启用gzip压缩减少传输数据量
- 分页加载:历史记录使用分页加载,避免一次性加载大量数据
- CDN加速:静态资源使用CDN加速访问
- 监控告警:建立接口性能监控和告警机制
- 数据清理:定期清理过期和失败状态的垃圾数据
- 索引优化:为常用查询字段添加数据库索引
文档更新时间:2025-12-10
版本:v2.1
兼容性:向后兼容,推荐使用标准的uniacid参数