-
Notifications
You must be signed in to change notification settings - Fork 0
⚡ virtual AI + Gemini 서비스 합병 #205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
요약WalkthroughGoogle Cloud Vertex AI Gemini 클라이언트 라이브러리 의존성을 추가하고, 이를 활용하는 새로운 Gemini 기반 외부 링크 추천 서비스를 구현했습니다. 기존 Perplexity 서비스를 Gemini 서비스로 대체하여 외부 링크 검색 기능을 통합했습니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 주의 사항:
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (8)
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendServiceImpl.java (2)
56-70: 주석 및 에러 메시지가 이전 서비스(Perplexity)를 참조합니다.Gemini 서비스로 전환되었지만, 코드 주석(Line 56)과 에러 메시지(Line 68)가 여전히 "Perplexity"를 참조하고 있습니다. 또한
System.err.println대신 SLF4J 로거 사용을 권장합니다.- // Perplexity 기반 외부 추천 받기 + // Gemini 기반 외부 추천 받기 List<RecommendedLinkResponse> external; try { external = geminiExternalSearchService.searchExternalLinks( recentUrls, tagNames, externalLimit, jobName, gender ); } catch (Exception e) { // 🔴 어떤 예외가 와도 외부는 포기하고 빈 리스트로 폴백 - System.err.println("[Perplexity] 외부 추천 실패: " + e.getMessage()); + log.warn("[Gemini] 외부 추천 실패: {}", e.getMessage()); external = List.of(); }로거 사용을 위해 클래스에
@Slf4j어노테이션 추가가 필요합니다:@Slf4j @Service @RequiredArgsConstructor public class ExternalRecommendServiceImpl implements ExternalRecommendService {
10-11: 주석 처리된 코드를 제거하세요.더 이상 사용하지 않는 PerplexityExternalSearchService 관련 import와 필드가 주석 처리되어 있습니다. 코드 정리를 위해 완전히 제거하는 것이 좋습니다.
-// import com.umc.linkyou.service.curation.perplexity.PerplexityExternalSearchService; import com.umc.linkyou.service.curation.gemini.GeminiExternalSearchService;- // private final PerplexityExternalSearchService perplexityExternalSearchService; // Perplexity 사용 private final GeminiExternalSearchService geminiExternalSearchService; // Gemini 사용Also applies to: 29-30
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendWorker.java (2)
36-72: Javadoc, 주석, 로그 메시지가 여전히 Perplexity를 참조합니다.Gemini 서비스로 전환되었지만 여러 곳에서 "Perplexity"를 참조하고 있습니다:
- Line 37: Javadoc
"Perplexity 호출"- Line 62: 주석
"// Perplexity"- Line 69, 71: 로그 메시지
"[Perplexity]"코드 유지보수성을 위해 모두 업데이트가 필요합니다.
/** - * Perplexity 호출 → curation_linku(type=EXTERNAL)에 url/title/imageUrl 저장 + * Gemini 호출 → curation_linku(type=EXTERNAL)에 url/title/imageUrl 저장 * 트랜잭션 경계는 이 Worker에서 관리한다. */- // Perplexity + // Gemini List<RecommendedLinkResponse> external; try { long t0 = System.currentTimeMillis(); external = geminiExternalSearchService.searchExternalLinks( recentUrls, topTags, externalLimit, jobName, gender ); - log.info("[Perplexity] elapsed={}ms", System.currentTimeMillis() - t0); + log.info("[Gemini] elapsed={}ms", System.currentTimeMillis() - t0); } catch (Exception e) { - log.warn("[Perplexity] 외부 추천 실패: {}", e.toString()); + log.warn("[Gemini] 외부 추천 실패: {}", e.toString()); external = List.of(); }
12-13: 주석 처리된 코드를 제거하세요.ExternalRecommendServiceImpl과 마찬가지로, 더 이상 사용하지 않는 PerplexityExternalSearchService 관련 코드를 완전히 제거하는 것이 좋습니다.
Also applies to: 31-32
src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java (4)
112-136: 시스템 프롬프트를 유저 프롬프트와 분리하여 설정하세요.현재 시스템 프롬프트와 유저 프롬프트가 문자열 연결로 전달되고 있습니다. Vertex AI GenerativeModel은
setSystemInstruction을 통해 시스템 지시사항을 별도로 설정할 수 있습니다. 이렇게 하면 모델이 역할과 규칙을 더 명확하게 인식합니다.모델 빌더에서 시스템 지시사항을 설정하거나, 런타임에 Content를 분리하여 전달하는 방식을 고려하세요.
64-68: 검색 결과의 일관성을 위해 temperature 값을 낮추는 것을 고려하세요.
temperature: 0.9는 높은 창의성을 유도하지만, 외부 링크 검색처럼 사실 기반 결과가 필요한 경우에는 일관성이 떨어질 수 있습니다. 0.3~0.5 정도의 값이 더 안정적인 결과를 제공할 수 있습니다.GenerationConfig generationConfig = GenerationConfig.newBuilder() .setMaxOutputTokens(2048) - .setTemperature(0.9f) // 창의성(다양한 검색 결과)을 위해 높임 + .setTemperature(0.4f) // 검색 결과 일관성을 위해 낮춤 .setResponseMimeType("application/json") // JSON 응답 강제 .build();
142-154: JSON 파싱 실패에 대한 방어 코드를 강화하세요.Gemini 응답이 예상 JSON 형식과 다를 경우 파싱 실패가 발생할 수 있습니다. 현재 외부 catch 블록에서 처리되지만, 더 구체적인 로깅이 도움이 될 수 있습니다.
- // 5. JSON 파싱 - List<Map<String, String>> parsed = objectMapper.readValue(jsonResponse, new TypeReference<>() {}); + // 5. JSON 파싱 (Markdown 코드블록 제거) + String cleanJson = jsonResponse.trim(); + if (cleanJson.startsWith("```")) { + cleanJson = cleanJson.replaceAll("^```json?\\s*", "").replaceAll("```$", "").trim(); + } + List<Map<String, String>> parsed = objectMapper.readValue(cleanJson, new TypeReference<>() {});
21-21: 사용하지 않는 import를 제거하세요.
java.io.IOException이 import되어 있지만 코드에서 사용되지 않습니다.-import java.io.IOException;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
build.gradle(1 hunks)src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java(1 hunks)src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendServiceImpl.java(3 hunks)src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendWorker.java(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java (2)
src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendServiceImpl.java (1)
Service(20-87)src/main/java/com/umc/linkyou/service/curation/linku/ExternalRecommendWorker.java (1)
Service(22-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
| // Google Cloud Vertex AI (Gemini) | ||
| implementation 'com.google.cloud:google-cloud-vertexai:1.14.0' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
google-cloud-vertexai latest version maven central 2025
💡 Result:
Latest Maven Central version (Nov 30, 2025): com.google.cloud:google-cloud-vertexai — 1.40.0 (released Nov 15, 2025). [1][2]
Maven coordinate:
com.google.cloud
google-cloud-vertexai
1.40.0
Sources:
[1] Google Cloud Java docs (google-cloud-vertexai overview, 1.40.0).
[2] Maven Central (repo1.maven.org) listing for com/google/cloud/google-cloud-vertexai showing 1.40.0 (2025-11-15).
Update google-cloud-vertexai to the latest version.
The dependency is outdated. Version 1.14.0 is significantly behind the current latest release (1.40.0, released November 15, 2025). Update the dependency to:
implementation 'com.google.cloud:google-cloud-vertexai:1.40.0'
This 26-version gap includes important updates, security patches, and feature improvements that should be incorporated.
🤖 Prompt for AI Agents
In build.gradle around lines 88 to 89 the google-cloud-vertexai dependency is
pinned to 1.14.0 and needs to be updated; change the implementation coordinate
to use the latest release (replace 1.14.0 with 1.40.0) so the project pulls the
updated library with recent fixes and features, then run a dependency
refresh/build to verify no breaking changes and update any code or tests if the
newer client API requires adjustments.
| @PostConstruct | ||
| public void init() { | ||
| try { | ||
| // 1. Vertex AI 클라이언트 초기화 | ||
| // (참고: 로컬 개발 시 환경변수 GOOGLE_APPLICATION_CREDENTIALS 설정 필수) | ||
| this.vertexAI = new VertexAI(projectId, location); | ||
|
|
||
| // 2. 구글 검색 도구(Grounding) 설정 | ||
| Tool googleSearchTool = Tool.newBuilder() | ||
| .setGoogleSearchRetrieval( | ||
| GoogleSearchRetrieval.newBuilder().build() | ||
| ) | ||
| .build(); | ||
|
|
||
| // 3. 생성 설정 (JSON 포맷 강제 등) | ||
| GenerationConfig generationConfig = GenerationConfig.newBuilder() | ||
| .setMaxOutputTokens(2048) | ||
| .setTemperature(0.9f) // 창의성(다양한 검색 결과)을 위해 높임 | ||
| .setResponseMimeType("application/json") // JSON 응답 강제 | ||
| .build(); | ||
|
|
||
| // 4. 모델 생성 | ||
| this.model = new GenerativeModel.Builder() | ||
| .setModelName(modelName) | ||
| .setVertexAi(vertexAI) | ||
| .setTools(Collections.singletonList(googleSearchTool)) | ||
| .setGenerationConfig(generationConfig) | ||
| .build(); | ||
|
|
||
| log.info("✅ Gemini Search Service 초기화 완료 (Project: {}, Location: {})", projectId, location); | ||
|
|
||
| } catch (Exception e) { | ||
| log.error("❌ Gemini 초기화 실패: GCP 인증 파일이나 설정을 확인하세요.", e); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
초기화 실패 시 서비스가 비정상 상태로 유지됩니다.
init() 메서드에서 예외 발생 시 로그만 남기고 계속 진행되어 model이 null인 상태로 서비스가 Spring 컨테이너에 등록됩니다. 이후 searchExternalLinks() 호출 시 NullPointerException이 발생할 수 있습니다.
초기화 실패 시 애플리케이션 시작을 중단하거나, searchExternalLinks()에서 model null 체크를 추가하세요:
@PostConstruct
public void init() {
try {
// ... 초기화 코드 ...
log.info("✅ Gemini Search Service 초기화 완료 (Project: {}, Location: {})", projectId, location);
} catch (Exception e) {
- log.error("❌ Gemini 초기화 실패: GCP 인증 파일이나 설정을 확인하세요.", e);
+ log.error("❌ Gemini 초기화 실패: GCP 인증 파일이나 설정을 확인하세요.", e);
+ throw new IllegalStateException("Gemini 서비스 초기화 실패", e);
}
}또는 searchExternalLinks() 시작 부분에 방어 코드를 추가하세요:
if (this.model == null) {
log.warn("Gemini 모델이 초기화되지 않음 - 빈 결과 반환");
return Collections.emptyList();
}🤖 Prompt for AI Agents
In
src/main/java/com/umc/linkyou/service/curation/gemini/GeminiExternalSearchService.java
around lines 49–83, the init() catch currently only logs the exception which
leaves the service registered with a null model; update the catch to fail fast
by rethrowing a RuntimeException (wrap the caught Exception with a clear
message) so Spring does not start the app in a broken state, and additionally
add a defensive null-check at the start of searchExternalLinks() that logs a
warning and returns an empty list if this.model is null to avoid
NullPointerException in case initialization still failed.
#️⃣연관된 이슈
📝작업 내용
스크린샷 (선택)
💬리뷰 요구사항(선택)
Summary by CodeRabbit
개선사항
✏️ Tip: You can customize this high-level summary in your review settings.