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 ObjectMapper objectMapper;
|
||||||
private final QaVectorService qaVectorService;
|
private final QaVectorService qaVectorService;
|
||||||
private final GameScopeService gameScopeService;
|
private final GameScopeService gameScopeService;
|
||||||
|
private final GameCatalogVectorService gameCatalogVectorService;
|
||||||
|
|
||||||
public SelectAiService(
|
public SelectAiService(
|
||||||
BackofficeProperties properties,
|
BackofficeProperties properties,
|
||||||
@@ -56,7 +57,8 @@ public class SelectAiService {
|
|||||||
Clock clock,
|
Clock clock,
|
||||||
ObjectMapper objectMapper,
|
ObjectMapper objectMapper,
|
||||||
QaVectorService qaVectorService,
|
QaVectorService qaVectorService,
|
||||||
GameScopeService gameScopeService
|
GameScopeService gameScopeService,
|
||||||
|
GameCatalogVectorService gameCatalogVectorService
|
||||||
) {
|
) {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
this.bearerTokenService = bearerTokenService;
|
this.bearerTokenService = bearerTokenService;
|
||||||
@@ -64,6 +66,7 @@ public class SelectAiService {
|
|||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
this.qaVectorService = qaVectorService;
|
this.qaVectorService = qaVectorService;
|
||||||
this.gameScopeService = gameScopeService;
|
this.gameScopeService = gameScopeService;
|
||||||
|
this.gameCatalogVectorService = gameCatalogVectorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonNode generateAndExecute(String bearerToken, String prompt) {
|
public JsonNode generateAndExecute(String bearerToken, String prompt) {
|
||||||
@@ -80,7 +83,9 @@ public class SelectAiService {
|
|||||||
BackofficeProperties.SelectAi selectAi = requiredSelectAi();
|
BackofficeProperties.SelectAi selectAi = requiredSelectAi();
|
||||||
ResolvedExecutionScope scope = resolveExecutionScope(bearerToken, normalizedPrompt, scopeGameKey);
|
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 generatedSql = generate(selectAi, enrichedPrompt.prompt(), "showsql");
|
||||||
String normalizedSql = validateReadOnlySql(generatedSql);
|
String normalizedSql = validateReadOnlySql(generatedSql);
|
||||||
QueryExecution execution = executeReadOnly(selectAi, normalizedSql);
|
QueryExecution execution = executeReadOnly(selectAi, normalizedSql);
|
||||||
@@ -88,6 +93,9 @@ public class SelectAiService {
|
|||||||
response.put("status", "SHOWSQL_AND_EXECUTED");
|
response.put("status", "SHOWSQL_AND_EXECUTED");
|
||||||
response.put("profile", selectAi.profile());
|
response.put("profile", selectAi.profile());
|
||||||
response.put("originalPrompt", normalizedPrompt);
|
response.put("originalPrompt", normalizedPrompt);
|
||||||
|
response.put("gameScopeType", gameContext.scopeType());
|
||||||
|
response.put("gameScopeStatus", gameContext.status());
|
||||||
|
response.put("gameCandidateCount", gameContext.candidates().size());
|
||||||
if (scope.gameKey() != null) {
|
if (scope.gameKey() != null) {
|
||||||
response.put("scopeGameKey", scope.gameKey());
|
response.put("scopeGameKey", scope.gameKey());
|
||||||
response.put("scopeDisplayName", scope.displayName());
|
response.put("scopeDisplayName", scope.displayName());
|
||||||
@@ -107,6 +115,18 @@ public class SelectAiService {
|
|||||||
return response;
|
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(
|
private ResolvedExecutionScope resolveExecutionScope(
|
||||||
String bearerToken, String originalPrompt, String scopeGameKey
|
String bearerToken, String originalPrompt, String scopeGameKey
|
||||||
) {
|
) {
|
||||||
@@ -397,4 +417,27 @@ public class SelectAiService {
|
|||||||
String prompt, String status, int exampleCount, List<QaVectorService.VectorExample> examples) {}
|
String prompt, String status, int exampleCount, List<QaVectorService.VectorExample> examples) {}
|
||||||
|
|
||||||
private record ResolvedExecutionScope(String prompt, String gameKey, String displayName) {}
|
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