当前位置: 首页 > news >正文

文澜知微开发日志(三)——DeepSeek AI 大模型接入

一、完成的工作项

序号工作项状态说明
1DeepSeek API 对接✅ 完成后端封装 API 调用,支持多轮对话
2通古AI API 对接(预留)✅ 完成古文专精模型接入
3文心一言 API 接入(预留)✅ 完成接口框架已搭建,待后续完善
4AI 配置管理✅ 完成通过 application.yml 管理多模型配置
5AI 对话接口✅ 完成实现 /api/ai/chat 和 /api/ai/models 接口
6AI 智读界面✅ 完成独立的 AI 对话页面
7多模型切换✅ 完成支持 DeepSeek/通古AI/文心一言切换
8Markdown 渲染✅ 完成AI 回复支持 Markdown 格式展示
9语音朗读功能✅ 完成浏览器 TTS 实现 AI 回复朗读

二、具体工作内容

1. 后端开发

功能说明
配置类创建AIConfig.java,使用@ConfigurationProperties读取多模型配置
服务层创建AIService.java,封装 DeepSeek/通古AI 的 API 调用逻辑
控制器层创建AIController.java,实现/api/ai/chat/api/ai/models接口
系统提示词分别为 DeepSeek 和通古AI 设计专属的系统提示词

2. 前端开发

模块文件功能说明
API 服务services/aiServ.ts封装 AI 对话的 API 请求方法
AI 智读页面views/ai/AIChat.vue独立的 AI 对话界面
路由配置router/index.ts添加/AIChat路由
菜单配置数据库menu添加「AI智读」一级菜单

三、技术实现详解

1. 整体架构

┌─────────────────────────────────────────────────────────────┐ │ 前端 (Vue3) │ ├─────────────────────────────────────────────────────────────┤ │ AIChat.vue │ │ - 模型选择栏(DeepSeek/通古AI/文心一言) │ │ - 对话消息列表 │ │ - 推荐问题 /自定义输入 │ │ - Markdown 渲染 + 语音朗读 │ └───────────────────────────┬─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 后端 (Spring Boot) │ ├─────────────────────────────────────────────────────────────┤ │ AIController.java │ AIService.java │ AIConfig.java │ │ - /api/ai/chat │ - 模型路由分发 │ - 多模型配置管理 │ │ - /api/ai/models │ - HTTP 请求封装 │ │ └─────────┬───────────┴────────┬─────────┴────────────────────┘ │ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ DeepSeek API │ │ 通古AI API │ │ 文心一言 API │ │ (通用大模型) │ │ (预留接口) │ │ (预留接口) │ └─────────────────┘ └─────────────────┘ └─────────────────┘

2. 多模型配置管理

(1)配置文件

application.yml中配置多个模型的参数:

ai: deepseek: api-key: sk-eeb4972c394d41e7a84b934ec966f78c api-url: https://api.deepseek.com/v1/chat/completions model: deepseek-chat tonggu: api-key: your-tonggu-api-key-here api-url: https://api.tongguai.com/v1/chat/completions model: tonggu-classical
(2)配置类实现 newConfig/AiConfig.java
@Component @ConfigurationProperties(prefix = "ai") public class AIConfig { private DeepSeek deepseek = new DeepSeek(); private Tonggu tonggu = new Tonggu(); public static class DeepSeek { private String apiKey; private String apiUrl; private String model; // getters and setters } public static class Tonggu { private String apiKey; private String apiUrl; private String model; // getters and setters } // getters and setters }

3. API调用实现

调用deepseek api的官方文档示例:

由于官方未提供 Java SDK,我们参考 Python 示例,自行封装了 Java 版本的 API 调用工具类AIService.java,核心逻辑如下:

官方 Python 示例我们的 Java 实现
requests.post()CloseableHttpClient.execute(HttpPost)
json=dataStringEntity(JSON.toJSONString(requestBody))
headers={"Authorization": "Bearer xxx"}httpPost.setHeader("Authorization", "Bearer " + apiKey)
response.json()JSON.parseObject(responseBody)
  1. 使用CloseableHttpClient发起 POST 请求

  2. 设置Content-TypeAuthorization请求头

  3. 构建符合 OpenAI 格式的 JSON 请求体

  4. 解析返回的 JSON 响应,提取choices[0].message.content

具体代码实现:

@Service public class AIService { @Autowired private AIConfig aiConfig; /** * 执行 HTTP 请求 */ private String executeHttpRequest(String url, String apiKey, String requestBody) throws IOException { // 1. 创建 HTTP 客户端 try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // 2. 构建 POST 请求 HttpPost httpPost = new HttpPost(url); // 3. 设置请求头 httpPost.setHeader("Content-Type", "application/json"); httpPost.setHeader("Authorization", "Bearer " + apiKey); // 4. 设置请求体 httpPost.setEntity(new StringEntity(requestBody, "UTF-8")); // 5. 发送请求并获取响应 try (CloseableHttpResponse response = httpClient.execute(httpPost)) { String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8"); // 6. 解析响应 return parseResponse(responseBody); } } } /** * 解析 API 响应 * 对应 Python 的 response.json()["choices"][0]["message"]["content"] */ private String parseResponse(String responseBody) { JSONObject json = JSON.parseObject(responseBody); JSONArray choices = json.getJSONArray("choices"); if (choices != null && !choices.isEmpty()) { JSONObject choice = choices.getJSONObject(0); return choice.getJSONObject("message").getString("content"); } return "AI 响应格式异常"; } }

3. DeepSeek API 接入

(1)获取 API Key
  1. 访问 DeepSeek 开放平台

  2. 注册账号并登录

  3. 进入「API Keys」页面,创建新的 API Key

  4. 复制保存 API Key(格式:sk-xxxxxxxxxxxxxxxx

(2)API 接口说明

DeepSeek 的对话接口与 OpenAI 兼容:

  • 接口地址https://api.deepseek.com/v1/chat/completions

  • 请求方式:POST

  • 认证方式:Bearer Token

请求体格式

json

{ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "你是一位古籍助手"}, {"role": "user", "content": "学而时习之是什么意思?"} ], "temperature": 0.7, "max_tokens": 2000 }

响应体格式

json

{ "id": "xxx", "choices": [ { "message": { "role": "assistant", "content": "学而时习之的意思是..." } } ] }
(3)服务层实现 newService/AIService.java
@Service public class AIService { @Autowired private AIConfig aiConfig; /** * 调用AI模型 */ public String chat(String model, String message, List<Map<String, String>> history) throws IOException { switch (model) { case "deepseek": return callDeepSeek(message, history); case "tonggu": return callTonggu(message, history); case "wenxin": return callWenxin(message, history); default: return "暂不支持该模型"; } } /** * 调用DeepSeek API */ private String callDeepSeek(String message, List<Map<String, String>> history) throws IOException { // 构建消息列表 JSONArray messages = new JSONArray(); // 添加系统提示 JSONObject systemMsg = new JSONObject(); systemMsg.put("role", "system"); systemMsg.put("content", getSystemPrompt()); messages.add(systemMsg); // 添加历史对话 - 增加空值检查 if (history != null) { for (Map<String, String> hist : history) { // 确保 content 不为 null String content = hist.get("content"); if (content == null) { content = ""; // 将 null 转为空字符串 } JSONObject histMsg = new JSONObject(); histMsg.put("role", hist.get("role")); histMsg.put("content", content); messages.add(histMsg); } } // 添加当前消息 - 确保 message 不为空 JSONObject userMsg = new JSONObject(); userMsg.put("role", "user"); userMsg.put("content", message == null ? "" : message); messages.add(userMsg); // 打印调试信息,查看消息内容 System.out.println("Messages to DeepSeek: " + messages.toJSONString()); // 构建请求体 JSONObject requestBody = new JSONObject(); requestBody.put("model", aiConfig.getDeepseek().getModel()); requestBody.put("messages", messages); requestBody.put("temperature", 0.7); requestBody.put("max_tokens", 2000); // 发送请求 return executeHttpRequest(aiConfig.getDeepseek().getApiUrl(), aiConfig.getDeepseek().getApiKey(), requestBody.toJSONString()); } /** * 系统提示词(通用) */ private String getSystemPrompt() { return "你是一位古籍智慧问答助手,专门解答关于中国古典文献、古文翻译、历史典故、诗词赏析等方面的问题。" + "请用中文回答,语言简洁明了,对于古文问题可以适当引用原文。"; } }

服务层设计

AIService作为 AI 功能的核心服务类,采用策略模式实现多模型路由。通过switch语句根据前端传入的模型标识,分发到对应的 API 调用方法。这种设计使得新增模型时只需添加新的分支,不影响现有逻辑。

在调用 DeepSeek API 时,构建符合 OpenAI 格式的消息列表,依次添加系统提示词、历史对话和当前消息。系统提示词预设了古籍专家的角色,确保 AI 回答的专业性。多处空值检查体现了防御性编程思想,调试日志则便于开发阶段的问题排查。

流程:

  1. 用户发送消息
  2. AIController 接收请求
  3. 调用 AIService.chat(model, message, history)
  4. 根据 model 路由到对应方法
  5. 构建消息列表:( 1. 添加 system 提示词 2. 添加 history 历史对话 3. 添加当前 user 消息)
  6. 构建请求体(model + messages + temperature + max_tokens)
  7. 发送 HTTP POST 请求到 DeepSeek API
  8. 解析响应,提取 choices[0].message.content
  9. 返回 AI 回复给前端

5. 控制器层实现 newController/AIController.java

@CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping("/api/ai") public class AIController { @Autowired private AIService aiService; /** * AI对话接口 */ @PostMapping("/chat") public DataResponse chat(@Valid @RequestBody DataRequest dataRequest) { String model = dataRequest.getString("model"); String message = dataRequest.getString("message"); List<Map<String, String>> history = dataRequest.getList("history"); // 防御性检查:确保消息不为空 if (message == null || message.trim().isEmpty()) { return CommonMethod.getReturnMessageError("消息内容不能为空"); } // 修复历史消息:确保角色交替 if (history != null && !history.isEmpty()) { List<Map<String, String>> fixedHistory = new ArrayList<>(); String expectedRole = "user"; for (Map<String, String> msg : history) { String role = msg.get("role"); if (role.equals(expectedRole)) { fixedHistory.add(msg); expectedRole = "assistant".equals(role) ? "user" : "assistant"; } } history = fixedHistory; } try { String reply = aiService.chat(model, message, history); Map<String, Object> result = new HashMap<>(); result.put("content", reply); result.put("model", model); result.put("timestamp", System.currentTimeMillis()); return CommonMethod.getReturnData(result); } catch (Exception e) { e.printStackTrace(); return CommonMethod.getReturnMessageError("AI服务暂时不可用"); } } /** * 获取可用模型列表 */ @PostMapping("/models") public DataResponse getModels() { List<Map<String, String>> models = new ArrayList<>(); Map<String, String> deepseek = new HashMap<>(); deepseek.put("id", "deepseek"); deepseek.put("name", "DeepSeek"); deepseek.put("description", "通用大模型,擅长各类问答"); models.add(deepseek); Map<String, String> tonggu = new HashMap<>(); tonggu.put("id", "tonggu"); tonggu.put("name", "通古AI"); tonggu.put("description", "古文专精模型,擅长古籍解读"); models.add(tonggu); Map<String, String> wenxin = new HashMap<>(); wenxin.put("id", "wenxin"); wenxin.put("name", "文心一言"); wenxin.put("description", "百度大模型,即将接入"); models.add(wenxin); return CommonMethod.getReturnData(models); } }

流程:

  1. 前端发起 POST /api/ai/chat
  2. @CrossOrigin 处理 CORS
  3. @RequestBody 解析 JSON
  4. 参数提取(模型识别-model, 用户当前信息-message, 历史对话-history每个元素包含role和content)
  5. 防御性检查:message 是否为空,避免无效API调用
  6. 历史消息修复:确保role交替,用户会话和ai助手交替会话,只保留符合交替规则的消息,不符合的直接跳过。
  7. 调用服务处aiService.chat()
  8. 返回 JSON 响应给前端

6. Markdown 渲染

在 Web 开发中,Markdown 并不能直接被浏览器识别,需要借助解析器(Parser)将其转换为 HTML,再进行渲染。

本项目使用marked库来解析 Markdown

特点:

  • 高性能,解析速度快
  • 兼容性好,社区活跃
  • API 灵活,可自定义渲染方式

适用场景:

  • 注重性能的应用(如实时预览)
  • 需要快速上手、语法覆盖率较广的项目
npm install marked

在vue中

import { marked } from 'marked'; methods: { renderMarkdown(content: string): string { if (!content) return ''; try { return marked.parse(content, { async: false }) as string; } catch (error) { console.error('Markdown解析失败:', error); return content.replace(/\n/g, '<br>'); } } }
<div class="message-text markdown-body" v-html="renderMarkdown(msg.content)" ></div>

7. 前端 AI 智读界面

(1)页面结构设计

仅作为测试页面,后续将开发AI阅读助手功能

功能点

  • 支持多轮对话,上下文关联

  • AI 回复支持 Markdown 格式(标题、列表、粗体、代码等)

  • 一键复制 AI 回复内容

  • 语音朗读 AI 回复

  • 加载状态提示

(2)语音朗读TTS(Text-to-Speech)
// 语音朗读 speakText(text: string) { if ('speechSynthesis' in window) { const utterance = new SpeechSynthesisUtterance(text); utterance.lang = 'zh-CN'; utterance.rate = 0.9; speechSynthesis.cancel(); speechSynthesis.speak(utterance); } else { console.log('浏览器不支持语音朗读'); } },

SpeechSynthesisUtterance 常用属性

属性说明示例值
lang语言'zh-CN'
rate语速0.9(0.1~10)
pitch音调1.0(0~2)
volume音量0.8(0~1)
voice指定声音通过getVoices()获取

Web Speech API 工作流程

  1. 用户点击「朗读」按钮
  2. 传入文字内容
  3. 创建 SpeechSynthesisUtterance 对象
  4. 配置语言、语速等参数
  5. 调用 speechSynthesis.speak()
  6. 浏览器调用系统 TTS 引擎
  7. 扬声器播放语音

四、下周计划

  1. AI 侧边栏集成:将 AI 功能集成到阅读页侧边栏

  2. 其他大模型接入:接入通古/文心一言

  3. 选中文字提问:阅读页选中文字后一键发送给 AI

http://www.cnnetsun.cn/news/2011819.html

相关文章:

  • 第1集:认识 Claude Code —— AI 编程新纪元
  • 嵌入式系统软件安全挑战与防护技术实践
  • 二极管原理与整流电路设计全解析
  • Phi-3.5-mini-instruct轻量大模型实测:7.6GB显存下支持batch_size=4并发
  • 从《黑客帝国》到你的游戏:用Unity平面反射低成本打造“水洼”与“光洁地板”的沉浸感
  • 别再只用passwd了!手把手教你用PAM配置Linux密码策略(CentOS 7/8实战)
  • 数字信号处理(DSP)基础与实时系统设计实战
  • 深度解析RyTuneX启动故障:5种高效解决方案与原理剖析
  • 终极指南:如何用unnpk工具破解网易游戏NPK资源包
  • Visual Studio 2022里遇到C6262警告别慌,手把手教你三种方法把大数组从栈搬到堆
  • 暗黑破坏神2终极现代化指南:d2dx让经典游戏在宽屏时代重生
  • 别再凭感觉并联电容了!用LTspice仿真带你看懂MLCC与电解电容组合的阻抗坑
  • 5分钟快速上手:Bulk Crap Uninstaller批量卸载工具完全指南
  • PyTorch分布式训练卡住不动?手把手教你排查torch.distributed.launch的5个常见坑
  • 给DIY玩家:如何用GS12170-IBE3芯片,低成本给你的4K显示器加装专业SDI接口?
  • 别再死记硬背了!用这3个真实项目案例(储蓄/订票/监护系统)彻底搞懂软件工程数据流图
  • Gerbv:重塑PCB设计验证流程的开源引擎
  • 如何永久保存微信聊天记录:本地数据备份与分析的完整指南
  • Ubuntu 22.04 LTS 下 RTL8188GU 无线网卡驱动的编译与自动连接配置
  • 保姆级教程:在WinPE命令行下用DISM部署Win11,绕过TPM检查的完整流程与避坑指南
  • WeChatFerry微信机器人:终极免费自动化工具完整指南
  • 别再死记硬背了!一张图看懂机器学习中各种矩阵的关系(SVD、特征分解、正交矩阵)
  • 如何让Windows 11系统重获新生:Win11Debloat深度优化实战指南
  • 不只是科研玩具:手把手教你用PCSE+WOFOST模型进行作物产量预测分析
  • 别再乱找字体了!盘点那些专为OCR识别设计的字体(附FontCreator预览技巧)
  • ActiveMQ管理后台保姆级使用指南:从登录到调优,手把手教你排查消息积压
  • 孩子说话晚、发音不清?别只练嘴!北思则从感统根源同步提升,让娃敢说、会说、说得清~
  • 突破性工业级PCB缺陷检测:DeepPCB数据集的深度解析与实战指南
  • 3分钟快速指南:如何用extract-video-ppt从视频中智能提取PPT演示文稿
  • ComfyUI-SUPIR:为AI图像修复注入工业级超分辨率魔法