大数跨境
0
0

FUST 与 DeepSeek:构建智能问答系统的实践探索

FUST 与 DeepSeek:构建智能问答系统的实践探索 知乎技术专栏
2025-09-23
1
导读:FUST 与 DeepSeek:构建智能问答系统的实践探索

近年来,随着大型语言模型的快速发展,智能问答系统已经成为企业提升用户体验、降低客服成本的重要工具。本文将介绍如何利用知乎开源的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/
├── deep-qa-api/                  # HTTP API模块
│   └── src/
│       ├── main/java/com/deepqa/api/
│       │   ├── config/           # 配置类
│       │   ├── controller/       # 控制器
│       │   └── dto/              # 数据传输对象
│       └── main/resources/
│           ├── static/           # 静态资源
│           ├── templates/        # Thymeleaf模板
│           └── application.properties # 应用配置
├── deep-qa-business/             # 业务逻辑模块
│   └── src/
│       └── main/java/com/deepqa/business/
│           ├── client/           # 外部服务客户端
│           ├── dao/              # 数据访问对象
│           ├── model/            # 数据模型
│           └── service/          # 业务服务
│               └── impl/         # 服务实现
└── proto/                        # Protocol Buffers定义
    ├── buf.yaml                  # Buf模块配置
    └── hello.proto               # 示例服务Proto定义

这种模块化设计有以下优势:

  1. 关注点分离:API层负责处理HTTP请求和响应,业务层负责核心业务逻辑
  2. 代码复用:业务逻辑可以被不同的接口层(HTTP、gRPC)复用
  3. 便于测试:各层可以独立测试
  4. 维护简单:模块边界清晰,降低了代码耦合度

四、环境准备

在开始项目开发前,需要准备以下环境:

  • 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框架的fust-boot-bom作为父项目,这样就可以统一管理FUST相关依赖的版本。

5.2 数据模型设计

deep-qa-business模块中,我们定义了以下主要数据模型:

5.2.1 聊天历史模型

@Data@Table(name = TABLE_NAME)public class ChatHistoryModel {    public static final String TABLE_NAME = "t_chat_history";    /**     * 主键ID     */    @Id    private Long id;    /**     * 创建时间     */    @DbAutoColumn    private LocalDateTime createdAt;    /**     * 更新时间     */    @DbAutoColumn    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为例:

@Mapperpublic interface ChatHistoryDao extends TemplateDao<ChatHistoryModel> {    /**     * 通过用户ID和会话ID查询对话记录     */    @Select({"SELECT * FROM ", ChatHistoryModel.TABLE_NAME,            " WHERE user_id = #{userId} AND session_id = #{sessionId} ORDER BY created_at ASC"})    @ResultMap("ChatHistoryModel")    List<ChatHistoryModel> findByUserIdAndSessionId(@Param("userId") Long userId, @Param("sessionId") String sessionId);    /**     * 删除会话记录     */    @Delete({"DELETE FROM ", ChatHistoryModel.TABLE_NAME, " WHERE user_id = #{userId} AND session_id = #{sessionId}"})    int deleteByUserIdAndSessionId(@Param("userId") Long userId, @Param("sessionId") String sessionId);    /**     * 按创建时间获取用户最近的会话列表(分组查询不同会话)     */    @Select({"SELECT DISTINCT session_id FROM ", ChatHistoryModel.TABLE_NAME,            " WHERE user_id = #{userId} ORDER BY MAX(created_at) DESC LIMIT #{limit}"})    List<String> findRecentSessionsByUserId(@Param("userId") Long userId, @Param("limit") 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负责具体的业务逻辑实现:

@Service@Slf4jpublic class ChatServiceImpl implements ChatService {    @Override    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<StringString>> 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<StringString> 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;        }    }
    // 其他方法实现...}

在这个实现中,我们可以看到:

  1. 保存用户消息到数据库
  2. 获取历史对话作为上下文
  3. 调用DeepSeek API获取AI回复
  4. 保存AI回复到数据库
  5. 异常处理机制

5.5 DeepSeek API集成

我们通过DeepSeekClient类与DeepSeek API进行交互:

@Componentpublic class DeepSeekClient {    private static final Logger LOGGER = LoggerFactory.getLogger(DeepSeekClient.class);    /**     * 发送消息到DeepSeek API获取回复     */    public String generateResponse(ConfigModel config, String prompt, List<Map<StringString>> history) {        try {            DeepSeekApi deepSeekApi = new DeepSeekApi(config.getServerUrl(), config.getApiKey());            // 构建选项            Map<StringObject> 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<StringgetAvailableModels(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请求和响应,项目中主要有三个控制器:ChatControllerConfigControllerHomeController。以ChatController为例:

@RestController@RequestMapping("/api/chat")public class ChatController {
    private static final int MAX_SESSIONS_COUNT = 10;    private static final int SESSION_TITLE_LENGTH = 8;
    private final ChatService chatService;
    @Autowired    public ChatController(ChatService chatService) {        this.chatService = chatService;    }
    /**     * 发送消息     */    @PostMapping("/send")    public ResponseDTO<ChatMessageDTO> sendMessage(@RequestBody 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());
        // 转换为DTO        ChatMessageDTO messageDTO = convertToDTO(response);
        return ResponseDTO.success(messageDTO);    }
    /**     * 获取会话历史记录     */    @GetMapping("/history/{sessionId}")    public ResponseDTO<List<ChatMessageDTO>> getChatHistory(@PathVariable 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 应用启动类

最后,我们看一下应用的入口类:

@SpringBootApplication@Import(ServiceConfiguration.class)@ComponentScan(basePackages = {"com.deepqa.api""com.deepqa.business"})@EntityScan(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的高性能服务框架
  • 可靠稳定:完善的异常处理和错误恢复机制
  • 可观测性:内置的监控和跟踪功能


九、参考资料

  1. FUST框架官方文档
  2. DeepSeek API文档
  3. Spring Boot官方文档
  4. MyBatis官方文档
  5. Armeria服务器文档

【声明】内容源于网络
0
0
知乎技术专栏
分享知乎技术日志,探索社区技术创新。
内容 17
粉丝 0
知乎技术专栏 分享知乎技术日志,探索社区技术创新。
总阅读34
粉丝0
内容17