Skip to content

feat: set search system message as config #612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Some unique features have been added:

[✓] Implement SSO login through the auth proxy feature (need to integrate a third-party authentication reverse proxy, it can support login protocols such as LDAP/OIDC/SAML)

[✓] Web Search functionality (Real-time web search based on Tavily API)

> [!CAUTION]
> This project is only published on GitHub, based on the MIT license, free and for open source learning usage. And there will be no any form of account selling, paid service, discussion group, discussion group and other behaviors. Beware of being deceived.

Expand Down Expand Up @@ -72,6 +74,13 @@ Some unique features have been added:
- [Manual packaging](#manual-packaging)
- [Backend service](#backend-service-1)
- [Frontend webpage](#frontend-webpage-1)
- [Auth Proxy Mode](#auth-proxy-mode)
- [Web Search Functionality](#web-search-functionality)
- [Features](#features)
- [Configuration](#configuration)
- [Usage](#usage)
- [Technical Implementation](#technical-implementation)
- [Notes](#notes)
- [Frequently Asked Questions](#frequently-asked-questions)
- [Contributing](#contributing)
- [Sponsorship](#sponsorship)
Expand Down Expand Up @@ -347,6 +356,92 @@ Recommended for current IdP to use LDAP protocol, using [authelia](https://www.a

Recommended for current IdP to use OIDC protocol, using [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy)

## Web Search Functionality

> [!TIP]
> Web Search functionality is based on [Tavily API](https://tavily.com/) implementation, allowing ChatGPT to access the latest web information to answer questions.

### Features

- **Real-time Web Search**: Get the latest web information based on Tavily API
- **Intelligent Query Extraction**: Automatically extract the most relevant search keywords from user questions
- **Search Result Integration**: Seamlessly integrate search results into AI conversations
- **Per-session Control**: Each conversation can independently enable or disable search functionality
- **Search History**: Save search queries and results to database
- **Configurable System Messages**: Support custom search-related system prompt messages

### Configuration

#### 1. Get Tavily API Key

1. Visit [Tavily Official Website](https://tavily.com/) to register an account
2. Obtain API Key

#### 2. Administrator Configuration

1. Login to the system as an administrator
2. Go to system settings page
3. Find "Web Search Configuration" option
4. Fill in the following configurations:
- **Enable Status**: Turn on/off global search functionality
- **API Key**: Enter Tavily API Key
- **Search Query System Message**: Prompt template for extracting search keywords
- **Search Result System Message**: Prompt template for processing search results

#### 3. System Message Templates

**Search Query Extraction Template** (for extracting search keywords from user questions):
```
You are a search query extraction assistant. Extract the most relevant search query from user's question and wrap it with <search_query></search_query> tags.
Current time: {current_time}
```

**Search Result Processing Template** (for processing conversations with search results):
```
You are a helpful assistant with access to real-time web search results. Use the provided search information to give accurate and up-to-date responses.
Current time: {current_time}
```

### Usage

#### User Operations

1. **Enable Search Functionality**:
- In the conversation interface, find the search toggle button
- Click to enable web search functionality for the current session

2. **Ask Questions for Real-time Information**:
- After enabling search, directly ask ChatGPT questions that require real-time information
- The system will automatically search for relevant information and integrate it into the response

3. **View Search History**:
- Search queries and results are saved in the database
- You can view specific search records through the database

#### Workflow

1. **User Question**: User asks a question in a search-enabled session
2. **Query Extraction**: System uses AI to extract search keywords from the question
3. **Web Search**: Call Tavily API for real-time search
4. **Result Integration**: Provide search results as context to AI
5. **Generate Response**: AI generates more accurate responses based on search results

### Technical Implementation

- **Search Engine**: Tavily API
- **Query Extraction**: Use OpenAI API to intelligently extract keywords
- **Result Format**: JSON format to store complete search results
- **Data Storage**: MongoDB stores search queries and results
- **Timeout Setting**: Search request timeout is 300 seconds

### Notes

- Web Search functionality requires additional Tavily API costs
- Search functionality will increase response time
- It is recommended to enable selectively based on actual needs
- Administrators can control the global search functionality status
- Each session can independently control whether to use search functionality


## Contributing

Expand Down
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

[✓] 通过 auth proxy 功能实现sso登录 (配合第三方身份验证反向代理 可实现支持 LDAP/OIDC/SAML 等协议登录)

[✓] Web Search 网络搜索功能 (基于 Tavily API 实现实时网络搜索)


> [!CAUTION]
> 声明:此项目只发布于 Github,基于 MIT 协议,免费且作为开源学习使用。并且不会有任何形式的卖号、付费服务、讨论群、讨论组等行为。谨防受骗。
Expand Down Expand Up @@ -74,6 +76,13 @@
- [手动打包](#手动打包)
- [后端服务](#后端服务-1)
- [前端网页](#前端网页-1)
- [Auth Proxy Mode](#auth-proxy-mode)
- [Web Search 网络搜索功能](#web-search-网络搜索功能)
- [功能特性](#功能特性)
- [配置方式](#配置方式)
- [使用方式](#使用方式)
- [技术实现](#技术实现)
- [注意事项](#注意事项)
- [常见问题](#常见问题)
- [参与贡献](#参与贡献)
- [赞助](#赞助)
Expand Down Expand Up @@ -358,6 +367,92 @@ pnpm build

当前 Idp 使用 OIDC 协议的 可以选择使用 [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy)

## Web Search 网络搜索功能

> [!TIP]
> Web Search 功能基于 [Tavily API](https://tavily.com/) 实现,可以让 ChatGPT 获取最新的网络信息来回答问题。

### 功能特性

- **实时网络搜索**: 基于 Tavily API 获取最新的网络信息
- **智能查询提取**: 自动从用户问题中提取最相关的搜索关键词
- **搜索结果整合**: 将搜索结果无缝整合到 AI 对话中
- **按会话控制**: 每个对话可以独立开启或关闭搜索功能
- **搜索历史记录**: 保存搜索查询和结果到数据库
- **可配置系统消息**: 支持自定义搜索相关的系统提示消息

### 配置方式

#### 1. 获取 Tavily API Key

1. 访问 [Tavily 官网](https://tavily.com/) 注册账号
2. 获取 API Key

#### 2. 管理员配置

1. 以管理员身份登录系统
2. 进入系统设置页面
3. 找到 "Web Search 配置" 选项
4. 填写以下配置:
- **启用状态**: 开启/关闭全局搜索功能
- **API Key**: 填入 Tavily API Key
- **搜索查询系统消息**: 用于提取搜索关键词的提示模板
- **搜索结果系统消息**: 用于处理搜索结果的提示模板

#### 3. 系统消息模板

**搜索查询提取模板** (用于从用户问题中提取搜索关键词):
```
You are a search query extraction assistant. Extract the most relevant search query from user's question and wrap it with <search_query></search_query> tags.
Current time: {current_time}
```

**搜索结果处理模板** (用于处理包含搜索结果的对话):
```
You are a helpful assistant with access to real-time web search results. Use the provided search information to give accurate and up-to-date responses.
Current time: {current_time}
```

### 使用方式

#### 用户端操作

1. **开启搜索功能**:
- 在对话界面中,找到搜索开关按钮
- 点击开启当前会话的网络搜索功能

2. **提问获取实时信息**:
- 开启搜索后,直接向 ChatGPT 提问需要实时信息的问题
- 系统会自动搜索相关信息并整合到回答中

3. **查看搜索历史**:
- 搜索查询和结果会保存在数据库中
- 可以通过数据库查看具体的搜索记录

#### 工作流程

1. **用户提问**: 用户在开启搜索的会话中提问
2. **查询提取**: 系统使用 AI 从问题中提取搜索关键词
3. **网络搜索**: 调用 Tavily API 进行实时搜索
4. **结果整合**: 将搜索结果作为上下文提供给 AI
5. **生成回答**: AI 基于搜索结果生成更准确的回答

### 技术实现

- **搜索引擎**: Tavily API
- **查询提取**: 使用 OpenAI API 智能提取关键词
- **结果格式**: JSON 格式存储完整搜索结果
- **数据存储**: MongoDB 存储搜索查询和结果
- **超时设置**: 搜索请求超时时间为 300 秒

### 注意事项

- Web Search 功能需要额外的 Tavily API 费用
- 搜索功能会增加响应时间
- 建议根据实际需求选择性开启
- 管理员可以控制全局搜索功能的开启状态
- 每个会话可以独立控制是否使用搜索功能


## 常见问题
Q: 为什么 `Git` 提交总是报错?
Expand Down
74 changes: 4 additions & 70 deletions service/src/chatgpt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,8 @@ import type { ChatMessage, RequestOptions } from './types'

dotenv.config()

function systemMessageWithSearchResult(currentTime: string): string {
return `You are an intelligent assistant that needs to answer user questions based on search results.

**Search Results Format Description:**
- Search results may contain irrelevant information, please filter and use accordingly

**Context Information:**
- Current time: ${currentTime}

**Response Requirements:**

1. **Content Processing**
- Screen and filter search results, selecting content most relevant to the question
- Synthesize information from multiple web pages, avoiding repetitive citations from a single source
- When using web page information, please indicate the source in your answer (post it back to the application in the form of a link) to facilitate users' verification of the information source

2. **Response Strategy**
- **Listing questions**: Limit to within 10 key points, prioritize providing the most relevant and complete information
- **Creative questions**: Make full use of search results to generate in-depth professional long-form answers
- **Objective Q&A**: Brief answers may appropriately supplement 1-2 sentences of related information

3. **Format Requirements**
- Respond using markdown (latex start with $).
- Use structured, paragraph-based answer format
- When answering in points, limit to within 5 points, merging related content
- Ensure answers are aesthetically pleasing and highly readable

4. **Language Standards**
- Keep answer language consistent with user's question language
- Do not change language unless specifically requested by the user

**Notes:**
- Not all search results are relevant, need to judge based on the question
- For listing questions, inform users they can check search sources for complete information
- Creative answers need to be multi-perspective, information-rich, and thoroughly discussed`
}

function systemMessageGetSearchQuery(currentTime: string): string {
return `You are an intelligent search assistant.
Current time: ${currentTime}

Before formally answering user questions, you need to analyze the user's questions and conversation context to determine whether you need to obtain more information through internet search to provide accurate answers.

**Task Flow:**
1. Carefully analyze the user's question content and previous conversation history
2. Based on the current time, determine whether the question involves time-sensitive information. If it involves time-sensitive issues, please inform the specific date to be searched in the returned results instead of referring to pronouns such as today or yesterday
3. Evaluate whether existing knowledge is sufficient to answer the question
4. If search is needed, generate a precise search query
5. If search is not needed, return empty result

**Output Format Requirements:**
- If search is needed: return <search_query>example search query keywords</search_query>
- If search is not needed: return <search_query></search_query>
- Do not include any other explanations or answer content
- Search query should be concise and clear, able to obtain the most relevant information

**Judgment Criteria:**
- Time-sensitive information (such as latest news, stock prices, weather, real-time data, etc.): search needed
- Latest policies, regulations, technological developments: may need search
- Common sense questions, historical facts, basic knowledge: usually no search needed
- Latest research or developments in professional fields: search recommended

**Notes:**
- Search query should target the core needs of user questions
- Consider the timeliness and accuracy requirements of information
- Prioritize obtaining the latest and most authoritative information sources

Please strictly return results according to the above format.`
function renderSystemMessage(template: string, currentTime: string): string {
return template.replace(/{current_time}/g, currentTime)
}

const ErrorCodeMessage: Record<string, string> = {
Expand Down Expand Up @@ -167,7 +101,7 @@ async function chatReplyProcess(options: RequestOptions) {
let hasSearchResult = false
const searchConfig = globalConfig.searchConfig
if (searchConfig.enabled && searchConfig?.options?.apiKey && searchEnabled) {
messages[0].content = systemMessageGetSearchQuery(dayjs().format('YYYY-MM-DD HH:mm:ss'))
messages[0].content = renderSystemMessage(searchConfig.systemMessageGetSearchQuery, dayjs().format('YYYY-MM-DD HH:mm:ss'))
const completion = await openai.chat.completions.create({
model,
messages,
Expand Down Expand Up @@ -201,7 +135,7 @@ search query: <search_query>${searchQuery}</search_query>
search result: <search_result>${searchResult}</search_result>`,
})

messages[0].content = systemMessageWithSearchResult(dayjs().format('YYYY-MM-DD HH:mm:ss'))
messages[0].content = renderSystemMessage(searchConfig.systemMessageWithSearchResult, dayjs().format('YYYY-MM-DD HH:mm:ss'))
hasSearchResult = true
}
}
Expand Down
2 changes: 2 additions & 0 deletions service/src/storage/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ export class SearchConfig {
public enabled: boolean
public provider?: SearchServiceProvider
public options?: SearchServiceOptions
public systemMessageWithSearchResult?: string
public systemMessageGetSearchQuery?: string
}

export enum SearchServiceProvider {
Expand Down
14 changes: 13 additions & 1 deletion src/components/common/Setting/Search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async function fetchConfig() {
loading.value = true
const { data } = await fetchChatConfig<ConfigState>()
if (!data.searchConfig)
data.searchConfig = new SearchConfig(false, '', { apiKey: '' })
data.searchConfig = new SearchConfig(false, '', { apiKey: '' }, '', '')
if (!data.searchConfig.options)
data.searchConfig.options = { apiKey: '' }
config.value = data.searchConfig
Expand Down Expand Up @@ -107,6 +107,18 @@ onMounted(() => {
/>
</div>
</div>
<div v-if="config && config.enabled" class="flex items-center space-x-4">
<span class="shrink-0 w-[100px]">{{ $t('setting.systemMessageWithSearchResult') }}</span>
<div class="flex-1">
<NInput v-model:value="config.systemMessageWithSearchResult" type="textarea" :autosize="{ minRows: 2 }" :placeholder="t('setting.systemMessageWithSearchResultPlaceholder')" />
</div>
</div>
<div v-if="config && config.enabled" class="flex items-center space-x-4">
<span class="shrink-0 w-[100px]">{{ $t('setting.systemMessageGetSearchQuery') }}</span>
<div class="flex-1">
<NInput v-model:value="config.systemMessageGetSearchQuery" type="textarea" :autosize="{ minRows: 2 }" :placeholder="t('setting.systemMessageGetSearchQueryPlaceholder')" />
</div>
</div>
<div class="flex items-center space-x-4">
<span class="shrink-0 w-[100px]" />
<div class="flex flex-wrap items-center gap-4">
Expand Down
6 changes: 5 additions & 1 deletion src/components/common/Setting/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,13 @@ export class SearchConfig {
enabled: boolean
provider: SearchServiceProvider
options: SearchServiceOptions
constructor(enabled: boolean, provider: SearchServiceProvider, options: SearchServiceOptions) {
systemMessageWithSearchResult: string
systemMessageGetSearchQuery: string
constructor(enabled: boolean, provider: SearchServiceProvider, options: SearchServiceOptions, systemMessageWithSearchResult: string, systemMessageGetSearchQuery: string) {
this.enabled = enabled
this.provider = provider
this.options = options
this.systemMessageWithSearchResult = systemMessageWithSearchResult
this.systemMessageGetSearchQuery = systemMessageGetSearchQuery
}
}
4 changes: 4 additions & 0 deletions src/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ export default {
searchEnabled: 'Search Enabled',
searchProvider: 'Search Provider',
searchApiKey: 'Search API Key',
systemMessageWithSearchResult: 'System message for conversations with search results',
systemMessageGetSearchQuery: 'System message for getting search query',
systemMessageWithSearchResultPlaceholder: 'System message template when with search results. Use {\'{current_time}\'} as placeholder for current time.',
systemMessageGetSearchQueryPlaceholder: 'System message template for generating search query word. Use {\'{current_time}\'} as placeholder for current time. Require LLM to return the query word in <search_query>example search query</search_query> tags or return empty <search_query></search_query> tag if not need search.',
searchTest: 'Test Search',
accessTokenExpiredTime: 'Expired Time',
userConfig: 'Users',
Expand Down
Loading
Loading