chain ADB chat game extraction before vector resolution
This commit is contained in:
@@ -4,16 +4,44 @@ import com.cloudhandson.vpdbackoffice.config.BackofficeProperties;
|
|||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class GameCatalogVectorService {
|
public class GameCatalogVectorService {
|
||||||
private final BackofficeProperties properties;
|
private final BackofficeProperties properties;
|
||||||
private final BearerTokenService bearerTokenService;
|
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.properties = properties;
|
||||||
this.bearerTokenService = bearerTokenService;
|
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) {
|
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 GameCandidate(String gameKey, String gameId, String gamePrefix, String gameName, String aliases, double similarity) {}
|
||||||
|
public record Resolution(String scopeType, List<GameCandidate> candidates) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ public class McpSseService {
|
|||||||
response = gameScopeResponse(gameScopeService.resolve(
|
response = gameScopeResponse(gameScopeService.resolve(
|
||||||
token, arguments.path("question").asText("")));
|
token, arguments.path("question").asText("")));
|
||||||
} else if (gameCatalogTool) {
|
} else if (gameCatalogTool) {
|
||||||
response = gameCatalogVectorResponse(gameCatalogVectorService.search(
|
response = gameCatalogVectorResponse(gameCatalogVectorService.resolve(
|
||||||
token, arguments.path("question").asText(""), arguments.path("topK").asInt(5)));
|
token, arguments.path("question").asText(""), arguments.path("topK").asInt(5)));
|
||||||
} else {
|
} else {
|
||||||
String prompt = arguments.path("prompt").asText("");
|
String prompt = arguments.path("prompt").asText("");
|
||||||
@@ -247,11 +247,12 @@ public class McpSseService {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectNode gameCatalogVectorResponse(List<GameCatalogVectorService.GameCandidate> candidates) {
|
private ObjectNode gameCatalogVectorResponse(GameCatalogVectorService.Resolution resolution) {
|
||||||
ObjectNode response = objectMapper.createObjectNode();
|
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");
|
ArrayNode items = response.putArray("matchedGames");
|
||||||
for (GameCatalogVectorService.GameCandidate c : candidates) {
|
for (GameCatalogVectorService.GameCandidate c : resolution.candidates()) {
|
||||||
ObjectNode item = items.addObject();
|
ObjectNode item = items.addObject();
|
||||||
item.put("gameKey", c.gameKey()); item.put("gameId", c.gameId());
|
item.put("gameKey", c.gameKey()); item.put("gameId", c.gameId());
|
||||||
item.put("gamePrefix", c.gamePrefix()); item.put("gameName", c.gameName());
|
item.put("gamePrefix", c.gamePrefix()); item.put("gameName", c.gameName());
|
||||||
|
|||||||
Reference in New Issue
Block a user