fix(mcp): guide Redis database selection

Closes #9684
This commit is contained in:
t8y2 committed 2026-09-20 14:26:36 +08:00
1 parent 765ae7e9c1
commit 8a56515e61
3 files changed
+112 -4

No files matched your search

+20
View File
@@ -77,6 +77,26 @@ describe("AI SQL dialect prompt", () => {
await setLocale("en");
});
it("gives Redis agents database and key-scan safety guidance", () => {
const prompt = buildSystemPrompt("general", context({ connectionName: "Redis", databaseType: "redis", database: "8", selectedDatabases: ["8"] }), "agent");
expect(prompt).toContain("Use dbx_execute_redis_command");
expect(prompt).toContain("db argument");
expect(prompt).toContain("Never send the SELECT command");
expect(prompt).toContain("Use SCAN, not KEYS");
expect(prompt).not.toContain("execute_query tool");
expect(prompt).not.toContain("Put SQL in a fenced");
});
it("keeps Redis ask mode command-oriented", () => {
const prompt = buildSystemPrompt("general", context({ connectionName: "Redis", databaseType: "redis", database: "8" }), "ask");
expect(prompt).toContain("Redis Ask mode");
expect(prompt).toContain("do not generate SELECT commands");
expect(prompt).not.toContain("Generate SQL and explanations only");
expect(buildUserPrompt("query", context({ databaseType: "redis", database: "8" }), "scan task keys", false)).toBe("scan task keys");
});
it("keeps attached text data out of the system prompt", () => {
const attachmentContext = context({
csvFiles: [{ name: "orders.csv", content: "id,total\n1,42", truncated: true }],
+53 -2
View File
@@ -275,8 +275,8 @@ export async function runAgentStream(input: AiRequestInput, history: api.AiMessa
export function buildUserPrompt(action: AiAction, context: AiContext, instruction: string, isZh: boolean): string {
const userRequest = instruction.trim() || (isZh ? "(无额外说明)" : "(No extra instruction provided.)");
const attachedTextData = formatAttachedTextData(context, isZh);
if (isVectorDbType(context.databaseType)) {
// Vector databases: skip SQL action instructions, only send the user's request
if (isVectorDbType(context.databaseType) || context.databaseType === "redis") {
// Non-SQL databases use their system prompt for command guidance.
return [userRequest, attachedTextData].filter(Boolean).join("\n\n");
}
const skill = aiSkillForAction(action);
@@ -315,6 +315,9 @@ export function buildSystemPrompt(action: AiAction, context: AiContext, mode: Ai
if (isVectorDbType(context.databaseType)) {
return buildVectorSystemPrompt(context, mode, custom);
}
if (context.databaseType === "redis") {
return buildRedisSystemPrompt(context, mode, custom);
}
const schema = formatSchema(context);
const resultPreview = context.lastResultPreview ? `\nLast result preview:\n${context.lastResultPreview}\n` : "";
const lastError = context.lastError ? `\nLast error:\n${context.lastError}\n` : "";
@@ -365,6 +368,35 @@ export function buildSystemPrompt(action: AiAction, context: AiContext, mode: Ai
return lines.filter(Boolean).join("\n");
}
function buildRedisSystemPrompt(context: AiContext, mode: AiAssistantMode, custom?: CustomPromptContext): string {
const isZh = isChineseLocale(currentLocale());
const resultPreview = context.lastResultPreview ? `\nLast result preview:\n${context.lastResultPreview}\n` : "";
const lastError = context.lastError ? `\nLast error:\n${context.lastError}\n` : "";
const lines: string[] = [
isZh ? "你是 DBX 内置的 Redis 数据库助手。用中文回复。" : "You are DBX's built-in Redis database assistant. Reply in English.",
isZh ? "精确、保守,并严格使用 Redis 命令语义;不要生成 SQL。" : "Be precise and conservative, follow Redis command semantics, and do not generate SQL.",
...buildModePromptLines(mode, isZh, context.databaseType),
...buildRichContentPromptLines(isZh),
...buildCustomInstructionLines(custom, isZh),
attachmentSafetyInstruction(isZh),
"",
"Database type: redis",
`Connection: ${context.connectionName}`,
`Database: ${context.database}`,
context.selectedDatabases?.length
? isZh
? `已选择 Redis 逻辑数据库:${JSON.stringify(context.selectedDatabases)}。调用工具时使用 db 参数指定目标数据库;MCP 授权仍然生效。`
: `Selected Redis logical databases: ${JSON.stringify(context.selectedDatabases)}. Use the db argument to select the target database; MCP authorization still applies.`
: "",
"",
`Current Redis command:\n${context.currentSql.trim() || "(empty)"}`,
lastError,
resultPreview,
];
return lines.filter(Boolean).join("\n");
}
function buildBasePromptLines(isZh: boolean): string[] {
return [
isZh ? "你是 DBX 内置的数据库助手。用中文回复。" : "You are DBX's built-in database assistant. Reply in English.",
@@ -487,6 +519,25 @@ function buildRichContentPromptLines(isZh: boolean): string[] {
function buildModePromptLines(mode: AiAssistantMode, isZh: boolean, databaseType: DatabaseType): string[] {
const currentTimeGuidance = currentTimeToolGuidance();
if (databaseType === "redis") {
if (mode === "agent") {
return [
isZh ? "你处于 Redis Agent 模式。查询或修改 Redis 数据时使用 dbx_execute_redis_command,不要生成或执行 SQL。" : "You are in Redis Agent mode. Use dbx_execute_redis_command to query or modify Redis data; do not generate or execute SQL.",
isZh
? "逻辑数据库必须通过工具的 db 参数选择;当前或已选择数据库也会由 DBX 作用域自动限定。禁止执行 SELECT 命令切换数据库。"
: "Select the logical database with the tool's db argument; DBX also scopes the current or selected database automatically. Never send the SELECT command to switch databases.",
isZh ? "遍历或匹配键必须使用 SCAN,不要使用 KEYS;需要完整结果时,使用返回的游标继续扫描直到游标为 0。" : "Use SCAN, not KEYS, to enumerate or match keys. For complete results, continue with the returned cursor until it reaches 0.",
currentTimeGuidance,
isZh ? "禁止不经确认直接执行 Redis 写命令;如果安全执行条件不满足,先说明原因,再给出只读替代方案。" : "Never execute Redis write commands without confirmation. If safe execution requirements are not met, explain why and provide a read-only alternative.",
];
}
return [
isZh
? "你处于 Redis Ask 模式。只生成 Redis 命令和说明,不要生成 SQL,也不要暗示已经执行或即将自动执行。需要指定逻辑数据库时说明 DB 编号,不要生成 SELECT 命令。"
: "You are in Redis Ask mode. Generate Redis commands and explanations only, not SQL, and do not imply that anything has run or will auto-run. State the target database number when needed; do not generate SELECT commands.",
currentTimeGuidance,
];
}
if (databaseType === "mongodb") {
if (mode === "agent") {
return [