|
|
@@ -40,14 +40,32 @@
|
|
|
<div class="field-tip">简要描述智能体的用途和特点。</div>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="系统提示词" prop="config.basic_config.system_prompt">
|
|
|
- <RichEditor v-model="form.config.basic_config.system_prompt" mode="prompt" :rows="8"
|
|
|
- placeholder="请输入系统提示词,可直接编写角色、目标、约束和回答风格" />
|
|
|
+ <div class="prompt-field">
|
|
|
+ <div class="prompt-variable-row">
|
|
|
+ <span class="prompt-variable-label">支持变量</span>
|
|
|
+ <el-tag v-for="variable in systemPromptVariables" :key="variable" class="prompt-variable-tag"
|
|
|
+ type="info" effect="plain" @click="insertPromptVariable('systemPrompt', variable)">
|
|
|
+ {{ formatPromptVariable(variable) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <el-input ref="systemPromptInputRef" v-model="form.config.basic_config.system_prompt" mode="prompt"
|
|
|
+ type="textarea" :rows="8" placeholder="请输入系统提示词,可直接编写角色、目标、约束和回答风格" />
|
|
|
+ </div>
|
|
|
<div class="field-tip">自定义系统提示词,定义智能体的行为和角色。</div>
|
|
|
</el-form-item>
|
|
|
<el-form-item v-if="form.mode === 'quick-answer'" label="上下文模板"
|
|
|
prop="config.basic_config.context_template">
|
|
|
- <RichEditor v-model="form.config.basic_config.context_template" mode="prompt" :rows="5"
|
|
|
- placeholder="请输入上下文模板,用于约束问答模式下的上下文组织方式" />
|
|
|
+ <div class="prompt-field">
|
|
|
+ <div class="prompt-variable-row">
|
|
|
+ <span class="prompt-variable-label">支持变量</span>
|
|
|
+ <el-tag v-for="variable in quickAnswerVariables" :key="variable" class="prompt-variable-tag"
|
|
|
+ type="info" effect="plain" @click="insertPromptVariable('contextTemplate', variable)">
|
|
|
+ {{ formatPromptVariable(variable) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <el-input ref="contextTemplateInputRef" v-model="form.config.basic_config.context_template"
|
|
|
+ mode="prompt" type="textarea" :rows="5" placeholder="请输入上下文模板,用于约束问答模式下的上下文组织方式" />
|
|
|
+ </div>
|
|
|
<div class="field-tip">定义如何将检索到的内容格式化后传递给模型。</div>
|
|
|
</el-form-item>
|
|
|
</div>
|
|
|
@@ -405,14 +423,33 @@
|
|
|
</el-form-item>
|
|
|
<el-form-item v-if="form.config.advanced_config.enable_rewrite" label="改写系统提示词"
|
|
|
prop="config.advanced_config.rewrite_prompt_system">
|
|
|
- <el-input v-model="form.config.advanced_config.rewrite_prompt_system" type="textarea" :rows="3"
|
|
|
- placeholder="请输入问题改写时的系统提示词" />
|
|
|
+ <div class="prompt-field">
|
|
|
+ <div class="prompt-variable-row">
|
|
|
+ <span class="prompt-variable-label">可用变量</span>
|
|
|
+ <el-tag v-for="variable in rewriteVariables" :key="variable" class="prompt-variable-tag"
|
|
|
+ type="info" effect="plain" @click="insertPromptVariable('rewritePromptSystem', variable)">
|
|
|
+ {{ formatPromptVariable(variable) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <el-input ref="rewritePromptSystemInputRef"
|
|
|
+ v-model="form.config.advanced_config.rewrite_prompt_system" type="textarea" :rows="3"
|
|
|
+ placeholder="请输入问题改写时的系统提示词" />
|
|
|
+ </div>
|
|
|
<div class="field-tip">用于问题改写的系统提示词。</div>
|
|
|
</el-form-item>
|
|
|
<el-form-item v-if="form.config.advanced_config.enable_rewrite" label="改写用户提示词"
|
|
|
prop="config.advanced_config.rewrite_prompt_user">
|
|
|
- <el-input v-model="form.config.advanced_config.rewrite_prompt_user" type="textarea" :rows="3"
|
|
|
- placeholder="请输入问题改写时的用户提示词" />
|
|
|
+ <div class="prompt-field">
|
|
|
+ <div class="prompt-variable-row">
|
|
|
+ <span class="prompt-variable-label">可用变量</span>
|
|
|
+ <el-tag v-for="variable in rewriteVariables" :key="variable" class="prompt-variable-tag"
|
|
|
+ type="info" effect="plain" @click="insertPromptVariable('rewritePromptUser', variable)">
|
|
|
+ {{ formatPromptVariable(variable) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <el-input ref="rewritePromptUserInputRef" v-model="form.config.advanced_config.rewrite_prompt_user"
|
|
|
+ type="textarea" :rows="3" placeholder="请输入问题改写时的用户提示词" />
|
|
|
+ </div>
|
|
|
<div class="field-tip">用于问题改写的用户提示词模板。</div>
|
|
|
</el-form-item>
|
|
|
</div>
|
|
|
@@ -442,7 +479,6 @@
|
|
|
import { computed, nextTick, reactive, ref, watch } from 'vue'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
import { agentApplication, aiModel, knowledge, resource } from '@repo/api-service'
|
|
|
-import RichEditor from '@/components/RichEditor/index.vue'
|
|
|
import EmojiPicker from 'vue3-emoji-picker'
|
|
|
import 'vue3-emoji-picker/css'
|
|
|
import type {
|
|
|
@@ -493,6 +529,10 @@ const skillOptions = ref<AgentSelectOption[]>([])
|
|
|
const skillLoading = ref(false)
|
|
|
const allSkillOptions = ref<AgentSelectOption[]>([])
|
|
|
const emojiDialogVisible = ref(false)
|
|
|
+const systemPromptInputRef = ref()
|
|
|
+const contextTemplateInputRef = ref()
|
|
|
+const rewritePromptSystemInputRef = ref()
|
|
|
+const rewritePromptUserInputRef = ref()
|
|
|
const supportedFileTypes = ['pdf', 'docx', 'txt', 'md', 'csv', 'xlsx', 'jpg']
|
|
|
const selectionModeOptions = [
|
|
|
{ label: '全部知识库', value: 'all' },
|
|
|
@@ -508,6 +548,14 @@ const fallbackStrategyOptions = [
|
|
|
{ label: '模型生成', value: 'model' },
|
|
|
{ label: '固定回复', value: 'fixed' }
|
|
|
]
|
|
|
+const smartReasoningSystemPromptVariables = [
|
|
|
+ 'knowledge_bases',
|
|
|
+ 'web_search_status',
|
|
|
+ 'current_time',
|
|
|
+ 'language'
|
|
|
+]
|
|
|
+const quickAnswerVariables = ['query', 'contexts', 'current_time', 'current_week', 'language']
|
|
|
+const rewriteVariables = ['query', 'conversation', 'current_time', 'yesterday', 'language']
|
|
|
|
|
|
const form = reactive<AgentFormData>({
|
|
|
name: '',
|
|
|
@@ -637,6 +685,9 @@ const skillCheckboxOptions = computed(() =>
|
|
|
form.config.setting_config.selected_skills
|
|
|
)
|
|
|
)
|
|
|
+const systemPromptVariables = computed(() =>
|
|
|
+ form.mode === 'quick-answer' ? quickAnswerVariables : smartReasoningSystemPromptVariables
|
|
|
+)
|
|
|
|
|
|
const fieldTabMap: Array<{ match: string | RegExp; tab: string }> = [
|
|
|
{ match: 'mode', tab: 'basic' },
|
|
|
@@ -701,6 +752,70 @@ function clearEmoji() {
|
|
|
form.avatar = ''
|
|
|
}
|
|
|
|
|
|
+function formatPromptVariable(variable: string) {
|
|
|
+ return `{{${variable}}}`
|
|
|
+}
|
|
|
+
|
|
|
+async function insertPromptVariable(
|
|
|
+ field: 'systemPrompt' | 'contextTemplate' | 'rewritePromptSystem' | 'rewritePromptUser',
|
|
|
+ variable: string
|
|
|
+) {
|
|
|
+ const variableText = formatPromptVariable(variable)
|
|
|
+ const fieldMap = {
|
|
|
+ systemPrompt: {
|
|
|
+ get value() {
|
|
|
+ return form.config.basic_config.system_prompt
|
|
|
+ },
|
|
|
+ set value(value: string) {
|
|
|
+ form.config.basic_config.system_prompt = value
|
|
|
+ },
|
|
|
+ ref: systemPromptInputRef
|
|
|
+ },
|
|
|
+ contextTemplate: {
|
|
|
+ get value() {
|
|
|
+ return form.config.basic_config.context_template
|
|
|
+ },
|
|
|
+ set value(value: string) {
|
|
|
+ form.config.basic_config.context_template = value
|
|
|
+ },
|
|
|
+ ref: contextTemplateInputRef
|
|
|
+ },
|
|
|
+ rewritePromptSystem: {
|
|
|
+ get value() {
|
|
|
+ return form.config.advanced_config.rewrite_prompt_system
|
|
|
+ },
|
|
|
+ set value(value: string) {
|
|
|
+ form.config.advanced_config.rewrite_prompt_system = value
|
|
|
+ },
|
|
|
+ ref: rewritePromptSystemInputRef
|
|
|
+ },
|
|
|
+ rewritePromptUser: {
|
|
|
+ get value() {
|
|
|
+ return form.config.advanced_config.rewrite_prompt_user
|
|
|
+ },
|
|
|
+ set value(value: string) {
|
|
|
+ form.config.advanced_config.rewrite_prompt_user = value
|
|
|
+ },
|
|
|
+ ref: rewritePromptUserInputRef
|
|
|
+ }
|
|
|
+ }[field]
|
|
|
+ const textarea = fieldMap.ref.value?.textarea as HTMLTextAreaElement | undefined
|
|
|
+ if (!textarea || typeof textarea.selectionStart !== 'number') {
|
|
|
+ fieldMap.value = `${fieldMap.value || ''}${variableText}`
|
|
|
+ await nextTick()
|
|
|
+ fieldMap.ref.value?.focus?.()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const currentValue = fieldMap.value || ''
|
|
|
+ const start = textarea.selectionStart
|
|
|
+ const end = textarea.selectionEnd
|
|
|
+ fieldMap.value = `${currentValue.slice(0, start)}${variableText}${currentValue.slice(end)}`
|
|
|
+ await nextTick()
|
|
|
+ fieldMap.ref.value?.focus?.()
|
|
|
+ const cursorPosition = start + variableText.length
|
|
|
+ textarea.setSelectionRange(cursorPosition, cursorPosition)
|
|
|
+}
|
|
|
+
|
|
|
function mergeOptions(
|
|
|
current: AgentSelectOption[],
|
|
|
items: AgentSelectOption[],
|
|
|
@@ -1006,6 +1121,8 @@ function applyDetail(item: AgentItem) {
|
|
|
form.config.advanced_config.fallback_response =
|
|
|
item.config?.advanced_config?.fallback_response || ''
|
|
|
form.config.advanced_config.fallback_prompt = item.config?.advanced_config?.fallback_prompt || ''
|
|
|
+
|
|
|
+ console.log('applyDetail', item, form)
|
|
|
}
|
|
|
|
|
|
async function loadKnowledgeBases() {
|
|
|
@@ -1056,9 +1173,9 @@ async function searchWebSearchProviders(keyword: string) {
|
|
|
if (res?.isSuccess) {
|
|
|
webSearchProviderOptions.value = mergeOptions(
|
|
|
webSearchProviderOptions.value,
|
|
|
- (res.result?.model || []).map((item) => ({
|
|
|
- label: item.name,
|
|
|
- value: item.provider
|
|
|
+ (res.result?.model || []).map((provider) => ({
|
|
|
+ label: provider,
|
|
|
+ value: provider
|
|
|
})),
|
|
|
form.config.web_search_config.web_search_provider_id
|
|
|
? [form.config.web_search_config.web_search_provider_id]
|
|
|
@@ -1087,10 +1204,10 @@ async function searchMcpServices(keyword: string) {
|
|
|
mcpServiceOptions.value = mergeOptions(
|
|
|
mcpServiceOptions.value,
|
|
|
((res.result?.model || []) as McpItem[])
|
|
|
- .filter((item) => item.id)
|
|
|
+ .filter((item): item is McpItem & { id: string } => !!item.id)
|
|
|
.map((item) => ({
|
|
|
- label: item.name,
|
|
|
- value: item.id!
|
|
|
+ label: item.name || item.id,
|
|
|
+ value: item.id
|
|
|
})),
|
|
|
form.config.setting_config.mcp_services
|
|
|
)
|
|
|
@@ -1579,6 +1696,35 @@ watch(
|
|
|
color: var(--agent-text-soft);
|
|
|
}
|
|
|
|
|
|
+ .prompt-field {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .prompt-variable-row {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 12px 14px;
|
|
|
+ border: 1px solid var(--agent-border);
|
|
|
+ border-radius: 12px;
|
|
|
+ background: rgba(247, 248, 252, 0.8);
|
|
|
+ }
|
|
|
+
|
|
|
+ .prompt-variable-label {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #64748b;
|
|
|
+ }
|
|
|
+
|
|
|
+ .prompt-variable-tag {
|
|
|
+ cursor: pointer;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+
|
|
|
.collapse-body {
|
|
|
padding: 20px;
|
|
|
}
|