Make Web Search Use Multiple Queries #3023

Closed
opened 2025-11-11 15:19:54 -06:00 by GiteaMirror · 3 comments
Owner

Originally created by @reecelikesramen on GitHub (Dec 16, 2024).

Feature Request

Is your feature request related to a problem? Please describe.
The web search feature only searches a single query, but the prompt (even the default one) generates multiple queries.

Describe the solution you'd like
I would like the web search feature to use all the search queries generated.

Describe alternatives you've considered

  • NUM_SEARCHES * NUM_QUERIES? or (NUM_SEARCHES / NUM_QUERIES) per query and allocate remainder to query[0]?
  • default web query prompt could be told to just make 1 good relevant query.

Additional context
Right now the web search feature takes in a list of queries, and it's hard coded to select the first in the list. This could be leading to poorer search quality, as the default prompt is encouraged to make several queries, and not necessarily put the best one first. Here's the relevant code:
open-webui/src/lib/components/chat/Chat.svelte

...
                 const prompt = userMessage.content;
		let queries = await generateQueries(
			localStorage.token,
			model,
			messages.filter((message) => message?.content?.trim()),
			prompt
		).catch((error) => {
			console.log(error);
			return [prompt];
		});

		if (queries.length === 0) {
			responseMessage.statusHistory.push({
				done: true,
				error: true,
				action: 'web_search',
				description: $i18n.t('No search query generated')
			});
			history.messages[responseMessageId] = responseMessage;
			return;
		}

                 // RELEVANT LINE HERE
		const searchQuery = queries[0];
...
Originally created by @reecelikesramen on GitHub (Dec 16, 2024). # Feature Request **Is your feature request related to a problem? Please describe.** The web search feature only searches a single query, but the prompt (even the default one) generates multiple queries. **Describe the solution you'd like** I would like the web search feature to use all the search queries generated. **Describe alternatives you've considered** - NUM_SEARCHES * NUM_QUERIES? or (NUM_SEARCHES / NUM_QUERIES) per query and allocate remainder to query[0]? - default web query prompt could be told to just make 1 good relevant query. **Additional context** Right now the web search feature takes in a list of queries, and it's hard coded to select the first in the list. This could be leading to poorer search quality, as the default prompt is encouraged to make several queries, and not necessarily put the best one first. Here's the relevant code: [open-webui](https://github.com/open-webui/open-webui/tree/29a271959556743e6deb4d55a5a982983335d7ab)/[src](https://github.com/open-webui/open-webui/tree/29a271959556743e6deb4d55a5a982983335d7ab/src)/[lib](https://github.com/open-webui/open-webui/tree/29a271959556743e6deb4d55a5a982983335d7ab/src/lib)/[components](https://github.com/open-webui/open-webui/tree/29a271959556743e6deb4d55a5a982983335d7ab/src/lib/components)/[chat](https://github.com/open-webui/open-webui/tree/29a271959556743e6deb4d55a5a982983335d7ab/src/lib/components/chat)/Chat.svelte ```py ... const prompt = userMessage.content; let queries = await generateQueries( localStorage.token, model, messages.filter((message) => message?.content?.trim()), prompt ).catch((error) => { console.log(error); return [prompt]; }); if (queries.length === 0) { responseMessage.statusHistory.push({ done: true, error: true, action: 'web_search', description: $i18n.t('No search query generated') }); history.messages[responseMessageId] = responseMessage; return; } // RELEVANT LINE HERE const searchQuery = queries[0]; ... ```
Author
Owner

@aaltulea commented on GitHub (Dec 17, 2024):

i was trying to achieve this. the code below will search 3 unique queries. you can change the number of queries by changing 3 in .slice(0, 3). overall, i didn't find it very useful.


	const getWebSearchResults = async (
		model: string,
		parentId: string,
		responseMessageId: string
	) => {
		// TODO: move this to the backend
		const responseMessage = history.messages[responseMessageId];
		const userMessage = history.messages[parentId];
		const messages = createMessagesList(history.currentId);

		responseMessage.statusHistory = [
			{
				done: false,
				action: 'web_search',
				description: $i18n.t('Generating search queries')
			}
		];
		history.messages[responseMessageId] = responseMessage;

		const prompt = userMessage.content;
		let queries = await generateQueries(
			localStorage.token,
			model,
			messages.filter((message) => message?.content?.trim()),
			prompt
		).catch((error) => {
			console.log(error);
			return [prompt];
		});

		if (queries.length === 0) {
			responseMessage.statusHistory.push({
				done: true,
				error: true,
				action: 'web_search',
				description: $i18n.t('No search query generated')
			});
			history.messages[responseMessageId] = responseMessage;
			return;
		}

		// Take the first three unique queries
		const uniqueQueries = [...new Set(queries)].slice(0, 3);
		const combinedResultsSet = new Set(); // Ensure unique combined results
		let searchedCount = 0;

		for (let i = 0; i < uniqueQueries.length; i++) {
			const searchQuery = uniqueQueries[i];

			responseMessage.statusHistory.push({
				done: false,
				action: 'web_search',
				description: $i18n.t(
					`Searching: "{searchQuery}" (${i + 1}/${uniqueQueries.length})`
				)
			});
			history.messages[responseMessageId] = responseMessage;

			const results = await processWebSearch(localStorage.token, searchQuery).catch((error) => {
				console.log(error);
				toast.error(error);
				return null;
			});

			if (results) {
				results.filenames.forEach((filename) => combinedResultsSet.add(filename)); // Add to Set
				searchedCount += results.filenames.length;

				if (!responseMessage.files) {
					responseMessage.files = [];
				}

				responseMessage.files.push({
					collection_name: results.collection_name,
					name: searchQuery,
					type: 'web_search_results',
					urls: results.filenames
				});
			}
		}

		const combinedResults = [...combinedResultsSet]; // Convert Set to Array

		if (combinedResults.length > 0) {
			responseMessage.statusHistory.push({
				done: true,
				action: 'web_search',
				description: $i18n.t('Searched {{count}} sites', { count: searchedCount }),
				urls: combinedResults
			});
		} else {
			responseMessage.statusHistory.push({
				done: true,
				error: true,
				action: 'web_search',
				description: 'No search results found'
			});
		}

		history.messages[responseMessageId] = responseMessage;
	};
@aaltulea commented on GitHub (Dec 17, 2024): i was trying to achieve this. the code below will search 3 unique queries. you can change the number of queries by changing 3 in `.slice(0, 3)`. overall, i didn't find it very useful. ``` const getWebSearchResults = async ( model: string, parentId: string, responseMessageId: string ) => { // TODO: move this to the backend const responseMessage = history.messages[responseMessageId]; const userMessage = history.messages[parentId]; const messages = createMessagesList(history.currentId); responseMessage.statusHistory = [ { done: false, action: 'web_search', description: $i18n.t('Generating search queries') } ]; history.messages[responseMessageId] = responseMessage; const prompt = userMessage.content; let queries = await generateQueries( localStorage.token, model, messages.filter((message) => message?.content?.trim()), prompt ).catch((error) => { console.log(error); return [prompt]; }); if (queries.length === 0) { responseMessage.statusHistory.push({ done: true, error: true, action: 'web_search', description: $i18n.t('No search query generated') }); history.messages[responseMessageId] = responseMessage; return; } // Take the first three unique queries const uniqueQueries = [...new Set(queries)].slice(0, 3); const combinedResultsSet = new Set(); // Ensure unique combined results let searchedCount = 0; for (let i = 0; i < uniqueQueries.length; i++) { const searchQuery = uniqueQueries[i]; responseMessage.statusHistory.push({ done: false, action: 'web_search', description: $i18n.t( `Searching: "{searchQuery}" (${i + 1}/${uniqueQueries.length})` ) }); history.messages[responseMessageId] = responseMessage; const results = await processWebSearch(localStorage.token, searchQuery).catch((error) => { console.log(error); toast.error(error); return null; }); if (results) { results.filenames.forEach((filename) => combinedResultsSet.add(filename)); // Add to Set searchedCount += results.filenames.length; if (!responseMessage.files) { responseMessage.files = []; } responseMessage.files.push({ collection_name: results.collection_name, name: searchQuery, type: 'web_search_results', urls: results.filenames }); } } const combinedResults = [...combinedResultsSet]; // Convert Set to Array if (combinedResults.length > 0) { responseMessage.statusHistory.push({ done: true, action: 'web_search', description: $i18n.t('Searched {{count}} sites', { count: searchedCount }), urls: combinedResults }); } else { responseMessage.statusHistory.push({ done: true, error: true, action: 'web_search', description: 'No search results found' }); } history.messages[responseMessageId] = responseMessage; }; ```
Author
Owner

@reecelikesramen commented on GitHub (Dec 17, 2024):

May I ask what search engine and query generator model you're using?

I noticed because I was trying to improve the web search prompt. I realized that if I asked a multipart question it would split up the queries like [q1, q2, q3] if I had 3 different topics, but it would only search q1, and the model would either not answer q2 or q3, or use no context to answer those questions.

I think it's worth having this issue here at least to come to a conclusion on what should be default: just 1 query, or multiple.

@reecelikesramen commented on GitHub (Dec 17, 2024): May I ask what search engine and query generator model you're using? I noticed because I was trying to improve the web search prompt. I realized that if I asked a multipart question it would split up the queries like [q1, q2, q3] if I had 3 different topics, but it would only search q1, and the model would either not answer q2 or q3, or use no context to answer those questions. I think it's worth having this issue here at least to come to a conclusion on what should be default: just 1 query, or multiple.
Author
Owner

@aaltulea commented on GitHub (Dec 18, 2024):

I am using qwen2.5 (7b and 32b), with searxng. In the settings, i specified 20 search results to read. Using the code I posed, the model was able to read 40-60 pages, but at the end it will only pick around 5 to render the answer, which is the same as if it ran one query and read 20 pages. I think if we were able to refine the prompt for search query generation, we will get better results.

@aaltulea commented on GitHub (Dec 18, 2024): I am using qwen2.5 (7b and 32b), with searxng. In the settings, i specified 20 search results to read. Using the code I posed, the model was able to read 40-60 pages, but at the end it will only pick around 5 to render the answer, which is the same as if it ran one query and read 20 pages. I think if we were able to refine the prompt for search query generation, we will get better results.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/open-webui#3023