mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-12 00:49:14 +08:00
♻️ refactor: move hub prompts to dedicated prompts-code-mode.ts
- Create src/renderer/src/config/prompts-code-mode.ts - Move HUB_MODE_SYSTEM_PROMPT_BASE and AUTO_MODE_SYSTEM_PROMPT_BASE - Move getHubModeSystemPrompt() and getAutoModeSystemPrompt() - Extract shared buildToolsSection() helper - Update parameterBuilder.ts import
This commit is contained in:
parent
b58569fcec
commit
0482167476
@ -26,7 +26,7 @@ import {
|
||||
isSupportedThinkingTokenModel,
|
||||
isWebSearchModel
|
||||
} from '@renderer/config/models'
|
||||
import { getAutoModeSystemPrompt } from '@renderer/config/prompts'
|
||||
import { getAutoModeSystemPrompt } from '@renderer/config/prompts-code-mode'
|
||||
import { fetchAllActiveServerTools } from '@renderer/services/ApiService'
|
||||
import { getDefaultModel } from '@renderer/services/AssistantService'
|
||||
import store from '@renderer/store'
|
||||
|
||||
266
src/renderer/src/config/prompts-code-mode.ts
Normal file
266
src/renderer/src/config/prompts-code-mode.ts
Normal file
@ -0,0 +1,266 @@
|
||||
import { generateMcpToolFunctionName } from '@shared/mcp'
|
||||
|
||||
export interface ToolInfo {
|
||||
name: string
|
||||
serverName?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Hub Mode System Prompt - For native MCP tool calling
|
||||
* Used when model supports native function calling via MCP protocol
|
||||
*/
|
||||
const HUB_MODE_SYSTEM_PROMPT_BASE = `
|
||||
## Hub MCP Tools – Code Execution Mode
|
||||
|
||||
You can discover and call MCP tools through the hub server using two meta-tools: **search** and **exec**.
|
||||
|
||||
### Critical Rules (Read First)
|
||||
|
||||
1. You MUST explicitly \`return\` the final value from your \`exec\` code. If you do not return a value, the result will be \`undefined\`.
|
||||
2. All MCP tools are async functions. Always call them as \`await ToolName(params)\`.
|
||||
3. Use the exact function names and parameter shapes returned by \`search\`.
|
||||
4. You CANNOT call \`search\` or \`exec\` from inside \`exec\` code—use them only as MCP tools.
|
||||
5. \`console.log\` output is NOT the result. Logs are separate; the final answer must come from \`return\`.
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Call \`search\` with relevant keywords to discover tools.
|
||||
2. Read the returned JavaScript function declarations and JSDoc to understand names and parameters.
|
||||
3. Call \`exec\` with JavaScript code that uses the discovered tools and ends with an explicit \`return\`.
|
||||
4. Use the \`exec\` result as your answer.
|
||||
|
||||
### What \`search\` Does
|
||||
|
||||
- Input: keyword string (comma-separated for OR-matching), plus optional \`limit\`.
|
||||
- Output: JavaScript async function declarations with JSDoc showing exact function names, parameters, and return types.
|
||||
|
||||
### What \`exec\` Does
|
||||
|
||||
- Runs JavaScript code in an isolated async context (wrapped as \`(async () => { your code })())\`.
|
||||
- All discovered tools are exposed as async functions: \`await ToolName(params)\`.
|
||||
- Available helpers:
|
||||
- \`parallel(...promises)\` → \`Promise.all(promises)\`
|
||||
- \`settle(...promises)\` → \`Promise.allSettled(promises)\`
|
||||
- \`console.log/info/warn/error/debug\`
|
||||
- Returns JSON with: \`result\` (your returned value), \`logs\` (optional), \`error\` (optional), \`isError\` (optional).
|
||||
|
||||
### Example: Single Tool Call
|
||||
|
||||
\`\`\`javascript
|
||||
// Step 1: search({ query: "browser,fetch" })
|
||||
// Step 2: exec with:
|
||||
const page = await CherryBrowser_fetch({ url: "https://example.com" })
|
||||
return page
|
||||
\`\`\`
|
||||
|
||||
### Example: Multiple Tools with Parallel
|
||||
|
||||
\`\`\`javascript
|
||||
const [forecast, time] = await parallel(
|
||||
Weather_getForecast({ city: "Paris" }),
|
||||
Time_getLocalTime({ city: "Paris" })
|
||||
)
|
||||
return { city: "Paris", forecast, time }
|
||||
\`\`\`
|
||||
|
||||
### Example: Handle Partial Failures with Settle
|
||||
|
||||
\`\`\`javascript
|
||||
const results = await settle(
|
||||
Weather_getForecast({ city: "Paris" }),
|
||||
Weather_getForecast({ city: "Tokyo" })
|
||||
)
|
||||
const successful = results.filter(r => r.status === "fulfilled").map(r => r.value)
|
||||
return { results, successful }
|
||||
\`\`\`
|
||||
|
||||
### Example: Error Handling
|
||||
|
||||
\`\`\`javascript
|
||||
try {
|
||||
const user = await User_lookup({ email: "user@example.com" })
|
||||
return { found: true, user }
|
||||
} catch (error) {
|
||||
return { found: false, error: String(error) }
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Common Mistakes to Avoid
|
||||
|
||||
❌ **Forgetting to return** (result will be \`undefined\`):
|
||||
\`\`\`javascript
|
||||
const data = await SomeTool({ id: "123" })
|
||||
// Missing return!
|
||||
\`\`\`
|
||||
|
||||
✅ **Always return**:
|
||||
\`\`\`javascript
|
||||
const data = await SomeTool({ id: "123" })
|
||||
return data
|
||||
\`\`\`
|
||||
|
||||
❌ **Only logging, not returning**:
|
||||
\`\`\`javascript
|
||||
const data = await SomeTool({ id: "123" })
|
||||
console.log(data) // Logs are NOT the result!
|
||||
\`\`\`
|
||||
|
||||
❌ **Missing await**:
|
||||
\`\`\`javascript
|
||||
const data = SomeTool({ id: "123" }) // Returns Promise, not value!
|
||||
return data
|
||||
\`\`\`
|
||||
|
||||
❌ **Awaiting before parallel**:
|
||||
\`\`\`javascript
|
||||
await parallel(await ToolA(), await ToolB()) // Wrong: runs sequentially
|
||||
\`\`\`
|
||||
|
||||
✅ **Pass promises directly to parallel**:
|
||||
\`\`\`javascript
|
||||
await parallel(ToolA(), ToolB()) // Correct: runs in parallel
|
||||
\`\`\`
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Always call \`search\` first to discover tools and confirm signatures.
|
||||
- Always use an explicit \`return\` at the end of \`exec\` code.
|
||||
- Use \`parallel\` for independent operations that can run at the same time.
|
||||
- Use \`settle\` when some calls may fail but you still want partial results.
|
||||
- Prefer a single \`exec\` call for multi-step flows.
|
||||
- Treat \`console.*\` as debugging only, never as the primary result.
|
||||
`
|
||||
|
||||
/**
|
||||
* Auto Mode System Prompt - For XML tool_use format
|
||||
* Used when model needs explicit XML format to invoke tools
|
||||
* Only teaches search and exec tools
|
||||
*/
|
||||
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_use>
|
||||
<name>{tool_name}</name>
|
||||
<arguments>{json_arguments}</arguments>
|
||||
</tool_use>
|
||||
|
||||
Rules:
|
||||
- \`{tool_name}\` MUST be either \`search\` or \`exec\`
|
||||
- \`<arguments>\` MUST contain valid JSON (no comments, no trailing commas)
|
||||
- Do NOT include extra text before or after the \`<tool_use>\` 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:
|
||||
<tool_use>
|
||||
<name>search</name>
|
||||
<arguments>{"query": "python,calculator"}</arguments>
|
||||
</tool_use>
|
||||
|
||||
Hub returns function signature:
|
||||
\`\`\`js
|
||||
async function CherryPython_pythonExecute(params: { code: string }): Promise<unknown>
|
||||
\`\`\`
|
||||
|
||||
Assistant calls exec:
|
||||
<tool_use>
|
||||
<name>exec</name>
|
||||
<arguments>{"code": "const result = await CherryPython_pythonExecute({ code: '15 * 7' }); return result;"}</arguments>
|
||||
</tool_use>
|
||||
|
||||
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
|
||||
`
|
||||
|
||||
function buildToolsSection(tools: ToolInfo[]): string {
|
||||
const existingNames = new Set<string>()
|
||||
return 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')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system prompt for Hub Mode (native MCP tool calling)
|
||||
*/
|
||||
export function getHubModeSystemPrompt(tools: ToolInfo[] = []): string {
|
||||
if (tools.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const toolsSection = buildToolsSection(tools)
|
||||
|
||||
return `${HUB_MODE_SYSTEM_PROMPT_BASE}
|
||||
### Available Tools
|
||||
${toolsSection}
|
||||
`
|
||||
}
|
||||
|
||||
/**
|
||||
* Get system prompt for Auto Mode (XML tool_use format)
|
||||
*/
|
||||
export function getAutoModeSystemPrompt(tools: ToolInfo[] = []): string {
|
||||
if (tools.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const toolsSection = buildToolsSection(tools)
|
||||
|
||||
return `${AUTO_MODE_SYSTEM_PROMPT_BASE}
|
||||
## Discoverable Tools (use search to get full signatures)
|
||||
${toolsSection}
|
||||
`
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
import { generateMcpToolFunctionName } from '@shared/mcp'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export const AGENT_PROMPT = `
|
||||
@ -469,258 +468,3 @@ Example: [nytimes.com](https://nytimes.com/some-page).
|
||||
If have multiple citations, please directly list them like this:
|
||||
[www.nytimes.com](https://nytimes.com/some-page)[www.bbc.com](https://bbc.com/some-page)
|
||||
`
|
||||
|
||||
const HUB_MODE_SYSTEM_PROMPT_BASE = `
|
||||
## Hub MCP Tools – Code Execution Mode
|
||||
|
||||
You can discover and call MCP tools through the hub server using two meta-tools: **search** and **exec**.
|
||||
|
||||
### Critical Rules (Read First)
|
||||
|
||||
1. You MUST explicitly \`return\` the final value from your \`exec\` code. If you do not return a value, the result will be \`undefined\`.
|
||||
2. All MCP tools are async functions. Always call them as \`await ToolName(params)\`.
|
||||
3. Use the exact function names and parameter shapes returned by \`search\`.
|
||||
4. You CANNOT call \`search\` or \`exec\` from inside \`exec\` code—use them only as MCP tools.
|
||||
5. \`console.log\` output is NOT the result. Logs are separate; the final answer must come from \`return\`.
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Call \`search\` with relevant keywords to discover tools.
|
||||
2. Read the returned JavaScript function declarations and JSDoc to understand names and parameters.
|
||||
3. Call \`exec\` with JavaScript code that uses the discovered tools and ends with an explicit \`return\`.
|
||||
4. Use the \`exec\` result as your answer.
|
||||
|
||||
### What \`search\` Does
|
||||
|
||||
- Input: keyword string (comma-separated for OR-matching), plus optional \`limit\`.
|
||||
- Output: JavaScript async function declarations with JSDoc showing exact function names, parameters, and return types.
|
||||
|
||||
### What \`exec\` Does
|
||||
|
||||
- Runs JavaScript code in an isolated async context (wrapped as \`(async () => { your code })())\`.
|
||||
- All discovered tools are exposed as async functions: \`await ToolName(params)\`.
|
||||
- Available helpers:
|
||||
- \`parallel(...promises)\` → \`Promise.all(promises)\`
|
||||
- \`settle(...promises)\` → \`Promise.allSettled(promises)\`
|
||||
- \`console.log/info/warn/error/debug\`
|
||||
- Returns JSON with: \`result\` (your returned value), \`logs\` (optional), \`error\` (optional), \`isError\` (optional).
|
||||
|
||||
### Example: Single Tool Call
|
||||
|
||||
\`\`\`javascript
|
||||
// Step 1: search({ query: "browser,fetch" })
|
||||
// Step 2: exec with:
|
||||
const page = await CherryBrowser_fetch({ url: "https://example.com" })
|
||||
return page
|
||||
\`\`\`
|
||||
|
||||
### Example: Multiple Tools with Parallel
|
||||
|
||||
\`\`\`javascript
|
||||
const [forecast, time] = await parallel(
|
||||
Weather_getForecast({ city: "Paris" }),
|
||||
Time_getLocalTime({ city: "Paris" })
|
||||
)
|
||||
return { city: "Paris", forecast, time }
|
||||
\`\`\`
|
||||
|
||||
### Example: Handle Partial Failures with Settle
|
||||
|
||||
\`\`\`javascript
|
||||
const results = await settle(
|
||||
Weather_getForecast({ city: "Paris" }),
|
||||
Weather_getForecast({ city: "Tokyo" })
|
||||
)
|
||||
const successful = results.filter(r => r.status === "fulfilled").map(r => r.value)
|
||||
return { results, successful }
|
||||
\`\`\`
|
||||
|
||||
### Example: Error Handling
|
||||
|
||||
\`\`\`javascript
|
||||
try {
|
||||
const user = await User_lookup({ email: "user@example.com" })
|
||||
return { found: true, user }
|
||||
} catch (error) {
|
||||
return { found: false, error: String(error) }
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Common Mistakes to Avoid
|
||||
|
||||
❌ **Forgetting to return** (result will be \`undefined\`):
|
||||
\`\`\`javascript
|
||||
const data = await SomeTool({ id: "123" })
|
||||
// Missing return!
|
||||
\`\`\`
|
||||
|
||||
✅ **Always return**:
|
||||
\`\`\`javascript
|
||||
const data = await SomeTool({ id: "123" })
|
||||
return data
|
||||
\`\`\`
|
||||
|
||||
❌ **Only logging, not returning**:
|
||||
\`\`\`javascript
|
||||
const data = await SomeTool({ id: "123" })
|
||||
console.log(data) // Logs are NOT the result!
|
||||
\`\`\`
|
||||
|
||||
❌ **Missing await**:
|
||||
\`\`\`javascript
|
||||
const data = SomeTool({ id: "123" }) // Returns Promise, not value!
|
||||
return data
|
||||
\`\`\`
|
||||
|
||||
❌ **Awaiting before parallel**:
|
||||
\`\`\`javascript
|
||||
await parallel(await ToolA(), await ToolB()) // Wrong: runs sequentially
|
||||
\`\`\`
|
||||
|
||||
✅ **Pass promises directly to parallel**:
|
||||
\`\`\`javascript
|
||||
await parallel(ToolA(), ToolB()) // Correct: runs in parallel
|
||||
\`\`\`
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Always call \`search\` first to discover tools and confirm signatures.
|
||||
- Always use an explicit \`return\` at the end of \`exec\` code.
|
||||
- Use \`parallel\` for independent operations that can run at the same time.
|
||||
- Use \`settle\` when some calls may fail but you still want partial results.
|
||||
- Prefer a single \`exec\` call for multi-step flows.
|
||||
- Treat \`console.*\` as debugging only, never as the primary result.
|
||||
`
|
||||
|
||||
interface ToolInfo {
|
||||
name: string
|
||||
serverName?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export function getHubModeSystemPrompt(tools: ToolInfo[] = []): string {
|
||||
if (tools.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const existingNames = new Set<string>()
|
||||
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 `${HUB_MODE_SYSTEM_PROMPT_BASE}
|
||||
### Available Tools
|
||||
${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_use>
|
||||
<name>{tool_name}</name>
|
||||
<arguments>{json_arguments}</arguments>
|
||||
</tool_use>
|
||||
|
||||
Rules:
|
||||
- \`{tool_name}\` MUST be either \`search\` or \`exec\`
|
||||
- \`<arguments>\` MUST contain valid JSON (no comments, no trailing commas)
|
||||
- Do NOT include extra text before or after the \`<tool_use>\` 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:
|
||||
<tool_use>
|
||||
<name>search</name>
|
||||
<arguments>{"query": "python,calculator"}</arguments>
|
||||
</tool_use>
|
||||
|
||||
Hub returns function signature:
|
||||
\`\`\`js
|
||||
async function CherryPython_pythonExecute(params: { code: string }): Promise<unknown>
|
||||
\`\`\`
|
||||
|
||||
Assistant calls exec:
|
||||
<tool_use>
|
||||
<name>exec</name>
|
||||
<arguments>{"code": "const result = await CherryPython_pythonExecute({ code: '15 * 7' }); return result;"}</arguments>
|
||||
</tool_use>
|
||||
|
||||
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<string>()
|
||||
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}
|
||||
`
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user