diff --git a/frontend/src/components/ai/AIBuiltinToolsCatalog.test.tsx b/frontend/src/components/ai/AIBuiltinToolsCatalog.test.tsx index cbdd806..7586dc6 100644 --- a/frontend/src/components/ai/AIBuiltinToolsCatalog.test.tsx +++ b/frontend/src/components/ai/AIBuiltinToolsCatalog.test.tsx @@ -28,6 +28,8 @@ describe('AIBuiltinToolsCatalog', () => { expect(markup).toContain('inspect_database_bundle'); expect(markup).toContain('查看 AI 当前能力'); expect(markup).toContain('inspect_ai_runtime'); + expect(markup).toContain('核对写入安全边界'); + expect(markup).toContain('inspect_ai_safety'); expect(markup).toContain('排查供应商与模型'); expect(markup).toContain('inspect_ai_providers'); expect(markup).toContain('排查聊天发送状态'); diff --git a/frontend/src/components/ai/AIBuiltinToolsCatalog.tsx b/frontend/src/components/ai/AIBuiltinToolsCatalog.tsx index 36171c7..eb90870 100644 --- a/frontend/src/components/ai/AIBuiltinToolsCatalog.tsx +++ b/frontend/src/components/ai/AIBuiltinToolsCatalog.tsx @@ -42,6 +42,11 @@ const BUILTIN_TOOL_FLOWS = [ steps: 'inspect_ai_runtime → inspect_ai_context / inspect_current_connection', description: '适合先确认当前模型、安全级别、上下文级别、Skills 和 MCP 工具,再决定让 AI 走哪条探针链路。', }, + { + title: '核对写入安全边界', + steps: 'inspect_ai_safety → inspect_ai_runtime → inspect_current_connection', + description: '适合先确认当前是不是只读、DDL/DML 到底允不允许、MCP 写操作是否还需要 allowMutating,再决定后续该走查询、改数据还是改结构。', + }, { title: '排查供应商与模型', steps: 'inspect_ai_providers → inspect_ai_runtime', diff --git a/frontend/src/components/ai/aiLocalToolExecutor.test.ts b/frontend/src/components/ai/aiLocalToolExecutor.test.ts index 3673ada..259053d 100644 --- a/frontend/src/components/ai/aiLocalToolExecutor.test.ts +++ b/frontend/src/components/ai/aiLocalToolExecutor.test.ts @@ -224,6 +224,57 @@ describe('aiLocalToolExecutor', () => { expect(result.content).toContain('"builtinToolCount":'); }); + it('returns the current ai safety snapshot so the model can inspect write boundaries and readonly guards', async () => { + const result = await executeLocalAIToolCall({ + toolCall: buildToolCall('inspect_ai_safety', {}), + connections: [{ + id: 'jvm-1', + name: 'JVM 诊断环境', + config: { + type: 'jvm', + host: '10.0.0.8', + port: 0, + user: '', + jvm: { + environment: 'uat', + readOnly: true, + diagnostic: { + transport: 'agent-bridge', + allowObserveCommands: true, + allowTraceCommands: true, + allowMutatingCommands: false, + }, + }, + }, + }], + tabs: [{ + id: 'diag-tab-1', + title: 'JVM 诊断', + type: 'jvm-diagnostic', + connectionId: 'jvm-1', + readOnly: true, + }], + activeTabId: 'diag-tab-1', + mcpTools: [], + toolContextMap: new Map(), + runtime: { + getDatabases: vi.fn(), + getTables: vi.fn(), + getAIRuntimeState: vi.fn().mockResolvedValue({ + safetyLevel: 'readwrite', + }), + }, + }); + + expect(result.success).toBe(true); + expect(result.content).toContain('"safetyLevel":"readwrite"'); + expect(result.content).toContain('"allowDML":true'); + expect(result.content).toContain('"allowDDL":false'); + expect(result.content).toContain('"readOnly":true'); + expect(result.content).toContain('"allowMutatingCommands":false'); + expect(result.content).toContain('allowMutating=true'); + }); + it('returns the current ai provider snapshot so the model can inspect provider readiness and model selection', async () => { const result = await executeLocalAIToolCall({ toolCall: buildToolCall('inspect_ai_providers', {}), diff --git a/frontend/src/components/ai/aiSafetyInsights.test.ts b/frontend/src/components/ai/aiSafetyInsights.test.ts new file mode 100644 index 0000000..b437943 --- /dev/null +++ b/frontend/src/components/ai/aiSafetyInsights.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from 'vitest'; + +import { buildAISafetySnapshot } from './aiSafetyInsights'; + +describe('buildAISafetySnapshot', () => { + it('describes readonly ai safety when no active connection is selected', () => { + const snapshot = buildAISafetySnapshot({ + safetyLevel: 'readonly', + connections: [], + }); + + expect(snapshot.safetyLevel).toBe('readonly'); + expect(snapshot.permissionMatrix.allowQuery).toBe(true); + expect(snapshot.permissionMatrix.allowDML).toBe(false); + expect(snapshot.permissionMatrix.allowDDL).toBe(false); + expect(snapshot.hasActiveConnection).toBe(false); + expect(snapshot.effectiveRestrictions).toContain('只读模式仅允许查询语句。'); + expect(snapshot.recommendations).toContain('如需执行 INSERT/UPDATE/DELETE,请先把 AI 安全级别切到读写模式。'); + }); + + it('includes jvm connection restrictions and MCP write confirmation hints', () => { + const snapshot = buildAISafetySnapshot({ + safetyLevel: 'readwrite', + connections: [ + { + id: 'jvm-1', + name: 'JVM 诊断环境', + config: { + type: 'jvm', + host: '10.0.0.8', + port: 0, + user: '', + jvm: { + environment: 'uat', + readOnly: true, + diagnostic: { + transport: 'agent-bridge', + allowObserveCommands: true, + allowTraceCommands: true, + allowMutatingCommands: false, + }, + }, + }, + }, + ], + tabs: [ + { + id: 'diag-tab-1', + title: 'JVM 诊断', + type: 'jvm-diagnostic', + connectionId: 'jvm-1', + readOnly: true, + }, + ], + activeTabId: 'diag-tab-1', + }); + + expect(snapshot.permissionMatrix.allowDML).toBe(true); + expect(snapshot.permissionMatrix.allowDDL).toBe(false); + expect(snapshot.activeConnection?.readOnly).toBe(true); + expect(snapshot.jvmGuards?.allowMutatingCommands).toBe(false); + expect(snapshot.effectiveRestrictions.join('\n')).toContain('allowMutating=true'); + expect(snapshot.effectiveRestrictions.join('\n')).toContain('当前 JVM 诊断明确禁止 mutating 命令'); + }); +}); diff --git a/frontend/src/components/ai/aiSafetyInsights.ts b/frontend/src/components/ai/aiSafetyInsights.ts new file mode 100644 index 0000000..b9d5390 --- /dev/null +++ b/frontend/src/components/ai/aiSafetyInsights.ts @@ -0,0 +1,135 @@ +import type { AISafetyLevel, SavedConnection, TabData } from '../../types'; + +const SAFETY_LEVEL_LABELS: Record = { + readonly: '只读', + readwrite: '读写', + full: '完全开放', +}; + +const SAFETY_RULE_TEXTS: Record = { + readonly: '只读模式仅允许查询语句。', + readwrite: '读写模式允许查询和 DML,DDL 仍会被阻止。', + full: '完全开放模式允许查询、DML 和 DDL;未识别操作仍会被阻止。', +}; + +const normalizeSafetyLevel = (value: AISafetyLevel | string | undefined): string => { + const level = String(value || 'readonly').trim().toLowerCase(); + if (level === 'readwrite' || level === 'full') { + return level; + } + return 'readonly'; +}; + +const buildPermissionMatrix = (safetyLevel: string) => ({ + allowQuery: true, + allowDML: safetyLevel === 'readwrite' || safetyLevel === 'full', + allowDDL: safetyLevel === 'full', + requiresConfirmationForAllowedNonQuery: true, + requiresMCPAllowMutatingForAllowedNonQuery: true, +}); + +export const buildAISafetySnapshot = (params: { + safetyLevel?: AISafetyLevel | string; + activeContext?: { connectionId: string; dbName?: string } | null; + tabs?: TabData[]; + activeTabId?: string | null; + connections: SavedConnection[]; +}) => { + const { + safetyLevel, + activeContext = null, + tabs = [], + activeTabId = null, + connections, + } = params; + + const normalizedSafetyLevel = normalizeSafetyLevel(safetyLevel); + const activeTab = tabs.find((tab) => tab.id === activeTabId); + const connectionId = String(activeContext?.connectionId || activeTab?.connectionId || '').trim(); + const activeDbName = String(activeContext?.dbName || activeTab?.dbName || '').trim(); + const connection = connectionId + ? connections.find((item) => item.id === connectionId) + : undefined; + const config = connection?.config; + const jvm = config?.jvm; + const diagnostic = jvm?.diagnostic; + const isJVMSession = config?.type === 'jvm'; + const activeResultReadOnly = activeTab?.readOnly === true; + const jvmReadOnly = isJVMSession && jvm?.readOnly !== false; + + const effectiveRestrictions = [ + SAFETY_RULE_TEXTS[normalizedSafetyLevel] || SAFETY_RULE_TEXTS.readonly, + ]; + if (normalizedSafetyLevel === 'readonly') { + effectiveRestrictions.push('当前安全级别下,任何 DML/DDL 都会被直接阻止。'); + } else { + effectiveRestrictions.push('任何允许通过的非查询语句都仍然需要人工确认。'); + effectiveRestrictions.push('如果通过 GoNavi MCP 的 execute_sql 执行非查询语句,还必须显式传 allowMutating=true。'); + } + if (activeResultReadOnly) { + effectiveRestrictions.push('当前活动页签结果集是只读的,不能把它当成可直接回写的数据网格。'); + } + if (jvmReadOnly) { + effectiveRestrictions.push('当前 JVM 连接本身是只读连接,默认只能按观察/排障思路生成诊断计划。'); + } + if (isJVMSession && diagnostic?.allowMutatingCommands !== true) { + effectiveRestrictions.push('当前 JVM 诊断明确禁止 mutating 命令,即使 AI 安全级别允许写入,也不能假设这类命令能执行。'); + } + + const recommendations: string[] = []; + if (normalizedSafetyLevel === 'readonly') { + recommendations.push('如需执行 INSERT/UPDATE/DELETE,请先把 AI 安全级别切到读写模式。'); + recommendations.push('如需执行 CREATE/ALTER/DROP/TRUNCATE 等结构变更,请切到完全开放模式。'); + } else if (normalizedSafetyLevel === 'readwrite') { + recommendations.push('当前已经允许 DML;如果目标是改表结构,仍需要切到完全开放模式。'); + } + if (activeResultReadOnly) { + recommendations.push('如果目标是编辑结果网格,请重新打开可编辑的表或查询结果,不要只看当前只读页签。'); + } + if (jvmReadOnly) { + recommendations.push('当前 JVM 连接按只读策略回答;需要变更类诊断前先确认连接策略是否应调整。'); + } + if (isJVMSession && diagnostic?.allowMutatingCommands !== true) { + recommendations.push('当前 JVM 诊断禁止 mutating 命令;如需执行高风险命令,应先调整诊断权限。'); + } + + return { + safetyLevel: normalizedSafetyLevel, + safetyLabel: SAFETY_LEVEL_LABELS[normalizedSafetyLevel] || normalizedSafetyLevel, + sqlRuleText: SAFETY_RULE_TEXTS[normalizedSafetyLevel] || SAFETY_RULE_TEXTS.readonly, + permissionMatrix: buildPermissionMatrix(normalizedSafetyLevel), + hasActiveConnection: Boolean(connection), + activeConnection: connection + ? { + connectionId: connection.id, + connectionName: connection.name, + connectionType: config?.type || '', + host: config?.host || '', + activeDbName: activeDbName || config?.database || '', + readOnly: activeResultReadOnly || jvmReadOnly, + } + : null, + activeTab: activeTab + ? { + id: activeTab.id, + title: activeTab.title || '', + type: activeTab.type || '', + readOnly: activeResultReadOnly, + } + : null, + jvmGuards: isJVMSession + ? { + readOnly: jvmReadOnly, + transport: diagnostic?.transport || 'agent-bridge', + allowObserveCommands: diagnostic?.allowObserveCommands !== false, + allowTraceCommands: diagnostic?.allowTraceCommands === true, + allowMutatingCommands: diagnostic?.allowMutatingCommands === true, + } + : null, + effectiveRestrictions, + recommendations, + message: connection + ? `当前 AI 安全级别为 ${SAFETY_LEVEL_LABELS[normalizedSafetyLevel] || normalizedSafetyLevel},活动连接为 ${connection.name}` + : `当前 AI 安全级别为 ${SAFETY_LEVEL_LABELS[normalizedSafetyLevel] || normalizedSafetyLevel},当前没有活动连接`, + }; +}; diff --git a/frontend/src/components/ai/aiSnapshotInspectionToolExecutor.ts b/frontend/src/components/ai/aiSnapshotInspectionToolExecutor.ts index cb600a5..d44e948 100644 --- a/frontend/src/components/ai/aiSnapshotInspectionToolExecutor.ts +++ b/frontend/src/components/ai/aiSnapshotInspectionToolExecutor.ts @@ -21,6 +21,7 @@ import { buildAIGuidanceSnapshot } from './aiPromptInsights'; import { buildAIChatReadinessSnapshot } from './aiChatReadiness'; import { buildAIProviderSnapshot } from './aiProviderInsights'; import { buildAIRuntimeSnapshot } from './aiRuntimeInsights'; +import { buildAISafetySnapshot } from './aiSafetyInsights'; import { buildSavedQueriesSnapshot, buildSqlSnippetsSnapshot, @@ -111,6 +112,21 @@ export async function executeSnapshotInspectionToolCall( success: true, }; } + case 'inspect_ai_safety': { + const runtimeState = typeof runtime?.getAIRuntimeState === 'function' + ? await runtime.getAIRuntimeState() + : undefined; + return { + content: JSON.stringify(buildAISafetySnapshot({ + safetyLevel: runtimeState?.safetyLevel, + activeContext, + tabs, + activeTabId, + connections, + })), + success: true, + }; + } case 'inspect_ai_providers': { const runtimeState = typeof runtime?.getAIRuntimeState === 'function' ? await runtime.getAIRuntimeState() @@ -254,6 +270,7 @@ export async function executeSnapshotInspectionToolCall( } catch (error: any) { const label = { inspect_ai_runtime: '读取当前 AI 运行状态失败', + inspect_ai_safety: '读取当前 AI 安全边界失败', inspect_ai_providers: '读取当前 AI 供应商配置失败', inspect_ai_chat_readiness: '读取 AI 聊天发送前置状态失败', inspect_mcp_setup: '读取 MCP 配置状态失败', diff --git a/frontend/src/components/ai/aiSystemContextMessages.test.ts b/frontend/src/components/ai/aiSystemContextMessages.test.ts index 2b46d90..da4ec15 100644 --- a/frontend/src/components/ai/aiSystemContextMessages.test.ts +++ b/frontend/src/components/ai/aiSystemContextMessages.test.ts @@ -68,7 +68,7 @@ describe('buildAISystemContextMessages', () => { connections: [connections[0]], tabs: [], activeTabId: null, - availableToolNames: ['inspect_workspace_tabs', 'inspect_ai_runtime', 'inspect_ai_providers', 'inspect_ai_chat_readiness', 'inspect_mcp_setup', 'inspect_ai_guidance', 'inspect_ai_context', 'inspect_current_connection', 'inspect_saved_connections', 'inspect_saved_queries', 'inspect_sql_snippets', 'get_columns'], + availableToolNames: ['inspect_workspace_tabs', 'inspect_ai_runtime', 'inspect_ai_safety', 'inspect_ai_providers', 'inspect_ai_chat_readiness', 'inspect_mcp_setup', 'inspect_ai_guidance', 'inspect_ai_context', 'inspect_current_connection', 'inspect_saved_connections', 'inspect_saved_queries', 'inspect_sql_snippets', 'get_columns'], skills, userPromptSettings, }); @@ -76,6 +76,7 @@ describe('buildAISystemContextMessages', () => { const joined = messages.map((message) => message.content).join('\n'); expect(joined).toContain('inspect_workspace_tabs 盘点当前工作区'); expect(joined).toContain('inspect_ai_runtime 读取当前 AI 运行状态'); + expect(joined).toContain('inspect_ai_safety 读取真实安全边界'); expect(joined).toContain('inspect_ai_providers 读取真实供应商配置'); expect(joined).toContain('inspect_ai_chat_readiness 读取真实发送前置状态'); expect(joined).toContain('inspect_mcp_setup 读取真实 MCP 配置'); diff --git a/frontend/src/components/ai/aiSystemContextMessages.ts b/frontend/src/components/ai/aiSystemContextMessages.ts index c3586e2..1b180cf 100644 --- a/frontend/src/components/ai/aiSystemContextMessages.ts +++ b/frontend/src/components/ai/aiSystemContextMessages.ts @@ -108,6 +108,19 @@ const appendAIRuntimeInspectionGuidance = ( }); }; +const appendAISafetyInspectionGuidance = ( + messages: AISystemContextMessage[], + availableToolNames: string[], +) => { + if (!availableToolNames.includes('inspect_ai_safety')) { + return; + } + messages.push({ + role: 'system', + content: '如果用户提到“为什么现在不能写”“当前是不是只读”“DDL 能不能执行”“allowMutating 要不要传”,优先调用 inspect_ai_safety 读取真实安全边界,不要只凭界面现象或记忆猜测。', + }); +}; + const appendAIProviderInspectionGuidance = ( messages: AISystemContextMessage[], availableToolNames: string[], @@ -271,6 +284,7 @@ export function buildAISystemContextMessages({ 7. 如果命令权限不允许某类操作,就不要输出该类命令;无法满足时直接说明限制。`, }); appendAIRuntimeInspectionGuidance(systemMessages, availableToolNames); + appendAISafetyInspectionGuidance(systemMessages, availableToolNames); appendCustomPromptGroup(systemMessages, ['jvmDiagnostic'], userPromptSettings); appendSkillPromptGroup(systemMessages, ['jvmDiagnostic'], skills, availableToolNames); return systemMessages; @@ -305,6 +319,7 @@ ${resourcePath ? `当前资源路径:${resourcePath}` : '当前未选中具体 6. 不要输出脚本、命令或“已经执行成功”之类的表述。`, }); appendAIRuntimeInspectionGuidance(systemMessages, availableToolNames); + appendAISafetyInspectionGuidance(systemMessages, availableToolNames); appendCustomPromptGroup(systemMessages, ['jvm'], userPromptSettings); appendSkillPromptGroup(systemMessages, ['jvm'], skills, availableToolNames); return systemMessages; @@ -377,6 +392,7 @@ SELECT * FROM users WHERE status = 1; }); } appendAIRuntimeInspectionGuidance(systemMessages, availableToolNames); + appendAISafetyInspectionGuidance(systemMessages, availableToolNames); appendAIChatReadinessInspectionGuidance(systemMessages, availableToolNames); appendAIProviderInspectionGuidance(systemMessages, availableToolNames); appendMCPSetupInspectionGuidance(systemMessages, availableToolNames); diff --git a/frontend/src/components/ai/messageBubble/AIMessageStatusBlocks.tsx b/frontend/src/components/ai/messageBubble/AIMessageStatusBlocks.tsx index d61640d..e381fdc 100644 --- a/frontend/src/components/ai/messageBubble/AIMessageStatusBlocks.tsx +++ b/frontend/src/components/ai/messageBubble/AIMessageStatusBlocks.tsx @@ -24,6 +24,7 @@ interface AIToolCallingBlockProps { const TOOL_ACTION_LABELS: Record = { inspect_ai_runtime: '读取当前 AI 运行状态', + inspect_ai_safety: '读取当前 AI 安全边界', inspect_ai_providers: '读取当前 AI 供应商与模型配置', inspect_ai_chat_readiness: '读取当前 AI 聊天发送前置状态', inspect_mcp_setup: '读取当前 MCP 配置状态', diff --git a/frontend/src/utils/aiBuiltinToolInfo.ts b/frontend/src/utils/aiBuiltinToolInfo.ts new file mode 100644 index 0000000..a889f82 --- /dev/null +++ b/frontend/src/utils/aiBuiltinToolInfo.ts @@ -0,0 +1,623 @@ +export interface AIChatToolDefinition { + type: "function"; + function: { + name: string; + description: string; + parameters: Record; + }; +} + +export interface AIBuiltinToolInfo { + name: string; + icon: string; + desc: string; + detail: string; + params: string; + tool: AIChatToolDefinition; +} + +export const BUILTIN_AI_TOOL_INFO: AIBuiltinToolInfo[] = [ + { + name: "get_connections", + icon: "🔗", + desc: "获取所有可用的数据库连接", + detail: + "返回连接 ID、名称、类型 (MySQL/PostgreSQL 等) 和 Host 地址。AI 根据返回信息决定优先探索哪个连接。", + params: "无参数", + tool: { + type: "function", + function: { + name: "get_connections", + description: + "当需要查询、操作数据库但用户没有选择任何连接上下文时,获取当前软件中可用的所有数据库连接信息。返回的数据包含连接ID(id)和名称(name)。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "get_databases", + icon: "🗄️", + desc: "获取指定连接下的所有数据库", + detail: "传入 connectionId,返回该连接下的数据库/Schema 名称列表。", + params: "connectionId: 连接 ID", + tool: { + type: "function", + function: { + name: "get_databases", + description: "获取指定连接(connectionId)下的所有数据库(Database/Schema)名。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID (从 get_connections 获取)" }, + }, + required: ["connectionId"], + }, + }, + }, + }, + { + name: "get_tables", + icon: "📋", + desc: "获取指定数据库下的所有表名", + detail: + "传入 connectionId 和 dbName,返回表名列表。AI 用它来定位用户提到的目标表。", + params: "connectionId, dbName", + tool: { + type: "function", + function: { + name: "get_tables", + description: + "当已经确定了目标连接和数据库名后,如果用户询问或隐式提到了表但你不知道确切表名,调用此工具获取该数据库下的所有表名列表(只含表名,帮助你推断目标表)。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + }, + required: ["connectionId", "dbName"], + }, + }, + }, + }, + { + name: "get_all_columns", + icon: "🧱", + desc: "获取指定数据库下所有表的字段摘要", + detail: + "传入 connectionId 和 dbName,返回跨表字段列表(表名、字段名、类型、注释)。适合用户只知道业务字段、不知道具体在哪张表时快速定位目标表。", + params: "connectionId, dbName", + tool: { + type: "function", + function: { + name: "get_all_columns", + description: + "获取指定数据库下全部表的字段摘要,返回表名、字段名、类型和注释。适用于按字段反查表、跨表梳理相同字段、做数据地图探索。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + }, + required: ["connectionId", "dbName"], + }, + }, + }, + }, + { + name: "get_columns", + icon: "🔍", + desc: "获取指定表的字段结构", + detail: + "传入 connectionId、dbName 和 tableName,返回每个字段的名称、类型、是否可空、默认值和注释。AI 在生成 SQL 前必须调用此工具确认真实字段名。", + params: "connectionId, dbName, tableName", + tool: { + type: "function", + function: { + name: "get_columns", + description: + "获取指定表的字段列表(字段名、类型、是否可空、默认值、注释等)。在生成 SQL 之前必须先调用此工具确认真实字段名,禁止猜测字段名。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + tableName: { type: "string", description: "表名" }, + }, + required: ["connectionId", "dbName", "tableName"], + }, + }, + }, + }, + { + name: "get_indexes", + icon: "🧭", + desc: "获取指定表的索引定义", + detail: + "传入 connectionId、dbName 和 tableName,返回索引名、索引列、唯一性和索引类型。AI 在做慢 SQL 分析、索引优化和执行计划推断时应优先调用。", + params: "connectionId, dbName, tableName", + tool: { + type: "function", + function: { + name: "get_indexes", + description: + "获取指定表的索引定义,包括索引名、字段顺序、唯一性和索引类型。适用于慢 SQL 分析、索引优化建议和确认现有索引覆盖情况。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + tableName: { type: "string", description: "表名" }, + }, + required: ["connectionId", "dbName", "tableName"], + }, + }, + }, + }, + { + name: "get_foreign_keys", + icon: "🧬", + desc: "获取指定表的外键关系", + detail: + "传入 connectionId、dbName 和 tableName,返回当前表到其他表的外键映射。AI 在推断表关系、生成联表 SQL 和评审数据一致性时可直接使用。", + params: "connectionId, dbName, tableName", + tool: { + type: "function", + function: { + name: "get_foreign_keys", + description: + "获取指定表的外键关系,包括本表字段、引用表、引用字段和约束名。适用于联表路径分析、ER 关系梳理和约束检查。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + tableName: { type: "string", description: "表名" }, + }, + required: ["connectionId", "dbName", "tableName"], + }, + }, + }, + }, + { + name: "get_triggers", + icon: "⏱️", + desc: "获取指定表的触发器定义", + detail: + "传入 connectionId、dbName 和 tableName,返回触发器名、触发时机、事件类型和语句体。AI 在分析隐式写入、副作用和审计逻辑时可直接查看。", + params: "connectionId, dbName, tableName", + tool: { + type: "function", + function: { + name: "get_triggers", + description: + "获取指定表的触发器定义,包括触发时机、事件和触发语句。适用于排查隐式数据变更、审计逻辑和表级副作用。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + tableName: { type: "string", description: "表名" }, + }, + required: ["connectionId", "dbName", "tableName"], + }, + }, + }, + }, + { + name: "get_table_ddl", + icon: "📝", + desc: "获取表的建表语句 (DDL)", + detail: + "传入 connectionId、dbName 和 tableName,返回完整的 CREATE TABLE 语句,包含字段定义、索引、约束等信息。", + params: "connectionId, dbName, tableName", + tool: { + type: "function", + function: { + name: "get_table_ddl", + description: "获取指定表的完整建表语句(CREATE TABLE DDL),包含字段、索引、约束等完整结构信息。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + tableName: { type: "string", description: "表名" }, + }, + required: ["connectionId", "dbName", "tableName"], + }, + }, + }, + }, + { + name: "preview_table_rows", + icon: "👀", + desc: "抽样预览指定表的前几行数据", + detail: + "传入 connectionId、dbName、tableName 和可选 limit,返回该表的前几行真实样例数据。适合先看数据形态、空值分布和枚举值,再决定怎么写 SQL。", + params: "connectionId, dbName, tableName, limit?", + tool: { + type: "function", + function: { + name: "preview_table_rows", + description: + "预览指定表的前几行样例数据。适用于快速理解字段取值形态、空值情况、时间格式和状态枚举,减少模型盲写 SQL。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + tableName: { type: "string", description: "表名" }, + limit: { type: "number", description: "可选,预览行数,默认 20,最大 100" }, + }, + required: ["connectionId", "dbName", "tableName"], + }, + }, + }, + }, + { + name: "inspect_table_bundle", + icon: "🧰", + desc: "一次抓取指定表的结构快照", + detail: + "传入 connectionId、dbName 和 tableName,返回字段、索引、外键、触发器和 DDL;还可以附带前几行样例数据。适合在写 SQL、评审表设计或排查副作用前先做完整摸底。", + params: "connectionId, dbName, tableName, includeSampleRows?, sampleLimit?", + tool: { + type: "function", + function: { + name: "inspect_table_bundle", + description: + "一次性获取指定表的结构快照,返回字段、索引、外键、触发器、DDL,以及可选样例数据。适用于做完整表设计摸底、快速理解表关系和降低模型多次往返调用。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + tableName: { type: "string", description: "表名" }, + includeSampleRows: { type: "boolean", description: "可选,是否附带前几行样例数据" }, + sampleLimit: { type: "number", description: "可选,样例行数,默认 10,最大 100" }, + }, + required: ["connectionId", "dbName", "tableName"], + }, + }, + }, + }, + { + name: "inspect_database_bundle", + icon: "🗂️", + desc: "一次抓取指定数据库的结构总览", + detail: + "传入 connectionId 和 dbName,返回库内表清单、表数量、总字段数,以及按表聚合的字段摘要预览。适合刚接手陌生库时先做全局摸底,再决定深入哪张表。", + params: "connectionId, dbName, includeColumns?, tableLimit?, perTableColumnLimit?", + tool: { + type: "function", + function: { + name: "inspect_database_bundle", + description: + "一次性获取指定数据库的结构总览,返回表名列表、总字段数,以及按表聚合的字段摘要预览。适用于陌生数据库摸底、做数据地图和快速选择下一步要深入分析的表。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + includeColumns: { type: "boolean", description: "可选,是否附带按表聚合的字段摘要,默认 true" }, + tableLimit: { type: "number", description: "可选,最多返回多少张表,默认 80,最大 200" }, + perTableColumnLimit: { type: "number", description: "可选,每张表最多返回多少个字段摘要,默认 8,最大 30" }, + }, + required: ["connectionId", "dbName"], + }, + }, + }, + }, + { + name: "inspect_ai_runtime", + icon: "🎛️", + desc: "查看当前 AI 自身运行状态", + detail: + "返回当前启用的模型供应商、模型名、安全级别、上下文级别、启用的 Skills,以及当前已暴露的内置工具和 MCP 工具。适合用户问“你现在能调用什么”“当前用的哪个模型”“为什么不能执行写操作”时,先读真实运行状态再回答。", + params: "无参数", + tool: { + type: "function", + function: { + name: "inspect_ai_runtime", + description: + "读取当前 AI 运行时快照,包括当前供应商、模型、安全级别、上下文级别、启用的 Skills、当前可用的内置工具与 MCP 工具。适用于用户询问当前 AI 能力边界、当前使用哪个模型、为什么不能执行某些操作时,先读取真实运行状态,避免模型猜测。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "inspect_ai_safety", + icon: "🛡️", + desc: "查看当前 AI 写入安全边界", + detail: + "返回当前 AI 安全级别对应的 SQL 允许范围、非只读语句是否仍需确认 / allowMutating,以及当前活动连接、页签或 JVM 诊断权限是否还叠加了只读限制。适合用户问“为什么现在不能写”“DDL 能不能执行”“allowMutating 要不要传”时先读真实边界。", + params: "无参数", + tool: { + type: "function", + function: { + name: "inspect_ai_safety", + description: + "读取当前 AI 安全边界快照,包括当前安全级别允许的 SQL 范围、非查询语句的确认要求、MCP execute_sql 对 allowMutating 的要求,以及当前活动连接、结果页签或 JVM 诊断权限是否额外处于只读限制。适用于用户提到为什么现在不能写、当前是不是只读、DDL 能不能执行、allowMutating 是否必须传时,先读取真实边界再回答。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "inspect_ai_providers", + icon: "🪪", + desc: "查看当前 AI 供应商与模型配置", + detail: + "返回当前配置了哪些 AI 供应商、哪个正在生效、各自的 baseUrl、已选模型、声明模型列表、密钥是否存在、自定义请求头 key,以及缺少密钥/模型/地址等待检查项。适合用户问“为什么没有模型”“API Key 有没有配”“当前到底配了哪些供应商”时先读真实配置。", + params: "无参数", + tool: { + type: "function", + function: { + name: "inspect_ai_providers", + description: + "读取当前 AI 供应商配置快照,包括供应商列表、活动供应商、接口地址、已选模型、声明模型列表、是否存在密钥、自定义请求头 key,以及缺少密钥/模型/地址等待检查项。适用于用户提到当前供应商、模型列表为空、API Key 是否配置、为什么 AI 不能正常发起请求时,先读取真实配置再解释。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "inspect_ai_chat_readiness", + icon: "🚦", + desc: "查看当前 AI 聊天是否具备发送条件", + detail: + "返回当前聊天输入区是否已经具备发送条件,包括有没有活动供应商、当前供应商是否缺密钥或接口地址、是否已选模型、当前连接/表结构上下文是否已挂载,以及下一步建议动作。适合用户问“为什么现在不能发送”“输入框到底缺什么配置”“当前 AI 聊天准备好了没有”时先读真实状态。", + params: "无参数", + tool: { + type: "function", + function: { + name: "inspect_ai_chat_readiness", + description: + "读取当前 AI 聊天输入区的发送前置状态,包括活动供应商、密钥和接口地址是否完整、是否已选模型、当前连接上下文和已挂载表结构数量,以及建议的下一步动作。适用于用户提到为什么现在不能发送、为什么输入区还没准备好、当前到底缺什么配置时,先读取真实状态再回答。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "inspect_mcp_setup", + icon: "🪛", + desc: "查看当前 MCP 配置与外部接入状态", + detail: + "返回当前本地配置了哪些 MCP 服务、哪些已启用、每个服务声明了什么启动命令,以及 Claude Code / Codex 这类外部客户端的写入状态与命令检测结果。适合用户问“我现在配了哪些 MCP”“为什么外部客户端还用不了”“MCP 到底写没写进去”时先读真实状态。", + params: "无参数", + tool: { + type: "function", + function: { + name: "inspect_mcp_setup", + description: + "读取当前本地 MCP 配置快照,包括 MCP 服务列表、启用状态、启动命令、环境变量 key、已发现工具,以及外部客户端的 GoNavi MCP 写入状态与本机 CLI 检测结果。适用于用户提到 MCP 服务配置、Claude/Codex 是否已接入、为什么外部客户端用不了、当前到底启用了哪些 MCP 时,先读取真实配置再回答。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "inspect_ai_guidance", + icon: "🧠", + desc: "查看当前 AI 提示词与 Skills 配置", + detail: + "返回当前用户自定义的全局/数据库/JVM 提示词,以及当前启用的 Skills、作用域、依赖工具和 skill prompt 内容。适合用户问“你现在到底带了哪些提示词”“为什么你会这样回答”“当前有哪些 Skills 在生效”时先读真实配置。", + params: "无参数", + tool: { + type: "function", + function: { + name: "inspect_ai_guidance", + description: + "读取当前 AI 的提示与技能配置快照,包括用户自定义提示词、当前启用的 Skills、作用域、依赖工具和各自的 system prompt。适用于用户提到当前提示词、当前 Skill、为什么 AI 当前会这样回答、当前有哪些规则在生效时,先读取真实配置再解释。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "inspect_ai_context", + icon: "🧷", + desc: "查看当前 AI 已关联的表结构上下文", + detail: + "返回当前对话已经挂载到 AI 上下文里的表清单、所属连接与数据库,以及每张表的 DDL 预览。适合用户说“看看我现在带了哪些表结构”“当前 AI 上下文是什么”时,先读取真实挂载状态再继续分析。", + params: "includeDDL?(默认 false), ddlLimit?(默认 4000)", + tool: { + type: "function", + function: { + name: "inspect_ai_context", + description: + "读取当前对话已经关联到 AI 上下文里的表结构快照,包括连接、数据库、表名,以及可选的 DDL 内容。适用于用户提到当前 AI 上下文、当前关联表、当前挂载的表结构时,先读取真实状态,避免模型凭记忆复述。", + parameters: { + type: "object", + properties: { + includeDDL: { type: "boolean", description: "可选,是否附带每张表的 DDL 内容,默认 false" }, + ddlLimit: { type: "number", description: "可选,DDL 截断长度,默认 4000,最大 12000" }, + }, + }, + }, + }, + }, + { + name: "inspect_current_connection", + icon: "🛰️", + desc: "查看当前活动连接/数据源摘要", + detail: + "返回当前活动连接的类型、地址、端口、当前数据库、是否启用 SSH/代理/HTTP 隧道,以及当前活动页签绑定的表信息。适合用户问“我现在连的是哪个库”“这个连接走没走 SSH”“当前数据源是什么类型”时先读取真实连接状态。", + params: "无参数", + tool: { + type: "function", + function: { + name: "inspect_current_connection", + description: + "读取当前活动连接或当前页签对应数据源的真实摘要,包括连接类型、地址、端口、当前数据库、SSH/代理/HTTP 隧道状态,以及当前页签绑定的表上下文。适用于用户提到当前连接、当前数据源、当前库地址、是否走 SSH、当前连的是哪种数据库时,先读取真实界面上下文,避免模型猜测。", + parameters: { type: "object", properties: {} }, + }, + }, + }, + { + name: "inspect_saved_connections", + icon: "🧭", + desc: "查看本地已保存连接清单", + detail: + "可按关键词或数据库类型过滤,返回本地保存的数据源列表、连接类型分布,以及每条连接的地址、当前库、SSH/代理/HTTP 隧道状态。适合用户问“我本地存了哪些连接”“帮我找 mysql / postgres 连接”“哪条连接配置了 SSH”时先读真实本地连接资产。", + params: "keyword?, type?, limit?", + tool: { + type: "function", + function: { + name: "inspect_saved_connections", + description: + "读取本地已保存连接清单,可按关键词和数据库类型过滤,并返回每条连接的类型、地址、当前库、SSH/代理/HTTP 隧道等摘要。适用于用户提到本地保存了哪些连接、要找哪条 mysql/postgres 连接、哪条连接启用了 SSH 或代理时,先读取真实本地连接资产再回答。", + parameters: { + type: "object", + properties: { + keyword: { type: "string", description: "可选,按连接名、ID、类型、主机、数据库名或 SSH/代理地址做关键词筛选" }, + type: { type: "string", description: "可选,只看某种数据库类型,例如 mysql、postgres、redis、mongodb" }, + limit: { type: "number", description: "可选,最多返回多少条连接,默认 20,最大 100" }, + }, + }, + }, + }, + }, + { + name: "inspect_active_tab", + icon: "📍", + desc: "查看当前活动页签上下文", + detail: + "返回当前活动页签的类型、连接、数据库、表名,以及当前 SQL / 命令页签里的草稿内容(超长会截断)。适合用户说“看我当前这条 SQL”“优化这个编辑器里的语句”时,先让 AI 直接读取当前工作区上下文。", + params: "includeContent?(默认 true)", + tool: { + type: "function", + function: { + name: "inspect_active_tab", + description: + "获取当前活动页签的上下文快照,包括页签类型、连接、数据库、表名,以及当前 SQL / 命令页签里的草稿内容。适用于用户提到当前页签、当前 SQL、当前编辑器、这条语句时,先读取真实界面上下文,避免让模型猜测。", + parameters: { + type: "object", + properties: { + includeContent: { type: "boolean", description: "可选,是否附带页签中的 SQL / 命令草稿内容,默认 true" }, + }, + }, + }, + }, + }, + { + name: "inspect_workspace_tabs", + icon: "🗃️", + desc: "查看当前工作区打开的页签总览", + detail: + "返回当前工作区里打开的页签列表、哪个是活动页签,以及每个页签对应的连接、数据库、表名等上下文。适合用户说“我现在开了哪些 SQL”“看看我工作区里有哪些页签”“帮我对比这几个查询页签”时,先读取真实工作区布局再继续分析。", + params: "limit?(默认 12), includeContent?(默认 false)", + tool: { + type: "function", + function: { + name: "inspect_workspace_tabs", + description: + "获取当前工作区已打开页签的总览,包括活动页签、页签类型、连接、数据库、表名,以及可选的 SQL / 命令草稿内容。适用于用户提到当前工作区、打开了哪些页签、哪几个查询页签、想对比多个编辑器内容时,先读取真实界面状态,避免模型猜测。", + parameters: { + type: "object", + properties: { + limit: { type: "number", description: "可选,最多返回多少个页签,默认 12,最大 30" }, + includeContent: { type: "boolean", description: "可选,是否附带页签中的 SQL / 命令草稿内容,默认 false" }, + }, + }, + }, + }, + }, + { + name: "inspect_recent_sql_logs", + icon: "🧾", + desc: "查看最近 SQL 执行日志", + detail: + "传入可选 limit 和 status,返回最近 SQL 执行记录,包括数据库、耗时、成功/失败、报错、受影响行数和 SQL 文本。适合追查刚执行失败的语句、定位慢查询,并让 AI 基于真实执行历史给出解释或优化建议。", + params: "limit?, status?(all|success|error)", + tool: { + type: "function", + function: { + name: "inspect_recent_sql_logs", + description: + "获取最近 SQL 执行日志摘要,可按成功/失败过滤。适用于回看刚执行过的 SQL、排查失败原因、定位慢查询,以及让 AI 基于真实执行历史给出解释和优化建议。", + parameters: { + type: "object", + properties: { + limit: { type: "number", description: "可选,返回多少条日志,默认 20,最大 100" }, + status: { + type: "string", + description: "可选,按执行状态过滤,支持 all、success、error,默认 all", + enum: ["all", "success", "error"], + }, + }, + }, + }, + }, + }, + { + name: "inspect_saved_queries", + icon: "💾", + desc: "查看本地已保存的 SQL 查询", + detail: + "可按关键词、连接或数据库过滤,返回保存查询的名称、所属连接、数据库和 SQL 预览。适合用户提到“我之前保存过的查询”“帮我找那条历史 SQL”时先从真实本地收藏里检索。", + params: "keyword?, connectionId?, dbName?, limit?, includeSql?(默认 true)", + tool: { + type: "function", + function: { + name: "inspect_saved_queries", + description: + "读取本地已保存的 SQL 查询列表,可按关键词、连接和数据库过滤,并返回每条查询的名称、所属连接、数据库与 SQL 预览。适用于用户想找历史查询、复用旧 SQL、核对保存脚本时,先读取真实本地记录。", + parameters: { + type: "object", + properties: { + keyword: { type: "string", description: "可选,按查询名称、SQL 文本、连接名或数据库名做关键词筛选" }, + connectionId: { type: "string", description: "可选,只看某个连接下保存的查询" }, + dbName: { type: "string", description: "可选,只看某个数据库下保存的查询" }, + limit: { type: "number", description: "可选,最多返回多少条,默认 12,最大 50" }, + includeSql: { type: "boolean", description: "可选,是否附带 SQL 预览,默认 true" }, + }, + }, + }, + }, + }, + { + name: "inspect_sql_snippets", + icon: "🧩", + desc: "查看 SQL 片段模板", + detail: + "返回本地 SQL 片段的 prefix、名称、说明和模板预览,可按关键词过滤。适合用户想找现成模板、补全片段、团队约定 SQL 模板时先读取真实片段库。", + params: "keyword?, limit?, includeBody?(默认 true)", + tool: { + type: "function", + function: { + name: "inspect_sql_snippets", + description: + "读取本地 SQL 片段模板列表,可按关键词过滤,并返回 prefix、名称、说明和模板预览。适用于用户想找 snippet、复用模板、核对 SQL 片段配置时,先读取真实本地片段库。", + parameters: { + type: "object", + properties: { + keyword: { type: "string", description: "可选,按 prefix、名称、描述或模板内容做关键词筛选" }, + limit: { type: "number", description: "可选,最多返回多少条,默认 20,最大 80" }, + includeBody: { type: "boolean", description: "可选,是否附带模板内容预览,默认 true" }, + }, + }, + }, + }, + }, + { + name: "execute_sql", + icon: "▶️", + desc: "执行 SQL 查询并返回结果", + detail: + "传入 connectionId、dbName 和 sql,在目标数据库上执行 SQL 并返回结果(最多 50 行)。受安全级别控制,只读模式下仅允许 SELECT/SHOW/DESCRIBE。", + params: "connectionId, dbName, sql", + tool: { + type: "function", + function: { + name: "execute_sql", + description: + "在指定连接和数据库上执行 SQL 查询并返回结果。受安全级别控制,只读模式下只能执行 SELECT/SHOW/DESCRIBE 等查询操作。结果最多返回 50 行。", + parameters: { + type: "object", + properties: { + connectionId: { type: "string", description: "连接ID" }, + dbName: { type: "string", description: "数据库名" }, + sql: { type: "string", description: "要执行的 SQL 语句" }, + }, + required: ["connectionId", "dbName", "sql"], + }, + }, + }, + }, +]; diff --git a/frontend/src/utils/aiToolRegistry.test.ts b/frontend/src/utils/aiToolRegistry.test.ts index 30e71df..e35b0f9 100644 --- a/frontend/src/utils/aiToolRegistry.test.ts +++ b/frontend/src/utils/aiToolRegistry.test.ts @@ -10,6 +10,13 @@ describe('aiToolRegistry', () => { expect(info?.tool.function.description).toContain('当前供应商'); }); + it('registers the ai-safety inspector as a builtin tool', () => { + const info = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_ai_safety'); + expect(info).toBeTruthy(); + expect(info?.desc).toContain('写入安全边界'); + expect(info?.tool.function.description).toContain('allowMutating'); + }); + it('registers the mcp-setup inspector as a builtin tool', () => { const info = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_mcp_setup'); expect(info).toBeTruthy(); @@ -79,6 +86,7 @@ describe('aiToolRegistry', () => { }]); expect(tools.some((item) => item.function.name === 'inspect_ai_runtime')).toBe(true); + expect(tools.some((item) => item.function.name === 'inspect_ai_safety')).toBe(true); expect(tools.some((item) => item.function.name === 'inspect_ai_providers')).toBe(true); expect(tools.some((item) => item.function.name === 'inspect_ai_chat_readiness')).toBe(true); expect(tools.some((item) => item.function.name === 'inspect_mcp_setup')).toBe(true); diff --git a/frontend/src/utils/aiToolRegistry.ts b/frontend/src/utils/aiToolRegistry.ts index d42f721..8d12b9c 100644 --- a/frontend/src/utils/aiToolRegistry.ts +++ b/frontend/src/utils/aiToolRegistry.ts @@ -1,611 +1,15 @@ import type { AIMCPToolDescriptor } from "../types"; +import { + BUILTIN_AI_TOOL_INFO, + type AIChatToolDefinition, + type AIBuiltinToolInfo, +} from "./aiBuiltinToolInfo"; -export interface AIChatToolDefinition { - type: "function"; - function: { - name: string; - description: string; - parameters: Record; - }; -} - -export interface AIBuiltinToolInfo { - name: string; - icon: string; - desc: string; - detail: string; - params: string; - tool: AIChatToolDefinition; -} - -export const BUILTIN_AI_TOOL_INFO: AIBuiltinToolInfo[] = [ - { - name: "get_connections", - icon: "🔗", - desc: "获取所有可用的数据库连接", - detail: - "返回连接 ID、名称、类型 (MySQL/PostgreSQL 等) 和 Host 地址。AI 根据返回信息决定优先探索哪个连接。", - params: "无参数", - tool: { - type: "function", - function: { - name: "get_connections", - description: - "当需要查询、操作数据库但用户没有选择任何连接上下文时,获取当前软件中可用的所有数据库连接信息。返回的数据包含连接ID(id)和名称(name)。", - parameters: { type: "object", properties: {} }, - }, - }, - }, - { - name: "get_databases", - icon: "🗄️", - desc: "获取指定连接下的所有数据库", - detail: "传入 connectionId,返回该连接下的数据库/Schema 名称列表。", - params: "connectionId: 连接 ID", - tool: { - type: "function", - function: { - name: "get_databases", - description: "获取指定连接(connectionId)下的所有数据库(Database/Schema)名。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID (从 get_connections 获取)" }, - }, - required: ["connectionId"], - }, - }, - }, - }, - { - name: "get_tables", - icon: "📋", - desc: "获取指定数据库下的所有表名", - detail: - "传入 connectionId 和 dbName,返回表名列表。AI 用它来定位用户提到的目标表。", - params: "connectionId, dbName", - tool: { - type: "function", - function: { - name: "get_tables", - description: - "当已经确定了目标连接和数据库名后,如果用户询问或隐式提到了表但你不知道确切表名,调用此工具获取该数据库下的所有表名列表(只含表名,帮助你推断目标表)。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - }, - required: ["connectionId", "dbName"], - }, - }, - }, - }, - { - name: "get_all_columns", - icon: "🧱", - desc: "获取指定数据库下所有表的字段摘要", - detail: - "传入 connectionId 和 dbName,返回跨表字段列表(表名、字段名、类型、注释)。适合用户只知道业务字段、不知道具体在哪张表时快速定位目标表。", - params: "connectionId, dbName", - tool: { - type: "function", - function: { - name: "get_all_columns", - description: - "获取指定数据库下全部表的字段摘要,返回表名、字段名、类型和注释。适用于按字段反查表、跨表梳理相同字段、做数据地图探索。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - }, - required: ["connectionId", "dbName"], - }, - }, - }, - }, - { - name: "get_columns", - icon: "🔍", - desc: "获取指定表的字段结构", - detail: - "传入 connectionId、dbName 和 tableName,返回每个字段的名称、类型、是否可空、默认值和注释。AI 在生成 SQL 前必须调用此工具确认真实字段名。", - params: "connectionId, dbName, tableName", - tool: { - type: "function", - function: { - name: "get_columns", - description: - "获取指定表的字段列表(字段名、类型、是否可空、默认值、注释等)。在生成 SQL 之前必须先调用此工具确认真实字段名,禁止猜测字段名。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - tableName: { type: "string", description: "表名" }, - }, - required: ["connectionId", "dbName", "tableName"], - }, - }, - }, - }, - { - name: "get_indexes", - icon: "🧭", - desc: "获取指定表的索引定义", - detail: - "传入 connectionId、dbName 和 tableName,返回索引名、索引列、唯一性和索引类型。AI 在做慢 SQL 分析、索引优化和执行计划推断时应优先调用。", - params: "connectionId, dbName, tableName", - tool: { - type: "function", - function: { - name: "get_indexes", - description: - "获取指定表的索引定义,包括索引名、字段顺序、唯一性和索引类型。适用于慢 SQL 分析、索引优化建议和确认现有索引覆盖情况。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - tableName: { type: "string", description: "表名" }, - }, - required: ["connectionId", "dbName", "tableName"], - }, - }, - }, - }, - { - name: "get_foreign_keys", - icon: "🧬", - desc: "获取指定表的外键关系", - detail: - "传入 connectionId、dbName 和 tableName,返回当前表到其他表的外键映射。AI 在推断表关系、生成联表 SQL 和评审数据一致性时可直接使用。", - params: "connectionId, dbName, tableName", - tool: { - type: "function", - function: { - name: "get_foreign_keys", - description: - "获取指定表的外键关系,包括本表字段、引用表、引用字段和约束名。适用于联表路径分析、ER 关系梳理和约束检查。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - tableName: { type: "string", description: "表名" }, - }, - required: ["connectionId", "dbName", "tableName"], - }, - }, - }, - }, - { - name: "get_triggers", - icon: "⏱️", - desc: "获取指定表的触发器定义", - detail: - "传入 connectionId、dbName 和 tableName,返回触发器名、触发时机、事件类型和语句体。AI 在分析隐式写入、副作用和审计逻辑时可直接查看。", - params: "connectionId, dbName, tableName", - tool: { - type: "function", - function: { - name: "get_triggers", - description: - "获取指定表的触发器定义,包括触发时机、事件和触发语句。适用于排查隐式数据变更、审计逻辑和表级副作用。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - tableName: { type: "string", description: "表名" }, - }, - required: ["connectionId", "dbName", "tableName"], - }, - }, - }, - }, - { - name: "get_table_ddl", - icon: "📝", - desc: "获取表的建表语句 (DDL)", - detail: - "传入 connectionId、dbName 和 tableName,返回完整的 CREATE TABLE 语句,包含字段定义、索引、约束等信息。", - params: "connectionId, dbName, tableName", - tool: { - type: "function", - function: { - name: "get_table_ddl", - description: "获取指定表的完整建表语句(CREATE TABLE DDL),包含字段、索引、约束等完整结构信息。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - tableName: { type: "string", description: "表名" }, - }, - required: ["connectionId", "dbName", "tableName"], - }, - }, - }, - }, - { - name: "preview_table_rows", - icon: "👀", - desc: "抽样预览指定表的前几行数据", - detail: - "传入 connectionId、dbName、tableName 和可选 limit,返回该表的前几行真实样例数据。适合先看数据形态、空值分布和枚举值,再决定怎么写 SQL。", - params: "connectionId, dbName, tableName, limit?", - tool: { - type: "function", - function: { - name: "preview_table_rows", - description: - "预览指定表的前几行样例数据。适用于快速理解字段取值形态、空值情况、时间格式和状态枚举,减少模型盲写 SQL。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - tableName: { type: "string", description: "表名" }, - limit: { type: "number", description: "可选,预览行数,默认 20,最大 100" }, - }, - required: ["connectionId", "dbName", "tableName"], - }, - }, - }, - }, - { - name: "inspect_table_bundle", - icon: "🧰", - desc: "一次抓取指定表的结构快照", - detail: - "传入 connectionId、dbName 和 tableName,返回字段、索引、外键、触发器和 DDL;还可以附带前几行样例数据。适合在写 SQL、评审表设计或排查副作用前先做完整摸底。", - params: "connectionId, dbName, tableName, includeSampleRows?, sampleLimit?", - tool: { - type: "function", - function: { - name: "inspect_table_bundle", - description: - "一次性获取指定表的结构快照,返回字段、索引、外键、触发器、DDL,以及可选样例数据。适用于做完整表设计摸底、快速理解表关系和降低模型多次往返调用。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - tableName: { type: "string", description: "表名" }, - includeSampleRows: { type: "boolean", description: "可选,是否附带前几行样例数据" }, - sampleLimit: { type: "number", description: "可选,样例行数,默认 10,最大 100" }, - }, - required: ["connectionId", "dbName", "tableName"], - }, - }, - }, - }, - { - name: "inspect_database_bundle", - icon: "🗂️", - desc: "一次抓取指定数据库的结构总览", - detail: - "传入 connectionId 和 dbName,返回库内表清单、表数量、总字段数,以及按表聚合的字段摘要预览。适合刚接手陌生库时先做全局摸底,再决定深入哪张表。", - params: "connectionId, dbName, includeColumns?, tableLimit?, perTableColumnLimit?", - tool: { - type: "function", - function: { - name: "inspect_database_bundle", - description: - "一次性获取指定数据库的结构总览,返回表名列表、总字段数,以及按表聚合的字段摘要预览。适用于陌生数据库摸底、做数据地图和快速选择下一步要深入分析的表。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - includeColumns: { type: "boolean", description: "可选,是否附带按表聚合的字段摘要,默认 true" }, - tableLimit: { type: "number", description: "可选,最多返回多少张表,默认 80,最大 200" }, - perTableColumnLimit: { type: "number", description: "可选,每张表最多返回多少个字段摘要,默认 8,最大 30" }, - }, - required: ["connectionId", "dbName"], - }, - }, - }, - }, - { - name: "inspect_ai_runtime", - icon: "🎛️", - desc: "查看当前 AI 自身运行状态", - detail: - "返回当前启用的模型供应商、模型名、安全级别、上下文级别、启用的 Skills,以及当前已暴露的内置工具和 MCP 工具。适合用户问“你现在能调用什么”“当前用的哪个模型”“为什么不能执行写操作”时,先读真实运行状态再回答。", - params: "无参数", - tool: { - type: "function", - function: { - name: "inspect_ai_runtime", - description: - "读取当前 AI 运行时快照,包括当前供应商、模型、安全级别、上下文级别、启用的 Skills、当前可用的内置工具与 MCP 工具。适用于用户询问当前 AI 能力边界、当前使用哪个模型、为什么不能执行某些操作时,先读取真实运行状态,避免模型猜测。", - parameters: { type: "object", properties: {} }, - }, - }, - }, - { - name: "inspect_ai_providers", - icon: "🪪", - desc: "查看当前 AI 供应商与模型配置", - detail: - "返回当前配置了哪些 AI 供应商、哪个正在生效、各自的 baseUrl、已选模型、声明模型列表、密钥是否存在、自定义请求头 key,以及缺少密钥/模型/地址等待检查项。适合用户问“为什么没有模型”“API Key 有没有配”“当前到底配了哪些供应商”时先读真实配置。", - params: "无参数", - tool: { - type: "function", - function: { - name: "inspect_ai_providers", - description: - "读取当前 AI 供应商配置快照,包括供应商列表、活动供应商、接口地址、已选模型、声明模型列表、是否存在密钥、自定义请求头 key,以及缺少密钥/模型/地址等待检查项。适用于用户提到当前供应商、模型列表为空、API Key 是否配置、为什么 AI 不能正常发起请求时,先读取真实配置再解释。", - parameters: { type: "object", properties: {} }, - }, - }, - }, - { - name: "inspect_ai_chat_readiness", - icon: "🚦", - desc: "查看当前 AI 聊天是否具备发送条件", - detail: - "返回当前聊天输入区是否已经具备发送条件,包括有没有活动供应商、当前供应商是否缺密钥或接口地址、是否已选模型、当前连接/表结构上下文是否已挂载,以及下一步建议动作。适合用户问“为什么现在不能发送”“输入框到底缺什么配置”“当前 AI 聊天准备好了没有”时先读真实状态。", - params: "无参数", - tool: { - type: "function", - function: { - name: "inspect_ai_chat_readiness", - description: - "读取当前 AI 聊天输入区的发送前置状态,包括活动供应商、密钥和接口地址是否完整、是否已选模型、当前连接上下文和已挂载表结构数量,以及建议的下一步动作。适用于用户提到为什么现在不能发送、为什么输入区还没准备好、当前到底缺什么配置时,先读取真实状态再回答。", - parameters: { type: "object", properties: {} }, - }, - }, - }, - { - name: "inspect_mcp_setup", - icon: "🪛", - desc: "查看当前 MCP 配置与外部接入状态", - detail: - "返回当前本地配置了哪些 MCP 服务、哪些已启用、每个服务声明了什么启动命令,以及 Claude Code / Codex 这类外部客户端的写入状态与命令检测结果。适合用户问“我现在配了哪些 MCP”“为什么外部客户端还用不了”“MCP 到底写没写进去”时先读真实状态。", - params: "无参数", - tool: { - type: "function", - function: { - name: "inspect_mcp_setup", - description: - "读取当前本地 MCP 配置快照,包括 MCP 服务列表、启用状态、启动命令、环境变量 key、已发现工具,以及外部客户端的 GoNavi MCP 写入状态与本机 CLI 检测结果。适用于用户提到 MCP 服务配置、Claude/Codex 是否已接入、为什么外部客户端用不了、当前到底启用了哪些 MCP 时,先读取真实配置再回答。", - parameters: { type: "object", properties: {} }, - }, - }, - }, - { - name: "inspect_ai_guidance", - icon: "🧠", - desc: "查看当前 AI 提示词与 Skills 配置", - detail: - "返回当前用户自定义的全局/数据库/JVM 提示词,以及当前启用的 Skills、作用域、依赖工具和 skill prompt 内容。适合用户问“你现在到底带了哪些提示词”“为什么你会这样回答”“当前有哪些 Skills 在生效”时先读真实配置。", - params: "无参数", - tool: { - type: "function", - function: { - name: "inspect_ai_guidance", - description: - "读取当前 AI 的提示与技能配置快照,包括用户自定义提示词、当前启用的 Skills、作用域、依赖工具和各自的 system prompt。适用于用户提到当前提示词、当前 Skill、为什么 AI 当前会这样回答、当前有哪些规则在生效时,先读取真实配置再解释。", - parameters: { type: "object", properties: {} }, - }, - }, - }, - { - name: "inspect_ai_context", - icon: "🧷", - desc: "查看当前 AI 已关联的表结构上下文", - detail: - "返回当前对话已经挂载到 AI 上下文里的表清单、所属连接与数据库,以及每张表的 DDL 预览。适合用户说“看看我现在带了哪些表结构”“当前 AI 上下文是什么”时,先读取真实挂载状态再继续分析。", - params: "includeDDL?(默认 false), ddlLimit?(默认 4000)", - tool: { - type: "function", - function: { - name: "inspect_ai_context", - description: - "读取当前对话已经关联到 AI 上下文里的表结构快照,包括连接、数据库、表名,以及可选的 DDL 内容。适用于用户提到当前 AI 上下文、当前关联表、当前挂载的表结构时,先读取真实状态,避免模型凭记忆复述。", - parameters: { - type: "object", - properties: { - includeDDL: { type: "boolean", description: "可选,是否附带每张表的 DDL 内容,默认 false" }, - ddlLimit: { type: "number", description: "可选,DDL 截断长度,默认 4000,最大 12000" }, - }, - }, - }, - }, - }, - { - name: "inspect_current_connection", - icon: "🛰️", - desc: "查看当前活动连接/数据源摘要", - detail: - "返回当前活动连接的类型、地址、端口、当前数据库、是否启用 SSH/代理/HTTP 隧道,以及当前活动页签绑定的表信息。适合用户问“我现在连的是哪个库”“这个连接走没走 SSH”“当前数据源是什么类型”时先读取真实连接状态。", - params: "无参数", - tool: { - type: "function", - function: { - name: "inspect_current_connection", - description: - "读取当前活动连接或当前页签对应数据源的真实摘要,包括连接类型、地址、端口、当前数据库、SSH/代理/HTTP 隧道状态,以及当前页签绑定的表上下文。适用于用户提到当前连接、当前数据源、当前库地址、是否走 SSH、当前连的是哪种数据库时,先读取真实界面上下文,避免模型猜测。", - parameters: { type: "object", properties: {} }, - }, - }, - }, - { - name: "inspect_saved_connections", - icon: "🧭", - desc: "查看本地已保存连接清单", - detail: - "可按关键词或数据库类型过滤,返回本地保存的数据源列表、连接类型分布,以及每条连接的地址、当前库、SSH/代理/HTTP 隧道状态。适合用户问“我本地存了哪些连接”“帮我找 mysql / postgres 连接”“哪条连接配置了 SSH”时先读真实本地连接资产。", - params: "keyword?, type?, limit?", - tool: { - type: "function", - function: { - name: "inspect_saved_connections", - description: - "读取本地已保存连接清单,可按关键词和数据库类型过滤,并返回每条连接的类型、地址、当前库、SSH/代理/HTTP 隧道等摘要。适用于用户提到本地保存了哪些连接、要找哪条 mysql/postgres 连接、哪条连接启用了 SSH 或代理时,先读取真实本地连接资产再回答。", - parameters: { - type: "object", - properties: { - keyword: { type: "string", description: "可选,按连接名、ID、类型、主机、数据库名或 SSH/代理地址做关键词筛选" }, - type: { type: "string", description: "可选,只看某种数据库类型,例如 mysql、postgres、redis、mongodb" }, - limit: { type: "number", description: "可选,最多返回多少条连接,默认 20,最大 100" }, - }, - }, - }, - }, - }, - { - name: "inspect_active_tab", - icon: "📍", - desc: "查看当前活动页签上下文", - detail: - "返回当前活动页签的类型、连接、数据库、表名,以及当前 SQL / 命令页签里的草稿内容(超长会截断)。适合用户说“看我当前这条 SQL”“优化这个编辑器里的语句”时,先让 AI 直接读取当前工作区上下文。", - params: "includeContent?(默认 true)", - tool: { - type: "function", - function: { - name: "inspect_active_tab", - description: - "获取当前活动页签的上下文快照,包括页签类型、连接、数据库、表名,以及当前 SQL / 命令页签里的草稿内容。适用于用户提到当前页签、当前 SQL、当前编辑器、这条语句时,先读取真实界面上下文,避免让模型猜测。", - parameters: { - type: "object", - properties: { - includeContent: { type: "boolean", description: "可选,是否附带页签中的 SQL / 命令草稿内容,默认 true" }, - }, - }, - }, - }, - }, - { - name: "inspect_workspace_tabs", - icon: "🗃️", - desc: "查看当前工作区打开的页签总览", - detail: - "返回当前工作区里打开的页签列表、哪个是活动页签,以及每个页签对应的连接、数据库、表名等上下文。适合用户说“我现在开了哪些 SQL”“看看我工作区里有哪些页签”“帮我对比这几个查询页签”时,先读取真实工作区布局再继续分析。", - params: "limit?(默认 12), includeContent?(默认 false)", - tool: { - type: "function", - function: { - name: "inspect_workspace_tabs", - description: - "获取当前工作区已打开页签的总览,包括活动页签、页签类型、连接、数据库、表名,以及可选的 SQL / 命令草稿内容。适用于用户提到当前工作区、打开了哪些页签、哪几个查询页签、想对比多个编辑器内容时,先读取真实界面状态,避免模型猜测。", - parameters: { - type: "object", - properties: { - limit: { type: "number", description: "可选,最多返回多少个页签,默认 12,最大 30" }, - includeContent: { type: "boolean", description: "可选,是否附带页签中的 SQL / 命令草稿内容,默认 false" }, - }, - }, - }, - }, - }, - { - name: "inspect_recent_sql_logs", - icon: "🧾", - desc: "查看最近 SQL 执行日志", - detail: - "传入可选 limit 和 status,返回最近 SQL 执行记录,包括数据库、耗时、成功/失败、报错、受影响行数和 SQL 文本。适合追查刚执行失败的语句、定位慢查询,并让 AI 基于真实执行历史给出解释或优化建议。", - params: "limit?, status?(all|success|error)", - tool: { - type: "function", - function: { - name: "inspect_recent_sql_logs", - description: - "获取最近 SQL 执行日志摘要,可按成功/失败过滤。适用于回看刚执行过的 SQL、排查失败原因、定位慢查询,以及让 AI 基于真实执行历史给出解释和优化建议。", - parameters: { - type: "object", - properties: { - limit: { type: "number", description: "可选,返回多少条日志,默认 20,最大 100" }, - status: { - type: "string", - description: "可选,按执行状态过滤,支持 all、success、error,默认 all", - enum: ["all", "success", "error"], - }, - }, - }, - }, - }, - }, - { - name: "inspect_saved_queries", - icon: "💾", - desc: "查看本地已保存的 SQL 查询", - detail: - "可按关键词、连接或数据库过滤,返回保存查询的名称、所属连接、数据库和 SQL 预览。适合用户提到“我之前保存过的查询”“帮我找那条历史 SQL”时先从真实本地收藏里检索。", - params: "keyword?, connectionId?, dbName?, limit?, includeSql?(默认 true)", - tool: { - type: "function", - function: { - name: "inspect_saved_queries", - description: - "读取本地已保存的 SQL 查询列表,可按关键词、连接和数据库过滤,并返回每条查询的名称、所属连接、数据库与 SQL 预览。适用于用户想找历史查询、复用旧 SQL、核对保存脚本时,先读取真实本地记录。", - parameters: { - type: "object", - properties: { - keyword: { type: "string", description: "可选,按查询名称、SQL 文本、连接名或数据库名做关键词筛选" }, - connectionId: { type: "string", description: "可选,只看某个连接下保存的查询" }, - dbName: { type: "string", description: "可选,只看某个数据库下保存的查询" }, - limit: { type: "number", description: "可选,最多返回多少条,默认 12,最大 50" }, - includeSql: { type: "boolean", description: "可选,是否附带 SQL 预览,默认 true" }, - }, - }, - }, - }, - }, - { - name: "inspect_sql_snippets", - icon: "🧩", - desc: "查看 SQL 片段模板", - detail: - "返回本地 SQL 片段的 prefix、名称、说明和模板预览,可按关键词过滤。适合用户想找现成模板、补全片段、团队约定 SQL 模板时先读取真实片段库。", - params: "keyword?, limit?, includeBody?(默认 true)", - tool: { - type: "function", - function: { - name: "inspect_sql_snippets", - description: - "读取本地 SQL 片段模板列表,可按关键词过滤,并返回 prefix、名称、说明和模板预览。适用于用户想找 snippet、复用模板、核对 SQL 片段配置时,先读取真实本地片段库。", - parameters: { - type: "object", - properties: { - keyword: { type: "string", description: "可选,按 prefix、名称、描述或模板内容做关键词筛选" }, - limit: { type: "number", description: "可选,最多返回多少条,默认 20,最大 80" }, - includeBody: { type: "boolean", description: "可选,是否附带模板内容预览,默认 true" }, - }, - }, - }, - }, - }, - { - name: "execute_sql", - icon: "▶️", - desc: "执行 SQL 查询并返回结果", - detail: - "传入 connectionId、dbName 和 sql,在目标数据库上执行 SQL 并返回结果(最多 50 行)。受安全级别控制,只读模式下仅允许 SELECT/SHOW/DESCRIBE。", - params: "connectionId, dbName, sql", - tool: { - type: "function", - function: { - name: "execute_sql", - description: - "在指定连接和数据库上执行 SQL 查询并返回结果。受安全级别控制,只读模式下只能执行 SELECT/SHOW/DESCRIBE 等查询操作。结果最多返回 50 行。", - parameters: { - type: "object", - properties: { - connectionId: { type: "string", description: "连接ID" }, - dbName: { type: "string", description: "数据库名" }, - sql: { type: "string", description: "要执行的 SQL 语句" }, - }, - required: ["connectionId", "dbName", "sql"], - }, - }, - }, - }, -]; +export { + BUILTIN_AI_TOOL_INFO, + type AIChatToolDefinition, + type AIBuiltinToolInfo, +} from "./aiBuiltinToolInfo"; export const BUILTIN_AI_TOOLS: AIChatToolDefinition[] = BUILTIN_AI_TOOL_INFO.map((item) => item.tool);