feat: add search enhance mode switch
This commit is contained in:
parent
b89213b1ab
commit
11620828ad
@ -80,8 +80,8 @@ afterPack: scripts/after-pack.js
|
|||||||
afterSign: scripts/notarize.js
|
afterSign: scripts/notarize.js
|
||||||
releaseInfo:
|
releaseInfo:
|
||||||
releaseNotes: |
|
releaseNotes: |
|
||||||
支持引文预览功能
|
知识库设置增加重排模型,提升知识库的准确性
|
||||||
界面显示优化
|
自定义服务商增加兼容模式
|
||||||
修复快捷弹窗无法对话问题
|
增加 Github Copilot 服务商
|
||||||
MCP:修复豆包无法使用问题
|
PlantUML 预览支持放大和缩小
|
||||||
MCP:修复 Linux 上无法安装问题
|
联网模式支持增强模式
|
||||||
|
|||||||
@ -1075,6 +1075,8 @@
|
|||||||
"search_provider_placeholder": "Choose a search service provider.",
|
"search_provider_placeholder": "Choose a search service provider.",
|
||||||
"search_result_default": "Default",
|
"search_result_default": "Default",
|
||||||
"search_with_time": "Search with dates included",
|
"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": {
|
"tavily": {
|
||||||
"api_key": "Tavily API Key",
|
"api_key": "Tavily API Key",
|
||||||
"api_key.placeholder": "Enter Tavily API Key",
|
"api_key.placeholder": "Enter Tavily API Key",
|
||||||
|
|||||||
@ -1075,6 +1075,8 @@
|
|||||||
"search_provider_placeholder": "検索サービスプロバイダーを選択する",
|
"search_provider_placeholder": "検索サービスプロバイダーを選択する",
|
||||||
"search_result_default": "デフォルト",
|
"search_result_default": "デフォルト",
|
||||||
"search_with_time": "日付を含む検索",
|
"search_with_time": "日付を含む検索",
|
||||||
|
"enhance_mode": "検索強化モード",
|
||||||
|
"enhance_mode_tooltip": "デフォルトモデルを使用して問題から検索キーワードを抽出し、検索を実行します",
|
||||||
"tavily": {
|
"tavily": {
|
||||||
"api_key": "Tavily API キー",
|
"api_key": "Tavily API キー",
|
||||||
"api_key.placeholder": "Tavily API キーを入力してください",
|
"api_key.placeholder": "Tavily API キーを入力してください",
|
||||||
|
|||||||
@ -1075,6 +1075,8 @@
|
|||||||
"search_provider_placeholder": "Выберите поставщика поисковых услуг",
|
"search_provider_placeholder": "Выберите поставщика поисковых услуг",
|
||||||
"search_result_default": "По умолчанию",
|
"search_result_default": "По умолчанию",
|
||||||
"search_with_time": "Поиск, содержащий дату",
|
"search_with_time": "Поиск, содержащий дату",
|
||||||
|
"enhance_mode": "Режим улучшения поиска",
|
||||||
|
"enhance_mode_tooltip": "Используйте модель по умолчанию для извлечения ключевых слов из проблемы и поиска",
|
||||||
"tavily": {
|
"tavily": {
|
||||||
"api_key": "Ключ API Tavily",
|
"api_key": "Ключ API Tavily",
|
||||||
"api_key.placeholder": "Введите ключ API Tavily",
|
"api_key.placeholder": "Введите ключ API Tavily",
|
||||||
|
|||||||
@ -1075,6 +1075,8 @@
|
|||||||
"search_provider_placeholder": "选择一个搜索服务商",
|
"search_provider_placeholder": "选择一个搜索服务商",
|
||||||
"search_result_default": "默认",
|
"search_result_default": "默认",
|
||||||
"search_with_time": "搜索包含日期",
|
"search_with_time": "搜索包含日期",
|
||||||
|
"enhance_mode": "搜索增强模式",
|
||||||
|
"enhance_mode_tooltip": "使用默认模型提取关键词后搜索",
|
||||||
"tavily": {
|
"tavily": {
|
||||||
"api_key": "Tavily API 密钥",
|
"api_key": "Tavily API 密钥",
|
||||||
"api_key.placeholder": "请输入 Tavily API 密钥",
|
"api_key.placeholder": "请输入 Tavily API 密钥",
|
||||||
|
|||||||
@ -1075,6 +1075,8 @@
|
|||||||
"search_provider_placeholder": "選擇一個搜尋服務商",
|
"search_provider_placeholder": "選擇一個搜尋服務商",
|
||||||
"search_result_default": "預設",
|
"search_result_default": "預設",
|
||||||
"search_with_time": "搜尋包含日期",
|
"search_with_time": "搜尋包含日期",
|
||||||
|
"enhance_mode": "搜索增強模式",
|
||||||
|
"enhance_mode_tooltip": "使用預設模型提取關鍵詞後搜索",
|
||||||
"tavily": {
|
"tavily": {
|
||||||
"api_key": "Tavily API 金鑰",
|
"api_key": "Tavily API 金鑰",
|
||||||
"api_key.placeholder": "請輸入 Tavily API 金鑰",
|
"api_key.placeholder": "請輸入 Tavily API 金鑰",
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const MCPSettings: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Paragraph
|
<Paragraph
|
||||||
|
className="selectable"
|
||||||
ellipsis={{
|
ellipsis={{
|
||||||
rows: 1,
|
rows: 1,
|
||||||
expandable: 'collapsible',
|
expandable: 'collapsible',
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
|
import { InfoCircleOutlined } from '@ant-design/icons'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import { setMaxResult, setSearchWithTime } from '@renderer/store/websearch'
|
import { setEnhanceMode, setMaxResult, setSearchWithTime } from '@renderer/store/websearch'
|
||||||
import { Slider, Switch } from 'antd'
|
import { Slider, Switch, Tooltip } from 'antd'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
|
||||||
@ -10,21 +11,32 @@ import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle
|
|||||||
const BasicSettings: FC = () => {
|
const BasicSettings: FC = () => {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const searchWithTime = useAppSelector((state) => state.websearch.searchWithTime)
|
const searchWithTime = useAppSelector((state) => state.websearch.searchWithTime)
|
||||||
|
const enhanceMode = useAppSelector((state) => state.websearch.enhanceMode)
|
||||||
const maxResults = useAppSelector((state) => state.websearch.maxResults)
|
const maxResults = useAppSelector((state) => state.websearch.maxResults)
|
||||||
|
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingGroup theme={theme}>
|
<SettingGroup theme={theme} style={{ paddingBottom: 8 }}>
|
||||||
<SettingTitle>{t('settings.general.title')}</SettingTitle>
|
<SettingTitle>{t('settings.general.title')}</SettingTitle>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitle>{t('settings.websearch.search_with_time')}</SettingRowTitle>
|
<SettingRowTitle>{t('settings.websearch.search_with_time')}</SettingRowTitle>
|
||||||
<Switch checked={searchWithTime} onChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
<Switch checked={searchWithTime} onChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider style={{ marginTop: 15, marginBottom: 5 }} />
|
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||||
<SettingRow style={{ marginBottom: -10 }}>
|
<SettingRow>
|
||||||
|
<SettingRowTitle>
|
||||||
|
{t('settings.websearch.enhance_mode')}
|
||||||
|
<Tooltip title={t('settings.websearch.enhance_mode_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 5, color: 'var(--color-icon)', cursor: 'pointer' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</SettingRowTitle>
|
||||||
|
<Switch checked={enhanceMode} onChange={(checked) => dispatch(setEnhanceMode(checked))} />
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||||
|
<SettingRow>
|
||||||
<SettingRowTitle>{t('settings.websearch.search_max_result')}</SettingRowTitle>
|
<SettingRowTitle>{t('settings.websearch.search_max_result')}</SettingRowTitle>
|
||||||
<Slider
|
<Slider
|
||||||
defaultValue={maxResults}
|
defaultValue={maxResults}
|
||||||
|
|||||||
@ -476,13 +476,18 @@ export default class AnthropicProvider extends BaseProvider {
|
|||||||
content: messages.map((m) => m.content).join('\n')
|
content: messages.map((m) => m.content).join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.sdk.messages.create({
|
const response = await this.sdk.messages.create(
|
||||||
|
{
|
||||||
messages: [userMessage] as Anthropic.Messages.MessageParam[],
|
messages: [userMessage] as Anthropic.Messages.MessageParam[],
|
||||||
model: model.id,
|
model: model.id,
|
||||||
system: systemMessage.content,
|
system: systemMessage.content,
|
||||||
stream: false,
|
stream: false,
|
||||||
max_tokens: 4096
|
max_tokens: 4096
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
timeout: 20 * 1000
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const content = response.content[0].type === 'text' ? response.content[0].text : ''
|
const content = response.content[0].type === 'text' ? response.content[0].text : ''
|
||||||
|
|
||||||
|
|||||||
@ -518,7 +518,10 @@ export default class GeminiProvider extends BaseProvider {
|
|||||||
temperature: assistant?.settings?.temperature
|
temperature: assistant?.settings?.temperature
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
this.requestOptions
|
{
|
||||||
|
...this.requestOptions,
|
||||||
|
timeout: 20 * 1000
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const chat = await geminiModel.startChat()
|
const chat = await geminiModel.startChat()
|
||||||
|
|||||||
@ -780,13 +780,18 @@ export default class OpenAIProvider extends BaseProvider {
|
|||||||
content: messages.map((m) => m.content).join('\n')
|
content: messages.map((m) => m.content).join('\n')
|
||||||
}
|
}
|
||||||
// @ts-ignore key is not typed
|
// @ts-ignore key is not typed
|
||||||
const response = await this.sdk.chat.completions.create({
|
const response = await this.sdk.chat.completions.create(
|
||||||
|
{
|
||||||
model: model.id,
|
model: model.id,
|
||||||
messages: [systemMessage, userMessage] as ChatCompletionMessageParam[],
|
messages: [systemMessage, userMessage] as ChatCompletionMessageParam[],
|
||||||
stream: false,
|
stream: false,
|
||||||
keep_alive: this.keepAliveTime,
|
keep_alive: this.keepAliveTime,
|
||||||
max_tokens: 1000
|
max_tokens: 1000
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
timeout: 20 * 1000
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 针对思考类模型的返回,总结仅截取</think>之后的内容
|
// 针对思考类模型的返回,总结仅截取</think>之后的内容
|
||||||
let content = response.choices[0].message?.content || ''
|
let content = response.choices[0].message?.content || ''
|
||||||
|
|||||||
@ -49,6 +49,7 @@ export async function fetchChatCompletion({
|
|||||||
const lastMessage = findLast(messages, (m) => m.role === 'user')
|
const lastMessage = findLast(messages, (m) => m.role === 'user')
|
||||||
const lastAnswer = findLast(messages, (m) => m.role === 'assistant')
|
const lastAnswer = findLast(messages, (m) => m.role === 'assistant')
|
||||||
const hasKnowledgeBase = !isEmpty(lastMessage?.knowledgeBaseIds)
|
const hasKnowledgeBase = !isEmpty(lastMessage?.knowledgeBaseIds)
|
||||||
|
|
||||||
if (lastMessage) {
|
if (lastMessage) {
|
||||||
if (hasKnowledgeBase) {
|
if (hasKnowledgeBase) {
|
||||||
window.message.info({
|
window.message.info({
|
||||||
@ -57,25 +58,28 @@ export async function fetchChatCompletion({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新消息状态为搜索中
|
||||||
|
onResponse({ ...message, status: 'searching' })
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 等待关键词生成完成
|
// 等待关键词生成完成
|
||||||
const searchSummaryAssistant = getDefaultAssistant()
|
const searchSummaryAssistant = getDefaultAssistant()
|
||||||
searchSummaryAssistant.model = assistant.model || getDefaultModel()
|
searchSummaryAssistant.model = assistant.model || getDefaultModel()
|
||||||
searchSummaryAssistant.prompt = SEARCH_SUMMARY_PROMPT
|
searchSummaryAssistant.prompt = SEARCH_SUMMARY_PROMPT
|
||||||
|
|
||||||
|
// 如果启用搜索增强模式,则使用搜索增强模式
|
||||||
|
if (WebSearchService.isEnhanceModeEnabled()) {
|
||||||
const keywords = await fetchSearchSummary({
|
const keywords = await fetchSearchSummary({
|
||||||
messages: lastAnswer ? [lastAnswer, lastMessage] : [lastMessage],
|
messages: lastAnswer ? [lastAnswer, lastMessage] : [lastMessage],
|
||||||
assistant: searchSummaryAssistant
|
assistant: searchSummaryAssistant
|
||||||
})
|
})
|
||||||
|
|
||||||
if (keywords) {
|
if (keywords) {
|
||||||
query = keywords
|
query = keywords
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
query = lastMessage.content
|
query = lastMessage.content
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新消息状态为搜索中
|
|
||||||
onResponse({ ...message, status: 'searching' })
|
|
||||||
|
|
||||||
// 等待搜索完成
|
// 等待搜索完成
|
||||||
const webSearch = await WebSearchService.search(webSearchProvider, query)
|
const webSearch = await WebSearchService.search(webSearchProvider, query)
|
||||||
|
|
||||||
@ -84,6 +88,7 @@ export async function fetchChatCompletion({
|
|||||||
...message.metadata,
|
...message.metadata,
|
||||||
webSearch: webSearch
|
webSearch: webSearch
|
||||||
}
|
}
|
||||||
|
|
||||||
window.keyv.set(`web-search-${lastMessage?.id}`, webSearch)
|
window.keyv.set(`web-search-${lastMessage?.id}`, webSearch)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Web search failed:', error)
|
console.error('Web search failed:', error)
|
||||||
@ -93,6 +98,7 @@ export async function fetchChatCompletion({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const allMCPTools = await window.api.mcp.listTools()
|
const allMCPTools = await window.api.mcp.listTools()
|
||||||
|
|
||||||
await AI.completions({
|
await AI.completions({
|
||||||
messages: filterUsefulMessages(messages),
|
messages: filterUsefulMessages(messages),
|
||||||
assistant,
|
assistant,
|
||||||
|
|||||||
@ -1,23 +1,10 @@
|
|||||||
import store from '@renderer/store'
|
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 { WebSearchProvider, WebSearchResponse } from '@renderer/types'
|
||||||
import { hasObjectKey } from '@renderer/utils'
|
import { hasObjectKey } from '@renderer/utils'
|
||||||
import WebSearchEngineProvider from '@renderer/webSearchProvider/WebSearchEngineProvider'
|
import WebSearchEngineProvider from '@renderer/webSearchProvider/WebSearchEngineProvider'
|
||||||
import dayjs from 'dayjs'
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否启用搜索增强模式
|
||||||
|
* @public
|
||||||
|
* @returns 如果启用搜索增强模式则返回true,否则返回false
|
||||||
|
*/
|
||||||
|
public isEnhanceModeEnabled(): boolean {
|
||||||
|
const { enhanceMode } = this.getWebSearchState()
|
||||||
|
return enhanceMode
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前默认的网络搜索提供商
|
* 获取当前默认的网络搜索提供商
|
||||||
* @public
|
* @public
|
||||||
|
|||||||
@ -1,11 +1,19 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import type { WebSearchProvider } from '@renderer/types'
|
import type { WebSearchProvider } from '@renderer/types'
|
||||||
|
|
||||||
export interface WebSearchState {
|
export interface WebSearchState {
|
||||||
|
// 默认搜索提供商的ID
|
||||||
defaultProvider: string
|
defaultProvider: string
|
||||||
|
// 所有可用的搜索提供商列表
|
||||||
providers: WebSearchProvider[]
|
providers: WebSearchProvider[]
|
||||||
|
// 是否在搜索查询中添加当前日期
|
||||||
searchWithTime: boolean
|
searchWithTime: boolean
|
||||||
|
// 搜索结果的最大数量
|
||||||
maxResults: number
|
maxResults: number
|
||||||
|
// 要排除的域名列表
|
||||||
excludeDomains: string[]
|
excludeDomains: string[]
|
||||||
|
// 是否启用搜索增强模式
|
||||||
|
enhanceMode: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: WebSearchState = {
|
const initialState: WebSearchState = {
|
||||||
@ -29,7 +37,8 @@ const initialState: WebSearchState = {
|
|||||||
],
|
],
|
||||||
searchWithTime: true,
|
searchWithTime: true,
|
||||||
maxResults: 5,
|
maxResults: 5,
|
||||||
excludeDomains: []
|
excludeDomains: [],
|
||||||
|
enhanceMode: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const websearchSlice = createSlice({
|
const websearchSlice = createSlice({
|
||||||
@ -59,6 +68,9 @@ const websearchSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setExcludeDomains: (state, action: PayloadAction<string[]>) => {
|
setExcludeDomains: (state, action: PayloadAction<string[]>) => {
|
||||||
state.excludeDomains = action.payload
|
state.excludeDomains = action.payload
|
||||||
|
},
|
||||||
|
setEnhanceMode: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.enhanceMode = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -70,7 +82,8 @@ export const {
|
|||||||
setDefaultProvider,
|
setDefaultProvider,
|
||||||
setSearchWithTime,
|
setSearchWithTime,
|
||||||
setExcludeDomains,
|
setExcludeDomains,
|
||||||
setMaxResult
|
setMaxResult,
|
||||||
|
setEnhanceMode
|
||||||
} = websearchSlice.actions
|
} = websearchSlice.actions
|
||||||
|
|
||||||
export default websearchSlice.reducer
|
export default websearchSlice.reducer
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user