Files
012-kaopeilian/docs/规划/全链路联调/Ai工作流/coze/陪练功能API接口规范.md
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

924 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 陪练功能API接口规范
## 一、通用规范
### 1.1 请求格式
**Base URL**
- 开发环境:`http://localhost:8000`
- 生产环境:`https://aiedu.ireborn.com.cn`
**请求头**
```http
Content-Type: application/json; charset=utf-8
Authorization: Bearer <access_token>
```
### 1.2 响应格式
**统一响应结构**
```json
{
"code": 200,
"message": "success",
"data": {},
"request_id": "uuid"
}
```
**分页响应结构**
```json
{
"code": 200,
"message": "success",
"data": {
"items": [],
"total": 100,
"page": 1,
"page_size": 20,
"pages": 5
}
}
```
### 1.3 状态码约定
| HTTP状态码 | code | 说明 |
|-----------|------|------|
| 200 | 200 | 成功 |
| 400 | 400 | 请求参数错误 |
| 401 | 401 | 未授权token无效或过期 |
| 403 | 403 | 无权限 |
| 404 | 404 | 资源不存在 |
| 500 | 500 | 服务器错误 |
## 二、数据模型
### 2.1 场景类型枚举 (SceneType)
```typescript
type SceneType = 'phone' | 'face' | 'complaint' | 'after-sales' | 'product-intro'
```
| 值 | 说明 |
|----|------|
| phone | 电话销售 |
| face | 面对面销售 |
| complaint | 客户投诉 |
| after-sales | 售后服务 |
| product-intro | 产品介绍 |
### 2.2 难度等级枚举 (Difficulty)
```typescript
type Difficulty = 'beginner' | 'junior' | 'intermediate' | 'senior' | 'expert'
```
| 值 | 说明 |
|----|------|
| beginner | 入门 |
| junior | 初级 |
| intermediate | 中级 |
| senior | 高级 |
| expert | 专家 |
### 2.3 场景状态枚举 (SceneStatus)
```typescript
type SceneStatus = 'active' | 'inactive'
```
| 值 | 说明 |
|----|------|
| active | 启用 |
| inactive | 禁用 |
### 2.4 陪练场景对象 (PracticeScene)
```typescript
interface PracticeScene {
id: number
name: string
description: string
type: SceneType
difficulty: Difficulty
status: SceneStatus
background: string
ai_role: string
objectives: string[]
keywords: string[]
duration: number
usage_count: number
rating: number
created_by: number
updated_by: number
created_at: string
updated_at: string
}
```
## 三、场景管理接口Manager
### 3.1 获取场景列表
**接口**`GET /api/v1/manager/practice-scenes`
**权限**manager、admin
**请求参数**
| 参数 | 类型 | 必填 | 说明 | 示例 |
|------|------|------|------|------|
| page | number | 否 | 页码默认1 | 1 |
| size | number | 否 | 每页数量默认20 | 20 |
| type | string | 否 | 场景类型筛选 | phone |
| difficulty | string | 否 | 难度筛选 | intermediate |
| status | string | 否 | 状态筛选 | active |
| search | string | 否 | 关键词搜索(名称、描述) | 销售 |
**请求示例**
```http
GET /api/v1/manager/practice-scenes?page=1&size=20&type=phone&difficulty=beginner
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**响应示例**
```json
{
"code": 200,
"message": "success",
"data": {
"items": [
{
"id": 1,
"name": "初次电话拜访客户",
"description": "模拟首次通过电话联系潜在客户的场景",
"type": "phone",
"difficulty": "beginner",
"status": "active",
"background": "你是一名销售专员...",
"ai_role": "AI扮演一位忙碌的采购经理...",
"objectives": ["学会专业的电话开场白", "快速建立信任关系"],
"keywords": ["开场白", "需求挖掘"],
"duration": 10,
"usage_count": 256,
"rating": 4.5,
"created_at": "2024-01-15T10:30:00",
"updated_at": "2024-03-18T15:20:00"
}
],
"total": 15,
"page": 1,
"page_size": 20,
"pages": 1
}
}
```
### 3.2 获取场景详情
**接口**`GET /api/v1/manager/practice-scenes/{id}`
**权限**manager、admin
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | 是 | 场景ID |
**请求示例**
```http
GET /api/v1/manager/practice-scenes/1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**响应示例**
```json
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"name": "初次电话拜访客户",
"description": "模拟首次通过电话联系潜在客户的场景",
"type": "phone",
"difficulty": "beginner",
"status": "active",
"background": "你是一名销售专员,需要通过电话联系一位从未接触过的潜在客户。客户是某公司的采购经理,你需要在短时间内引起他的兴趣。",
"ai_role": "AI扮演一位忙碌且略显不耐烦的采购经理对推销电话比较抵触但如果销售人员能够快速切入他的需求点他会愿意继续交谈。",
"objectives": ["学会专业的电话开场白", "快速建立信任关系", "有效探询客户需求", "预约下次沟通时间"],
"keywords": ["开场白", "需求挖掘", "时间管理", "预约技巧"],
"duration": 10,
"usage_count": 256,
"rating": 4.5,
"created_by": 1,
"updated_by": 1,
"created_at": "2024-01-15T10:30:00",
"updated_at": "2024-03-18T15:20:00"
}
}
```
### 3.3 创建场景
**接口**`POST /api/v1/manager/practice-scenes`
**权限**manager、admin
**请求体**
```typescript
interface CreateSceneRequest {
name: string // 必填最长200字符
description: string // 必填,场景描述
type: SceneType // 必填
difficulty: Difficulty // 必填
status?: SceneStatus // 可选默认active
background: string // 必填,场景背景
ai_role: string // 必填AI角色描述
objectives: string[] // 必填,练习目标数组
keywords: string[] // 必填,关键词数组
duration?: number // 可选预计时长分钟默认10
}
```
**请求示例**
```http
POST /api/v1/manager/practice-scenes
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"name": "",
"description": "",
"type": "product-intro",
"difficulty": "junior",
"status": "active",
"background": "",
"ai_role": "AI",
"objectives": ["", "", ""],
"keywords": ["", "", ""],
"duration": 12
}
```
**响应示例**
```json
{
"code": 200,
"message": "创建场景成功",
"data": {
"id": 6,
"name": "产品功能介绍",
"description": "练习向客户详细介绍产品功能特点",
"type": "product-intro",
"difficulty": "junior",
"status": "active",
"background": "客户对你们的产品有一定了解...",
"ai_role": "AI扮演一位专业的采购人员...",
"objectives": ["清晰介绍产品功能", "突出产品优势", "回答技术问题"],
"keywords": ["产品介绍", "功能展示", "优势分析"],
"duration": 12,
"usage_count": 0,
"rating": 0.0,
"created_at": "2025-10-13T18:30:00"
}
}
```
### 3.4 更新场景
**接口**`PUT /api/v1/manager/practice-scenes/{id}`
**权限**manager、admin
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | 是 | 场景ID |
**请求体**:与创建场景相同,所有字段可选
**请求示例**
```http
PUT /api/v1/manager/practice-scenes/6
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"name": "",
"duration": 15,
"status": "active"
}
```
**响应示例**
```json
{
"code": 200,
"message": "更新场景成功",
"data": {
"id": 6,
"name": "产品功能详细介绍",
"duration": 15,
"updated_at": "2025-10-13T19:00:00"
}
}
```
### 3.5 删除场景
**接口**`DELETE /api/v1/manager/practice-scenes/{id}`
**权限**manager、admin
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | 是 | 场景ID |
**请求示例**
```http
DELETE /api/v1/manager/practice-scenes/6
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**响应示例**
```json
{
"code": 200,
"message": "删除场景成功",
"data": {
"id": 6
}
}
```
### 3.6 切换场景状态
**接口**`PUT /api/v1/manager/practice-scenes/{id}/toggle-status`
**权限**manager、admin
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | 是 | 场景ID |
**请求示例**
```http
PUT /api/v1/manager/practice-scenes/6/toggle-status
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**响应示例**
```json
{
"code": 200,
"message": "场景状态已切换",
"data": {
"id": 6,
"status": "inactive"
}
}
```
## 四、学员查询接口Trainee
### 4.1 获取可用场景列表
**接口**`GET /api/v1/practice/scenes`
**权限**trainee、manager、admin
**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | number | 否 | 页码默认1 |
| size | number | 否 | 每页数量默认20 |
| type | string | 否 | 场景类型筛选 |
| difficulty | string | 否 | 难度筛选 |
| search | string | 否 | 关键词搜索 |
**说明**仅返回status=active的场景
**请求示例**
```http
GET /api/v1/practice/scenes?page=1&size=20&type=phone
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**响应示例**:同管理接口的列表响应
### 4.2 获取场景详情
**接口**`GET /api/v1/practice/scenes/{id}`
**权限**trainee、manager、admin
**路径参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | number | 是 | 场景ID |
**请求示例**
```http
GET /api/v1/practice/scenes/1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**响应示例**:同管理接口的详情响应
## 五、陪练对话接口
### 5.1 开始陪练对话
**接口**`POST /api/v1/practice/start`
**权限**trainee、manager、admin
**协议**SSE (Server-Sent Events)
**请求体**
```typescript
interface StartPracticeRequest {
scene_id?: number // 场景ID陪练中心模式可选
scene_name: string // 场景名称(必填)
scene_description?: string // 场景描述(可选)
scene_background: string // 场景背景(必填)
scene_ai_role: string // AI角色必填
scene_objectives: string[] // 练习目标(必填)
scene_keywords?: string[] // 关键词(可选)
user_message: string // 用户消息(必填)
conversation_id?: string // 对话ID续接对话时必填
is_first: boolean // 是否首次消息(必填)
}
```
**⚠️ 重要说明**
- **首次消息 (is_first=true)**后端会将场景信息background, ai_role, objectives等拼接到user_message前面作为完整的系统提示发送给Coze让AI理解角色设定
- **后续消息 (is_first=false)**仅发送user_message不再重复场景信息
**请求示例**
**首次消息请求示例**
```http
POST /api/v1/practice/start
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"scene_id": 1,
"scene_name": "访",
"scene_description": "",
"scene_background": "",
"scene_ai_role": "AI",
"scene_objectives": ["", "", ""],
"scene_keywords": ["", "", ""],
"user_message": "XX",
"is_first": true
}
```
**后端处理**首次消息时后端会将场景信息构建为完整的场景设定文本拼接到user_message前面发送给Coze。
**实际发送给Coze的内容**
```
# 陪练场景设定
## 场景名称
初次电话拜访客户
## 场景背景
你是一名销售专员,需要通过电话联系一位从未接触过的潜在客户...
## AI角色要求
AI扮演一位忙碌且略显不耐烦的采购经理...
## 练习目标
1. 学会专业的电话开场白
2. 快速建立信任关系
3. 有效探询客户需求
## 关键词
开场白, 需求挖掘, 时间管理
---
现在开始陪练对话。请你严格按照上述场景设定扮演角色,与学员进行实战对话练习。
学员的第一句话您好我是XX公司的销售顾问想占用您几分钟时间
```
**后续消息请求示例**
```http
POST /api/v1/practice/start
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"user_message": "",
"conversation_id": "conv_abc123",
"is_first": false
}
```
**后端处理**后续消息直接发送user_message不再拼接场景信息。
**SSE事件格式**
#### 事件1对话创建
```
event: conversation.chat.created
data: {"conversation_id":"conv_abc123","chat_id":"chat_xyz789"}
```
#### 事件2消息增量实时打字
```
event: message.delta
data: {"content":"您"}
event: message.delta
data: {"content":"好"}
event: message.delta
data: {"content":""}
```
#### 事件3消息完成
```
event: message.completed
data: {"content":"您好,我现在很忙,你有什么事吗?"}
```
#### 事件4对话完成
```
event: conversation.completed
data: {"token_count":156}
```
#### 事件5结束标记
```
event: done
data: [DONE]
```
#### 事件6错误
```
event: error
data: {"error":"对话失败: Network Error"}
```
**前端处理示例**
```javascript
const response = await fetch('/api/v1/practice/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(requestData)
})
const reader = response.body.getReader()
const decoder = new TextDecoder()
while (true) {
const { value, done } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
const lines = chunk.split('\n\n')
for (const line of lines) {
if (!line.trim() || !line.startsWith('event: ')) continue
const [eventLine, dataLine] = line.split('\n')
const event = eventLine.replace('event: ', '')
const data = JSON.parse(dataLine.replace('data: ', ''))
switch (event) {
case 'conversation.chat.created':
conversationId.value = data.conversation_id
break
case 'message.delta':
aiMessage.content += data.content
break
case 'message.completed':
console.log('消息完成')
break
case 'conversation.completed':
console.log('Token用量:', data.token_count)
break
case 'error':
ElMessage.error(data.error)
break
}
}
}
```
### 5.2 中断对话
**接口**`POST /api/v1/practice/interrupt`
**权限**trainee、manager、admin
**请求体**
```typescript
interface InterruptPracticeRequest {
conversation_id: string
chat_id: string
}
```
**请求示例**
```http
POST /api/v1/practice/interrupt
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"conversation_id": "conv_abc123",
"chat_id": "chat_xyz789"
}
```
**响应示例**
```json
{
"code": 200,
"message": "对话已中断",
"data": {
"conversation_id": "conv_abc123",
"chat_id": "chat_xyz789"
}
}
```
### 5.3 获取对话列表
**接口**`GET /api/v1/practice/conversations`
**权限**trainee、manager、admin
**请求参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | number | 否 | 页码默认1 |
| size | number | 否 | 每页数量默认20 |
**请求示例**
```http
GET /api/v1/practice/conversations?page=1&size=20
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**响应示例**
```json
{
"code": 200,
"message": "success",
"data": {
"items": [
{
"id": "conv_abc123",
"name": "初次电话拜访客户 - 2025-10-13",
"created_at": 1697184000000
}
],
"has_more": false,
"page": 1,
"size": 20
}
}
```
## 六、Dify场景提取接口
### 6.1 从课程提取场景
**接口**`POST /api/v1/practice/extract-scene`
**权限**trainee、manager、admin
**请求体**
```typescript
interface ExtractSceneRequest {
course_id: number
}
```
**请求示例**
```http
POST /api/v1/practice/extract-scene
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"course_id": 5
}
```
**响应示例**
```json
{
"code": 200,
"message": "场景提取成功",
"data": {
"scene": {
"name": "轻医美产品咨询陪练",
"description": "模拟客户咨询轻医美产品的场景,重点练习产品介绍和价格谈判技巧",
"type": "product-intro",
"difficulty": "intermediate",
"background": "客户是一位30岁女性对面部抗衰项目感兴趣之前了解过玻尿酸填充现在想详细咨询你们的产品和价格。",
"ai_role": "AI扮演一位对轻医美有一定了解的客户关注产品安全性和性价比会提出具体的技术问题和价格异议。",
"objectives": [
"了解客户具体需求和预算",
"专业介绍产品成分和效果",
"处理价格异议,强调价值",
"建立客户信任"
],
"keywords": ["抗衰", "玻尿酸", "价格", "安全性"]
},
"workflow_run_id": "wf_run_abc123",
"task_id": "task_xyz789"
}
}
```
## 七、错误码说明
| code | message | 说明 | 处理建议 |
|------|---------|------|---------|
| 200 | success | 成功 | 正常处理 |
| 400 | 参数错误 | 请求参数不合法 | 检查参数格式 |
| 401 | 未授权 | token无效或过期 | 重新登录 |
| 403 | 无权限 | 当前角色无权限 | 提示用户无权限 |
| 404 | 资源不存在 | 场景不存在 | 返回列表页 |
| 409 | 资源冲突 | 场景名称重复 | 提示修改名称 |
| 500 | 服务器错误 | 内部错误 | 提示稍后重试 |
| 503 | 服务不可用 | Coze/Dify服务异常 | 提示稍后重试 |
## 八、附录
### 8.1 完整TypeScript类型定义
```typescript
// 场景类型
type SceneType = 'phone' | 'face' | 'complaint' | 'after-sales' | 'product-intro'
type Difficulty = 'beginner' | 'junior' | 'intermediate' | 'senior' | 'expert'
type SceneStatus = 'active' | 'inactive'
// 场景对象
interface PracticeScene {
id: number
name: string
description: string
type: SceneType
difficulty: Difficulty
status: SceneStatus
background: string
ai_role: string
objectives: string[]
keywords: string[]
duration: number
usage_count: number
rating: number
created_by: number
updated_by: number
created_at: string
updated_at: string
}
// API响应
interface ResponseModel<T = any> {
code: number
message: string
data: T
request_id?: string
}
interface PaginatedResponse<T> {
items: T[]
total: number
page: number
page_size: number
pages: number
}
// 请求对象
interface CreateSceneRequest {
name: string
description: string
type: SceneType
difficulty: Difficulty
status?: SceneStatus
background: string
ai_role: string
objectives: string[]
keywords: string[]
duration?: number
}
interface StartPracticeRequest {
scene_id?: number
scene_name: string
scene_background: string
scene_ai_role: string
scene_objectives: string[]
user_message: string
conversation_id?: string
is_first: boolean
}
interface ExtractSceneRequest {
course_id: number
}
// SSE事件
interface SSEEvent {
event: 'conversation.chat.created' | 'message.delta' | 'message.completed' | 'conversation.completed' | 'error' | 'done'
data: any
}
```
### 8.2 API调用示例完整
```javascript
// src/api/practice.ts
import request from '@/utils/request'
export const practiceApi = {
// 获取场景列表
getScenes(params) {
return request.get('/api/v1/practice/scenes', { params })
},
// 获取场景详情
getSceneDetail(id) {
return request.get(`/api/v1/practice/scenes/${id}`)
},
// 开始陪练SSE流式
startPractice(data) {
// SSE需要特殊处理不能用普通的request
return fetch(`${import.meta.env.VITE_API_BASE_URL}/api/v1/practice/start`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getToken()}`
},
body: JSON.stringify(data)
})
},
// 中断对话
interruptPractice(data) {
return request.post('/api/v1/practice/interrupt', data)
},
// 获取对话列表
getConversations(params) {
return request.get('/api/v1/practice/conversations', { params })
},
// 提取场景
extractScene(data) {
return request.post('/api/v1/practice/extract-scene', data)
}
}
// 管理接口
export const practiceManagerApi = {
// 获取场景列表
getScenes(params) {
return request.get('/api/v1/manager/practice-scenes', { params })
},
// 创建场景
createScene(data) {
return request.post('/api/v1/manager/practice-scenes', data)
},
// 更新场景
updateScene(id, data) {
return request.put(`/api/v1/manager/practice-scenes/${id}`, data)
},
// 删除场景
deleteScene(id) {
return request.delete(`/api/v1/manager/practice-scenes/${id}`)
},
// 切换状态
toggleStatus(id) {
return request.put(`/api/v1/manager/practice-scenes/${id}/toggle-status`)
}
}
```
---
**文档版本**v1.0
**最后更新**2025-10-13
**维护人**:考培练系统开发团队