【原】无脑操作:IDEA + maven + SpringAI + 讯飞星火大模型实现简单智能对话

1、实现效果

图片1

 

2、设置pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>3.4.4</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>cn.temptation</groupId>
12     <artifactId>studyAI</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>studyAI</name>
15     <description>studyAI</description>
16     <url/>
17 
18     <properties>
19         <java.version>17</java.version>
20         <spring-ai.version>1.0.3</spring-ai.version>
21     </properties>
22 
23     <dependencyManagement>
24         <dependencies>
25             <dependency>
26                 <groupId>org.springframework.ai</groupId>
27                 <artifactId>spring-ai-bom</artifactId>
28                 <version>1.0.3</version>
29                 <type>pom</type>
30                 <scope>import</scope>
31             </dependency>
32         </dependencies>
33     </dependencyManagement>
34 
35     <dependencies>
36         <!-- Lombok -->
37         <dependency>
38             <groupId>org.projectlombok</groupId>
39             <artifactId>lombok</artifactId>
40             <version>1.18.22</version>
41         </dependency>
42         <!-- web -->
43         <dependency>
44             <groupId>org.springframework.boot</groupId>
45             <artifactId>spring-boot-starter-web</artifactId>
46         </dependency>
47         <!-- thymeleaf -->
48         <dependency>
49             <groupId>org.springframework.boot</groupId>
50             <artifactId>spring-boot-starter-thymeleaf</artifactId>
51         </dependency>
52         <!-- spark4j -->
53         <dependency>
54             <groupId>io.github.briqt</groupId>
55             <artifactId>xunfei-spark4j</artifactId>
56             <version>1.3.0</version>
57         </dependency>
58         <!-- 热部署 -->
59         <dependency>
60             <groupId>org.springframework.boot</groupId>
61             <artifactId>spring-boot-devtools</artifactId>
62             <optional>true</optional>
63         </dependency>
64     </dependencies>
65 
66     <build>
67         <plugins>
68             <plugin>
69                 <groupId>org.springframework.boot</groupId>
70                 <artifactId>spring-boot-maven-plugin</artifactId>
71             </plugin>
72         </plugins>
73     </build>
74 
75 </project>

 

3、设置application.yaml

图片2

图片3

 1 spring:
 2   application:
 3     name: iflytek-ai
 4   thymeleaf:
 5     cache: false
 6 
 7 xunfei:
 8   client:
 9     appId: 从讯飞开放平台上找到自己的应用的APPID
10     apiSecret: 从讯飞开放平台上找到自己的应用的APISecret
11     apiKey: 从讯飞开放平台上找到自己的应用的APIKey
12 
13 server:
14   port: 80

 

4、整个程序结构如下图所示:

图片4

 

5、编写SparkConfig.java配置类

 1 package cn.temptation.config;
 2 
 3 import io.github.briqt.spark4j.SparkClient;
 4 import lombok.Data;
 5 import org.springframework.boot.context.properties.ConfigurationProperties;
 6 import org.springframework.context.annotation.Bean;
 7 import org.springframework.context.annotation.Configuration;
 8 
 9 @Configuration
10 @ConfigurationProperties(prefix = "xunfei.client")
11 @Data
12 public class SparkConfig {
13     private String appid;
14     private String apiSecret;
15     private String apiKey;
16 
17     @Bean
18     public SparkClient sparkClient() {
19         SparkClient sparkClient = new SparkClient();
20         sparkClient.appid = this.appid;
21         sparkClient.apiSecret = this.apiSecret;
22         sparkClient.apiKey = this.apiKey;
23 
24         return sparkClient;
25     }
26 }

 

6、使用xunfei-spark4j时,想使用免费的Spark Lite时,有一个坑,因为SparkApiVersion枚举的结构如下:

 1 public enum SparkApiVersion {
 2     V1_5("v1.1", "https://spark-api.xf-yun.com/v1.1/chat", "general"),
 3     V2_0("v2.1", "https://spark-api.xf-yun.com/v2.1/chat", "generalv2"),
 4     V3_0("v3.1", "https://spark-api.xf-yun.com/v3.1/chat", "generalv3"),
 5     V3_5("v3.5", "https://spark-api.xf-yun.com/v3.5/chat", "generalv3.5"),
 6     V4_0("v4.0", "https://spark-api.xf-yun.com/v4.0/chat", "4.0Ultra");
 7 
 8     private final String version;
 9     private final String url;
10     private final String domain;
11 
12     private SparkApiVersion(String version, String url, String domain) {
13         this.version = version;
14         this.url = url;
15         this.domain = domain;
16     }
17 
18     public String getVersion() {
19         return this.version;
20     }
21 
22     public String getUrl() {
23         return this.url;
24     }
25 
26     public String getDomain() {
27         return this.domain;
28     }
29 }

这里找不到免费的Spark Lite,所以需要自己修改。编写EnumReflectionUtil.java反射工具类。

1 public class EnumReflectionUtil {
2     public static void setEnumField(Enum<?> enumConstant, String fieldName, Object newValue) throws Exception {
3         Field field = enumConstant.getClass().getDeclaredField(fieldName);
4         field.setAccessible(true);
5         field.set(enumConstant, newValue);
6     }
7 }

 

7、编写聊天处理类ChatWithController.java

 1 @Controller
 2 public class ChatWithController {
 3     // 初始化客户端
 4     @Resource
 5     private SparkClient sparkClient;
 6 
 7     // 通过反射类,在使用大模型时,指定使用免费的Spark Lite大模型
 8     @PostConstruct
 9     public void init() throws Exception {
10         // 修改 V1_5 的版本信息
11         EnumReflectionUtil.setEnumField(SparkApiVersion.V1_5, "version", "v1.1");
12         EnumReflectionUtil.setEnumField(SparkApiVersion.V1_5, "url", "https://spark-api.xf-yun.com/v1.1/chat");
13         EnumReflectionUtil.setEnumField(SparkApiVersion.V1_5, "domain", "lite");
14     }
15 
16     // AI预设System角色的条件
17     public static final String PRECONDITION = "你是 iflytek";
18 
19     // 跳转前端页面
20     @RequestMapping("/")
21     public String index() {
22         return "index";
23     }
24 
25     // 和讯飞星火大模型Spark Lite对话
26     @RequestMapping(value = "/chat", produces = "application/json")
27     public ResponseEntity<?> sendHttpToSpark(@RequestBody Map<String, String> map) {
28         // 消息列表
29         List<SparkMessage> messages = new ArrayList<>();
30         // 设置System角色,则使用下句
31 //        messages.add(SparkMessage.systemContent(PRECONDITION));
32         // 获取前端输入的对话内容,设置User角色
33         messages.add(SparkMessage.userContent(map.get("message")));
34 
35         // 构造请求
36         SparkRequest sparkRequest = SparkRequest.builder()
37                 .messages(messages)
38                 .apiVersion(SparkApiVersion.V1_5)
39                 .build();
40 
41         SparkSyncChatResponse chatResponse = sparkClient.chatSync(sparkRequest);
42 
43         String responseContent = chatResponse.getContent();
44 
45         Map<String, Object> response = new HashMap<>();
46         response.put("response", responseContent);
47 
48         return ResponseEntity.ok(response);
49     }
50 }

 

8、编写前端交互页面index.html

  1 <!DOCTYPE html>
  2 <html lang="zh-CN">
  3 <head>
  4     <meta charset="UTF-8">
  5     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6     <title>AI 智能助手</title>
  7     <link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
  8     <style>
  9         :root {
 10             --primary-color: #4a90e2;
 11             --secondary-color: #f5f5f5;
 12             --success-color: #34d399;
 13             --error-color: #ef4444;
 14         }
 15 
 16         * {
 17             box-sizing: border-box;
 18             margin: 0;
 19             padding: 0;
 20         }
 21 
 22         body {
 23             font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
 24             background: #f0f2f5;
 25             min-height: 100vh;
 26         }
 27 
 28         .container {
 29             max-width: 1200px;
 30             margin: 0 auto;
 31             padding: 20px;
 32         }
 33 
 34         .chat-container {
 35             background: white;
 36             border-radius: 12px;
 37             box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 38             height: 80vh;
 39             display: flex;
 40             flex-direction: column;
 41         }
 42 
 43         #chat-box {
 44             flex: 1;
 45             overflow-y: auto;
 46             padding: 20px;
 47             scroll-behavior: smooth;
 48         }
 49 
 50         .message {
 51             display: flex;
 52             gap: 12px;
 53             margin-bottom: 16px;
 54         }
 55 
 56         .message.user {
 57             flex-direction: row-reverse;
 58         }
 59 
 60         .avatar {
 61             width: 40px;
 62             height: 40px;
 63             border-radius: 50%;
 64             background: var(--secondary-color);
 65             display: flex;
 66             align-items: center;
 67             justify-content: center;
 68             flex-shrink: 0;
 69         }
 70 
 71         .message-content {
 72             max-width: 70%;
 73             padding: 12px 16px;
 74             border-radius: 12px;
 75             position: relative;
 76         }
 77 
 78         .user .message-content {
 79             background: var(--primary-color);
 80             color: white;
 81             border-radius: 12px 12px 0 12px;
 82         }
 83 
 84         .bot .message-content {
 85             background: var(--secondary-color);
 86             border-radius: 12px 12px 12px 0;
 87         }
 88 
 89         .input-area {
 90             border-top: 1px solid #eee;
 91             padding: 16px;
 92             display: flex;
 93             gap: 12px;
 94             background: white;
 95         }
 96 
 97         #user-input {
 98             flex: 1;
 99             padding: 12px;
100             border: 1px solid #e2e8f0;
101             border-radius: 8px;
102             font-size: 16px;
103             transition: all 0.3s ease;
104         }
105 
106         #user-input:focus {
107             outline: none;
108             border-color: var(--primary-color);
109             box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2);
110         }
111 
112         button {
113             padding: 12px 24px;
114             background: var(--primary-color);
115             color: white;
116             border: none;
117             border-radius: 8px;
118             cursor: pointer;
119             transition: all 0.2s ease;
120             display: flex;
121             align-items: center;
122             gap: 8px;
123         }
124 
125         button:hover {
126             background: #357abd;
127             transform: translateY(-1px);
128         }
129 
130         button:disabled {
131             background: #94a3b8;
132             cursor: not-allowed;
133         }
134 
135         @media (max-width: 768px) {
136             .container {
137                 padding: 10px;
138             }
139 
140             .message-content {
141                 max-width: 85%;
142             }
143         }
144     </style>
145 </head>
146 <body>
147 <div class="container">
148     <h1 style="margin-bottom: 20px; color: #1e293b;">AI 智能助手 <i class="fas fa-robot"></i></h1>
149 
150     <div class="chat-container">
151         <div id="chat-box">
152             <div class="message bot">
153                 <div class="avatar"><i class="fas fa-robot"></i></div>
154                 <div class="message-content">您好!我是智能助手,可以回答各种问题...</div>
155             </div>
156         </div>
157 
158         <div class="input-area">
159             <input type="text" id="user-input" placeholder="输入消息...">
160             <button onclick="sendMessage()" id="send-btn">
161                 <i class="fas fa-paper-plane"></i> 发送
162             </button>
163         </div>
164     </div>
165 </div>
166 
167 <script>
168     let isSending = false
169 
170     // 消息处理
171     async function sendMessage() {
172         if (isSending) return
173 
174         const input = document.getElementById('user-input')
175         const message = input.value.trim()
176         if (!message) return
177 
178         isSending = true
179         input.disabled = true
180         document.getElementById('send-btn').disabled = true
181 
182         try {
183             appendMessage(message, 'user')
184             input.value = ''
185 
186             const response = await fetch('/chat', {
187                 method: 'POST',
188                 headers: {'Content-Type': 'application/json'},
189                 body: JSON.stringify({message})
190             })
191 
192             const data = await response.json()
193             if (response.ok) {
194                 appendMessage(data.response, 'bot')
195             } else {
196                 appendMessage(`错误:${data.error}`, 'bot')
197             }
198         } catch (error) {
199             appendMessage('网络请求失败', 'bot')
200         } finally {
201             isSending = false
202             input.disabled = false
203             document.getElementById('send-btn').disabled = false
204             input.focus()
205         }
206     }
207 
208     function appendMessage(message, sender) {
209         const chatBox = document.getElementById('chat-box')
210         const isUser = sender === 'user'
211 
212         const messageEl = document.createElement('div')
213         messageEl.className = `message ${isUser ? 'user' : 'bot'}`
214         messageEl.innerHTML = `
215                 <div class="avatar">
216                     ${isUser ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>'}
217                 </div>
218                 <div class="message-content">${message}</div>
219             `
220 
221         chatBox.appendChild(messageEl)
222         chatBox.scrollTop = chatBox.scrollHeight
223     }
224 
225     // 回车发送
226     document.getElementById('user-input').addEventListener('keypress', (e) => {
227         if (e.key === 'Enter' && !e.shiftKey) {
228             e.preventDefault()
229             sendMessage()
230         }
231     })
232 </script>
233 </body>
234 </html>

 

本文编写过程中参考了网络上一些大佬的经验,在此一并感谢。

 抛砖引玉,期待更多人研究讯飞星火大模型...

posted @ 2025-10-24 09:30  {name:"代码屠夫"}  阅读(10)  评论(0)    收藏  举报