# 智能客服API接口文档 ## 一、接口说明 本接口用于连接微信小程序与Dify聊天机器人,实现智能客服功能。支持流式和非流式两种响应模式,具备完整的事务保护、数据一致性和状态管理功能。 ## 二、配置说明 ### 1. 安装插件 在ThinkPHP后台的插件管理页面中,找到智能客服插件(aikefu)并点击安装按钮。 ### 2. 配置插件 1. 进入智能客服配置页面 2. 输入从Dify平台获取的API密钥 3. 配置API基础地址(默认:https://api.dify.ai/v1) 4. 配置聊天接口端点(默认:/chat-messages) 5. 启用智能客服功能 ### 3. 获取Dify API密钥 1. 登录Dify平台 2. 进入工作台 3. 选择您的聊天机器人项目 4. 点击"发布"按钮 5. 在API访问页面获取API密钥 ## 三、接口列表 ### 1. 系统健康检查 **接口地址**:/api/kefu/health **请求方式**:POST **请求参数**: | 参数名 | 类型 | 必填 | 说明 | | ------ | ---- | ---- | ---- | | uniacid | int | 是 | 站点ID | | check_type | string | 否 | 检查类型:full(完整)、basic(基础)、ai_service(AI服务),默认full | **响应示例**: ```json { "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 | 否 | 访问令牌 | **响应示例**: ```json { "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) ```json { "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) **响应示例**: ```javascript 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 | 否 | 访问令牌 | **响应示例**: ```json { "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 | 否 | 访问令牌 | **响应示例**: ```json { "code": 0, "message": "success", "data": {} } ``` ## 四、前端调用示例 ### 1. 非流式聊天(Fetch API) ```javascript // 非流式聊天 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) ```javascript // 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) ```javascript // 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调用示例 ```javascript // 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; } } ``` ## 五、使用流程 1. **初始化检查**:小程序端启动时,调用`health`和`info`接口检查服务状态 2. **获取会话**:进入客服页面时,系统会自动创建或使用已有会话 3. **发送消息**:用户输入消息后,调用`chat`接口发送消息,获取机器人回复 4. **显示消息**:将用户消息和机器人回复显示在聊天界面 5. **加载历史记录**:需要时调用`getHistory`接口加载历史消息 6. **维护会话**:保持会话ID,用于后续消息交流 7. **清理数据**:根据用户需求调用`clearConversation`接口清理历史数据 ## 六、数据存储机制 ### 1. 存储状态 | 状态值 | 含义 | 说明 | |--------|------|------| | `streaming` | 流式中 | 正在进行流式输出的临时数据 | | `completed` | 已完成 | 正常完成的对话数据 | | `failed` | 失败 | 流式过程中发生失败的数据 | ### 2. 事务保护 - **流式对话**:使用临时会话ID机制,失败时自动回滚 - **非流式对话**:完整的事务保护,确保数据一致性 - **重复检查**:避免重复存储相同消息 ### 3. 数据一致性 - 用户消息和助手消息通过`conversation_id`关联 - 会话状态实时更新,便于管理和监控 - 详细的日志记录,便于问题排查 ## 七、注意事项 1. **必填参数**:所有接口都需要`uniacid`(站点ID)参数 2. **参数更新**:新版本使用`query`替代`message`,使用`uniacid`替代`site_id` 3. **事件驱动**:后端采用事件驱动架构,所有业务逻辑通过事件处理器执行 4. **安全性**:请确保Dify API密钥的安全性,不要泄露给前端 5. **用户标识**:建议对用户ID进行加密处理,避免直接使用敏感信息 6. **流式体验**:推荐使用`stream: true`参数获得更好的用户体验 7. **会话管理**:建议实现会话管理机制,定期清理过期会话 8. **频率限制**:建议添加请求频率限制,防止恶意请求 9. **生产环境**:在生产环境中,建议关闭DEBUG模式 10. **数据完整性**:系统已内置事务保护和重复检查机制 ## 八、测试建议 1. **基础检查**:首先调用`health`接口检查系统状态 2. **配置验证**:调用`info`接口验证配置信息 3. **接口测试**:使用Postman或类似工具测试各个API接口 4. **流式测试**:测试`chat`接口的流式响应功能 5. **完整流程**:在小程序端集成并测试完整流程 6. **边界测试**:模拟不同场景下的用户输入,测试机器人回复效果 7. **压力测试**:测试接口在高并发情况下的表现 8. **数据验证**:检查非流式对话的存储完整性和一致性 ## 九、常见问题 ### 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` ## 十、性能优化建议 1. **缓存配置**:可对`info`接口返回的配置信息进行客户端缓存 2. **连接复用**:HTTP请求使用连接池,减少建立连接的开销 3. **压缩传输**:启用gzip压缩减少传输数据量 4. **分页加载**:历史记录使用分页加载,避免一次性加载大量数据 5. **CDN加速**:静态资源使用CDN加速访问 6. **监控告警**:建立接口性能监控和告警机制 7. **数据清理**:定期清理过期和失败状态的垃圾数据 8. **索引优化**:为常用查询字段添加数据库索引 --- **文档更新时间**:2025-12-10 **版本**:v2.1 **兼容性**:向后兼容,推荐使用标准的`uniacid`参数