近年来,随着大型语言模型的快速发展,智能问答系统已经成为企业提升用户体验、降低客服成本的重要工具。本文将介绍如何利用知乎开源的FUST微服务框架和DeepSeek API,搭建一个高性能、可扩展的智能问答系统。
FUST 谐音 Fast,是知乎开发的一款基于Spring Boot的微服务开发框架,旨在帮助开发者快速构建高质量的微服务应用。它集成了主流组件,提供标准化的开发范式,并在知乎的大规模生产环境经受了考验。
我们将通过实现一个名为”Deep QA”的项目,展示如何将FUST框架与DeepSeek API结合,构建一个功能完善的智能问答系统。
二、技术栈概述
2.1 FUST框架
FUST是基于Spring Boot 3.x开发的微服务框架,具有以下优势:
- 开箱即用:集成主流组件,提供标准化的开发范式
- 高性能:基于高性能的Spring Boot 3.x和Armeria构建
- 可观测性:内置完整的监控和追踪方案
- 灵活扩展:提供丰富的扩展点,满足各种定制需求
- 生产验证:在知乎大规模生产环境经受考验
2.2 DeepSeek API
DeepSeek提供了一系列强大的语言模型API,包括:
- deepseek-chat:通用对话模型
- deepseek-coder:代码生成和理解模型
三、项目架构
Deep QA采用经典的多模块微服务架构,主要包含以下两个核心模块:
deep-qa/ |
这种模块化设计有以下优势:
- 关注点分离:API层负责处理HTTP请求和响应,业务层负责核心业务逻辑
- 代码复用:业务逻辑可以被不同的接口层(HTTP、gRPC)复用
- 便于测试:各层可以独立测试
- 维护简单:模块边界清晰,降低了代码耦合度
四、环境准备
在开始项目开发前,需要准备以下环境:
- JDK 17+
- Maven 3.8+
- MySQL 5.7+
- Redis 6.0+
- DeepSeek API密钥
由于FUST框架尚未发布到Maven中央仓库,需要先将其发布到本地Maven仓库:
# 克隆FUST仓库git clone https://github.com/zhihu/fust.gitcd fust# 发布到本地Maven仓库./gradlew publishToMavenLocal
五、项目实现
5.1 项目配置
父项目的pom.xml配置:
<parent><groupId>com.zhihu.fust</groupId><artifactId>fust-boot-bom</artifactId><version>0.1.0</version></parent>
fust-boot-bom作为父项目,这样就可以统一管理FUST相关依赖的版本。
5.2 数据模型设计
在deep-qa-business模块中,我们定义了以下主要数据模型:
5.2.1 聊天历史模型
(name = TABLE_NAME)public class ChatHistoryModel {public static final String TABLE_NAME = "t_chat_history";/*** 主键ID*/private Long id;/*** 创建时间*/private LocalDateTime createdAt;/*** 更新时间*/private LocalDateTime updatedAt;/*** 用户ID*/private Long userId;/*** 会话ID*/private String sessionId;/*** 角色(user/assistant)*/private String role;/*** 消息内容*/private String content;/*** 是否包含图片*/private Boolean hasImage;/*** 图片URL*/private String imageUrl;}
这里我们使用了@DbAutoColumn注解来标记自动填充的字段,FUST框架会自动处理这些字段的创建和更新。
5.3 数据访问层
数据访问层使用MyBatis实现,FUST框架提供了TemplateDao接口,简化了常见的CRUD操作。以ChatHistoryDao为例:
public interface ChatHistoryDao extends TemplateDao<ChatHistoryModel> {/*** 通过用户ID和会话ID查询对话记录*/" WHERE user_id = #{userId} AND session_id = #{sessionId} ORDER BY created_at ASC"})List<ChatHistoryModel> findByUserIdAndSessionId( Long userId, String sessionId);/*** 删除会话记录*/int deleteByUserIdAndSessionId( Long userId, String sessionId);/*** 按创建时间获取用户最近的会话列表(分组查询不同会话)*/" WHERE user_id = #{userId} ORDER BY MAX(created_at) DESC LIMIT #{limit}"})List<String> findRecentSessionsByUserId( Long userId, int limit);}
通过继承TemplateDao,我们自动获得了常见的CRUD操作,同时还可以根据业务需求自定义其他查询方法。
5.4 业务服务层
业务服务层是应用的核心,负责实现业务逻辑。以ChatService接口及其实现类为例:
public interface ChatService {/*** 发送消息*/ChatHistoryModel sendMessage(Long userId, String sessionId, String message);/*** 获取聊天历史*/List<ChatHistoryModel> getChatHistory(Long userId, String sessionId);/*** 获取用户会话列表*/List<String> getUserSessions(Long userId, int limit);/*** 创建新会话*/String createNewSession(Long userId);/*** 删除会话*/boolean deleteSession(Long userId, String sessionId);}
实现类ChatServiceImpl负责具体的业务逻辑实现:
public class ChatServiceImpl implements ChatService {public ChatHistoryModel sendMessage(Long userId, String sessionId, String message) {// 保存用户消息ChatHistoryModel userMessage = ...;chatHistoryDao.create(userMessage);// 获取用户DeepSeek配置ConfigModel config = configService.getUserConfig(userId);try {// 获取历史对话记录用于上下文List<ChatHistoryModel> historyList = chatHistoryDao.findByUserIdAndSessionId(userId, sessionId);// 将历史转换为DeepSeek API需要的格式 (限制最近10条消息)List<Map<String, String>> conversationHistory = new java.util.ArrayList<>();int startIndex = Math.max(0, historyList.size() - 10);for (int i = startIndex; i < historyList.size(); i++) {ChatHistoryModel historyItem = historyList.get(i);if (!"user".equals(historyItem.getRole()) && !"assistant".equals(historyItem.getRole())) {continue;}Map<String, String> messageMap = new java.util.HashMap<>();messageMap.put("role", historyItem.getRole());messageMap.put("content", historyItem.getContent());conversationHistory.add(messageMap);}// 调用DeepSeek API获取回复String aiReply = deepSeekClient.generateResponse(config, message, conversationHistory);// 保存AI回复ChatHistoryModel aiMessage = ...;chatHistoryDao.create(aiMessage);return aiMessage;} catch (Exception e) {log.error("Failed to get response from DeepSeek API", e);// 保存错误消息ChatHistoryModel errorMessage = ...;chatHistoryDao.create(errorMessage);return errorMessage;}}// 其他方法实现...}
在这个实现中,我们可以看到:
-
保存用户消息到数据库 -
获取历史对话作为上下文 -
调用DeepSeek API获取AI回复 -
保存AI回复到数据库 -
异常处理机制
5.5 DeepSeek API集成
我们通过DeepSeekClient类与DeepSeek API进行交互:
public class DeepSeekClient {private static final Logger LOGGER = LoggerFactory.getLogger(DeepSeekClient.class);/*** 发送消息到DeepSeek API获取回复*/public String generateResponse(ConfigModel config, String prompt, List<Map<String, String>> history) {try {DeepSeekApi deepSeekApi = new DeepSeekApi(config.getServerUrl(), config.getApiKey());// 构建选项Map<String, Object> options = new HashMap<>();options.put("temperature", config.getTemperature());options.put("top_p", config.getTopP());options.put("context_window", config.getContextSize());return deepSeekApi.generateCompletion(config.getModelName(), prompt, history, options);} catch (Exception e) {LOGGER.error("Error calling DeepSeek API", e);return "调用AI服务出错: " + e.getMessage();}}/*** 获取可用模型列表*/public List<String> getAvailableModels(String serverUrl, String apiKey) {try {DeepSeekApi deepSeekApi = new DeepSeekApi(serverUrl, apiKey);List<DeepSeekApi.Model> models = deepSeekApi.listModels();return models.stream().map(DeepSeekApi.Model::getName).collect(Collectors.toList());} catch (Exception e) {LOGGER.error("Error fetching DeepSeek models", e);return List.of("deepseek-chat", "deepseek-coder", "deepseek-lite");}}/*** 测试与DeepSeek API的连接*/public boolean testConnection(String serverUrl, String apiKey) {try {DeepSeekApi deepSeekApi = new DeepSeekApi(serverUrl, apiKey);return deepSeekApi.testConnection();} catch (Exception e) {LOGGER.error("Failed to connect to DeepSeek API: {}", serverUrl, e);return false;}}}
这个客户端类提供了三个主要功能:生成回复、获取可用模型列表和测试连接。
5.6 Web控制器层
Web控制器层负责处理HTTP请求和响应,项目中主要有三个控制器:ChatController、ConfigController和HomeController。以ChatController为例:
public class ChatController {private static final int MAX_SESSIONS_COUNT = 10;private static final int SESSION_TITLE_LENGTH = 8;private final ChatService chatService;public ChatController(ChatService chatService) {this.chatService = chatService;}/*** 发送消息*/public ResponseDTO<ChatMessageDTO> sendMessage( SendMessageRequestDTO request) {// 简单验证if (request.getMessage() == null || request.getMessage().trim().isEmpty()) {return ResponseDTO.error("消息内容不能为空");}// 模拟用户认证 (实际应从会话或令牌中获取用户ID)Long userId = 1L;// 如果没有会话ID,创建新会话String sessionId = request.getSessionId();if (sessionId == null || sessionId.trim().isEmpty()) {sessionId = chatService.createNewSession(userId);}// 发送消息并获取回复ChatHistoryModel response = chatService.sendMessage(userId, sessionId, request.getMessage());// 转换为DTOChatMessageDTO messageDTO = convertToDTO(response);return ResponseDTO.success(messageDTO);}/*** 获取会话历史记录*/public ResponseDTO<List<ChatMessageDTO>> getChatHistory( String sessionId) {// 模拟用户认证Long userId = 1L;List<ChatHistoryModel> history = chatService.getChatHistory(userId, sessionId);List<ChatMessageDTO> historyDTO = history.stream().map(this::convertToDTO).collect(Collectors.toList());return ResponseDTO.success(historyDTO);}// 其他API方法.../*** 将ChatHistoryModel转换为ChatMessageDTO*/private ChatMessageDTO convertToDTO(ChatHistoryModel model) {ChatMessageDTO dto = new ChatMessageDTO();dto.setId(model.getId().toString());dto.setRole(model.getRole());dto.setContent(model.getContent());if (model.getCreatedAt() != null) {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");dto.setTime(model.getCreatedAt().format(formatter));}if (model.getHasImage() && model.getImageUrl() != null) {dto.setHasImage(true);dto.setImageUrl(model.getImageUrl());} else {dto.setHasImage(false);}return dto;}}
FUST框架提供了统一的响应格式ResponseDTO,让API接口返回的数据更加规范和一致。
5.7 应用启动类
最后,我们看一下应用的入口类:
(ServiceConfiguration.class)(basePackages = {"com.deepqa.api", "com.deepqa.business"})(basePackages = {"com.deepqa.business.model"})public class DeepQAMain {public static void main(String[] args) {// 设置默认环境变量if (System.getProperty("env.name") == null && System.getenv("ENV_NAME") == null) {System.setProperty("env.name", "dev");}TelemetryInitializer.init();SpringApplication application = new SpringApplication(DeepQAMain.class);application.setAdditionalProfiles("api");application.run(args);}}
注意这里调用了TelemetryInitializer.init(),这是FUST框架提供的遥测初始化方法,用于收集应用指标和监控数据。
六、项目部署与运行
FUST框架提供了一系列脚本来简化项目的构建和运行过程。本文 demo 项目代码地址访问 deep-qa。
6.1 项目构建
首先,编译Maven项目:
mvn clean package
然后,使用构建脚本:
bash build.sh
6.2 项目运行
根据不同的环境,使用不同的环境配置脚本:
开发环境
# 使用开发环境配置source ./dev-env.sh# 然后运行HTTP服务bash run.sh deep-qa-api/target
生产环境
# 设置生产环境数据库密码(敏感信息不要硬编码)export DB_USER="production_username"export DB_PASSWORD="production_password"# 使用生产环境配置source ./prod-env.sh# 然后运行HTTP服务bash run.sh deep-qa-api/target
6.3 服务访问
服务启动后,可以通过以下URL访问:
http://localhost:8080/
八、总结与展望
通过本文,我们详细介绍了如何使用FUST框架和DeepSeek API构建一个功能完整的智能问答系统。这个系统具有以下特点:
- 模块化架构:清晰的模块划分和责任分配
- 扩展性强:可以方便地添加新功能和扩展现有功能
- 高性能:基于FUST和Armeria的高性能服务框架
- 可靠稳定:完善的异常处理和错误恢复机制
- 可观测性:内置的监控和跟踪功能
九、参考资料
-
FUST框架官方文档 -
DeepSeek API文档 -
Spring Boot官方文档 -
MyBatis官方文档 -
Armeria服务器文档

