在当今互联网时代,GPT、文心一言、通义千问等等模型的不断兴起,互联网可能正进入一个AI时代。本文讲通过一个小案列来讲述我们怎么通过AI给我们的项目、产品等赋能。本文将详细介绍如何使用 React 和 Java 搭建一个小型文本摘要工具,并基于 Hugging Face 提供的 API 来实现智能摘要功能。从功能分析到代码实现,我们将为你展现一个完整的技术实现过程。
我们的目标是构建一个 Web 应用,用户可以通过简单的界面输入文本并快速获取摘要内容。具体功能包括:
前端
后端
1.1 项目结构
我们将采用分层架构,将代码分为以下模块:
目录结构如下:
backend/
├── src/main/java/com/lucifer/summarizer/
│ ├── config/ # 配置类
│ ├── controller/ # 控制器
│ ├── service/ # 服务层
│ ├── client/ # AI服务交互
│ ├── dto/ # 数据传输对象
│ ├── exception/ # 异常处理
│ └── SummarizerApplication.java
└── src/main/resources/
├── application.yml # 配置文件
1.2 核心代码实现
1. 配置文件
application.yml
1 2 3 4 5 6 |
server: port: 8080 ? huggingface: api-key: your-huggingface-api-key model-url: https://api-inference.huggingface.co/models/facebook/bart-large-cnn |
2. 配置类
HuggingFaceConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.lucifer.summarizer.config; ? import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; ? @Configuration @Getter public class HuggingFaceConfig { ? @Value("${huggingface.api-key}") private String apiKey; ? @Value("${huggingface.model-url}") private String modelURL; } ? |
3. 数据传输对象 (DTO)
TextInputDTO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.lucifer.summarizer.dto; ? import jakarta.validation.constraints.NotBlank; import lombok.Data; ? import java.io.Serializable; ? @Data public class TextInputDTO implements Serializable { ? @NotBlank(message = "文本不能为空") private String text; } |
TextOutputDTO.java
1 2 3 4 5 6 7 8 9 10 11 |
package com.lucifer.summarizer.dto; ? import lombok.AllArgsConstructor; import lombok.Data; ? @Data @AllArgsConstructor public class TextOutputDTO implements Serializable { ? private String summary; } |
4. 客户端
HuggingFaceClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.lucifer.summarizer.client; ? import com.lucifer.summarizer.config.HuggingFaceConfig; import lombok.AllArgsConstructor; import okhttp3.*; import org.springframework.stereotype.Component; ? import java.io.IOException; ? @Component @AllArgsConstructor public class HuggingFaceClient { ? private final HuggingFaceConfig config; ? public String getSummary(String text) throws IOException { OkHttpClient client = new OkHttpClient(); ? RequestBody body = RequestBody.create( "{"inputs":"" + text + ""}", MediaType.get("application/json") ); Request request = new Request.Builder() .url(config.getModelURL()) .addHeader("Authorization", "Bearer " + config.getApiKey()) .post(body) .build(); ? try (Response response = client.newCall(request).execute()) { if (response.isSuccessful()) { assert response.body() != null; return response.body().string(); } else { throw new IOException("Failed to get summary: " + response.message()); } } } } ? |
5. 服务层
SummarizerService.java
1 2 3 4 5 6 |
package com.lucifer.summarizer.service; ? public interface SummarizerService { ? String summarizeText(String text); } |
SummarizerServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.lucifer.summarizer.service.impl; ? import com.lucifer.summarizer.client.HuggingFaceClient; import com.lucifer.summarizer.service.SummarizerService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; ? import java.io.IOException; ? @Service @AllArgsConstructor public class SummarizerServiceImpl implements SummarizerService { ? private final HuggingFaceClient client; ? @Override public String summarizeText(String text) { try { return client.getSummary(text); } catch (IOException e) { throw new RuntimeException("Error while summarizing text", e); } } } |
6. 控制器
SummarizerController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.lucifer.summarizer.controller; ? import com.lucifer.summarizer.dto.TextInputDTO; import com.lucifer.summarizer.dto.TextOutputDTO; import com.lucifer.summarizer.service.SummarizerService; import lombok.AllArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import jakarta.validation.Valid; ? @RestController @AllArgsConstructor @RequestMapping("/api/v1") public class SummarizerController { ? private final SummarizerService summarizerService; ? @PostMapping("/summarizer") public ResponseEntity<TextOutputDTO> summarize(@Valid @RequestBody TextInputDTO input) { String summary = summarizerService.summarizeText(input.getText()); return ResponseEntity.ok(new TextOutputDTO(summary)); } } |
7. 异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package com.lucifer.summarizer.exception; ? import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; ? @ControllerAdvice public class GlobalExceptionHandler { ? @ExceptionHandler(RuntimeException.class) public ResponseEntity<String> handleRuntimeException(RuntimeException ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error: " + ex.getMessage()); } ? @ExceptionHandler(Exception.class) public ResponseEntity<String> handleException(Exception ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Error: " + ex.getMessage()); } } |
8. 服务启动
9. 接口测试
2.1 初始化 React 项目
使用 create-react-app 初始化项目:
1 2 |
npx create-react-app text-summary-app cd text-summary-app |
安装 Axios:
1 |
npm install axios |
2.2 创建主界面组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import React, { useState } from "react"; import axios from "axios"; ? const TextSummary = () => { const [text, setText] = useState(""); const [summary, setSummary] = useState(""); const [loading, setLoading] = useState(false); ? const handleSubmit = async () => { if (text.trim().length < 10) { alert("请输入至少10个字符的文本!"); return; } ? setLoading(true); try { const response = await axios.post("/api/v1/summarizer", { text }); setSummary(response.data); } catch (error) { alert("请求失败,请稍后重试!"); } finally { setLoading(false); } }; ? return ( <div style={{ padding: "20px", fontFamily: "Arial, sans-serif" }}> <h1>AI 文本摘要工具</h1> <textarea style={{ width: "100%", height: "150px", marginBottom: "20px" }} value={text} onChange={(e) => setText(e.target.value)} placeholder="输入需要摘要的文本..." ></textarea> <button onClick={handleSubmit} disabled={loading}> {loading ? "处理中..." : "生成摘要"} </button> {summary && ( <div style={{ marginTop: "20px", padding: "10px", border: "1px solid #ccc" }}> <h2>摘要结果:</h2> <p>{summary}</p> </div> )} </div> ); }; ? export default TextSummary; |
2.3 配置代理(开发环境)
在 package.json 中添加代理配置:
1 |
"proxy": "http://localhost:9527" |
1.用户体验:
2.性能优化:
3.错误处理:
4.扩展功能: