大厂Java面试实录:从Spring Boot到AI技术的在线教育场景深度解析
大厂Java面试实录:从Spring Boot到AI技术的在线教育场景深度解析
面试场景:在线教育平台技术面试
面试官:谢飞机,欢迎来到我们公司面试。今天我们将围绕在线教育平台进行技术交流,请你放松心态,如实回答。
谢飞机:谢谢面试官,我会尽力表现的!
第一轮提问:Spring Boot基础与教育系统架构
面试官:首先,在线教育平台需要处理大量的课程和学生数据。请问我使用Spring Boot构建教育系统时,如何利用自动配置来简化开发?
谢飞机:嗯...Spring Boot的自动配置就是会根据类路径下的jar包自动配置Bean。比如如果看到spring-boot-starter-web,就会自动配置Tomcat、DispatcherServlet这些。在教育系统里,我们可以用@SpringBootApplication注解,然后通过@ConditionalOnClass这样的条件注解来控制Bean的创建。
面试官:回答得不错。那在教育平台中,我们经常需要实时推送课程更新和作业提醒,请问如何使用Spring WebFlux来实现响应式的实时通知?
谢飞机:Spring WebFlux是基于响应式编程的,可以用Mono和Flux来处理异步数据流。对于实时通知,我们可以创建一个Flux,然后通过WebSocket或者Server-Sent Events实时推送通知。这样可以避免阻塞,提高系统的并发处理能力。
面试官:很好。在教育平台中,课程信息需要频繁查询,请问我应该如何设计Redis缓存来优化课程信息的读取性能?
谢飞机:Redis缓存的话,可以用String结构存储课程JSON数据,Key可以用"course:#{courseId}"这样的格式。对于热门课程,可以用Set来维护。还可以用Redis的过期时间自动清理不活跃的课程数据。对了,还可以用Pipeline批量减少网络开销。
面试官:思路清晰。最后一个问题,教育平台需要处理大量的学生作业,请问如何使用Spring Data JPA来优化作业的批量查询和更新?
谢飞机:Spring Data JPA的话,可以用@Query注解写原生SQL,或者用Specification动态查询。批量更新的话,可以用saveAll()方法,或者用@Modifying注解写批量更新语句。对于分页查询,用Pageable对象配合Page返回结果。
面试官:很好,第一轮回答得不错。接下来我们进入第二轮。
第二轮提问:微服务架构与教育系统高并发
面试官:在线教育平台通常采用微服务架构,请问在微服务拆分时,如何合理设计课程服务的边界?
谢飞机:课程服务的边界...应该按照业务能力来拆分。比如可以分成课程管理服务、学生服务、作业服务、考试服务等等。每个服务都有自己的数据库,通过API互相调用。课程服务可以负责课程内容的存储和管理,学生服务负责用户信息和学习进度跟踪。
面试官:那在微服务通信中,我们如何使用OpenFeign来实现服务间的优雅调用?
谢飞机:OpenFeign是声明式的HTTP客户端,可以用@FeignClient注解定义接口。比如定义一个StudentClient接口,用@GetMapping("/api/students/{id}")这样的注解来调用学生服务。OpenFeign会自动实现HTTP调用,还支持负载均衡、熔断这些功能。
面试官:在教育平台中,学生选课和退课操作频繁,请问如何使用Kafka来实现课程状态变更的异步通知?
谢飞机:Kafka的话,可以创建一个"course-events"主题,课程服务在状态变更时发送消息。其他服务比如学生服务、作业服务可以订阅这个主题。消息可以用JSON格式,包含课程ID、操作类型、学生ID这些字段。这样可以实现服务解耦,提高系统的响应速度。
面试官:很好。最后一个问题,教育平台需要保证高可用性,请问如何使用Resilience4j来实现服务的熔断和限流?
谢飞机:Resilience4j是一个轻量级的容错库。可以用CircuitBreaker来设置熔断规则,当错误率达到阈值就熔断。用RateLimiter来限制请求频率,用Bulkhead来限制并发线程数。比如在调用外部API时,可以加上@CircuitBreaker注解,当服务不可用时快速失败。
面试官:不错,第二轮回答得还可以。接下来我们进入最后一轮。
第三轮提问:AI技术与教育智能化
面试官:现代在线教育平台都在引入AI技术来个性化学习体验。请问在Spring Boot中如何集成Spring AI来实现智能学习推荐?
谢飞机:Spring AI...这个我了解不多,应该是Spring框架对AI的支持。可以用Spring AI的客户端调用大模型API,把学生的学习历史、课程难度、学习进度作为输入,让AI推荐最适合的课程内容。然后把这个推荐结果保存到数据库,供学生使用。
面试官:那在教育平台中,我们如何使用向量数据库来实现相似课程的快速检索?
谢飞机:向量数据库...这个我接触不多。应该是把课程内容的特征转换成向量,然后存储在Milvus或者Chroma这样的向量数据库里。当需要查找相似课程时,可以用余弦相似度来计算课程之间的相似性,这样可以快速找到相似的学习资源。
面试官:在教育智能辅导中,我们如何使用RAG技术来提高回答的准确性和相关性?
谢飞机:RAG是检索增强生成...应该是先从教材和课程资料中检索相关的知识,然后结合这些知识来生成回答。比如当学生问数学问题时,系统先从知识库中查找相关的公式和例题,然后基于这些信息给出详细的解答。
面试官:最后一个问题,教育平台需要处理大量的自然语言查询,请问如何使用Embedding模型来实现智能语义搜索?
谢飞机:Embedding模型...这个我了解不多。应该是把学生的自然语言查询转换成向量,然后在向量空间中进行相似度匹配。比如学生问"如何学习Java编程",系统把这个查询转换成向量,然后在课程数据库中查找相似的课程内容,推荐相关的学习资源。
面试官:好的,今天的面试就到这里。你的回答有对有错,我们会综合评估。请你回家等通知吧。
谢飞机:谢谢面试官,我会继续学习的!
详细答案解析
第一轮答案详解
1. Spring Boot自动配置在教育系统中的应用
业务场景:在线教育平台需要快速构建基础架构,处理课程管理、学生管理、作业管理、考试管理等核心功能。
技术实现:
java
@SpringBootApplication
public class EducationApplication {
public static void main(String[] args) {
SpringApplication.run(EducationApplication.class, args);
}
}
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
@Service
public class CourseService {
@Autowired
private CourseRepository courseRepository;
public Course getCourseById(Long courseId) {
return courseRepository.findById(courseId).orElse(null);
}
}
技术要点:
@SpringBootApplication包含@EnableAutoConfiguration,自动配置Spring上下文@ConditionalOnClass根据类路径条件自动配置Bean@ConfigurationProperties绑定配置文件属性到Bean- 教育系统中可自动配置数据源、Redis、消息队列等基础设施
2. Spring WebFlux实现响应式实时通知
业务场景:实时推送课程更新、作业提醒、考试通知等信息,支持大量并发连接,避免阻塞式I/O影响系统性能。
技术实现:
java
@RestController
@RequestMapping("/api/notifications")
public class NotificationController {
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Notification> getNotificationStream() {
return notificationService.getRealTimeNotifications();
}
@MessageMapping("/course.update")
@SendTo("/topic/notifications")
public Notification updateNotification(Notification notification) {
return notificationService.updateNotification(notification);
}
}
@Service
public class NotificationService {
public Flux<Notification> getRealTimeNotifications() {
return Flux.interval(Duration.ofSeconds(1))
.flatMap(i -> notificationRepository.findAll());
}
public Notification updateNotification(Notification notification) {
return notificationRepository.save(notification);
}
}
技术要点:
- 使用
Flux<Notification>处理异步数据流 MediaType.TEXT_EVENT_STREAM_VALUE支持Server-Sent Events- WebSocket集成实现双向实时通信
- 响应式编程避免阻塞,提高并发处理能力
3. Redis缓存优化课程信息读取
业务场景:课程信息频繁查询,需要减少数据库压力,提高响应速度,特别是在选课高峰期。
技术实现:
java
@Service
public class CourseCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public Course getCourseFromCache(Long courseId) {
String key = "course:" + courseId;
return (Course) redisTemplate.opsForValue().get(key);
}
public void cacheCourse(Course course) {
String key = "course:" + courseId;
redisTemplate.opsForValue().set(key, course, 30, TimeUnit.MINUTES);
}
public void batchCacheCourses(List<Course> courses) {
Map<String, Object> map = new HashMap<>();
courses.forEach(c -> map.put("course:" + c.getId(), c));
redisTemplate.opsForValue().multiSet(map);
}
}
技术要点:
- 使用String结构存储JSON序列化的课程数据
- 合理设置过期时间,避免内存泄漏
- Pipeline批量操作减少网络开销
- 设置合理的缓存穿透和缓存雪崩防护
4. Spring Data JPA优化作业批量操作
业务场景:批量查询和更新学生作业,提高数据处理效率,特别是在批改作业和统计分析时。
技术实现:
java
@Repository
public interface AssignmentRepository extends JpaRepository<Assignment, Long> {
@Query("SELECT a FROM Assignment a WHERE a.status = :status")
List<Assignment> findByStatus(@Param("status") AssignmentStatus status);
@Query(value = "SELECT * FROM assignments WHERE status = ?1 LIMIT ?2", nativeQuery = true)
Page<Assignment> findByStatusNative(AssignmentStatus status, Pageable pageable);
@Modifying
@Query("UPDATE Assignment a SET a.status = :newStatus WHERE a.id IN :ids")
int updateStatusByIds(@Param("newStatus") AssignmentStatus newStatus,
@Param("ids") List<Long> ids);
}
@Service
public class AssignmentService {
@Autowired
private AssignmentRepository assignmentRepository;
public List<Assignment> getPendingAssignments() {
return assignmentRepository.findByStatus(AssignmentStatus.PENDING);
}
public Page<Assignment> getAssignmentsByPage(AssignmentStatus status, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("submitTime").descending());
return assignmentRepository.findByStatusNative(status, pageable);
}
@Transactional
public void batchUpdateStatus(List<Long> ids, AssignmentStatus newStatus) {
assignmentRepository.updateStatusByIds(newStatus, ids);
}
}
技术要点:
- 使用
@Query注解优化复杂查询 - 原生SQL查询提高性能
@Modifying注解支持批量更新- 分页查询避免内存溢出
- 事务管理保证数据一致性
第二轮答案详解
1. 微服务边界设计
业务场景:在线教育系统复杂度高,需要合理拆分微服务,提高系统可维护性和扩展性。
技术实现:
java
// 课程服务边界设计
@RestController
@RequestMapping("/api/courses")
public class CourseController {
@Autowired
private CourseService courseService;
@PostMapping
public ResponseEntity<Course> createCourse(@RequestBody CourseRequest request) {
Course course = courseService.createCourse(request);
return ResponseEntity.ok(course);
}
@GetMapping("/{id}/content")
public ResponseEntity<CourseContent> getCourseContent(@PathVariable Long id) {
CourseContent content = courseService.getCourseContent(id);
return ResponseEntity.ok(content);
}
}
// 学生服务边界设计
@RestController
@RequestMapping("/api/students")
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/{id}/progress")
public ResponseEntity<StudentProgress> getStudentProgress(@PathVariable Long id) {
StudentProgress progress = studentService.getStudentProgress(id);
return ResponseEntity.ok(progress);
}
@PostMapping("/{id}/enroll")
public ResponseEntity<Student> enrollCourse(@PathVariable Long id,
@RequestBody EnrollmentRequest request) {
Student student = studentService.enrollCourse(id, request);
return ResponseEntity.ok(student);
}
}
技术要点:
- 按业务能力拆分:课程服务、学生服务、作业服务、考试服务
- 单一职责原则:每个服务专注于特定业务领域
- 数据独立性:每个服务有自己的数据库
- API设计:RESTful接口,清晰明确的资源路径
2. OpenFeign服务间调用
业务场景:微服务间需要优雅的HTTP调用,避免手动处理HTTP请求细节。
技术实现:
java
// 学生服务客户端
@FeignClient(name = "student-service", url = "${student.service.url}")
public interface StudentClient {
@GetMapping("/api/students/{id}")
ResponseEntity<Student> getStudentById(@PathVariable Long id);
@GetMapping("/api/students/{id}/progress")
ResponseEntity<StudentProgress> getStudentProgress(@PathVariable Long id);
@PostMapping("/api/students/{id}/enroll")
ResponseEntity<Student> enrollCourse(@PathVariable Long id,
@RequestBody EnrollmentRequest request);
}
// 课程服务中使用
@Service
public class CourseService {
@Autowired
private StudentClient studentClient;
@Autowired
private AssignmentClient assignmentClient;
public Course createCourse(CourseRequest request) {
// 调用学生服务验证学生信息
Student student = studentClient.getStudentById(request.getInstructorId()).getBody();
// 调用作业服务创建作业模板
AssignmentTemplate template = assignmentClient.createAssignmentTemplate(request.getAssignmentTemplate());
// 创建课程
Course course = new Course();
course.setTitle(request.getTitle());
course.setDescription(request.getDescription());
course.setInstructorId(request.getInstructorId());
course.setAssignmentTemplateId(template.getId());
return courseRepository.save(course);
}
}
技术要点:
@FeignClient声明式HTTP客户端- 接口定义与远程服务API保持一致
- 自动集成负载均衡、熔断、重试
- 简化服务间调用代码,提高可维护性
3. Kafka课程状态异步通知
业务场景:课程状态变更频繁,需要实时通知相关服务,避免同步调用的性能瓶颈。
技术实现:
java
// 课程服务 - 消息生产者
@Service
public class CourseService {
@Autowired
private KafkaTemplate<String, CourseEvent> kafkaTemplate;
@Autowired
private CourseRepository courseRepository;
@Transactional
public Course updateCourseStatus(Long courseId, CourseStatus newStatus) {
Course course = courseRepository.findById(courseId)
.orElseThrow(() -> new CourseNotFoundException(courseId));
CourseStatus oldStatus = course.getStatus();
course.setStatus(newStatus);
course = courseRepository.save(course);
// 发送状态变更事件
CourseEvent event = new CourseEvent();
event.setCourseId(courseId);
event.setOldStatus(oldStatus);
event.setNewStatus(newStatus);
event.setTimestamp(LocalDateTime.now());
kafkaTemplate.send("course-events", event);
return course;
}
}
// 学生服务 - 消息消费者
@Service
public class StudentService {
@Autowired
private StudentRepository studentRepository;
@KafkaListener(topics = "course-events", groupId = "student-group")
public void handleCourseEvent(CourseEvent event) {
if (event.getNewStatus() == CourseStatus.PUBLISHED) {
// 课程发布后通知相关学生
notifyStudentsForCourse(event.getCourseId());
} else if (event.getNewStatus() == CourseStatus.CANCELLED) {
// 课程取消后通知已报名学生
notifyCancellationToEnrolledStudents(event.getCourseId());
}
}
private void notifyStudentsForCourse(Long courseId) {
// 实现课程通知逻辑
}
private void notifyCancellationToEnrolledStudents(Long courseId) {
// 实现取消通知逻辑
}
}
技术要点:
- 主题设计:按业务事件类型定义主题
- 消息结构:包含足够的上下文信息
- 消费者分组:实现消息的负载均衡
- 事件驱动架构:服务解耦,提高系统弹性
4. Resilience4j服务容错
业务场景:教育系统依赖多个外部服务,需要保证系统在服务不可用时的稳定性。
技术实现:
java
@Configuration
public class Resilience4jConfig {
@Bean
public CircuitBreakerConfig circuitBreakerConfig() {
return CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.slidingWindowSize(10)
.slidingWindowType(SlidingWindowType.COUNT_BASED)
.build();
}
@Bean
public RateLimiterConfig rateLimiterConfig() {
return RateLimiterConfig.custom()
.limitForPeriod(100)
.limitRefreshPeriod(Duration.ofSeconds(1))
.timeoutDuration(Duration.ofMillis(0))
.build();
}
}
@Service
public class ExternalServiceClient {
@Autowired
private CircuitBreaker circuitBreaker;
@Autowired
private RateLimiter rateLimiter;
@Autowired
private RestTemplate restTemplate;
@CircuitBreaker(name = "externalService", fallbackMethod = "getExternalDataFallback")
@RateLimiter(name = "externalService")
public ExternalData getExternalData(String endpoint) {
return restTemplate.getForObject(endpoint, ExternalData.class);
}
public ExternalData getExternalDataFallback(String endpoint, Exception ex) {
ExternalData fallbackData = new ExternalData();
fallbackData.setData("Fallback data due to service unavailability");
fallbackData.setTimestamp(LocalDateTime.now());
return fallbackData;
}
@Bulkhead(name = "externalService", type = Bulkhead.Type.THREADPOOL)
@Recover
public ExternalData handleBulkheadFailure(Exception ex) {
ExternalData fallbackData = new ExternalData();
fallbackData.setData("System busy, please try again later");
fallbackData.setTimestamp(LocalDateTime.now());
return fallbackData;
}
}
技术要点:
- 熔断器:快速失败,避免资源浪费
- 限流器:控制请求频率,保护系统
- 舱壁隔离:限制并发线程数
- 降级策略:提供备用数据或默认值
- 配置灵活:可根据业务需求调整参数
第三轮答案详解
1. Spring AI智能学习推荐
业务场景:利用AI技术分析学生学习行为,个性化推荐课程内容和练习题目,提高学习效果。
技术实现:
java
@Configuration
public class SpringAiConfig {
@Bean
public OpenAiClient openAiClient() {
return OpenAiClient.builder()
.apiKey("${openai.api.key}")
.build();
}
}
@Service
public class LearningRecommendationService {
@Autowired
private OpenAiClient openAiClient;
@Autowired
private StudentProgressService studentProgressService;
@Autowired
private CourseContentService courseContentService;
public Recommendation recommendLearningPath(Long studentId) {
// 构建AI提示
String prompt = buildPrompt(studentId);
// 调用AI模型
AiResponse response = openAiClient.complete(prompt);
// 解析AI响应
Recommendation aiRecommendation = parseAiResponse(response.getContent());
// 验证和优化推荐
Recommendation optimizedRecommendation = validateAndOptimizeRecommendation(aiRecommendation);
// 保存推荐结果到数据库
return recommendationRepository.save(optimizedRecommendation);
}
private String buildPrompt(Long studentId) {
StudentProgress progress = studentProgressService.getStudentProgress(studentId);
List<CourseContent> completedContents = courseContentService.getCompletedContents(studentId);
return String.format(
"作为教育推荐专家,请为以下学生设计个性化学习路径:" +
"学生信息:%s,已完成内容:%s,学习进度:%s,学习偏好:%s。" +
"请返回JSON格式的推荐结果,包含推荐课程、练习题目、学习计划等信息。",
progress.getStudentInfo(),
completedContents,
progress.getProgressPercentage(),
progress.getLearningPreferences()
);
}
private Recommendation parseAiResponse(String aiResponse) {
// 使用JSON解析库解析AI返回的结果
return JsonUtils.fromJson(aiResponse, Recommendation.class);
}
private Recommendation validateAndOptimizeRecommendation(Recommendation recommendation) {
// 验证推荐的合理性
// 结合学生学习历史优化推荐
// 考虑课程难度和先修要求
return recommendation;
}
}
技术要点:
- Spring AI集成:简化AI模型调用
- 提示工程:构建高质量的AI提示词
- 结果解析:JSON格式的结构化数据
- 推荐验证:结合学生历史数据优化推荐
- 持久化存储:保存推荐结果供后续使用
2. 向量数据库相似课程检索
业务场景:快速查找相似课程内容,为学生提供相关学习资源,丰富学习体验。
技术实现:
java
@Service
public class SimilarCourseService {
@Autowired
private MilvusClient milvusClient;
@Autowired
private CourseVectorService courseVectorService;
public List<Course> findSimilarCourses(Course targetCourse, int topK) {
// 将目标课程转换为向量
float[] targetVector = courseVectorService.courseToVector(targetCourse);
// 在向量数据库中搜索相似课程
SearchParam searchParam = SearchParam.newBuilder()
.withCollectionName("course_contents")
.withVectors(targetVector)
.withTopK(topK)
.withMetricType(MetricType.L2)
.build();
SearchResult searchResult = milvusClient.search(searchParam);
// 解析搜索结果
return parseSearchResult(searchResult);
}
public void buildCourseVectors(List<Course> courses) {
List<InsertParam> insertParams = courses.stream()
.map(course -> {
float[] vector = courseVectorService.courseToVector(course);
return InsertParam.newBuilder()
.withCollectionName("course_contents")
.withPrimaryField(course.getId())
.withVectorField(vector)
.withAdditionalFields(course.getFeatures())
.build();
})
.collect(Collectors.toList());
// 批量插入向量数据
insertParams.forEach(milvusClient::insert);
}
private List<Course> parseSearchResult(SearchResult searchResult) {
return searchResult.getResults().stream()
.map(result -> {
Course course = new Course();
course.setId(result.getId());
course.setSimilarityScore(result.getScore());
return course;
})
.collect(Collectors.toList());
}
}
@Component
public class CourseVectorService {
@Autowired
private EmbeddingModel embeddingModel;
public float[] courseToVector(Course course) {
// 构建课程特征文本
String featureText = String.format(
"课程标题:%s,课程描述:%s,难度级别:%s,分类:%s,标签:%s",
course.getTitle(),
course.getDescription(),
course.getDifficultyLevel(),
course.getCategory(),
course.getTags()
);
// 使用Embedding模型转换为向量
return embeddingModel.embed(featureText);
}
}
技术要点:
- 向量化:将课程特征转换为数值向量
- 相似度计算:使用余弦相似度或欧氏距离
- Milvus/Chroma:专门的向量数据库
- 批量处理:提高数据导入效率
- 特征工程:选择合适的课程特征
3. RAG技术在教育智能辅导中的应用
业务场景:提高智能辅导回答准确性,基于教材和课程资料提供专业的教育咨询服务。
技术实现:
java
@Service
public class EducationTutorService {
@Autowired
private DocumentRetriever documentRetriever;
@Autowired
private ChatModel chatModel;
@Autowired
private KnowledgeBaseService knowledgeBaseService;
public TutorResponse handleStudentQuery(StudentQuery query) {
// 1. 检索相关知识文档
List<Document> relevantDocuments = documentRetriever.retrieve(
query.getQuestion(),
5
);
// 2. 构建增强提示
String enhancedPrompt = buildEnhancedPrompt(query, relevantDocuments);
// 3. 调用大模型生成回答
AiResponse aiResponse = chatModel.complete(enhancedPrompt);
// 4. 处理和验证回答
TutorResponse response = processResponse(aiResponse, relevantDocuments);
// 5. 记录对话历史
saveConversationHistory(query, response);
return response;
}
private String buildEnhancedPrompt(StudentQuery query, List<Document> documents) {
String context = documents.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
return String.format(
"基于以下教育知识库信息,请回答学生的问题:" +
"\n\n知识库信息:\n%s" +
"\n\n学生问题:%s" +
"\n\n请提供准确、专业的教育回答,如果知识库中没有相关信息,请明确告知。",
context,
query.getQuestion()
);
}
private TutorResponse processResponse(AiResponse aiResponse, List<Document> documents) {
TutorResponse response = new TutorResponse();
response.setAnswer(aiResponse.getContent());
response.setSources(documents.stream()
.map(Document::getSource)
.collect(Collectors.toList()));
response.setConfidence(calculateConfidence(documents));
return response;
}
private double calculateConfidence(List<Document> documents) {
// 基于检索到的文档数量和质量计算置信度
return Math.min(1.0, documents.size() * 0.2);
}
@EventListener
public void handleNewDocument(DocumentAddedEvent event) {
// 当知识库新增文档时,更新向量索引
documentRetriever.updateIndex(event.getDocument());
}
}
技术要点:
- 文档检索:基于语义相似度查找相关知识
- 提示增强:结合检索结果构建上下文
- 回答验证:评估回答的准确性和置信度
- 知识库维护:定期更新和优化文档索引
- 对话管理:记录学生交互历史
4. Embedding模型实现语义搜索
业务场景:理解学生自然语言查询意图,提供精准的教育资源搜索和学习建议。
技术实现:
java
@Service
public class EducationSearchService {
@Autowired
private EmbeddingModel embeddingModel;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Autowired
private QueryIntentAnalyzer intentAnalyzer;
public SearchResult semanticSearch(String query) {
// 1. 分析查询意图
QueryIntent intent = intentAnalyzer.analyzeIntent(query);
// 2. 将查询转换为向量
float[] queryVector = embeddingModel.embed(query);
// 3. 构建语义搜索查询
NativeSearchQuery searchQuery = buildSemanticSearchQuery(queryVector, intent);
// 4. 执行搜索
SearchHits<EducationDocument> searchHits =
elasticsearchTemplate.search(searchQuery, EducationDocument.class);
// 5. 处理和排序结果
return processSearchResults(searchHits, query);
}
private NativeSearchQuery buildSemanticSearchQuery(float[] queryVector, QueryIntent intent) {
// 构建向量相似度查询
VectorScriptQueryBuilder vectorQuery = QueryBuilders
.scriptQuery(new Script(
"cosineSimilarity(params.query_vector, 'doc_vector') + 1.0",
Collections.singletonMap("query_vector", queryVector)
));
// 构建意图过滤器
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(vectorQuery)
.filter(buildIntentFilter(intent));
return new NativeSearchQueryBuilder()
.withQuery(boolQuery)
.withPageable(PageRequest.of(0, 10))
.build();
}
private BoolQueryBuilder buildIntentFilter(QueryIntent intent) {
BoolQueryBuilder filter = QueryBuilders.boolQuery();
switch (intent.getType()) {
case COURSE_SEARCH:
filter.must(QueryBuilders.termQuery("category", "course"));
break;
case ASSIGNMENT_HELP:
filter.must(QueryBuilders.termQuery("category", "assignment"));
break;
case EXAM_PREPARATION:
filter.must(QueryBuilders.termQuery("category", "exam"));
break;
default:
filter.must(QueryBuilders.existsQuery("content"));
}
return filter;
}
private SearchResult processSearchResults(SearchHits<EducationDocument> searchHits, String query) {
SearchResult result = new SearchResult();
result.setQuery(query);
result.setTotalResults(searchHits.getTotalHits());
List<SearchResultItem> items = searchHits.stream()
.map(hit -> {
SearchResultItem item = new SearchResultItem();
item.setId(hit.getId());
item.setTitle(hit.getContent().getTitle());
item.setSummary(hit.getContent().getSummary());
item.setRelevanceScore(hit.getScore());
item.setUrl(hit.getContent().getUrl());
return item;
})
.sorted(Comparator.comparingDouble(SearchResultItem::getRelevanceScore).reversed())
.collect(Collectors.toList());
result.setItems(items);
return result;
}
// 定期重新索引文档
@Scheduled(fixedRate = 3600000) // 每小时执行一次
public void reindexDocuments() {
List<EducationDocument> documents = educationDocumentRepository.findAll();
documents.forEach(doc -> {
float[] vector = embeddingModel.embed(doc.getContent());
doc.setVector(vector);
educationDocumentRepository.save(doc);
});
}
}
技术要点:
- 意图识别:分析查询的真实意图
- 向量化搜索:将查询转换为向量进行相似度匹配
- 多维度过滤:结合意图和类别进行精确过滤
- 相关性排序:基于相似度分数排序结果
- 定期更新:保持向量索引的时效性