feat: support system prompt variables (#5995)

* feat: support system prompt variables

* feat: add tip

* fix ci test fail
This commit is contained in:
purefkh 2025-05-27 21:49:25 +08:00 committed by GitHub
parent 5d8e241b3f
commit ad79d356fa
12 changed files with 82 additions and 16 deletions

View File

@ -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",

View File

@ -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": "外部からインポート",

View File

@ -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": "Добавить",

View File

@ -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": "从外部导入",

View File

@ -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": "從外部導入",

View File

@ -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)}>

View File

@ -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

View File

@ -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[] = []

View File

@ -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[] = []

View File

@ -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

View File

@ -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
]

View File

@ -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)