refs #737: move game query planning into ADB MCP tool

This commit is contained in:
devmrko
2026-07-27 16:39:58 +09:00
parent 8dffa2f515
commit 00a939f9a1
6 changed files with 263 additions and 288 deletions

View File

@@ -200,12 +200,12 @@ public class McpSseService {
} else if (gameScopeTool) {
response = gameScopeResponse(gameScopeService.resolve(
token, arguments.path("question").asText("")));
} else if (gameCatalogTool) {
response = gameCatalogVectorResponse(gameCatalogVectorService.resolve(
token, arguments.path("question").asText(""), arguments.path("topK").asInt(5)));
} else if (gameQueryPlanTool) {
response = gameQueryPlanResponse(gameCatalogVectorService.resolve(
token, arguments.path("question").asText(""), arguments.path("topK").asInt(5)));
} else if (gameCatalogTool || gameQueryPlanTool) {
response = gameCatalogVectorService.queryPlan(
token,
arguments.path("question").asText(""),
arguments.path("topK").asInt(5)
);
} else {
String prompt = arguments.path("prompt").asText("");
response = queryTool
@@ -258,50 +258,6 @@ public class McpSseService {
return response;
}
private ObjectNode gameCatalogVectorResponse(GameCatalogVectorService.Resolution resolution) {
ObjectNode response = objectMapper.createObjectNode();
response.put("scopeType", resolution.scopeType());
boolean hasUnmatched = resolution.mentions().stream()
.anyMatch(mention -> "UNMATCHED".equals(mention.status()));
response.put("status", resolution.candidates().isEmpty()
? "NO_MATCH" : hasUnmatched ? "PARTIAL" : "CANDIDATES");
ArrayNode items = response.putArray("matchedGames");
for (GameCatalogVectorService.GameCandidate c : resolution.candidates()) {
addGameCandidate(items.addObject(), c, true);
}
ArrayNode mentionResults = response.putArray("mentionResults");
for (GameCatalogVectorService.MentionResolution mention
: resolution.mentions()) {
ObjectNode item = mentionResults.addObject();
item.put("mention", mention.mention());
item.put("status", mention.status());
if (mention.matchedGame() != null) {
item.put("matchedGameKey", mention.matchedGame().gameKey());
}
ArrayNode candidates = item.putArray("candidateGames");
for (GameCatalogVectorService.GameCandidate candidate
: mention.candidates()) {
addGameCandidate(candidates.addObject(), candidate, false);
}
}
return response;
}
private void addGameCandidate(
ObjectNode item,
GameCatalogVectorService.GameCandidate candidate,
boolean includeAliases
) {
item.put("gameKey", candidate.gameKey());
item.put("gameId", candidate.gameId());
item.put("gamePrefix", candidate.gamePrefix());
item.put("gameName", candidate.gameName());
if (includeAliases) {
item.put("aliases", candidate.aliases());
}
item.put("cosineDistance", candidate.similarity());
}
private ObjectNode qaVectorStoreResponse(QaVectorService.VectorStoreResult stored) {
ObjectNode response = objectMapper.createObjectNode();
response.put("status", "QA_VECTOR_STORED");
@@ -409,44 +365,6 @@ public class McpSseService {
-1L, "게임 질의 계획", SELECT_AI_TOOL_PATH);
}
private ObjectNode gameQueryPlanResponse(GameCatalogVectorService.Resolution resolution) {
ObjectNode response = gameCatalogVectorResponse(resolution);
List<GameCatalogVectorService.MentionResolution> unmatchedMentions =
resolution.mentions().stream()
.filter(mention -> "UNMATCHED".equals(mention.status()))
.toList();
response.put("status", resolution.candidates().isEmpty()
? "NO_MATCH" : unmatchedMentions.isEmpty() ? "SUPPORTED" : "PARTIAL");
ArrayNode supported = response.putArray("supportedGames");
for (GameCatalogVectorService.GameCandidate c : resolution.candidates()) {
ObjectNode item = supported.addObject();
item.put("gameKey", c.gameKey());
item.put("gameId", c.gameId());
item.put("gameName", c.gameName());
item.put("gamePrefix", c.gamePrefix());
item.put("cosineDistance", c.similarity());
}
ArrayNode unmatched = response.putArray("unmatchedGames");
for (GameCatalogVectorService.MentionResolution mention
: unmatchedMentions) {
ObjectNode item = unmatched.addObject();
item.put("mention", mention.mention());
item.put("reasonCode", "NO_CATALOG_IDENTITY_MATCH");
}
response.put("nextAction", resolution.candidates().isEmpty()
? "STOP_NO_MATCH"
: ("MULTI_GAME".equals(resolution.scopeType())
? "CALL_FEWSHOT_PER_GAME" : "CALL_FEWSHOT"));
String mode = switch (resolution.scopeType()) {
case "SINGLE_GAME" -> "SINGLE";
case "MULTI_GAME" -> "FAN_OUT";
case "ALL_GAMES" -> "GROUPED";
default -> "BLOCKED";
};
response.put("executionMode", mode);
return response;
}
private String selectAiProfile() {
BackofficeProperties.SelectAi selectAi = properties == null ? null : properties.selectAi();
if (selectAi == null || selectAi.profile() == null || selectAi.profile().isBlank()) {