mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-25 03:10:08 +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
5d8e241b3f
commit
ad79d356fa
@ -8,6 +8,7 @@
|
||||
"add.name.placeholder": "Enter name",
|
||||
"add.prompt": "Prompt",
|
||||
"add.prompt.placeholder": "Enter prompt",
|
||||
"add.prompt.variables.tip": "Available variables: {{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||
"add.title": "Create Agent",
|
||||
"import": {
|
||||
"title": "Import from External",
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"add.name.placeholder": "名前を入力",
|
||||
"add.prompt": "プロンプト",
|
||||
"add.prompt.placeholder": "プロンプトを入力",
|
||||
"add.prompt.variables.tip": "利用可能な変数:{{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||
"add.title": "エージェントを作成",
|
||||
"import": {
|
||||
"title": "外部からインポート",
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"add.name.placeholder": "Введите имя",
|
||||
"add.prompt": "Промпт",
|
||||
"add.prompt.placeholder": "Введите промпт",
|
||||
"add.prompt.variables.tip": "Доступные переменные: {{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||
"add.title": "Создать агента",
|
||||
"delete.popup.content": "Вы уверены, что хотите удалить этого агента?",
|
||||
"edit.message.add.title": "Добавить",
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"add.name.placeholder": "输入名称",
|
||||
"add.prompt": "提示词",
|
||||
"add.prompt.placeholder": "输入提示词",
|
||||
"add.prompt.variables.tip": "可用的变量:{{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||
"add.title": "创建智能体",
|
||||
"import": {
|
||||
"title": "从外部导入",
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"add.name.placeholder": "輸入名稱",
|
||||
"add.prompt": "提示詞",
|
||||
"add.prompt.placeholder": "輸入提示詞",
|
||||
"add.prompt.variables.tip": "可用的變數:{{date}}, {{time}}, {{datetime}}, {{system}}, {{arch}}, {{language}}, {{model_name}}",
|
||||
"add.title": "建立智慧代理人",
|
||||
"import": {
|
||||
"title": "從外部導入",
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import 'emoji-picker-element'
|
||||
|
||||
import { CloseCircleFilled } from '@ant-design/icons'
|
||||
import { CloseCircleFilled, QuestionCircleOutlined } from '@ant-design/icons'
|
||||
import EmojiPicker from '@renderer/components/EmojiPicker'
|
||||
import { Box, HSpaceBetweenStack, HStack } from '@renderer/components/Layout'
|
||||
import { estimateTextTokens } from '@renderer/services/TokenService'
|
||||
import { Assistant, AssistantSettings } from '@renderer/types'
|
||||
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 { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -90,9 +90,12 @@ const AssistantPromptSettings: React.FC<Props> = ({ assistant, updateAssistant }
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
</HStack>
|
||||
<Box mt={8} mb={8} style={{ fontWeight: 'bold' }}>
|
||||
{t('common.prompt')}
|
||||
</Box>
|
||||
<HStack mt={8} mb={8} alignItems="center" gap={4}>
|
||||
<Box style={{ fontWeight: 'bold' }}>{t('common.prompt')}</Box>
|
||||
<Tooltip title={t('agents.add.prompt.variables.tip')}>
|
||||
<QuestionCircleOutlined size={14} color="var(--color-text-2)" />
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
<TextAreaContainer>
|
||||
{showMarkdown ? (
|
||||
<MarkdownContainer onClick={() => setShowMarkdown(false)}>
|
||||
|
||||
@ -243,7 +243,7 @@ export default class AnthropicProvider extends BaseProvider {
|
||||
})
|
||||
|
||||
if (this.useSystemPromptForTools && mcpTools && mcpTools.length) {
|
||||
systemPrompt = buildSystemPrompt(systemPrompt, mcpTools)
|
||||
systemPrompt = await buildSystemPrompt(systemPrompt, mcpTools)
|
||||
}
|
||||
|
||||
let systemMessage: TextBlockParam | undefined = undefined
|
||||
|
||||
@ -370,7 +370,7 @@ export default class GeminiProvider extends BaseProvider {
|
||||
})
|
||||
|
||||
if (this.useSystemPromptForTools) {
|
||||
systemInstruction = buildSystemPrompt(assistant.prompt || '', mcpTools)
|
||||
systemInstruction = await buildSystemPrompt(assistant.prompt || '', mcpTools)
|
||||
}
|
||||
|
||||
const toolResponses: MCPToolResponse[] = []
|
||||
|
||||
@ -399,7 +399,7 @@ export default class OpenAIProvider extends BaseOpenAIProvider {
|
||||
})
|
||||
|
||||
if (this.useSystemPromptForTools) {
|
||||
systemMessage.content = buildSystemPrompt(systemMessage.content || '', mcpTools)
|
||||
systemMessage.content = await buildSystemPrompt(systemMessage.content || '', mcpTools)
|
||||
}
|
||||
|
||||
const userMessages: ChatCompletionMessageParam[] = []
|
||||
|
||||
@ -352,7 +352,7 @@ export abstract class BaseOpenAIProvider extends BaseProvider {
|
||||
tools = tools.concat(extraTools)
|
||||
|
||||
if (this.useSystemPromptForTools) {
|
||||
systemMessageInput.text = buildSystemPrompt(systemMessageInput.text || '', mcpTools)
|
||||
systemMessageInput.text = await buildSystemPrompt(systemMessageInput.text || '', mcpTools)
|
||||
}
|
||||
systemMessageContent.push(systemMessageInput)
|
||||
systemMessage.content = systemMessageContent
|
||||
|
||||
@ -29,26 +29,26 @@ describe('prompt', () => {
|
||||
})
|
||||
|
||||
describe('buildSystemPrompt', () => {
|
||||
it('should build prompt with tools', () => {
|
||||
it('should build prompt with tools', async () => {
|
||||
const userPrompt = 'Custom user system prompt'
|
||||
const tools = [
|
||||
{ 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('test-tool')
|
||||
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 result = buildSystemPrompt(userPrompt, [])
|
||||
const result = await buildSystemPrompt(userPrompt, [])
|
||||
|
||||
expect(result).toBe(userPrompt)
|
||||
})
|
||||
|
||||
it('should handle null or undefined user prompt', () => {
|
||||
it('should handle null or undefined user prompt', async () => {
|
||||
const tools = [
|
||||
{ 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'
|
||||
|
||||
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.
|
||||
|
||||
@ -147,7 +147,65 @@ ${availableTools}
|
||||
</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) {
|
||||
return SYSTEM_PROMPT.replace('{{ USER_SYSTEM_PROMPT }}', userSystemPrompt)
|
||||
.replace('{{ TOOL_USE_EXAMPLES }}', ToolUseExamples)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user