当前位置:   article > 正文

在顶顶通呼叫中心中间件(mod_cti基于FreeSWITCH)-与讯飞星火知识大模型对接 实现机器人问答功能_freeswitch cti java开发

freeswitch cti java开发

目录

1.准备工作

2.java 后端接口说明

                 1.项目说明

                 2.项目结构

                 3.项目代码

3 在ccadmin 里面配置 

4.在sipphone 配置测试 

5.点击呼叫可向机器人提问啦


  实现目标 :希望能够以语音的形式向提问机器人提出问题,机器人语音回答提的问题

1.准备工作

       1.安装了FreeSWITCH 
       2.安装了(mod_cti基于FreeSWITCH)-语音识别(asr)接口 
       3.下载ccAdminsipphone(方便测试)
       4.申请了免费的星火大模型套餐,获取到相关key 和相关信息,代码里面要填写的

2.java 后端接口说明

        1.项目说明

                这个项目是使用java 代码实现与讯飞大模型对接,实现机器人问答功能。

        2.项目结构

                SpringBoot Demo 项目的结构:

                        

        3.项目代码

                项目代码已上传至github:GitHub - ddtxu/ddtxf

                Maven 依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-configuration-processor</artifactId>
  4. <optional>true</optional>
  5. </dependency>
  6. <dependency>
  7. <groupId>cn.hutool</groupId>
  8. <artifactId>hutool-all</artifactId>
  9. <version>5.8.18</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-starter-web</artifactId>
  14. </dependency>
  15. <dependency>
  16. <groupId>com.alibaba</groupId>
  17. <artifactId>fastjson</artifactId>
  18. <version>1.2.67</version>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.java-websocket</groupId>
  22. <artifactId>Java-WebSocket</artifactId>
  23. <version>1.3.8</version>
  24. </dependency>
  25. <dependency>
  26. <groupId>com.squareup.okhttp3</groupId>
  27. <artifactId>okhttp</artifactId>
  28. <version>4.10.0</version>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.projectlombok</groupId>
  32. <artifactId>lombok</artifactId>
  33. </dependency>
  34. <dependency>
  35. <groupId>cn.hutool</groupId>
  36. <artifactId>hutool-all</artifactId>
  37. <version>5.8.18</version>
  38. <scope>compile</scope>
  39. </dependency>
 application.yml 
  1. xfxh:
  2. # 服务引擎使用 讯飞星火认知大模型V2.0,如果使用 V1.5 需要将 hostUrl 修改为 https://spark-api.xf-yun.com/v1.1/chat
  3. hostUrl: https://spark-api.xf-yun.com/v2.1/chat
  4. # 发送请求时指定的访问领域,如果是 V1.5版本 设置为 general,如果是 V2版本 设置为 generalv2
  5. domain: generalv2
  6. # 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。取值 [0,1]
  7. temperature: 0.5
  8. # 模型回答的tokens的最大长度,V1.5取值为[1,4096],V2.0取值为[1,8192]。
  9. maxTokens: 2048
  10. # 大模型回复问题的最大响应时长,单位 s
  11. maxResponseTime: 30
  12. # 允许同时连接大模型的 websocket 数,如果是普通(免费)用户为 2,超过这个数连接响应会报错,具体参考官网。
  13. QPS: 2
  14. # 用于权限验证,从服务接口认证信息中获取
  15. appId:
  16. # 用于权限验证,从服务接口认证信息中获取
  17. apiSecret:
  18. # 用于权限验证,从服务接口认证信息中获取
  19. apiKey:
  20. server:
  21. port: 8013
config 包
  1. @Configuration
  2. @ConfigurationProperties(prefix = "xfxh")
  3. @Data
  4. public class XfXhConfig {
  5. /**
  6. * 服务引擎使用 讯飞星火认知大模型V2.0,如果使用 V1.5 需要将 hostUrl 修改为 https://spark-api.xf-yun.com/v1.1/chat
  7. */
  8. private String hostUrl;
  9. /**
  10. * 发送请求时指定的访问领域,如果是 V1.5版本 设置为 general,如果是 V2版本 设置为 generalv2
  11. */
  12. private String domain;
  13. /**
  14. * 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高。取值 [0,1]
  15. */
  16. private Float temperature;
  17. /**
  18. * 模型回答的tokens的最大长度,V1.5取值为[1,4096],V2.0取值为[1,8192]。
  19. */
  20. private Integer maxTokens;
  21. /**
  22. * 大模型回复问题的最大响应时长,单位 s
  23. */
  24. private Integer maxResponseTime;
  25. /**
  26. * 用于权限验证,从服务接口认证信息中获取
  27. */
  28. private String appId;
  29. /**
  30. * 用于权限验证,从服务接口认证信息中获取
  31. */
  32. private String apiKey;
  33. /**
  34. * 用于权限验证,从服务接口认证信息中获取
  35. */
  36. private String apiSecret;
  37. }
DTO 包  

        MsgDTO 

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @JsonInclude(JsonInclude.Include.NON_NULL)
  5. public class MsgDTO {
  6. /**
  7. * 角色
  8. */
  9. private String role;
  10. /**
  11. * 消息内容
  12. */
  13. private String content;
  14. /**
  15. * 响应结果字段:结果序号,取值为[0,10]; 当前为保留字段,开发者可忽略
  16. */
  17. private Integer index;
  18. public static final String ROLE_USER = "user";
  19. public static final String ROLE_ASSISTANT = "assistant";
  20. public static MsgDTO createUserMsg(String content) {
  21. return new MsgDTO(ROLE_USER, content, null);
  22. }
  23. public static MsgDTO createAssistantMsg(String content) {
  24. return new MsgDTO(ROLE_ASSISTANT, content, null);
  25. }
  26. }
  1. @NoArgsConstructor
  2. @Data
  3. public class ResponseDTO {
  4. @JsonProperty("header")
  5. private HeaderDTO header;
  6. @JsonProperty("payload")
  7. private PayloadDTO payload;
  8. @NoArgsConstructor
  9. @Data
  10. public static class HeaderDTO {
  11. /**
  12. * 错误码,0表示正常,非0表示出错
  13. */
  14. @JsonProperty("code")
  15. private Integer code;
  16. /**
  17. * 会话是否成功的描述信息
  18. */
  19. @JsonProperty("message")
  20. private String message;
  21. /**
  22. * 会话的唯一id,用于讯飞技术人员查询服务端会话日志使用,出现调用错误时建议留存该字段
  23. */
  24. @JsonProperty("sid")
  25. private String sid;
  26. /**
  27. * 会话状态,取值为[0,1,2];0代表首次结果;1代表中间结果;2代表最后一个结果
  28. */
  29. @JsonProperty("status")
  30. private Integer status;
  31. }
  32. @NoArgsConstructor
  33. @Data
  34. public static class PayloadDTO {
  35. @JsonProperty("choices")
  36. private ChoicesDTO choices;
  37. /**
  38. * 在最后一次结果返回
  39. */
  40. @JsonProperty("usage")
  41. private UsageDTO usage;
  42. @NoArgsConstructor
  43. @Data
  44. public static class ChoicesDTO {
  45. /**
  46. * 文本响应状态,取值为[0,1,2]; 0代表首个文本结果;1代表中间文本结果;2代表最后一个文本结果
  47. */
  48. @JsonProperty("status")
  49. private Integer status;
  50. /**
  51. * 返回的数据序号,取值为[0,9999999]
  52. */
  53. @JsonProperty("seq")
  54. private Integer seq;
  55. /**
  56. * 响应文本
  57. */
  58. @JsonProperty("text")
  59. private List<MsgDTO> text;
  60. }
  61. @NoArgsConstructor
  62. @Data
  63. public static class UsageDTO {
  64. @JsonProperty("text")
  65. private TextDTO text;
  66. @NoArgsConstructor
  67. @Data
  68. public static class TextDTO {
  69. /**
  70. * 保留字段,可忽略
  71. */
  72. @JsonProperty("question_tokens")
  73. private Integer questionTokens;
  74. /**
  75. * 包含历史问题的总tokens大小
  76. */
  77. @JsonProperty("prompt_tokens")
  78. private Integer promptTokens;
  79. /**
  80. * 回答的tokens大小
  81. */
  82. @JsonProperty("completion_tokens")
  83. private Integer completionTokens;
  84. /**
  85. * prompt_tokens和completion_tokens的和,也是本次交互计费的tokens大小
  86. */
  87. @JsonProperty("total_tokens")
  88. private Integer totalTokens;
  89. }
  90. }
  91. }
  92. }
  1. @NoArgsConstructor
  2. @Data
  3. public class RequestDTO {
  4. @JsonProperty("header")
  5. private HeaderDTO header;
  6. @JsonProperty("parameter")
  7. private ParameterDTO parameter;
  8. @JsonProperty("payload")
  9. private PayloadDTO payload;
  10. @NoArgsConstructor
  11. @Data
  12. @AllArgsConstructor
  13. public static class HeaderDTO {
  14. /**
  15. * 应用appid,从开放平台控制台创建的应用中获取
  16. */
  17. @JSONField(name = "app_id")
  18. private String appId;
  19. /**
  20. * 每个用户的id,用于区分不同用户,最大长度32
  21. */
  22. @JSONField(name = "uid")
  23. private String uid;
  24. }
  25. @NoArgsConstructor
  26. @Data
  27. @AllArgsConstructor
  28. public static class ParameterDTO {
  29. private ChatDTO chat;
  30. @NoArgsConstructor
  31. @Data
  32. @AllArgsConstructor
  33. public static class ChatDTO {
  34. /**
  35. * 指定访问的领域,general指向V1.5版本 generalv2指向V2版本。注意:不同的取值对应的url也不一样!
  36. */
  37. @JsonProperty("domain")
  38. private String domain;
  39. /**
  40. * 核采样阈值。用于决定结果随机性,取值越高随机性越强即相同的问题得到的不同答案的可能性越高
  41. */
  42. @JsonProperty("temperature")
  43. private Float temperature;
  44. /**
  45. * 模型回答的tokens的最大长度
  46. */
  47. @JSONField(name = "max_tokens")
  48. private Integer maxTokens;
  49. }
  50. }
  51. @NoArgsConstructor
  52. @Data
  53. @AllArgsConstructor
  54. public static class PayloadDTO {
  55. @JsonProperty("message")
  56. private MessageDTO message;
  57. @NoArgsConstructor
  58. @Data
  59. @AllArgsConstructor
  60. public static class MessageDTO {
  61. @JsonProperty("text")
  62. private List<MsgDTO> text;
  63. }
  64. }
  65. }
listener 包 
XfXhWebSocketListener
  1. @Slf4j
  2. public class XfXhWebSocketListener extends WebSocketListener {
  3. private StringBuilder answer = new StringBuilder();
  4. private boolean wsCloseFlag = false;
  5. public StringBuilder getAnswer() {
  6. return answer;
  7. }
  8. public boolean isWsCloseFlag() {
  9. return wsCloseFlag;
  10. }
  11. @Override
  12. public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
  13. super.onOpen(webSocket, response);
  14. }
  15. @Override
  16. public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
  17. super.onMessage(webSocket, text);
  18. // 将大模型回复的 JSON 文本转为 ResponseDTO 对象
  19. ResponseDTO responseData = JSONObject.parseObject(text, ResponseDTO.class);
  20. // 如果响应数据中的 header 的 code 值不为 0,则表示响应错误
  21. if (responseData.getHeader().getCode() != 0) {
  22. // 日志记录
  23. log.error("发生错误,错误码为:" + responseData.getHeader().getCode() + "; " + "信息:" + responseData.getHeader().getMessage());
  24. // 设置回答
  25. this.answer = new StringBuilder("大模型响应错误,请稍后再试");
  26. // 关闭连接标识
  27. wsCloseFlag = true;
  28. return;
  29. }
  30. // 将回答进行拼接
  31. for (MsgDTO msgDTO : responseData.getPayload().getChoices().getText()) {
  32. this.answer.append(msgDTO.getContent());
  33. }
  34. // 对最后一个文本结果进行处理
  35. if (2 == responseData.getHeader().getStatus()) {
  36. wsCloseFlag = true;
  37. }
  38. }
  39. @Override
  40. public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) {
  41. super.onFailure(webSocket, t, response);
  42. }
  43. @Override
  44. public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
  45. super.onClosed(webSocket, code, reason);
  46. }
  47. }
component 包

        XfXhStreamClient

  1. @Component
  2. @Slf4j
  3. public class XfXhStreamClient {
  4. @Resource
  5. private XfXhConfig xfXhConfig;
  6. @Value("${xfxh.QPS}")
  7. private int connectionTokenCount;
  8. /**
  9. * 获取令牌
  10. */
  11. public static int GET_TOKEN_STATUS = 0;
  12. /**
  13. * 归还令牌
  14. */
  15. public static int BACK_TOKEN_STATUS = 1;
  16. /**
  17. * 操作令牌
  18. *
  19. * @param status 0-获取令牌 1-归还令牌
  20. * @return 是否操作成功
  21. */
  22. public synchronized boolean operateToken(int status) {
  23. if (status == GET_TOKEN_STATUS) {
  24. // 获取令牌
  25. if (connectionTokenCount != 0) {
  26. // 说明还有令牌,将令牌数减一
  27. connectionTokenCount -= 1;
  28. return true;
  29. } else {
  30. return false;
  31. }
  32. } else {
  33. // 放回令牌
  34. connectionTokenCount += 1;
  35. return true;
  36. }
  37. }
  38. /**
  39. * 发送消息
  40. *
  41. * @param uid 每个用户的id,用于区分不同用户
  42. * @param msgList 发送给大模型的消息,可以包含上下文内容
  43. * @return 获取websocket连接,以便于我们在获取完整大模型回复后手动关闭连接
  44. */
  45. public WebSocket sendMsg(String uid, List<MsgDTO> msgList, WebSocketListener listener) {
  46. // 获取鉴权url
  47. String authUrl = this.getAuthUrl();
  48. // 鉴权方法生成失败,直接返回 null
  49. if (authUrl == null) {
  50. return null;
  51. }
  52. OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
  53. // 将 https/http 连接替换为 ws/wss 连接
  54. String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");
  55. Request request = new Request.Builder().url(url).build();
  56. // 建立 wss 连接
  57. WebSocket webSocket = okHttpClient.newWebSocket(request, listener);
  58. // 组装请求参数
  59. RequestDTO requestDTO = getRequestParam(uid, msgList);
  60. // 发送请求
  61. webSocket.send(JSONObject.toJSONString(requestDTO));
  62. return webSocket;
  63. }
  64. /**
  65. * 生成鉴权方法,具体实现不用关心,这是讯飞官方定义的鉴权方式
  66. *
  67. * @return 鉴权访问大模型的路径
  68. */
  69. public String getAuthUrl() {
  70. try {
  71. URL url = new URL(xfXhConfig.getHostUrl());
  72. // 时间
  73. SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
  74. format.setTimeZone(TimeZone.getTimeZone("GMT"));
  75. String date = format.format(new Date());
  76. // 拼接
  77. String preStr = "host: " + url.getHost() + "\n" +
  78. "date: " + date + "\n" +
  79. "GET " + url.getPath() + " HTTP/1.1";
  80. // SHA256加密
  81. Mac mac = Mac.getInstance("hmacsha256");
  82. SecretKeySpec spec = new SecretKeySpec(xfXhConfig.getApiSecret().getBytes(StandardCharsets.UTF_8), "hmacsha256");
  83. mac.init(spec);
  84. byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
  85. // Base64加密
  86. String sha = Base64.getEncoder().encodeToString(hexDigits);
  87. // 拼接
  88. String authorizationOrigin = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", xfXhConfig.getApiKey(), "hmac-sha256", "host date request-line", sha);
  89. // 拼接地址
  90. HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().
  91. addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorizationOrigin.getBytes(StandardCharsets.UTF_8))).
  92. addQueryParameter("date", date).
  93. addQueryParameter("host", url.getHost()).
  94. build();
  95. return httpUrl.toString();
  96. } catch (Exception e) {
  97. log.error("鉴权方法中发生错误:" + e.getMessage());
  98. return null;
  99. }
  100. }
  101. /**
  102. * 获取请求参数
  103. *
  104. * @param uid 每个用户的id,用于区分不同用户
  105. * @param msgList 发送给大模型的消息,可以包含上下文内容
  106. * @return 请求DTO,该 DTO 转 json 字符串后生成的格式参考 resources/demo-json/request.json
  107. */
  108. public RequestDTO getRequestParam(String uid, List<MsgDTO> msgList) {
  109. RequestDTO requestDTO = new RequestDTO();
  110. requestDTO.setHeader(new RequestDTO.HeaderDTO(xfXhConfig.getAppId(), uid));
  111. requestDTO.setParameter(new RequestDTO.ParameterDTO(new RequestDTO.ParameterDTO.ChatDTO(xfXhConfig.getDomain(), xfXhConfig.getTemperature(), xfXhConfig.getMaxTokens())));
  112. requestDTO.setPayload(new RequestDTO.PayloadDTO(new RequestDTO.PayloadDTO.MessageDTO(msgList)));
  113. return requestDTO;
  114. }
  115. }
启动类 
  1. @SpringBootApplication
  2. public class XfXhApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(XfXhApplication.class, args);
  5. }
  6. }
DDTestController
  1. @RestController
  2. @RequestMapping("/test")
  3. @Slf4j
  4. public class DDTestController {
  5. @Resource
  6. private XfXhStreamClient xfXhStreamClient;
  7. @Resource
  8. private XfXhConfig xfXhConfig;
  9. @PostMapping("/flow")
  10. public Map<String, Object> flow(@RequestBody Map<String, Object> tokenMap) throws Exception {
  11. String asrAddr = "127.0.0.1:9988";
  12. String ttsAddr = "";//从顶顶通官网获取相关路径
  13. Long timestamp = (Long) tokenMap.get("timestamp");
  14. String method = (String) tokenMap.get("method");
  15. String callid = (String) tokenMap.get("callid");
  16. String appid = (String) tokenMap.get("appid");
  17. Map<String, Object> resultMap = new HashMap<>();
  18. if ("create".equals(method)) {
  19. String callSource = (String) tokenMap.get("call_source");
  20. log.info("callSource ===>{}", callSource);
  21. String sourceName = (String) tokenMap.get("source_name");
  22. log.info("sourceName ===>{}", sourceName);
  23. Map<String, Object> ttsMap = new HashMap<>();
  24. resultMap.put("action", "cti_play_and_detect_speech");
  25. String date = LocalDateTime.now().toString();
  26. resultMap.put("argument", "'1' '64' '0' '0.8' '" + asrAddr + "' '120' '800' '5000' '20000' '' '' '" + appid + "' '1' '" + date + "' 'wav'");
  27. ttsMap.put("ttsurl", ttsAddr);
  28. ttsMap.put("ttsvoicename", "");
  29. ttsMap.put("ttsconfig", "");
  30. ttsMap.put("ttsengine", "");
  31. ttsMap.put("ttsvolume", 0);
  32. ttsMap.put("ttsspeechrate", 0);
  33. ttsMap.put("ttspitchrate", 0);
  34. resultMap.put("tts", ttsMap);
  35. resultMap.put("privatedata", "test");
  36. List<String> list = Arrays.asList("欢迎进入测试程序,被叫号码是", "15307306845", "请继续说话测试吧", "等待音乐.wav");
  37. resultMap.put("playbacks", list);
  38. resultMap.put("sound_file_dir", "/ddt/fs/sounds/cti/acd");
  39. resultMap.put("pre_tts_text", Arrays.asList("徐先生", "2023年10月10日"));
  40. resultMap.put("quickresponse", true);
  41. resultMap.put("log", "create succeed");
  42. } else if ("input".equals(method)) {
  43. String privatedata = (String) tokenMap.get("privatedata");
  44. log.info("privatedata ===>{}", privatedata);
  45. String input_type = (String) tokenMap.get("input_type");
  46. log.info("input_type ===>{}", input_type);
  47. String input_args = (String) tokenMap.get("input_args");
  48. log.info("input_args ===>{}", input_args);
  49. Long input_start_time = (Long) tokenMap.get("input_start_time");
  50. log.info("input_start_time ===>{}", input_start_time);
  51. Integer input_duration = (Integer) tokenMap.get("input_duration");
  52. log.info("input_duration ===>{}", input_duration);
  53. Integer play_progress = (Integer) tokenMap.get("play_progress");
  54. log.info("play_progress ===>{}", play_progress);
  55. if ("complete".equals(input_type)) {
  56. // String inputArgSub = input_args.substring(0, 6);
  57. if (input_args.contains("hangup")) {
  58. resultMap.put("action", "hangup");
  59. resultMap.put("log", "挂机");
  60. } else if (input_args.contains("record")) {
  61. String recordfile = StrUtil.subWithLength(input_args, 7, input_args.length() - 8);
  62. resultMap.put("action", "cti_play_and_detect_speech");
  63. String date = LocalDateTime.now().toString();
  64. resultMap.put("argument", "'1' '1' '0' '0.8' '" + asrAddr + "' '120' '800' '5000' '20000' '' '' " + appid + " '1' '" + date + "' 'wav'");
  65. resultMap.put("privatedata", "test");
  66. resultMap.put("playbacks", Arrays.asList("刚刚的录音内容是", recordfile, "请继续说话,可以说关键词,人工,转接,暂停,停止,分机来测试"));
  67. resultMap.put("quickresponse", true);
  68. resultMap.put("log", "播放录音");
  69. } else {
  70. resultMap.put("action", "cti_play_and_detect_speech");
  71. String date = LocalDateTime.now().toString();
  72. resultMap.put("argument", "'1' '1' '0' '0.8' '" + asrAddr + "' '120' '800' '5000' '20000' '' '' '" + appid + "' '1' '" + date + "' 'wav'");
  73. resultMap.put("privatedata", "test");
  74. resultMap.put("playbacks", Collections.singletonList("动作执行完成,这里必须放音,请继续说话,可以继续提问"));
  75. resultMap.put("quickresponse", true);
  76. resultMap.put("log", "重新开始放音");
  77. }
  78. } else {
  79. String prefix = StrUtil.sub(input_args, 0, 1);
  80. String text = StrUtil.subSuf(input_args, 1);
  81. if ("S".equals(prefix)) {
  82. if (!"stop".equals(privatedata)) {
  83. if (play_progress > 0) {
  84. resultMap.put("commands", Collections.singletonList("uuid_cti_play_and_detect_speech_break_play " + callid));
  85. resultMap.put("privatedata", "stop");
  86. resultMap.put("log", "停止放音,但是不停止ASR识别。模拟关键词打断");
  87. }
  88. }
  89. } else if ("F".equals(prefix)) {
  90. if (text.contains("挂断")) {
  91. resultMap.put("action", "hangup");
  92. resultMap.put("privatedata", "test");
  93. resultMap.put("playbacks", Collections.singletonList("谢谢你的测试,再见"));
  94. resultMap.put("log", "挂机");
  95. }
  96. else{
  97. //根据用户语音提问讯飞大模型
  98. String str = sendQuestion(text);
  99. resultMap.put("action", "cti_play_and_detect_speech");
  100. String date = LocalDateTime.now().toString();
  101. resultMap.put("argument", "'1' '1' '0' '0.8' '" + asrAddr + "' '120' '800' '5000' '20000' '' '' '" + appid + "' '1' '" + date + "' 'wav'");
  102. resultMap.put("privatedata", "test");
  103. //回答的问题转语音
  104. resultMap.put("playbacks", Collections.singletonList(str));
  105. resultMap.put("quickresponse", true);
  106. resultMap.put("log", "播放识别结果");
  107. }
  108. }
  109. if ("D".equals(prefix)) {
  110. resultMap.put("action", "cti_play_and_detect_speech");
  111. String date = LocalDateTime.now().toString();
  112. resultMap.put("argument", "'1' '1' '0' '0.8' '" + asrAddr + "' '120' '800' '10000' '20000' '' '' '" + appid + "' '1' '" + date + "' 'wav'");
  113. resultMap.put("privatedata", "test");
  114. resultMap.put("dtmf_terminators", "#");
  115. resultMap.put("playbacks", Arrays.asList("刚刚的按键内容是", text, "请继续按键测试吧,并以#号键结束"));
  116. resultMap.put("log", "按键识别结果");
  117. } else {
  118. resultMap.put("log", "no processing");
  119. }
  120. }
  121. } else if ("destory".equals(method)) {
  122. resultMap.put("log", "destory succeed");
  123. }
  124. return resultMap;
  125. }
  126. private StringBuilder respons(String str){
  127. str.replaceAll("\\s+", "");
  128. String[] parts = str.split("[,。!]");
  129. StringBuilder sb = new StringBuilder();
  130. for (int i = 0; i < parts.length; i++) {
  131. sb.append("\"").append(parts[i]).append("\"");
  132. if (i != parts.length - 1) {
  133. sb.append(","); }
  134. }
  135. return sb;
  136. }
  137. private String sendQuestion( String question) {
  138. // 如果是无效字符串,则不对大模型进行请求
  139. if (StrUtil.isBlank(question)) {
  140. return "无效问题,请重新输入";
  141. }
  142. // 获取连接令牌
  143. if (!xfXhStreamClient.operateToken(XfXhStreamClient.GET_TOKEN_STATUS)) {
  144. return "当前大模型连接数过多,请稍后再试";
  145. }
  146. // 创建消息对象
  147. MsgDTO msgDTO = MsgDTO.createUserMsg(question);
  148. // 创建监听器
  149. XfXhWebSocketListener listener = new XfXhWebSocketListener();
  150. // 发送问题给大模型,生成 websocket 连接
  151. WebSocket webSocket = xfXhStreamClient.sendMsg(UUID.randomUUID().toString().substring(0, 10), Collections.singletonList(msgDTO), listener);
  152. if (webSocket == null) {
  153. // 归还令牌
  154. xfXhStreamClient.operateToken(XfXhStreamClient.BACK_TOKEN_STATUS);
  155. return "系统内部错误,请联系管理员";
  156. }
  157. try {
  158. int count = 0;
  159. // 为了避免死循环,设置循环次数来定义超时时长
  160. int maxCount = xfXhConfig.getMaxResponseTime() * 5;
  161. while (count <= maxCount) {
  162. Thread.sleep(200);
  163. if (listener.isWsCloseFlag()) {
  164. break;
  165. }
  166. count++;
  167. }
  168. if (count > maxCount) {
  169. return "大模型响应超时,请联系管理员";
  170. }
  171. // 响应大模型的答案
  172. return listener.getAnswer().toString();
  173. } catch (InterruptedException e) {
  174. log.error("错误:" + e.getMessage());
  175. return "系统内部错误,请联系管理员";
  176. } finally {
  177. // 关闭 websocket 连接
  178. webSocket.close(1000, "");
  179. // 归还令牌
  180. xfXhStreamClient.operateToken(XfXhStreamClient.BACK_TOKEN_STATUS);
  181. }
  182. }
  183. }

3 在ccadmin 里面配置 

        基础配置 -> 拨号方案 - >http话术 -> cti_robot  填写项目接口

4.在sipphone 配置测试 

sipphone可在http://www.ddrj.com/sipphone/index.html  下载

5.点击呼叫可向机器人提问啦

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/619274
推荐阅读
相关标签
  

闽ICP备14008679号