面试官:如何实现大模型连续对话?

所有的大模型本身是不进行信息存储的,也不提供连续对话功能,所以想要实现连续对话功能需要开发者自己写代码才能实现。那怎么才能实现大模型的连续对话功能呢?

大模型连续对话功能不同的框架实现也是不同的,以行业使用最多的 Java AI 框架 Spring AI 和 Spring AI Alibaba 为例,给大家演示一下它们连续对话是如何实现的。

1.SpringAI连续对话实现

Spring AI 以 MySQL 数据库为例,我们来实现一下它的连续对话功能。

PS:我们只有先讲对话存储起来,才能实现连续对话功能,所以我们需要借助数据库存储来连续对话。

1.1 准备工作

1.创建表

/* by 01022.hk - online tools website : 01022.hk/zh/iq.html */
CREATE TABLE chat_message (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  conversation_id VARCHAR(255) NOT NULL,
  role VARCHAR(50) NOT NULL,
  context TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2.添加数据库和 MyBatisPlus 依赖:

/* by 01022.hk - online tools website : 01022.hk/zh/iq.html */
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
  <version>3.5.11</version>
</dependency>

<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
  <scope>runtime</scope>
</dependency>

3.设置配置文件:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8
    username: root
    password: 12345678
    driver-class-name: com.mysql.cj.jdbc.Driver
# 配置打印 MyBatis 执行的 SQL
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置打印 MyBatis 执行的 SQL
logging:
  level:
    com:
      ai:
        deepseek: debug

4.编写实体类

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.Date;

@Getter
@Setter
@TableName("chat_message")
public class ChatMessageDO implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private String conversationId;

    private String role;

    private String context;

    private Date createdAt;
}

5.编写 Mapper:

import com.ai.chat.entity.ChatMessageDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ChatMessageMapper extends BaseMapper<ChatMessageDO> {
}

1.2 自定义ChatMemory类

自定义的 ChatMemory 实现类,将对话记录存储到 MySQL:

import com.ai.deepseek.entity.ChatMessageDO;
import com.ai.deepseek.mapper.ChatMessageMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Component
public class MySQLChatMemory implements ChatMemory {
    @Autowired
    private ChatMessageMapper repository;

    @Override
    public void add(String conversationId, Message message) {
        ChatMessageDO entity = new ChatMessageDO();
        entity.setConversationId(conversationId);
        entity.setRole(message.getMessageType().name());
        entity.setContext(message.getText());
        repository.insert(entity);
    }

    @Override
    public void add(String conversationId, List<Message> messages) {
        messages.forEach(message -> add(conversationId, message));
    }

    @Override
    public List<Message> get(String conversationId, int lastN) {
        LambdaQueryWrapper<ChatMessageDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ChatMessageDO::getConversationId, conversationId);
        // queryWrapper.orderByDesc(ChatMessageDO::getId);
        return repository.selectList(queryWrapper)
        .stream()
        .limit(lastN)
        .map(e -> new UserMessage(e.getContext()))
        .collect(Collectors.toList());
    }

    @Override
    public void clear(String conversationId) {
        LambdaQueryWrapper<ChatMessageDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ChatMessageDO::getConversationId, conversationId);
        repository.delete(queryWrapper);
    }
}

1.3 代码调用

编写代码测试历史对话保存到 MySQL 的功能:

import com.ai.deepseek.component.MySQLChatMemory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/multi")
public class MultiChatController {

    @Autowired
    private ChatClient chatClient;
    @Autowired
    private MySQLChatMemory chatMemory;

    @RequestMapping("/chat")
    public Flux<String> chat(@RequestParam("msg") String msg,
                             @RequestParam(defaultValue = "default") String sessionId) {
        // 添加MessageChatMemoryAdvisor,自动管理上下文
        MessageChatMemoryAdvisor advisor =
        new MessageChatMemoryAdvisor(chatMemory, sessionId, 10); // 保留最近5条历史
        return chatClient.prompt()
        .user(msg)
        .advisors(advisor) // 关键:注入记忆管理
        .stream()
        .content();
    }
}

以上程序执行结果如下:

未命名__2025-07-18+16_45_29

2.SpringAIAlibaba实现连续对话

Spring AI Alibaba 连续对话的实现就简单很多了,因为它内置了 MySQL 和 Redis 的连续对话存储方式,接下来以 Redis 为例演示 SAA 的连续对话实现,它的实现步骤如下:

  1. 添加依赖。
  2. 设置配置文件,配置 Redis 连接信息。
  3. 添加 Redis 配置类,注入 RedisChatMemoryRepository 对象。
  4. 配置 ChatClient 实现连续对话。

具体实现如下。

2.1 添加依赖

<dependency>
  <groupId>com.alibaba.cloud.ai</groupId>
  <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
</dependency>

2.2 设置配置文件

设置配置文件,配置 Redis 连接信息:

spring:
  ai:
    memory:
      redis:
        host: localhost
        port: 6379
        timeout: 5000

2.3 添加Redis配置类

添加 Redis 配置类,注入 RedisChatMemoryRepository 对象,实现 Redis 自定义存储器注入:

import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedisMemoryConfig {

    @Value("${spring.ai.memory.redis.host}")
    private String redisHost;
    @Value("${spring.ai.memory.redis.port}")
    private int redisPort;
    //    @Value("${spring.ai.memory.redis.password}")
    //    private String redisPassword;
    @Value("${spring.ai.memory.redis.timeout}")
    private int redisTimeout;

    @Bean
    public RedisChatMemoryRepository redisChatMemoryRepository() {
        return RedisChatMemoryRepository.builder()
        .host(redisHost)
        .port(redisPort)
        // 若没有设置密码则注释该项
        //           .password(redisPassword)
        .timeout(redisTimeout)
        .build();
    }
}

2.4 配置ChatClient实现连续对话

import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static org.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID;

@RestController
@RequestMapping("/redis")
public class RedisMemoryController {

    private final ChatClient chatClient;
    private final int MAXMESSAGES = 10;
    private final MessageWindowChatMemory messageWindowChatMemory;

    public RedisMemoryController(ChatModel dashscopeChatModel,
                                 RedisChatMemoryRepository redisChatMemoryRepository) {
        this.messageWindowChatMemory = MessageWindowChatMemory.builder()
        .chatMemoryRepository(redisChatMemoryRepository)
        .maxMessages(MAXMESSAGES)
        .build();

        this.chatClient = ChatClient.builder(dashscopeChatModel)
        .defaultAdvisors(
            MessageChatMemoryAdvisor.builder(messageWindowChatMemory)
            .build()
        )
        .build();
    }

    @GetMapping("/call")
    public String call(String msg, String cid) {
        return chatClient.prompt(msg)
        .advisors(
            a -> a.param(CONVERSATION_ID, cid)
        )
        .call().content();
    }
}

小结

通过以上代码大家也可以看出来,使用 Spring AI 实现连续对话是比较复杂的,需要自己实现数据库增删改查的代码,并且重写 ChatMemory 才能实现连续对话功能;而 Spring AI Alibaba 因为内置了连续对话的多种实现(Redis 和其他数据库),所以只需要简单配置就可以实现了。

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:场景题、SpringAI、SpringAIAlibaba、并发编程、MySQL、Redis、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、JVM、设计模式、消息队列、AI常见面试题等。

关注下面二维码,订阅更多精彩内容。
微信打赏
关注公众号(加好友):


作者: 王磊的博客
出处: http://vipstone.cnblogs.com/

<think>嗯,用户现在问的是C++的面试经验,我需要帮他整理一下这方面的内容。首先,我得回想一下之前用户的历史对话,之前他问过Mesh组网方案,现在转向技术面试,可能是准备求职,尤其是C++相关的职位。 用户可能是计算机相关专业的学生或者刚毕业的求职者,想了解C++面试的重点和常见问题。需要覆盖哪些方面呢?通常C++面试会涉及基础语法、面向对象、内存管理、STL、新标准特性,还有算法和设计模式等。 要考虑用户可能对某些概念不太熟悉,需要解释清楚,比如虚函数、智能指针这些。同时,用户可能希望知道面试中常考的问题类型,比如手写代码、原理题、项目经验等。可能还需要给出准备建议,比如推荐书籍和刷题网站。 另外,用户可能需要了解面试中的注意事项,比如沟通能力和问题解决思路。可能还需要提醒他注意公司业务方向,不同公司可能侧重点不同,比如游戏开发注重性能,互联网公司可能问高并发。 需要结构清晰,分点列出,每个部分给出关键知识点和示例。比如基础语法部分可以提到const、static、指针和引用的区别,内存管理讲new/delete和内存泄漏,面向对象包括多态、继承、虚函数表等。 还要考虑用户可能对某些术语不太了解,需要用简单的中文解释,避免过于专业的术语堆砌。比如解释RAII机制时,可以结合实例说明如何自动管理资源。 最后,给出准备建议,比如推荐《Effective C++》和刷LeetCode,提醒项目经验的重要性,以及模拟面试的必要性。确保内容全面,但不过于冗长,方便用户快速抓住重点。</think>### C++ 面试核心知识点与高频问题解析 #### 一、基础语法与特性 1. **指针与引用** - 区别:指针是变量存储地址,引用是别名(必须初始化且不可变) - 应用场景:引用用于函数参数传递(避免拷贝),指针用于动态内存管理 ```cpp int a = 10; int* ptr = &a; // 指针 int& ref = a; // 引用 ``` 2. **const 关键字** - `const int* p`:指向常量的指针(值不可改) - `int* const p`:常量指针(地址不可改) - `const成员函数`:承诺不修改对象状态(可被const对象调用) 3. **static 关键字** - 类内静态成员:所有实例共享,需类外初始化 - 静态局部变量:生命周期延长至程序结束 - 静态函数:无`this`指针,只能访问静态成员 #### 二、面向对象编程 1. **多态实现原理** - 虚函数表(vtable):每个类维护虚函数地址表 - 动态绑定:通过对象虚表指针(vptr)实现运行时多态 ```cpp class Base { public: virtual void func() { cout << "Base"; } }; class Derived : public Base { public: void func() override { cout << "Derived"; } }; ``` 2. **构造函数/析构函数** - 虚析构函数必要性:基类指针删除派生类对象时避免内存泄漏 - 移动构造函数:`ClassName(ClassName&& other)`,提升资源转移效率 3. **四种类型转换** - `static_cast`:基础类型转换(编译期检查) - `dynamic_cast`:类层次向下转型(运行时检查) - `const_cast`:去除const属性 - `reinterpret_cast`:低风险指针类型转换 #### 三、内存管理 1. **new/delete 与 malloc/free** - `new`会调用构造函数,`malloc`仅分配内存 - `delete`会调用析构函数,`free`直接释放内存 2. **智能指针** - `unique_ptr`:独占所有权(禁止拷贝) - `shared_ptr`:引用计数共享所有权 - `weak_ptr`:解决shared_ptr循环引用问题 ```cpp auto sp = std::make_shared<int>(42); std::weak_ptr<int> wp = sp; ``` 3. **内存泄漏检测** - Valgrind工具分析 - 重载`new/delete`记录分配日志 - RAII机制自动管理资源 #### 四、STL 容器与算法 1. **容器底层实现** - `vector`:动态数组(连续内存) - `list`:双向链表 - `map/set`:红黑树(自动排序) - `unordered_map`:哈希表(O(1)查找) 2. **迭代器失效场景** - `vector`插入/删除导致后续迭代器失效 - `map`删除当前迭代器需先保存下一个节点 3. **常用算法** ```cpp std::sort(vec.begin(), vec.end()); // 快速排序 auto it = std::find_if(vec.begin(), vec.end(), [](int x){ return x>5; }); ``` #### 五、C++11/14/17 新特性 1. **右值引用与移动语义** ```cpp std::string str1 = "Hello"; std::string str2 = std::move(str1); // str1变为空 ``` 2. **Lambda 表达式** ```cpp auto func = [capture](params) -> retType { body }; ``` 3. **自动类型推导** ```cpp auto x = 5; // int decltype(x) y = x; // int ``` #### 六、高频面试问题 1. **手写代码题** - 实现字符串类(考虑拷贝控制) - 二叉树遍历(递归/非递归) - 线程安全单例模式 2. **原理分析题** - 虚函数表内存布局 - vector扩容机制(2倍增长) - 智能指针循环引用解决方案 3. **项目深度问题** - 如何优化C++程序性能? - 多线程编程遇到哪些问题?如何解决? - 如何设计跨平台C++模块? #### 七、面试准备建议 1. **知识体系构建** - 重点书籍:《Effective C++》《深度探索C++对象模型》 - 刷题平台:LeetCode(Top 100 Liked Questions)、牛客网 2. **项目经验提炼** - STAR法则描述项目:Situation-Task-Action-Result - 准备技术难点复盘(如内存优化、并发问题) 3. **模拟面试训练** - 录制视频观察表达流畅度 - 针对岗位需求准备特定领域知识(如游戏开发需掌握图形学基础) #### 八、注意事项 1. 回答问题时先确认问题边界(如面试官问"多态",需明确指编译期还是运行期) 2. 手写代码时注重边界条件检查和代码规范 3. 遇到难题可展示解决思路(如分治法、空间换时间) 附:**常见陷阱题示例** ```cpp // 输出结果是什么? int i = 0; cout << i++ << ++i; // 未定义行为(UB) ``` 通过系统化准备C++核心知识点+高频问题+实战演练,可显著提升面试通过率。建议根据目标公司业务方向(如腾讯重网络编程,网易游戏重性能优化)针对性强化相关知识模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值