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

22 KiB
Raw Blame History

陪练功能API接口规范

一、通用规范

1.1 请求格式

Base URL

  • 开发环境:http://localhost:8000
  • 生产环境:https://aiedu.ireborn.com.cn

请求头

Content-Type: application/json; charset=utf-8
Authorization: Bearer <access_token>

1.2 响应格式

统一响应结构

{
  "code": 200,
  "message": "success",
  "data": {},
  "request_id": "uuid"
}

分页响应结构

{
  "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)

type SceneType = 'phone' | 'face' | 'complaint' | 'after-sales' | 'product-intro'
说明
phone 电话销售
face 面对面销售
complaint 客户投诉
after-sales 售后服务
product-intro 产品介绍

2.2 难度等级枚举 (Difficulty)

type Difficulty = 'beginner' | 'junior' | 'intermediate' | 'senior' | 'expert'
说明
beginner 入门
junior 初级
intermediate 中级
senior 高级
expert 专家

2.3 场景状态枚举 (SceneStatus)

type SceneStatus = 'active' | 'inactive'
说明
active 启用
inactive 禁用

2.4 陪练场景对象 (PracticeScene)

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 关键词搜索(名称、描述) 销售

请求示例

GET /api/v1/manager/practice-scenes?page=1&size=20&type=phone&difficulty=beginner
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

响应示例

{
  "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

请求示例

GET /api/v1/manager/practice-scenes/1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

响应示例

{
  "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

请求体

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
}

请求示例

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
}

响应示例

{
  "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

请求体:与创建场景相同,所有字段可选

请求示例

PUT /api/v1/manager/practice-scenes/6
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "name": "产品功能详细介绍",
  "duration": 15,
  "status": "active"
}

响应示例

{
  "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

请求示例

DELETE /api/v1/manager/practice-scenes/6
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

响应示例

{
  "code": 200,
  "message": "删除场景成功",
  "data": {
    "id": 6
  }
}

3.6 切换场景状态

接口PUT /api/v1/manager/practice-scenes/{id}/toggle-status

权限manager、admin

路径参数

参数 类型 必填 说明
id number 场景ID

请求示例

PUT /api/v1/manager/practice-scenes/6/toggle-status
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

响应示例

{
  "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的场景

请求示例

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

请求示例

GET /api/v1/practice/scenes/1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

响应示例:同管理接口的详情响应

五、陪练对话接口

5.1 开始陪练对话

接口POST /api/v1/practice/start

权限trainee、manager、admin

协议SSE (Server-Sent Events)

请求体

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不再重复场景信息

请求示例 首次消息请求示例

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公司的销售顾问想占用您几分钟时间

后续消息请求示例

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"}

前端处理示例

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

请求体

interface InterruptPracticeRequest {
  conversation_id: string
  chat_id: string
}

请求示例

POST /api/v1/practice/interrupt
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "conversation_id": "conv_abc123",
  "chat_id": "chat_xyz789"
}

响应示例

{
  "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

请求示例

GET /api/v1/practice/conversations?page=1&size=20
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

响应示例

{
  "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

请求体

interface ExtractSceneRequest {
  course_id: number
}

请求示例

POST /api/v1/practice/extract-scene
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "course_id": 5
}

响应示例

{
  "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类型定义

// 场景类型
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调用示例完整

// 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 维护人:考培练系统开发团队