chain ADB chat game extraction before vector resolution

This commit is contained in:
devmrko
2026-07-27 14:14:20 +09:00
parent ed3139f9f7
commit beaef2d171
2 changed files with 35 additions and 5 deletions

View File

@@ -4,16 +4,44 @@ import com.cloudhandson.vpdbackoffice.config.BackofficeProperties;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
@Service
public class GameCatalogVectorService {
private final BackofficeProperties properties;
private final BearerTokenService bearerTokenService;
private final ObjectMapper objectMapper;
public GameCatalogVectorService(BackofficeProperties properties, BearerTokenService bearerTokenService) {
public GameCatalogVectorService(BackofficeProperties properties, BearerTokenService bearerTokenService, ObjectMapper objectMapper) {
this.properties = properties;
this.bearerTokenService = bearerTokenService;
this.objectMapper = objectMapper;
}
public Resolution resolve(String token, String question, int topK) {
String raw = extractMentions(token, question);
try {
JsonNode json = objectMapper.readTree(raw);
String hint = json.path("scope_hint").asText("UNKNOWN");
List<GameCandidate> all = new ArrayList<>();
for (JsonNode mention : json.path("game_mentions")) {
all.addAll(search(token, mention.asText(""), topK));
}
return new Resolution(hint, all);
} catch (Exception e) { throw new AppException("게임명 추출 결과 JSON 파싱 실패: " + e.getMessage()); }
}
private String extractMentions(String token, String question) {
BackofficeProperties.SelectAi db = properties.selectAi();
try (Connection c = DriverManager.getConnection(db.dbUrl(), db.dbUsername(), db.dbPassword());
CallableStatement s = c.prepareCall("{ ? = call sg_game_extract_mentions(?) }")) {
s.registerOutParameter(1, Types.CLOB); s.setString(2, question); s.execute();
Clob value = (Clob) s.getObject(1);
return value == null ? "{\"game_mentions\":[],\"scope_hint\":\"UNKNOWN\"}"
: value.getSubString(1, (int) value.length());
} catch (SQLException e) { throw new AppException("ADB 게임명 추출 실패: " + e.getMessage()); }
}
public List<GameCandidate> search(String token, String question, int topK) {
@@ -34,4 +62,5 @@ public class GameCatalogVectorService {
}
public record GameCandidate(String gameKey, String gameId, String gamePrefix, String gameName, String aliases, double similarity) {}
public record Resolution(String scopeType, List<GameCandidate> candidates) {}
}

View File

@@ -194,7 +194,7 @@ public class McpSseService {
response = gameScopeResponse(gameScopeService.resolve(
token, arguments.path("question").asText("")));
} else if (gameCatalogTool) {
response = gameCatalogVectorResponse(gameCatalogVectorService.search(
response = gameCatalogVectorResponse(gameCatalogVectorService.resolve(
token, arguments.path("question").asText(""), arguments.path("topK").asInt(5)));
} else {
String prompt = arguments.path("prompt").asText("");
@@ -247,11 +247,12 @@ public class McpSseService {
return response;
}
private ObjectNode gameCatalogVectorResponse(List<GameCatalogVectorService.GameCandidate> candidates) {
private ObjectNode gameCatalogVectorResponse(GameCatalogVectorService.Resolution resolution) {
ObjectNode response = objectMapper.createObjectNode();
response.put("status", candidates.isEmpty() ? "NO_MATCH" : "CANDIDATES");
response.put("scopeType", resolution.scopeType());
response.put("status", resolution.candidates().isEmpty() ? "NO_MATCH" : "CANDIDATES");
ArrayNode items = response.putArray("matchedGames");
for (GameCatalogVectorService.GameCandidate c : candidates) {
for (GameCatalogVectorService.GameCandidate c : resolution.candidates()) {
ObjectNode item = items.addObject();
item.put("gameKey", c.gameKey()); item.put("gameId", c.gameId());
item.put("gamePrefix", c.gamePrefix()); item.put("gameName", c.gameName());