mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-28 17:31:32 +08:00
✨ feat(ai-tools): 新增 AI 提示与技能配置探针
This commit is contained in:
@@ -879,6 +879,7 @@ export const AIChatPanel: React.FC<AIChatPanelProps> = ({
|
||||
savedQueries: useStore.getState().savedQueries,
|
||||
sqlSnippets: useStore.getState().sqlSnippets,
|
||||
skills,
|
||||
userPromptSettings,
|
||||
dynamicModels,
|
||||
});
|
||||
const toolResultMsg: AIChatMessage = buildToolResultMessage({
|
||||
|
||||
@@ -30,6 +30,8 @@ describe('AIBuiltinToolsCatalog', () => {
|
||||
expect(markup).toContain('inspect_ai_runtime');
|
||||
expect(markup).toContain('排查 MCP 接入状态');
|
||||
expect(markup).toContain('inspect_mcp_setup');
|
||||
expect(markup).toContain('查看当前提示与 Skills');
|
||||
expect(markup).toContain('inspect_ai_guidance');
|
||||
expect(markup).toContain('查看当前 AI 上下文');
|
||||
expect(markup).toContain('inspect_ai_context');
|
||||
expect(markup).toContain('查看当前连接');
|
||||
|
||||
@@ -47,6 +47,11 @@ const BUILTIN_TOOL_FLOWS = [
|
||||
steps: 'inspect_mcp_setup → inspect_ai_runtime',
|
||||
description: '适合先确认当前配置了哪些 MCP 服务、哪些已启用、外部客户端有没有写入当前 GoNavi 路径,再结合运行时工具列表判断为什么某个工具没暴露出来。',
|
||||
},
|
||||
{
|
||||
title: '查看当前提示与 Skills',
|
||||
steps: 'inspect_ai_guidance → inspect_ai_runtime',
|
||||
description: '适合先确认当前自定义提示词、启用的 Skills、依赖工具和生效范围,再解释为什么 AI 当前会这样回答或为什么某个规则没有触发。',
|
||||
},
|
||||
{
|
||||
title: '查看当前 AI 上下文',
|
||||
steps: 'inspect_ai_context → inspect_table_bundle / get_columns',
|
||||
|
||||
@@ -280,6 +280,41 @@ describe('aiLocalToolExecutor', () => {
|
||||
expect(result.content).toContain('"launchCommandPreview":"gonavi-mcp-server stdio"');
|
||||
});
|
||||
|
||||
it('returns the current ai guidance snapshot so the model can inspect active prompts and enabled skills', async () => {
|
||||
const result = await executeLocalAIToolCall({
|
||||
toolCall: buildToolCall('inspect_ai_guidance', {}),
|
||||
connections: [buildConnection()],
|
||||
mcpTools: [],
|
||||
toolContextMap: new Map(),
|
||||
userPromptSettings: {
|
||||
global: '回答前先核对上下文。',
|
||||
database: '生成 SQL 时只读优先。',
|
||||
jvm: '',
|
||||
jvmDiagnostic: '',
|
||||
},
|
||||
skills: [{
|
||||
id: 'skill-1',
|
||||
name: '结构审查',
|
||||
description: '优先核对字段',
|
||||
systemPrompt: '先看字段和索引,再给结论。',
|
||||
enabled: true,
|
||||
scopes: ['database'],
|
||||
requiredTools: ['get_columns'],
|
||||
}],
|
||||
runtime: {
|
||||
getDatabases: vi.fn(),
|
||||
getTables: vi.fn(),
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('"customPromptCount":2');
|
||||
expect(result.content).toContain('"scope":"global"');
|
||||
expect(result.content).toContain('回答前先核对上下文');
|
||||
expect(result.content).toContain('"enabledSkillCount":1');
|
||||
expect(result.content).toContain('"name":"结构审查"');
|
||||
});
|
||||
|
||||
it('returns the current connection snapshot so the model can inspect host, db, and ssh state', async () => {
|
||||
const result = await executeLocalAIToolCall({
|
||||
toolCall: buildToolCall('inspect_current_connection', {}),
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
AIMCPToolDescriptor,
|
||||
AISkillConfig,
|
||||
AIToolCall,
|
||||
AIUserPromptSettings,
|
||||
SavedConnection,
|
||||
SavedQuery,
|
||||
SqlSnippet,
|
||||
@@ -54,6 +55,7 @@ export interface ExecuteLocalAIToolCallOptions {
|
||||
savedQueries?: SavedQuery[];
|
||||
sqlSnippets?: SqlSnippet[];
|
||||
skills?: AISkillConfig[];
|
||||
userPromptSettings?: AIUserPromptSettings;
|
||||
dynamicModels?: string[];
|
||||
runtime?: Partial<AILocalToolRuntime>;
|
||||
}
|
||||
@@ -217,6 +219,7 @@ export async function executeLocalAIToolCall({
|
||||
savedQueries = [],
|
||||
sqlSnippets = [],
|
||||
skills = [],
|
||||
userPromptSettings,
|
||||
dynamicModels = [],
|
||||
runtime,
|
||||
}: ExecuteLocalAIToolCallOptions): Promise<ExecuteLocalAIToolCallResult> {
|
||||
@@ -240,6 +243,7 @@ export async function executeLocalAIToolCall({
|
||||
savedQueries,
|
||||
sqlSnippets,
|
||||
skills,
|
||||
userPromptSettings,
|
||||
dynamicModels,
|
||||
runtime: mergedRuntime,
|
||||
});
|
||||
|
||||
42
frontend/src/components/ai/aiPromptInsights.test.ts
Normal file
42
frontend/src/components/ai/aiPromptInsights.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { buildAIGuidanceSnapshot } from './aiPromptInsights';
|
||||
|
||||
describe('aiPromptInsights', () => {
|
||||
it('summarizes active custom prompts and enabled skills for runtime inspection', () => {
|
||||
const snapshot = buildAIGuidanceSnapshot({
|
||||
userPromptSettings: {
|
||||
global: '回答前先核对上下文。',
|
||||
database: '默认只读优先。',
|
||||
jvm: '',
|
||||
jvmDiagnostic: '',
|
||||
},
|
||||
skills: [
|
||||
{
|
||||
id: 'skill-1',
|
||||
name: '结构审查',
|
||||
description: '优先核对字段和索引',
|
||||
systemPrompt: '先看字段,再给结论。',
|
||||
enabled: true,
|
||||
scopes: ['database'],
|
||||
requiredTools: ['get_columns'],
|
||||
},
|
||||
{
|
||||
id: 'skill-2',
|
||||
name: 'JVM 诊断',
|
||||
description: '诊断命令审查',
|
||||
systemPrompt: '先列风险。',
|
||||
enabled: false,
|
||||
scopes: ['jvmDiagnostic'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(snapshot.customPromptCount).toBe(2);
|
||||
expect(snapshot.customPrompts.find((item) => item.scope === 'global')?.enabled).toBe(true);
|
||||
expect(snapshot.enabledSkillCount).toBe(1);
|
||||
expect(snapshot.disabledSkillCount).toBe(1);
|
||||
expect(snapshot.enabledSkills[0].name).toBe('结构审查');
|
||||
expect(snapshot.enabledSkills[0].requiredTools).toEqual(['get_columns']);
|
||||
});
|
||||
});
|
||||
60
frontend/src/components/ai/aiPromptInsights.ts
Normal file
60
frontend/src/components/ai/aiPromptInsights.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { AISkillConfig, AIUserPromptSettings } from '../../types';
|
||||
|
||||
const PROMPT_SCOPE_LABELS: Record<keyof AIUserPromptSettings, string> = {
|
||||
global: '全局',
|
||||
database: '数据库会话',
|
||||
jvm: 'JVM 资源分析',
|
||||
jvmDiagnostic: 'JVM 诊断',
|
||||
};
|
||||
|
||||
export const buildAIGuidanceSnapshot = (params: {
|
||||
userPromptSettings?: AIUserPromptSettings;
|
||||
skills?: AISkillConfig[];
|
||||
}) => {
|
||||
const userPromptSettings = params.userPromptSettings || {
|
||||
global: '',
|
||||
database: '',
|
||||
jvm: '',
|
||||
jvmDiagnostic: '',
|
||||
};
|
||||
const skills = Array.isArray(params.skills) ? params.skills : [];
|
||||
|
||||
const customPrompts = (Object.keys(PROMPT_SCOPE_LABELS) as Array<keyof AIUserPromptSettings>).map((scope) => {
|
||||
const content = String(userPromptSettings[scope] || '').trim();
|
||||
return {
|
||||
scope,
|
||||
label: PROMPT_SCOPE_LABELS[scope],
|
||||
enabled: content.length > 0,
|
||||
charCount: content.length,
|
||||
content,
|
||||
};
|
||||
});
|
||||
|
||||
const enabledSkills = skills
|
||||
.filter((skill) => skill?.enabled)
|
||||
.map((skill) => ({
|
||||
id: skill.id,
|
||||
name: skill.name,
|
||||
description: skill.description || '',
|
||||
scopes: Array.isArray(skill.scopes) ? skill.scopes : [],
|
||||
requiredTools: Array.isArray(skill.requiredTools) ? skill.requiredTools : [],
|
||||
systemPrompt: String(skill.systemPrompt || '').trim(),
|
||||
systemPromptCharCount: String(skill.systemPrompt || '').trim().length,
|
||||
}))
|
||||
.sort((left, right) => left.name.localeCompare(right.name));
|
||||
|
||||
const disabledSkillCount = skills.filter((skill) => skill && !skill.enabled).length;
|
||||
const enabledPromptCount = customPrompts.filter((item) => item.enabled).length;
|
||||
|
||||
return {
|
||||
customPromptCount: enabledPromptCount,
|
||||
customPrompts,
|
||||
skillCount: skills.length,
|
||||
enabledSkillCount: enabledSkills.length,
|
||||
disabledSkillCount,
|
||||
enabledSkills,
|
||||
message: enabledPromptCount > 0 || enabledSkills.length > 0
|
||||
? `当前已启用 ${enabledPromptCount} 条自定义提示词、${enabledSkills.length} 个 Skills`
|
||||
: '当前没有启用自定义提示词或 Skills',
|
||||
};
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
AIProviderConfig,
|
||||
AISafetyLevel,
|
||||
AISkillConfig,
|
||||
AIUserPromptSettings,
|
||||
SavedConnection,
|
||||
SavedQuery,
|
||||
SqlSnippet,
|
||||
@@ -16,6 +17,7 @@ import { BUILTIN_AI_TOOL_INFO } from '../../utils/aiToolRegistry';
|
||||
import { buildAIContextSnapshot } from './aiContextInsights';
|
||||
import { buildCurrentConnectionSnapshot } from './aiConnectionInsights';
|
||||
import { buildMCPSetupSnapshot } from './aiMCPInsights';
|
||||
import { buildAIGuidanceSnapshot } from './aiPromptInsights';
|
||||
import { buildAIRuntimeSnapshot } from './aiRuntimeInsights';
|
||||
import {
|
||||
buildSavedQueriesSnapshot,
|
||||
@@ -53,6 +55,7 @@ interface ExecuteSnapshotInspectionToolCallOptions {
|
||||
savedQueries?: SavedQuery[];
|
||||
sqlSnippets?: SqlSnippet[];
|
||||
skills?: AISkillConfig[];
|
||||
userPromptSettings?: AIUserPromptSettings;
|
||||
dynamicModels?: string[];
|
||||
runtime?: AISnapshotInspectionRuntime;
|
||||
}
|
||||
@@ -80,6 +83,7 @@ export async function executeSnapshotInspectionToolCall(
|
||||
savedQueries = [],
|
||||
sqlSnippets = [],
|
||||
skills = [],
|
||||
userPromptSettings,
|
||||
dynamicModels = [],
|
||||
runtime,
|
||||
} = options;
|
||||
@@ -118,6 +122,14 @@ export async function executeSnapshotInspectionToolCall(
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
case 'inspect_ai_guidance':
|
||||
return {
|
||||
content: JSON.stringify(buildAIGuidanceSnapshot({
|
||||
userPromptSettings,
|
||||
skills,
|
||||
})),
|
||||
success: true,
|
||||
};
|
||||
case 'inspect_current_connection':
|
||||
return {
|
||||
content: JSON.stringify(buildCurrentConnectionSnapshot({
|
||||
@@ -199,6 +211,7 @@ export async function executeSnapshotInspectionToolCall(
|
||||
const label = {
|
||||
inspect_ai_runtime: '读取当前 AI 运行状态失败',
|
||||
inspect_mcp_setup: '读取 MCP 配置状态失败',
|
||||
inspect_ai_guidance: '读取当前 AI 提示与技能配置失败',
|
||||
inspect_current_connection: '读取当前连接失败',
|
||||
inspect_active_tab: '读取当前活动页签失败',
|
||||
inspect_workspace_tabs: '读取当前工作区页签失败',
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('buildAISystemContextMessages', () => {
|
||||
connections: [connections[0]],
|
||||
tabs: [],
|
||||
activeTabId: null,
|
||||
availableToolNames: ['inspect_workspace_tabs', 'inspect_ai_runtime', 'inspect_mcp_setup', 'inspect_ai_context', 'inspect_current_connection', 'inspect_saved_queries', 'inspect_sql_snippets', 'get_columns'],
|
||||
availableToolNames: ['inspect_workspace_tabs', 'inspect_ai_runtime', 'inspect_mcp_setup', 'inspect_ai_guidance', 'inspect_ai_context', 'inspect_current_connection', 'inspect_saved_queries', 'inspect_sql_snippets', 'get_columns'],
|
||||
skills,
|
||||
userPromptSettings,
|
||||
});
|
||||
@@ -77,6 +77,7 @@ describe('buildAISystemContextMessages', () => {
|
||||
expect(joined).toContain('inspect_workspace_tabs 盘点当前工作区');
|
||||
expect(joined).toContain('inspect_ai_runtime 读取当前 AI 运行状态');
|
||||
expect(joined).toContain('inspect_mcp_setup 读取真实 MCP 配置');
|
||||
expect(joined).toContain('inspect_ai_guidance 读取真实提示与技能配置');
|
||||
expect(joined).toContain('inspect_ai_context 读取当前挂载的表结构上下文');
|
||||
expect(joined).toContain('inspect_current_connection');
|
||||
expect(joined).toContain('inspect_saved_queries');
|
||||
|
||||
@@ -121,6 +121,19 @@ const appendMCPSetupInspectionGuidance = (
|
||||
});
|
||||
};
|
||||
|
||||
const appendAIGuidanceInspectionGuidance = (
|
||||
messages: AISystemContextMessage[],
|
||||
availableToolNames: string[],
|
||||
) => {
|
||||
if (!availableToolNames.includes('inspect_ai_guidance')) {
|
||||
return;
|
||||
}
|
||||
messages.push({
|
||||
role: 'system',
|
||||
content: '如果用户提到“你现在带了哪些提示词”“当前生效的是哪些 Skills”“为什么你会这样回答”“当前数据库/JVM prompt 是什么”,优先调用 inspect_ai_guidance 读取真实提示与技能配置,不要凭记忆概括。',
|
||||
});
|
||||
};
|
||||
|
||||
const resolveDatabaseDisplayType = (config: ConnectionConfig | undefined): string => {
|
||||
const dbType = config?.type || 'unknown';
|
||||
return dbType === 'diros' ? 'Doris' : dbType.charAt(0).toUpperCase() + dbType.slice(1);
|
||||
@@ -339,6 +352,7 @@ SELECT * FROM users WHERE status = 1;
|
||||
}
|
||||
appendAIRuntimeInspectionGuidance(systemMessages, availableToolNames);
|
||||
appendMCPSetupInspectionGuidance(systemMessages, availableToolNames);
|
||||
appendAIGuidanceInspectionGuidance(systemMessages, availableToolNames);
|
||||
if (availableToolNames.includes('inspect_current_connection')) {
|
||||
systemMessages.push({
|
||||
role: 'system',
|
||||
|
||||
@@ -25,6 +25,7 @@ interface AIToolCallingBlockProps {
|
||||
const TOOL_ACTION_LABELS: Record<string, string> = {
|
||||
inspect_ai_runtime: '读取当前 AI 运行状态',
|
||||
inspect_mcp_setup: '读取当前 MCP 配置状态',
|
||||
inspect_ai_guidance: '读取当前 AI 提示与技能配置',
|
||||
get_connections: '获取可用连接信息',
|
||||
get_databases: '扫描数据库列表',
|
||||
get_tables: '分析表结构信息',
|
||||
|
||||
@@ -17,6 +17,13 @@ describe('aiToolRegistry', () => {
|
||||
expect(info?.tool.function.description).toContain('外部客户端');
|
||||
});
|
||||
|
||||
it('registers the ai-guidance inspector as a builtin tool', () => {
|
||||
const info = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_ai_guidance');
|
||||
expect(info).toBeTruthy();
|
||||
expect(info?.desc).toContain('提示词与 Skills');
|
||||
expect(info?.tool.function.description).toContain('自定义提示词');
|
||||
});
|
||||
|
||||
it('registers the current-connection inspector as a builtin tool', () => {
|
||||
const info = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_current_connection');
|
||||
expect(info).toBeTruthy();
|
||||
@@ -52,6 +59,7 @@ describe('aiToolRegistry', () => {
|
||||
|
||||
expect(tools.some((item) => item.function.name === 'inspect_ai_runtime')).toBe(true);
|
||||
expect(tools.some((item) => item.function.name === 'inspect_mcp_setup')).toBe(true);
|
||||
expect(tools.some((item) => item.function.name === 'inspect_ai_guidance')).toBe(true);
|
||||
expect(tools.some((item) => item.function.name === 'inspect_current_connection')).toBe(true);
|
||||
expect(tools.some((item) => item.function.name === 'inspect_saved_queries')).toBe(true);
|
||||
expect(tools.some((item) => item.function.name === 'inspect_sql_snippets')).toBe(true);
|
||||
|
||||
@@ -343,6 +343,23 @@ export const BUILTIN_AI_TOOL_INFO: AIBuiltinToolInfo[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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: "🧷",
|
||||
|
||||
Reference in New Issue
Block a user