mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-07 05:39:05 +08:00
feat: support system prompt variables (#5995)
* feat: support system prompt variables * feat: add tip * fix ci test fail
This commit is contained in:
parent
f171839830
commit
65273b055c
@ -8,6 +8,7 @@
|
|||||||
"add.name.placeholder": "Enter name",
|
"add.name.placeholder": "Enter name",
|
||||||
"add.prompt": "Prompt",
|
"add.prompt": "Prompt",
|
||||||
"add.prompt.placeholder": "Enter prompt",
|
"add.prompt.placeholder": "Enter prompt",
|
||||||
|
"add.prompt.variables.tip": "Available variables: {{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||||
"add.title": "Create Agent",
|
"add.title": "Create Agent",
|
||||||
"import": {
|
"import": {
|
||||||
"title": "Import from External",
|
"title": "Import from External",
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
"add.name.placeholder": "名前を入力",
|
"add.name.placeholder": "名前を入力",
|
||||||
"add.prompt": "プロンプト",
|
"add.prompt": "プロンプト",
|
||||||
"add.prompt.placeholder": "プロンプトを入力",
|
"add.prompt.placeholder": "プロンプトを入力",
|
||||||
|
"add.prompt.variables.tip": "利用可能な変数:{{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||||
"add.title": "エージェントを作成",
|
"add.title": "エージェントを作成",
|
||||||
"import": {
|
"import": {
|
||||||
"title": "外部からインポート",
|
"title": "外部からインポート",
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
"add.name.placeholder": "Введите имя",
|
"add.name.placeholder": "Введите имя",
|
||||||
"add.prompt": "Промпт",
|
"add.prompt": "Промпт",
|
||||||
"add.prompt.placeholder": "Введите промпт",
|
"add.prompt.placeholder": "Введите промпт",
|
||||||
|
"add.prompt.variables.tip": "Доступные переменные: {{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||||
"add.title": "Создать агента",
|
"add.title": "Создать агента",
|
||||||
"delete.popup.content": "Вы уверены, что хотите удалить этого агента?",
|
"delete.popup.content": "Вы уверены, что хотите удалить этого агента?",
|
||||||
"edit.message.add.title": "Добавить",
|
"edit.message.add.title": "Добавить",
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
"add.name.placeholder": "输入名称",
|
"add.name.placeholder": "输入名称",
|
||||||
"add.prompt": "提示词",
|
"add.prompt": "提示词",
|
||||||
"add.prompt.placeholder": "输入提示词",
|
"add.prompt.placeholder": "输入提示词",
|
||||||
|
"add.prompt.variables.tip": "可用的变量:{{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||||
"add.title": "创建智能体",
|
"add.title": "创建智能体",
|
||||||
"import": {
|
"import": {
|
||||||
"title": "从外部导入",
|
"title": "从外部导入",
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
"add.name.placeholder": "輸入名稱",
|
"add.name.placeholder": "輸入名稱",
|
||||||
"add.prompt": "提示詞",
|
"add.prompt": "提示詞",
|
||||||
"add.prompt.placeholder": "輸入提示詞",
|
"add.prompt.placeholder": "輸入提示詞",
|
||||||
|
"add.prompt.variables.tip": "可用的變數:{{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||||
"add.title": "建立智慧代理人",
|
"add.title": "建立智慧代理人",
|
||||||
"import": {
|
"import": {
|
||||||
"title": "從外部導入",
|
"title": "從外部導入",
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import 'emoji-picker-element'
|
import 'emoji-picker-element'
|
||||||
|
|
||||||
import { CloseCircleFilled } from '@ant-design/icons'
|
import { CloseCircleFilled, QuestionCircleOutlined } from '@ant-design/icons'
|
||||||
import EmojiPicker from '@renderer/components/EmojiPicker'
|
import EmojiPicker from '@renderer/components/EmojiPicker'
|
||||||
import { Box, HSpaceBetweenStack, HStack } from '@renderer/components/Layout'
|
import { Box, HSpaceBetweenStack, HStack } from '@renderer/components/Layout'
|
||||||
import { estimateTextTokens } from '@renderer/services/TokenService'
|
import { estimateTextTokens } from '@renderer/services/TokenService'
|
||||||
import { Assistant, AssistantSettings } from '@renderer/types'
|
import { Assistant, AssistantSettings } from '@renderer/types'
|
||||||
import { getLeadingEmoji } from '@renderer/utils'
|
import { getLeadingEmoji } from '@renderer/utils'
|
||||||
import { Button, Input, Popover } from 'antd'
|
import { Button, Input, Popover, Tooltip } from 'antd'
|
||||||
import TextArea from 'antd/es/input/TextArea'
|
import TextArea from 'antd/es/input/TextArea'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -90,9 +90,12 @@ const AssistantPromptSettings: React.FC<Props> = ({ assistant, updateAssistant }
|
|||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Box mt={8} mb={8} style={{ fontWeight: 'bold' }}>
|
<HStack mt={8} mb={8} alignItems="center" gap={4}>
|
||||||
{t('common.prompt')}
|
<Box style={{ fontWeight: 'bold' }}>{t('common.prompt')}</Box>
|
||||||
</Box>
|
<Tooltip title={t('agents.add.prompt.variables.tip')}>
|
||||||
|
<QuestionCircleOutlined size={14} color="var(--color-text-2)" />
|
||||||
|
</Tooltip>
|
||||||
|
</HStack>
|
||||||
<TextAreaContainer>
|
<TextAreaContainer>
|
||||||
{showMarkdown ? (
|
{showMarkdown ? (
|
||||||
<MarkdownContainer onClick={() => setShowMarkdown(false)}>
|
<MarkdownContainer onClick={() => setShowMarkdown(false)}>
|
||||||
|
|||||||
@ -243,7 +243,7 @@ export default class AnthropicProvider extends BaseProvider {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (this.useSystemPromptForTools && mcpTools && mcpTools.length) {
|
if (this.useSystemPromptForTools && mcpTools && mcpTools.length) {
|
||||||
systemPrompt = buildSystemPrompt(systemPrompt, mcpTools)
|
systemPrompt = await buildSystemPrompt(systemPrompt, mcpTools)
|
||||||
}
|
}
|
||||||
|
|
||||||
let systemMessage: TextBlockParam | undefined = undefined
|
let systemMessage: TextBlockParam | undefined = undefined
|
||||||
|
|||||||
@ -370,7 +370,7 @@ export default class GeminiProvider extends BaseProvider {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (this.useSystemPromptForTools) {
|
if (this.useSystemPromptForTools) {
|
||||||
systemInstruction = buildSystemPrompt(assistant.prompt || '', mcpTools)
|
systemInstruction = await buildSystemPrompt(assistant.prompt || '', mcpTools)
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolResponses: MCPToolResponse[] = []
|
const toolResponses: MCPToolResponse[] = []
|
||||||
|
|||||||
@ -399,7 +399,7 @@ export default class OpenAIProvider extends BaseOpenAIProvider {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (this.useSystemPromptForTools) {
|
if (this.useSystemPromptForTools) {
|
||||||
systemMessage.content = buildSystemPrompt(systemMessage.content || '', mcpTools)
|
systemMessage.content = await buildSystemPrompt(systemMessage.content || '', mcpTools)
|
||||||
}
|
}
|
||||||
|
|
||||||
const userMessages: ChatCompletionMessageParam[] = []
|
const userMessages: ChatCompletionMessageParam[] = []
|
||||||
|
|||||||
@ -352,7 +352,7 @@ export abstract class BaseOpenAIProvider extends BaseProvider {
|
|||||||
tools = tools.concat(extraTools)
|
tools = tools.concat(extraTools)
|
||||||
|
|
||||||
if (this.useSystemPromptForTools) {
|
if (this.useSystemPromptForTools) {
|
||||||
systemMessageInput.text = buildSystemPrompt(systemMessageInput.text || '', mcpTools)
|
systemMessageInput.text = await buildSystemPrompt(systemMessageInput.text || '', mcpTools)
|
||||||
}
|
}
|
||||||
systemMessageContent.push(systemMessageInput)
|
systemMessageContent.push(systemMessageInput)
|
||||||
systemMessage.content = systemMessageContent
|
systemMessage.content = systemMessageContent
|
||||||
|
|||||||
@ -29,26 +29,26 @@ describe('prompt', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('buildSystemPrompt', () => {
|
describe('buildSystemPrompt', () => {
|
||||||
it('should build prompt with tools', () => {
|
it('should build prompt with tools', async () => {
|
||||||
const userPrompt = 'Custom user system prompt'
|
const userPrompt = 'Custom user system prompt'
|
||||||
const tools = [
|
const tools = [
|
||||||
{ id: 'test-tool', description: 'Test tool description', inputSchema: { type: 'object' } } as MCPTool
|
{ id: 'test-tool', description: 'Test tool description', inputSchema: { type: 'object' } } as MCPTool
|
||||||
]
|
]
|
||||||
const result = buildSystemPrompt(userPrompt, tools)
|
const result = await buildSystemPrompt(userPrompt, tools)
|
||||||
|
|
||||||
expect(result).toContain(userPrompt)
|
expect(result).toContain(userPrompt)
|
||||||
expect(result).toContain('test-tool')
|
expect(result).toContain('test-tool')
|
||||||
expect(result).toContain('Test tool description')
|
expect(result).toContain('Test tool description')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return user prompt without tools', () => {
|
it('should return user prompt without tools', async () => {
|
||||||
const userPrompt = 'Custom user system prompt'
|
const userPrompt = 'Custom user system prompt'
|
||||||
const result = buildSystemPrompt(userPrompt, [])
|
const result = await buildSystemPrompt(userPrompt, [])
|
||||||
|
|
||||||
expect(result).toBe(userPrompt)
|
expect(result).toBe(userPrompt)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle null or undefined user prompt', () => {
|
it('should handle null or undefined user prompt', async () => {
|
||||||
const tools = [
|
const tools = [
|
||||||
{ id: 'test-tool', description: 'Test tool description', inputSchema: { type: 'object' } } as MCPTool
|
{ id: 'test-tool', description: 'Test tool description', inputSchema: { type: 'object' } } as MCPTool
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
import store from '@renderer/store'
|
||||||
import { MCPTool } from '@renderer/types'
|
import { MCPTool } from '@renderer/types'
|
||||||
|
|
||||||
export const SYSTEM_PROMPT = `In this environment you have access to a set of tools you can use to answer the user's question. \
|
export const SYSTEM_PROMPT = `In this environment you have access to a set of tools you can use to answer the user's question. \
|
||||||
You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
|
You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
|
||||||
|
|
||||||
@ -147,7 +147,65 @@ ${availableTools}
|
|||||||
</tools>`
|
</tools>`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const buildSystemPrompt = (userSystemPrompt: string, tools?: MCPTool[]): string => {
|
export const buildSystemPrompt = async (userSystemPrompt: string, tools?: MCPTool[]): Promise<string> => {
|
||||||
|
if (typeof userSystemPrompt === 'string') {
|
||||||
|
const now = new Date()
|
||||||
|
if (userSystemPrompt.includes('{{date}}')) {
|
||||||
|
const date = now.toLocaleDateString()
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{date}}/g, date)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSystemPrompt.includes('{{time}}')) {
|
||||||
|
const time = now.toLocaleTimeString()
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{time}}/g, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSystemPrompt.includes('{{datetime}}')) {
|
||||||
|
const datetime = now.toLocaleString()
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{datetime}}/g, datetime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSystemPrompt.includes('{{system}}')) {
|
||||||
|
try {
|
||||||
|
const systemType = await window.api.system.getDeviceType()
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{system}}/g, systemType)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get system type:', error)
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{system}}/g, 'Unknown System')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSystemPrompt.includes('{{language}}')) {
|
||||||
|
try {
|
||||||
|
const language = store.getState().settings.language
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{language}}/g, language)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get language:', error)
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{language}}/g, 'Unknown System Language')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSystemPrompt.includes('{{arch}}')) {
|
||||||
|
try {
|
||||||
|
const appInfo = await window.api.getAppInfo()
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{arch}}/g, appInfo.arch)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get architecture:', error)
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{arch}}/g, 'Unknown Architecture')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSystemPrompt.includes('{{model_name}}')) {
|
||||||
|
try {
|
||||||
|
const modelName = store.getState().llm.defaultModel.name
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{model_name}}/g, modelName)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get model name:', error)
|
||||||
|
userSystemPrompt = userSystemPrompt.replace(/{{model_name}}/g, 'Unknown Model')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tools && tools.length > 0) {
|
if (tools && tools.length > 0) {
|
||||||
return SYSTEM_PROMPT.replace('{{ USER_SYSTEM_PROMPT }}', userSystemPrompt)
|
return SYSTEM_PROMPT.replace('{{ USER_SYSTEM_PROMPT }}', userSystemPrompt)
|
||||||
.replace('{{ TOOL_USE_EXAMPLES }}', ToolUseExamples)
|
.replace('{{ TOOL_USE_EXAMPLES }}', ToolUseExamples)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user