inject game catalog context into few-shot prompt
This commit is contained in:
@@ -49,6 +49,7 @@ public class SelectAiService {
|
||||
private final ObjectMapper objectMapper;
|
||||
private final QaVectorService qaVectorService;
|
||||
private final GameScopeService gameScopeService;
|
||||
private final GameCatalogVectorService gameCatalogVectorService;
|
||||
|
||||
public SelectAiService(
|
||||
BackofficeProperties properties,
|
||||
@@ -56,7 +57,8 @@ public class SelectAiService {
|
||||
Clock clock,
|
||||
ObjectMapper objectMapper,
|
||||
QaVectorService qaVectorService,
|
||||
GameScopeService gameScopeService
|
||||
GameScopeService gameScopeService,
|
||||
GameCatalogVectorService gameCatalogVectorService
|
||||
) {
|
||||
this.properties = properties;
|
||||
this.bearerTokenService = bearerTokenService;
|
||||
@@ -64,6 +66,7 @@ public class SelectAiService {
|
||||
this.objectMapper = objectMapper;
|
||||
this.qaVectorService = qaVectorService;
|
||||
this.gameScopeService = gameScopeService;
|
||||
this.gameCatalogVectorService = gameCatalogVectorService;
|
||||
}
|
||||
|
||||
public JsonNode generateAndExecute(String bearerToken, String prompt) {
|
||||
@@ -80,7 +83,9 @@ public class SelectAiService {
|
||||
BackofficeProperties.SelectAi selectAi = requiredSelectAi();
|
||||
ResolvedExecutionScope scope = resolveExecutionScope(bearerToken, normalizedPrompt, scopeGameKey);
|
||||
|
||||
EnrichedPrompt enrichedPrompt = enrichWithFewShot(bearerToken, selectAi, scope.prompt());
|
||||
GameContext gameContext = resolveGameContext(bearerToken, normalizedPrompt);
|
||||
EnrichedPrompt enrichedPrompt = enrichWithFewShot(
|
||||
bearerToken, selectAi, gameContext.prompt(scope.prompt()));
|
||||
String generatedSql = generate(selectAi, enrichedPrompt.prompt(), "showsql");
|
||||
String normalizedSql = validateReadOnlySql(generatedSql);
|
||||
QueryExecution execution = executeReadOnly(selectAi, normalizedSql);
|
||||
@@ -88,6 +93,9 @@ public class SelectAiService {
|
||||
response.put("status", "SHOWSQL_AND_EXECUTED");
|
||||
response.put("profile", selectAi.profile());
|
||||
response.put("originalPrompt", normalizedPrompt);
|
||||
response.put("gameScopeType", gameContext.scopeType());
|
||||
response.put("gameScopeStatus", gameContext.status());
|
||||
response.put("gameCandidateCount", gameContext.candidates().size());
|
||||
if (scope.gameKey() != null) {
|
||||
response.put("scopeGameKey", scope.gameKey());
|
||||
response.put("scopeDisplayName", scope.displayName());
|
||||
@@ -107,6 +115,18 @@ public class SelectAiService {
|
||||
return response;
|
||||
}
|
||||
|
||||
private GameContext resolveGameContext(String bearerToken, String question) {
|
||||
try {
|
||||
List<GameCatalogVectorService.GameCandidate> candidates =
|
||||
gameCatalogVectorService.search(bearerToken, question, 5);
|
||||
String status = candidates.isEmpty() ? "NO_MATCH" : "RESOLVED";
|
||||
String scopeType = candidates.isEmpty() ? "UNKNOWN" : "SINGLE_GAME";
|
||||
return new GameContext(scopeType, status, candidates);
|
||||
} catch (Exception ignored) {
|
||||
return new GameContext("UNKNOWN", "UNAVAILABLE", List.of());
|
||||
}
|
||||
}
|
||||
|
||||
private ResolvedExecutionScope resolveExecutionScope(
|
||||
String bearerToken, String originalPrompt, String scopeGameKey
|
||||
) {
|
||||
@@ -397,4 +417,27 @@ public class SelectAiService {
|
||||
String prompt, String status, int exampleCount, List<QaVectorService.VectorExample> examples) {}
|
||||
|
||||
private record ResolvedExecutionScope(String prompt, String gameKey, String displayName) {}
|
||||
|
||||
private record GameContext(
|
||||
String scopeType, String status, List<GameCatalogVectorService.GameCandidate> candidates) {
|
||||
String prompt(String original) {
|
||||
StringBuilder context = new StringBuilder();
|
||||
context.append("[GAME CATALOG MATCH]\n")
|
||||
.append("scope_type: ").append(scopeType).append('\n')
|
||||
.append("status: ").append(status).append('\n');
|
||||
for (GameCatalogVectorService.GameCandidate candidate : candidates) {
|
||||
context.append("game_key: ").append(candidate.gameKey()).append('\n')
|
||||
.append("game_id: ").append(candidate.gameId()).append('\n')
|
||||
.append("game_prefix: ").append(candidate.gamePrefix()).append('\n')
|
||||
.append("game_name: ").append(candidate.gameName()).append('\n')
|
||||
.append("matched_aliases: ").append(candidate.aliases()).append('\n')
|
||||
.append("cosine_distance: ").append(candidate.similarity()).append('\n');
|
||||
}
|
||||
context.append("[GAME SCOPE POLICY]\n")
|
||||
.append("Use only DB-approved game scope when status is RESOLVED. ")
|
||||
.append("When status is NO_MATCH or UNKNOWN, do not infer a game-specific object.\n\n")
|
||||
.append(original);
|
||||
return context.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user