feat: Add agent readiness checks and detailed configuration messages

- Implemented a function to retrieve reasons for agent unavailability, enhancing user feedback on configuration issues.
- Updated the agent status message to include specific missing configurations, improving clarity for users.
- Enhanced localization support for new messages related to agent configuration requirements in English, Russian, and Chinese.
- Added navigation options for users to quickly access model configuration settings when the agent is not ready.
This commit is contained in:
wizardchen
2025-11-20 15:43:46 +08:00
parent 73fbc4552c
commit 8d76417897
8 changed files with 124 additions and 25 deletions

View File

@@ -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';
},

View File

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

View File

@@ -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: 'Веб-поиск выключен',

View File

@@ -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: "最大迭代次数",

View File

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

View File

@@ -33,6 +33,9 @@
</div>
<span v-if="!isAgentReady" class="status-hint">
{{ agentStatusMessage }}
<t-link v-if="needsModelConfig" @click="handleGoToModelSettings" theme="primary">
{{ $t('agentSettings.status.goConfigureModels') }}
</t-link>
</span>
<p v-if="!isAgentReady" class="status-tip">
<t-icon name="info-circle" class="tip-icon" />
@@ -302,7 +305,7 @@
</t-tabs>
</div>
<div v-else-if="activeSection === 'models'" class="section-block">
<div v-else-if="activeSection === 'models'" class="section-block" data-conversation-section="models">
<div class="section-header">
<h2>{{ $t('conversationSettings.menus.models') }}</h2>
<p class="section-description">{{ $t('conversationSettings.models.description') }}</p>
@@ -808,7 +811,13 @@ const saveConversationConfig = async (partial: Partial<ConversationConfig>, 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> = {}): AgentConfig => ({
@@ -823,6 +832,14 @@ const buildAgentConfigPayload = (overrides: Partial<AgentConfig> = {}): 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<ModelConfig[]>([])
const rerankModels = ref<ModelConfig[]>([])

View File

@@ -4,4 +4,5 @@ const (
DefaultAgentTemperature = 0.7
DefaultAgentMaxIterations = 20
DefaultAgentReflectionEnabled = false
DefaultUseCustomSystemPrompt = false
)

View File

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