diff --git a/frontend/src/components/Input-field.vue b/frontend/src/components/Input-field.vue index 8999914f0..22b1740f0 100644 --- a/frontend/src/components/Input-field.vue +++ b/frontend/src/components/Input-field.vue @@ -591,6 +591,25 @@ const handleGoToAgentSettings = () => { } } +// 获取 Agent 不就绪的原因 +const getAgentNotReadyReasons = (): string[] => { + const reasons: string[] = [] + const config = settingsStore.agentConfig || { allowedTools: [] } + const models = settingsStore.conversationModels || { summaryModelId: '', rerankModelId: '' } + + if (!config.allowedTools || config.allowedTools.length === 0) { + reasons.push(t('input.agentMissingAllowedTools')) + } + if (!models.summaryModelId || models.summaryModelId.trim() === '') { + reasons.push(t('input.agentMissingSummaryModel')) + } + if (!models.rerankModelId || models.rerankModelId.trim() === '') { + reasons.push(t('input.agentMissingRerankModel')) + } + + return reasons +} + const toggleAgentMode = () => { // 如果要启用 Agent,先检查是否就绪 // 注意:isAgentReady 是从 store 中计算的,需要确保 store 中的配置是最新的 @@ -598,16 +617,19 @@ const toggleAgentMode = () => { // 尝试启用 Agent,先检查是否就绪 const agentReady = settingsStore.isAgentReady if (!agentReady) { + const reasons = getAgentNotReadyReasons() + const reasonsText = reasons.join('、') + // 创建带跳转链接的自定义消息 - const messageContent = h('div', { style: 'display: flex; align-items: center; gap: 8px; flex-wrap: wrap;' }, [ - h('span', { style: 'flex: 1; min-width: 0;' }, t('input.messages.agentNotReadyDetail')), + const messageContent = h('div', { style: 'display: flex; flex-direction: column; gap: 8px; max-width: 320px;' }, [ + h('span', { style: 'color: #333; line-height: 1.5;' }, t('input.messages.agentNotReadyDetail', { reasons: reasonsText })), h('a', { href: '#', onClick: (e: Event) => { e.preventDefault(); handleGoToAgentSettings(); }, - style: 'color: #07C05F; text-decoration: none; font-weight: 500; cursor: pointer; white-space: nowrap; flex-shrink: 0;', + style: 'color: #07C05F; text-decoration: none; font-weight: 500; cursor: pointer; align-self: flex-start;', onMouseenter: (e: Event) => { (e.target as HTMLElement).style.textDecoration = 'underline'; }, diff --git a/frontend/src/i18n/locales/en-US.ts b/frontend/src/i18n/locales/en-US.ts index a08549860..0ef81d921 100644 --- a/frontend/src/i18n/locales/en-US.ts +++ b/frontend/src/i18n/locales/en-US.ts @@ -642,7 +642,10 @@ export default { normalModeDesc: 'Knowledge base RAG Q&A', agentModeDesc: 'ReAct reasoning framework with multi-step thinking', agentNotReadyTooltip: 'Agent is not ready. Please finish configuration first.', - agentNotReadyDetail: 'Agent is not ready. Please complete the agent configuration in settings (thinking model, rerank model, and allowed tools).', + agentNotReadyDetail: 'Agent is not ready. Please configure the following: {reasons}', + agentMissingAllowedTools: 'Allowed tools', + agentMissingSummaryModel: 'Chat Model (Summary Model)', + agentMissingRerankModel: 'Rerank Model', goToSettings: 'Go to settings →', webSearch: { toggleOn: 'Enable Web Search', @@ -666,7 +669,10 @@ export default { agentSwitchedOff: 'Switched to Normal Mode', agentEnabled: 'Agent Mode enabled', agentDisabled: 'Agent Mode disabled', - agentNotReadyDetail: 'Agent is not ready. Please complete the agent configuration in settings (thinking model, rerank model, and allowed tools).', + agentNotReadyDetail: 'Agent is not ready. Please configure the following: {reasons}', + agentMissingAllowedTools: 'Allowed tools', + agentMissingSummaryModel: 'Chat Model (Summary Model)', + agentMissingRerankModel: 'Rerank Model', webSearchNotConfigured: 'Web search engine is not configured. Please configure a provider and credentials in settings.', webSearchEnabled: 'Web search enabled', webSearchDisabled: 'Web search disabled', @@ -1311,7 +1317,8 @@ export default { missingThinkingModel: 'Thinking model', missingRerankModel: 'Rerank model', missingAllowedTools: 'Allowed tools', - pleaseConfigure: 'Please configure {items}' + pleaseConfigure: 'Please configure {items}', + goConfigureModels: 'Configure models →' }, maxIterations: { label: 'Max Iterations', diff --git a/frontend/src/i18n/locales/ru-RU.ts b/frontend/src/i18n/locales/ru-RU.ts index 69f652301..7c72b534a 100644 --- a/frontend/src/i18n/locales/ru-RU.ts +++ b/frontend/src/i18n/locales/ru-RU.ts @@ -785,7 +785,7 @@ export default { uninitializedBanner: 'Некоторые базы знаний не инициализированы. Сначала настройте модели в разделе настроек, чтобы добавлять документы.', empty: { title: 'Базы знаний отсутствуют', - description: 'Нажмите «Создать базу знаний» в правом верхнем углу, чтобы добавить первую базу.' + description: 'Нажмите «Создать базу знаний» в левом быстром действии, чтобы добавить первую базу.' }, delete: { confirmTitle: 'Подтверждение удаления', @@ -1177,7 +1177,8 @@ export default { missingThinkingModel: 'модель мышления', missingRerankModel: 'модель ранжирования', missingAllowedTools: 'разрешённые инструменты', - pleaseConfigure: 'Пожалуйста, настройте: {items}' + pleaseConfigure: 'Пожалуйста, настройте: {items}', + goConfigureModels: 'Перейти к настройке моделей →' }, maxIterations: { label: 'Макс. число итераций', @@ -1512,7 +1513,10 @@ export default { normalModeDesc: 'RAG-вопросы и ответы по базе знаний', agentModeDesc: 'Шаблон рассуждений ReAct, многошаговое мышление', agentNotReadyTooltip: 'Agent не готов. Пожалуйста, завершите настройку.', - agentNotReadyDetail: 'Agent не готов. Сначала завершите настройку агента в параметрах (модель мышления, модель rerank и разрешённые инструменты).', + agentNotReadyDetail: 'Agent не готов. Пожалуйста, настройте следующее: {reasons}', + agentMissingAllowedTools: 'Разрешённые инструменты', + agentMissingSummaryModel: 'Модель беседы (Summary Model)', + agentMissingRerankModel: 'Модель переранжирования (Rerank Model)', goToSettings: 'Перейти к настройкам →', webSearch: { toggleOn: 'Включить веб-поиск', @@ -1536,7 +1540,10 @@ export default { agentSwitchedOff: 'Переключено в обычный режим', agentEnabled: 'Agent режим включён', agentDisabled: 'Agent режим отключён', - agentNotReadyDetail: 'Agent не готов. Сначала завершите настройку агента в параметрах (модель мышления, модель rerank и разрешённые инструменты).', + agentNotReadyDetail: 'Agent не готов. Пожалуйста, настройте следующее: {reasons}', + agentMissingAllowedTools: 'Разрешённые инструменты', + agentMissingSummaryModel: 'Модель беседы (Summary Model)', + agentMissingRerankModel: 'Модель переранжирования (Rerank Model)', webSearchNotConfigured: 'Веб-поиск не настроен. Сначала выберите провайдера и настройте ключи в разделе настроек.', webSearchEnabled: 'Веб-поиск включён', webSearchDisabled: 'Веб-поиск выключен', diff --git a/frontend/src/i18n/locales/zh-CN.ts b/frontend/src/i18n/locales/zh-CN.ts index ac18951e1..e119c2cb1 100644 --- a/frontend/src/i18n/locales/zh-CN.ts +++ b/frontend/src/i18n/locales/zh-CN.ts @@ -1010,7 +1010,7 @@ export default { "部分知识库尚未初始化,需要先在设置中配置模型信息才能添加知识文档", empty: { title: "暂无知识库", - description: "点击右上角“新建知识库”按钮创建第一个知识库", + description: "点击左侧快捷操作“新建知识库”按钮创建第一个知识库", }, delete: { confirmTitle: "删除确认", @@ -1162,7 +1162,7 @@ export default { title: "模型配置", description: "为知识库选择合适的 AI 模型", llmLabel: "LLM 大语言模型", - llmDesc: "用于总结和摘要的大语言模型(可选)", + llmDesc: "用于总结和摘要的大语言模型", llmPlaceholder: "请选择 LLM 模型(可选)", embeddingLabel: "Embedding 嵌入模型", embeddingDesc: "用于文本向量化的嵌入模型", @@ -1267,7 +1267,10 @@ export default { agentModeDesc: "ReAct 推理框架,多步思考", agentNotReadyTooltip: "Agent 未就绪,请先在设置中完成配置", agentNotReadyDetail: - "Agent 未就绪,请先在设置中完成 Agent 配置(思考模型、Rerank 模型和允许的工具)", + "Agent 未就绪,需要配置以下内容:{reasons}", + agentMissingAllowedTools: "允许的工具", + agentMissingSummaryModel: "对话模型(Summary Model)", + agentMissingRerankModel: "重排模型(Rerank Model)", goToSettings: "前往设置 →", webSearch: { toggleOn: "开启网络搜索", @@ -1292,7 +1295,7 @@ export default { agentEnabled: "Agent 模式已启用", agentDisabled: "Agent 模式已禁用", agentNotReadyDetail: - "Agent 未就绪,请先在设置中完成 Agent 配置(思考模型、Rerank 模型和允许的工具)", + "Agent 未就绪,需要配置以下内容:{reasons}", webSearchNotConfigured: "未配置网络搜索引擎,请先在设置中完成搜索引擎选择与接口配置。", webSearchEnabled: "网络搜索已开启", @@ -1313,9 +1316,12 @@ export default { notReady: "未就绪", hint: "配置完成后,Agent 状态将自动变为“可用”,此时可在对话界面开启 Agent 模式", missingThinkingModel: "思考模型", + missingSummaryModel: "对话模型(Summary Model)", missingRerankModel: "Rerank 模型", missingAllowedTools: "允许的工具", pleaseConfigure: "请配置{items}", + goToConfig: "前往配置对话模型", + goConfigureModels: "前往配置模型 →", }, maxIterations: { label: "最大迭代次数", diff --git a/frontend/src/stores/settings.ts b/frontend/src/stores/settings.ts index 5e791bfe0..52c945924 100644 --- a/frontend/src/stores/settings.ts +++ b/frontend/src/stores/settings.ts @@ -65,7 +65,7 @@ const defaultSettings: Settings = { agentConfig: { maxIterations: 5, temperature: 0.7, - allowedTools: ["knowledge_search", "multi_kb_search", "list_knowledge_bases"], + allowedTools: [], // 默认为空,需要通过 API 从后端加载 system_prompt_web_enabled: "", system_prompt_web_disabled: "", use_custom_system_prompt: false @@ -99,12 +99,15 @@ export const useSettingsStore = defineStore("settings", { isAgentEnabled: (state) => state.settings.isAgentEnabled || false, // Agent 是否就绪(配置完整) + // 需要满足:1) 配置了允许的工具 2) 设置了对话模型 3) 设置了重排模型 isAgentReady: (state) => { const config = state.settings.agentConfig || defaultSettings.agentConfig const models = state.settings.conversationModels || defaultSettings.conversationModels - return config.allowedTools.length > 0 && - models.summaryModelId !== '' && - models.rerankModelId !== '' + return Boolean( + config.allowedTools && config.allowedTools.length > 0 && + models.summaryModelId && models.summaryModelId.trim() !== '' && + models.rerankModelId && models.rerankModelId.trim() !== '' + ) }, // 获取 Agent 配置 diff --git a/frontend/src/views/settings/AgentSettings.vue b/frontend/src/views/settings/AgentSettings.vue index 8ee10b403..b80d1ae2d 100644 --- a/frontend/src/views/settings/AgentSettings.vue +++ b/frontend/src/views/settings/AgentSettings.vue @@ -33,6 +33,9 @@ {{ agentStatusMessage }} + + {{ $t('agentSettings.status.goConfigureModels') }} +

@@ -302,7 +305,7 @@ -

+

{{ $t('conversationSettings.menus.models') }}

{{ $t('conversationSettings.models.description') }}

@@ -808,7 +811,13 @@ const saveConversationConfig = async (partial: Partial, toas // 计算 Agent 是否就绪 const isAgentReady = computed(() => { - return localAllowedTools.value.length > 0 + return ( + localAllowedTools.value.length > 0 && + localSummaryModelId.value && + localSummaryModelId.value.trim() !== '' && + localConversationRerankModelId.value && + localConversationRerankModelId.value.trim() !== '' + ) }) const buildAgentConfigPayload = (overrides: Partial = {}): AgentConfig => ({ @@ -823,6 +832,14 @@ const buildAgentConfigPayload = (overrides: Partial = {}): AgentCon ...overrides, }) +// 是否缺少模型配置 +const needsModelConfig = computed(() => { + return ( + (!localSummaryModelId.value || localSummaryModelId.value.trim() === '') || + (!localConversationRerankModelId.value || localConversationRerankModelId.value.trim() === '') + ) +}) + // Agent 状态提示消息 const agentStatusMessage = computed(() => { const missing: string[] = [] @@ -831,6 +848,14 @@ const agentStatusMessage = computed(() => { missing.push(t('agentSettings.status.missingAllowedTools')) } + if (!localSummaryModelId.value || localSummaryModelId.value.trim() === '') { + missing.push(t('agentSettings.status.missingSummaryModel')) + } + + if (!localConversationRerankModelId.value || localConversationRerankModelId.value.trim() === '') { + missing.push(t('agentSettings.status.missingRerankModel')) + } + if (missing.length === 0) { return '' } @@ -838,6 +863,25 @@ const agentStatusMessage = computed(() => { return t('agentSettings.status.pleaseConfigure', { items: missing.join('、') }) }) +// 跳转到模型配置 +const handleGoToModelSettings = () => { + router.push('/platform/settings') + + setTimeout(() => { + const event = new CustomEvent('settings-nav', { + detail: { section: 'agent', subsection: 'models' } + }) + window.dispatchEvent(event) + + setTimeout(() => { + const sectionEl = document.querySelector('[data-conversation-section="models"]') + if (sectionEl) { + sectionEl.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } + }, 150) + }, 100) +} + // 模型列表状态 const chatModels = ref([]) const rerankModels = ref([]) diff --git a/internal/agent/const.go b/internal/agent/const.go index 4462ce2ca..120b5000d 100644 --- a/internal/agent/const.go +++ b/internal/agent/const.go @@ -4,4 +4,5 @@ const ( DefaultAgentTemperature = 0.7 DefaultAgentMaxIterations = 20 DefaultAgentReflectionEnabled = false + DefaultUseCustomSystemPrompt = false ) diff --git a/internal/application/service/session.go b/internal/application/service/session.go index 4073e07a0..302ad4531 100644 --- a/internal/application/service/session.go +++ b/internal/application/service/session.go @@ -7,6 +7,8 @@ import ( "fmt" "strings" + "github.com/Tencent/WeKnora/internal/agent" + "github.com/Tencent/WeKnora/internal/agent/tools" chatpipline "github.com/Tencent/WeKnora/internal/application/service/chat_pipline" llmcontext "github.com/Tencent/WeKnora/internal/application/service/llmcontext" "github.com/Tencent/WeKnora/internal/config" @@ -858,15 +860,22 @@ func (s *sessionService) AgentQA(ctx context.Context, session *types.Session, qu tenantInfo := ctx.Value(types.TenantInfoContextKey).(*types.Tenant) // Check if agent is enabled at session level - if session.AgentConfig == nil || !session.AgentConfig.AgentModeEnabled { - logger.Warnf(ctx, "Agent not enabled for session: %s", sessionID) - return errors.New("agent not enabled for this session") + if session.AgentConfig == nil { + logger.Warnf(ctx, "Agent config not found for session: %s", sessionID) + return errors.New("agent config not found for session") } // Check if tenant has agent configuration if tenantInfo.AgentConfig == nil { - logger.Warnf(ctx, "Tenant %d has no agent configuration", tenantInfo.ID) - return errors.New("tenant has no agent configuration") + tenantInfo.AgentConfig = &types.AgentConfig{ + MaxIterations: agent.DefaultAgentMaxIterations, + ReflectionEnabled: agent.DefaultAgentReflectionEnabled, + AllowedTools: tools.DefaultAllowedTools(), + Temperature: agent.DefaultAgentTemperature, + SystemPromptWebEnabled: agent.ProgressiveRAGSystemPromptWithWeb, + SystemPromptWebDisabled: agent.ProgressiveRAGSystemPromptWithoutWeb, + UseCustomSystemPrompt: agent.DefaultUseCustomSystemPrompt, + } } // Create runtime AgentConfig by merging session and tenant configs