文章目录
- [后台文章发布页添加 AI 自动生成摘要功能](#后台文章发布页添加 AI 自动生成摘要功能)
后台文章发布页添加 AI 自动生成摘要功能
之前给博客加了个 AI 智能助手,用户搜索时可以对话。最近又加了个小功能:在后台写文章时,可以一键让 AI 生成文章摘要,不用自己手写了。
需求
后台文章发布页面有个「文章摘要」字段,最多 100 字。之前都是手动写,或者从文章内容里截取前 100 字。现在想加个按钮,点击后 AI 自动读取文章内容,生成一段 100 字左右的摘要。
实现思路
- 后端:新增一个接口,接收文章内容,调用 AI API(非流式,同步返回),生成摘要
- 前端:在「文章摘要」输入框旁边加个「AI 生成」按钮,点击后调用接口,把返回的摘要填入输入框
后端实现
1. AI 服务类添加摘要生成方法
在 AiService.java 中添加一个非流式的方法:
java
/**
* 生成文章摘要(非流式,同步调用)
*
* @param content 文章内容
* @return 100字左右的摘要
*/
public String generateSummary(String content) {
// 截取前 3000 字,避免 token 过长
String truncated = content.length() > 3000 ? content.substring(0, 3000) : content;
JSONArray messagesArray = new JSONArray();
// 系统提示词:要求生成 100 字摘要,不要 Markdown,直接输出文本
JSONObject systemMsg = new JSONObject();
systemMsg.put("role", "system");
systemMsg.put("content", "你是一个文章摘要生成助手。请根据用户提供的文章内容,生成一段100字左右的中文摘要。" +
"要求:简洁明了,提炼核心内容,不要使用 Markdown 格式,不要加任何前缀(如"摘要:"),直接输出摘要文本。");
messagesArray.add(systemMsg);
// 用户消息:文章内容
JSONObject userMsg = new JSONObject();
userMsg.put("role", "user");
userMsg.put("content", "请为以下文章生成100字左右的摘要:\n\n" + truncated);
messagesArray.add(userMsg);
JSONObject requestBody = new JSONObject();
requestBody.put("model", deepSeekProperties.getModel());
requestBody.put("messages", messagesArray);
requestBody.put("stream", false); // 非流式,同步返回
requestBody.put("temperature", 0.3); // 降低随机性,更稳定
requestBody.put("max_tokens", 200); // 最多 200 token,约 100 字
Request request = new Request.Builder()
.url(deepSeekProperties.getApiUrl())
.addHeader("Authorization", "Bearer " + deepSeekProperties.getApiKey())
.addHeader("Content-Type", "application/json")
.post(RequestBody.create(requestBody.toJSONString(), MediaType.parse("application/json")))
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
String body = response.body() != null ? response.body().string() : "";
log.error("AI 摘要生成失败: {} {}", response.code(), body);
throw new RuntimeException("AI 摘要生成失败: " + response.code());
}
String responseBody = response.body().string();
JSONObject jsonResponse = JSON.parseObject(responseBody);
JSONArray choices = jsonResponse.getJSONArray("choices");
if (choices != null && !choices.isEmpty()) {
JSONObject message = choices.getJSONObject(0).getJSONObject("message");
if (message != null) {
String summary = message.getString("content");
// 去除首尾空白,截取前 100 字
if (summary != null) {
summary = summary.trim();
if (summary.length() > 100) {
summary = summary.substring(0, 100);
}
return summary;
}
}
}
return "";
} catch (IOException e) {
log.error("AI 摘要生成异常", e);
throw new RuntimeException("AI 摘要生成异常: " + e.getMessage());
}
}
关键点:
stream: false:非流式调用,同步返回结果temperature: 0.3:降低随机性,摘要更稳定max_tokens: 200:限制输出长度,约 100 字- 截取前 3000 字:避免文章太长导致 token 超限
- 系统提示词明确要求:不要 Markdown,不要前缀,直接输出摘要文本
2. 控制器添加接口
在 AiController.java 中添加:
java
/**
* AI 生成文章摘要
*
* @param body 请求体,包含 content 字段(文章内容)
* @return 100字左右的摘要
*/
@ApiOperation(value = "AI 生成文章摘要")
@PostMapping("/admin/ai/summary")
public Result<String> generateSummary(@RequestBody Map<String, String> body) {
String content = body.get("content");
if (content == null || content.trim().isEmpty()) {
return Result.fail("文章内容不能为空");
}
String summary = aiService.generateSummary(content);
return Result.success(summary);
}
接口放在 /admin/ 路径下,需要登录才能访问(后台管理功能)。
前端实现
1. API 函数
在 api/article/index.ts 中添加:
typescript
/**
* AI 生成文章摘要
* @param content 文章内容
* @returns 摘要文本
*/
export function generateAiSummary(content: string): AxiosPromise<Result<string>> {
return request({
url: "/admin/ai/summary",
method: "post",
timeout: 60000, // AI 调用可能需要较长时间,设置 60 秒超时
data: { content },
});
}
注意设置了 timeout: 60000(60 秒),因为 AI 调用可能需要几秒到十几秒。
2. 页面添加按钮
在 views/blog/article/write.vue 中,找到「文章摘要」的表单项,修改为:
vue
<el-form-item label="文章摘要" prop="articleDesc">
<div style="display: flex; flex-direction: column; gap: 8px;">
<el-input v-model="articleForm.articleDesc" resize="none" maxlength="100"
:autosize="{ minRows: 5, maxRows: 5 }" style="width: 300px" show-word-limit
type="textarea" />
<el-button type="primary" :loading="aiLoading" :icon="MagicStick"
style="width: 120px;" @click="handleAiSummary">
{{ aiLoading ? 'AI生成中...' : 'AI 生成' }}
</el-button>
</div>
</el-form-item>
按钮使用了 Element Plus 的 MagicStick 图标(魔法棒),点击时显示 loading 状态。
3. 添加逻辑
在 script 部分:
typescript
import { generateAiSummary } from "@/api/article";
import { MagicStick } from '@element-plus/icons-vue';
// 添加 loading 状态
const aiLoading = ref(false);
// AI 生成摘要的处理函数
const handleAiSummary = async () => {
const content = articleForm.value.articleContent;
if (!content || content.trim() === '') {
ElMessage.warning('请先编写文章内容');
return;
}
aiLoading.value = true;
try {
const { data } = await generateAiSummary(content);
if (data.flag && data.data) {
articleForm.value.articleDesc = data.data;
ElMessage.success('AI 摘要生成成功');
} else {
ElMessage.error(data.msg || 'AI 摘要生成失败');
}
} catch (e: any) {
ElMessage.error('AI 摘要生成失败,请稍后重试');
} finally {
aiLoading.value = false;
}
};
逻辑:
- 检查文章内容是否为空
- 调用 API,显示 loading
- 成功后把返回的摘要填入
articleForm.articleDesc - 失败显示错误提示
遇到的问题
1. 超时问题
问题:前端请求超时(默认 10 秒),AI 调用可能需要 10-20 秒。
解决 :在 API 函数中设置 timeout: 60000,单个请求覆盖全局超时设置。
2. AI 返回格式问题
问题 :AI 可能返回带 Markdown 格式的文本(如 **摘要:**这是内容),或者带前缀「摘要:」。
解决:在系统提示词中明确要求「不要使用 Markdown 格式,不要加任何前缀,直接输出摘要文本」。后端再截取前 100 字,确保长度符合要求。
3. 文章内容过长
问题:文章可能有几万字,全部传给 AI 会超出 token 限制。
解决:后端截取前 3000 字,足够生成摘要,也避免 token 超限。
使用效果
- 在后台写文章页面,先写好文章内容
- 点击「发布文章」打开弹窗
- 在「文章摘要」下方点击「AI 生成」按钮
- 等待几秒(按钮显示「AI生成中...」)
- AI 自动生成 100 字摘要并填入输入框
- 可以手动修改后再发布
总结
这个功能实现起来不算复杂:
- 后端:新增一个非流式的 AI 调用方法,专门用于生成摘要
- 前端:加个按钮,调用 API,把结果填入输入框
关键是:
- 系统提示词要明确(不要 Markdown,不要前缀)
- 设置合适的
temperature和max_tokens - 前端设置足够的超时时间
- 处理错误情况,给用户友好提示
现在写文章时,摘要可以一键生成,省了不少时间。如果生成的不满意,还可以手动修改。