diff --git a/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts b/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts index 1d92660072..21b07af811 100644 --- a/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts +++ b/src/renderer/src/aiCore/prepareParams/parameterBuilder.ts @@ -26,7 +26,7 @@ import { isSupportedThinkingTokenModel, isWebSearchModel } from '@renderer/config/models' -import { getHubModeSystemPrompt } from '@renderer/config/prompts' +import { getAutoModeSystemPrompt } from '@renderer/config/prompts' import { fetchAllActiveServerTools } from '@renderer/services/ApiService' import { getDefaultModel } from '@renderer/services/AssistantService' import store from '@renderer/store' @@ -245,16 +245,18 @@ export async function buildStreamTextParams( params.tools = tools } - if (assistant.prompt) { - let systemPrompt = await replacePromptVariables(assistant.prompt, model.name) - if (getEffectiveMcpMode(assistant) === 'auto') { - const allActiveTools = await fetchAllActiveServerTools() - systemPrompt = systemPrompt + '\n\n' + getHubModeSystemPrompt(allActiveTools) - } - params.system = systemPrompt - } else if (getEffectiveMcpMode(assistant) === 'auto') { + let systemPrompt = assistant.prompt ? await replacePromptVariables(assistant.prompt, model.name) : '' + + if (getEffectiveMcpMode(assistant) === 'auto') { const allActiveTools = await fetchAllActiveServerTools() - params.system = getHubModeSystemPrompt(allActiveTools) + const autoModePrompt = getAutoModeSystemPrompt(allActiveTools) + if (autoModePrompt) { + systemPrompt = systemPrompt ? `${systemPrompt}\n\n${autoModePrompt}` : autoModePrompt + } + } + + if (systemPrompt) { + params.system = systemPrompt } logger.debug('params', params) diff --git a/src/renderer/src/config/prompts.ts b/src/renderer/src/config/prompts.ts index fda3ec085d..db0128633d 100644 --- a/src/renderer/src/config/prompts.ts +++ b/src/renderer/src/config/prompts.ts @@ -619,3 +619,108 @@ export function getHubModeSystemPrompt(tools: ToolInfo[] = []): string { ${toolsSection} ` } + +const AUTO_MODE_SYSTEM_PROMPT_BASE = ` +You can discover and invoke MCP tools through a hub using TWO meta-tools: \`search\` and \`exec\`. + +## Tool Invocation Format + +When you want to call a tool, output exactly one XML block: + + + {tool_name} + {json_arguments} + + +Rules: +- \`{tool_name}\` MUST be either \`search\` or \`exec\` +- \`\` MUST contain valid JSON (no comments, no trailing commas) +- Do NOT include extra text before or after the \`\` block + +## Available Tools + +1. **search** - Discover MCP tools by keyword + \`\`\`json + { "query": "keyword1,keyword2", "limit": 10 } + \`\`\` + Returns JavaScript function declarations with JSDoc showing names, parameters, and return types. + +2. **exec** - Execute JavaScript that calls discovered tools + \`\`\`json + { "code": "const r = await ToolName({...}); return r;" } + \`\`\` + **CRITICAL:** You MUST \`return\` the final value, or result will be \`undefined\`. + +## Workflow + +1. Call \`search\` with keywords to discover tools +2. Read the returned function signatures carefully +3. Call \`exec\` with JavaScript code that: + - Uses ONLY functions returned by \`search\` + - Calls them with \`await\` + - Ends with explicit \`return\` +4. Answer the user based on the result + +## Example + +User: "Calculate 15 * 7" + +Assistant calls search: + + search + {"query": "python,calculator"} + + +Hub returns function signature: +\`\`\`js +async function CherryPython_pythonExecute(params: { code: string }): Promise +\`\`\` + +Assistant calls exec: + + exec + {"code": "const result = await CherryPython_pythonExecute({ code: '15 * 7' }); return result;"} + + +Hub returns: { "result": 105 } + +Assistant answers: "15 × 7 = 105" + +## Common Mistakes + +❌ Forgetting to return (result will be undefined): +\`\`\`js +await SomeTool({ id: "123" }) +\`\`\` + +✅ Always return: +\`\`\`js +const data = await SomeTool({ id: "123" }); return data; +\`\`\` + +❌ Calling exec before search - you must discover tools first + +❌ Using functions not returned by search +` + +export function getAutoModeSystemPrompt(tools: ToolInfo[] = []): string { + if (tools.length === 0) { + return '' + } + + const existingNames = new Set() + const toolsSection = tools + .map((t) => { + const functionName = generateMcpToolFunctionName(t.serverName, t.name, existingNames) + const desc = t.description || '' + const normalizedDesc = desc.replace(/\s+/g, ' ').trim() + const truncatedDesc = normalizedDesc.length > 50 ? `${normalizedDesc.slice(0, 50)}...` : normalizedDesc + return `- ${functionName}: ${truncatedDesc}` + }) + .join('\n') + + return `${AUTO_MODE_SYSTEM_PROMPT_BASE} +## Discoverable Tools (use search to get full signatures) +${toolsSection} +` +}