From 11620828ad926a92cccf8f9041f255cbceabcf13 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 19 Mar 2025 16:59:43 +0800 Subject: [PATCH] feat: add search enhance mode switch --- electron-builder.yml | 10 ++++---- src/renderer/src/i18n/locales/en-us.json | 2 ++ src/renderer/src/i18n/locales/ja-jp.json | 2 ++ src/renderer/src/i18n/locales/ru-ru.json | 2 ++ src/renderer/src/i18n/locales/zh-cn.json | 2 ++ src/renderer/src/i18n/locales/zh-tw.json | 2 ++ .../src/pages/settings/MCPSettings/index.tsx | 1 + .../WebSearchSettings/BasicSettings.tsx | 22 ++++++++++++---- .../src/providers/AnthropicProvider.ts | 19 ++++++++------ src/renderer/src/providers/GeminiProvider.ts | 5 +++- src/renderer/src/providers/OpenAIProvider.ts | 19 ++++++++------ src/renderer/src/services/ApiService.ts | 24 +++++++++++------- src/renderer/src/services/WebSearchService.ts | 25 ++++++++----------- src/renderer/src/store/websearch.ts | 17 +++++++++++-- 14 files changed, 102 insertions(+), 50 deletions(-) diff --git a/electron-builder.yml b/electron-builder.yml index 6189ea57..adb00ecb 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -80,8 +80,8 @@ afterPack: scripts/after-pack.js afterSign: scripts/notarize.js releaseInfo: releaseNotes: | - 支持引文预览功能 - 界面显示优化 - 修复快捷弹窗无法对话问题 - MCP:修复豆包无法使用问题 - MCP:修复 Linux 上无法安装问题 + 知识库设置增加重排模型,提升知识库的准确性 + 自定义服务商增加兼容模式 + 增加 Github Copilot 服务商 + PlantUML 预览支持放大和缩小 + 联网模式支持增强模式 diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index c9720f68..fd01aa6c 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1075,6 +1075,8 @@ "search_provider_placeholder": "Choose a search service provider.", "search_result_default": "Default", "search_with_time": "Search with dates included", + "enhance_mode": "Search enhance mode", + "enhance_mode_tooltip": "Use the default model to extract search keywords from the problem and search", "tavily": { "api_key": "Tavily API Key", "api_key.placeholder": "Enter Tavily API Key", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index c3e3dc0b..a345b549 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1075,6 +1075,8 @@ "search_provider_placeholder": "検索サービスプロバイダーを選択する", "search_result_default": "デフォルト", "search_with_time": "日付を含む検索", + "enhance_mode": "検索強化モード", + "enhance_mode_tooltip": "デフォルトモデルを使用して問題から検索キーワードを抽出し、検索を実行します", "tavily": { "api_key": "Tavily API キー", "api_key.placeholder": "Tavily API キーを入力してください", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index e8aa14b0..abf3a1d2 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1075,6 +1075,8 @@ "search_provider_placeholder": "Выберите поставщика поисковых услуг", "search_result_default": "По умолчанию", "search_with_time": "Поиск, содержащий дату", + "enhance_mode": "Режим улучшения поиска", + "enhance_mode_tooltip": "Используйте модель по умолчанию для извлечения ключевых слов из проблемы и поиска", "tavily": { "api_key": "Ключ API Tavily", "api_key.placeholder": "Введите ключ API Tavily", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 8c5125c4..629bde9d 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1075,6 +1075,8 @@ "search_provider_placeholder": "选择一个搜索服务商", "search_result_default": "默认", "search_with_time": "搜索包含日期", + "enhance_mode": "搜索增强模式", + "enhance_mode_tooltip": "使用默认模型提取关键词后搜索", "tavily": { "api_key": "Tavily API 密钥", "api_key.placeholder": "请输入 Tavily API 密钥", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index d9d7ca7f..26a69b94 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1075,6 +1075,8 @@ "search_provider_placeholder": "選擇一個搜尋服務商", "search_result_default": "預設", "search_with_time": "搜尋包含日期", + "enhance_mode": "搜索增強模式", + "enhance_mode_tooltip": "使用預設模型提取關鍵詞後搜索", "tavily": { "api_key": "Tavily API 金鑰", "api_key.placeholder": "請輸入 Tavily API 金鑰", diff --git a/src/renderer/src/pages/settings/MCPSettings/index.tsx b/src/renderer/src/pages/settings/MCPSettings/index.tsx index b04d2fb0..785390b6 100644 --- a/src/renderer/src/pages/settings/MCPSettings/index.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/index.tsx @@ -80,6 +80,7 @@ const MCPSettings: FC = () => { return ( { const { theme } = useTheme() const searchWithTime = useAppSelector((state) => state.websearch.searchWithTime) + const enhanceMode = useAppSelector((state) => state.websearch.enhanceMode) const maxResults = useAppSelector((state) => state.websearch.maxResults) const dispatch = useAppDispatch() return ( <> - + {t('settings.general.title')} {t('settings.websearch.search_with_time')} dispatch(setSearchWithTime(checked))} /> - - + + + + {t('settings.websearch.enhance_mode')} + + + + + dispatch(setEnhanceMode(checked))} /> + + + {t('settings.websearch.search_max_result')} m.content).join('\n') } - const response = await this.sdk.messages.create({ - messages: [userMessage] as Anthropic.Messages.MessageParam[], - model: model.id, - system: systemMessage.content, - stream: false, - max_tokens: 4096 - }) + const response = await this.sdk.messages.create( + { + messages: [userMessage] as Anthropic.Messages.MessageParam[], + model: model.id, + system: systemMessage.content, + stream: false, + max_tokens: 4096 + }, + { + timeout: 20 * 1000 + } + ) const content = response.content[0].type === 'text' ? response.content[0].text : '' diff --git a/src/renderer/src/providers/GeminiProvider.ts b/src/renderer/src/providers/GeminiProvider.ts index d9b49418..b73c9d0d 100644 --- a/src/renderer/src/providers/GeminiProvider.ts +++ b/src/renderer/src/providers/GeminiProvider.ts @@ -518,7 +518,10 @@ export default class GeminiProvider extends BaseProvider { temperature: assistant?.settings?.temperature } }, - this.requestOptions + { + ...this.requestOptions, + timeout: 20 * 1000 + } ) const chat = await geminiModel.startChat() diff --git a/src/renderer/src/providers/OpenAIProvider.ts b/src/renderer/src/providers/OpenAIProvider.ts index 76fecc30..47828e77 100644 --- a/src/renderer/src/providers/OpenAIProvider.ts +++ b/src/renderer/src/providers/OpenAIProvider.ts @@ -780,13 +780,18 @@ export default class OpenAIProvider extends BaseProvider { content: messages.map((m) => m.content).join('\n') } // @ts-ignore key is not typed - const response = await this.sdk.chat.completions.create({ - model: model.id, - messages: [systemMessage, userMessage] as ChatCompletionMessageParam[], - stream: false, - keep_alive: this.keepAliveTime, - max_tokens: 1000 - }) + const response = await this.sdk.chat.completions.create( + { + model: model.id, + messages: [systemMessage, userMessage] as ChatCompletionMessageParam[], + stream: false, + keep_alive: this.keepAliveTime, + max_tokens: 1000 + }, + { + timeout: 20 * 1000 + } + ) // 针对思考类模型的返回,总结仅截取之后的内容 let content = response.choices[0].message?.content || '' diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index 393473ee..80a24409 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -49,6 +49,7 @@ export async function fetchChatCompletion({ const lastMessage = findLast(messages, (m) => m.role === 'user') const lastAnswer = findLast(messages, (m) => m.role === 'assistant') const hasKnowledgeBase = !isEmpty(lastMessage?.knowledgeBaseIds) + if (lastMessage) { if (hasKnowledgeBase) { window.message.info({ @@ -57,25 +58,28 @@ export async function fetchChatCompletion({ }) } + // 更新消息状态为搜索中 + onResponse({ ...message, status: 'searching' }) + try { // 等待关键词生成完成 const searchSummaryAssistant = getDefaultAssistant() searchSummaryAssistant.model = assistant.model || getDefaultModel() searchSummaryAssistant.prompt = SEARCH_SUMMARY_PROMPT - const keywords = await fetchSearchSummary({ - messages: lastAnswer ? [lastAnswer, lastMessage] : [lastMessage], - assistant: searchSummaryAssistant - }) - if (keywords) { - query = keywords + // 如果启用搜索增强模式,则使用搜索增强模式 + if (WebSearchService.isEnhanceModeEnabled()) { + const keywords = await fetchSearchSummary({ + messages: lastAnswer ? [lastAnswer, lastMessage] : [lastMessage], + assistant: searchSummaryAssistant + }) + if (keywords) { + query = keywords + } } else { query = lastMessage.content } - // 更新消息状态为搜索中 - onResponse({ ...message, status: 'searching' }) - // 等待搜索完成 const webSearch = await WebSearchService.search(webSearchProvider, query) @@ -84,6 +88,7 @@ export async function fetchChatCompletion({ ...message.metadata, webSearch: webSearch } + window.keyv.set(`web-search-${lastMessage?.id}`, webSearch) } catch (error) { console.error('Web search failed:', error) @@ -93,6 +98,7 @@ export async function fetchChatCompletion({ } const allMCPTools = await window.api.mcp.listTools() + await AI.completions({ messages: filterUsefulMessages(messages), assistant, diff --git a/src/renderer/src/services/WebSearchService.ts b/src/renderer/src/services/WebSearchService.ts index b0d7a379..9b7718af 100644 --- a/src/renderer/src/services/WebSearchService.ts +++ b/src/renderer/src/services/WebSearchService.ts @@ -1,23 +1,10 @@ import store from '@renderer/store' -import { setDefaultProvider } from '@renderer/store/websearch' +import { setDefaultProvider, WebSearchState } from '@renderer/store/websearch' import { WebSearchProvider, WebSearchResponse } from '@renderer/types' import { hasObjectKey } from '@renderer/utils' import WebSearchEngineProvider from '@renderer/webSearchProvider/WebSearchEngineProvider' import dayjs from 'dayjs' -interface WebSearchState { - // 默认搜索提供商的ID - defaultProvider: string - // 所有可用的搜索提供商列表 - providers: WebSearchProvider[] - // 是否在搜索查询中添加当前日期 - searchWithTime: boolean - // 搜索结果的最大数量 - maxResults: number - // 要排除的域名列表 - excludeDomains: string[] -} - /** * 提供网络搜索相关功能的服务类 */ @@ -55,6 +42,16 @@ class WebSearchService { return false } + /** + * 检查是否启用搜索增强模式 + * @public + * @returns 如果启用搜索增强模式则返回true,否则返回false + */ + public isEnhanceModeEnabled(): boolean { + const { enhanceMode } = this.getWebSearchState() + return enhanceMode + } + /** * 获取当前默认的网络搜索提供商 * @public diff --git a/src/renderer/src/store/websearch.ts b/src/renderer/src/store/websearch.ts index 46011087..ac3f4627 100644 --- a/src/renderer/src/store/websearch.ts +++ b/src/renderer/src/store/websearch.ts @@ -1,11 +1,19 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import type { WebSearchProvider } from '@renderer/types' + export interface WebSearchState { + // 默认搜索提供商的ID defaultProvider: string + // 所有可用的搜索提供商列表 providers: WebSearchProvider[] + // 是否在搜索查询中添加当前日期 searchWithTime: boolean + // 搜索结果的最大数量 maxResults: number + // 要排除的域名列表 excludeDomains: string[] + // 是否启用搜索增强模式 + enhanceMode: boolean } const initialState: WebSearchState = { @@ -29,7 +37,8 @@ const initialState: WebSearchState = { ], searchWithTime: true, maxResults: 5, - excludeDomains: [] + excludeDomains: [], + enhanceMode: false } const websearchSlice = createSlice({ @@ -59,6 +68,9 @@ const websearchSlice = createSlice({ }, setExcludeDomains: (state, action: PayloadAction) => { state.excludeDomains = action.payload + }, + setEnhanceMode: (state, action: PayloadAction) => { + state.enhanceMode = action.payload } } }) @@ -70,7 +82,8 @@ export const { setDefaultProvider, setSearchWithTime, setExcludeDomains, - setMaxResult + setMaxResult, + setEnhanceMode } = websearchSlice.actions export default websearchSlice.reducer