[FEAT] Add native Baidu Search provider for Agent web browsing (#5388)

* feat: add native baidu search provider for agent web browsing

* chore: address baidu search review feedback

* refactor baiduSearch internal util to be locally scoped

---------

Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
This commit is contained in:
jimmyzhuu
2026-04-23 08:53:20 +08:00
committed by GitHub
parent 55567239b0
commit 680d38a3ce
8 changed files with 170 additions and 1 deletions

View File

@@ -382,6 +382,9 @@ TTS_PROVIDER="native"
#------ Bing Search ----------- https://portal.azure.com/
# AGENT_BING_SEARCH_API_KEY=
#------ Baidu Search ----------- https://cloud.baidu.com/doc/qianfan-api/s/Wmbq4z7e5
# AGENT_BAIDU_SEARCH_API_KEY=
#------ Serply.io ----------- https://serply.io/
# AGENT_SERPLY_API_KEY=

View File

@@ -147,6 +147,7 @@ const SystemSettings = {
"searchapi",
"serper-dot-dev",
"bing-search",
"baidu-search",
"serply-engine",
"searxng-engine",
"tavily-search",
@@ -483,6 +484,7 @@ const SystemSettings = {
AgentSearchApiEngine: process.env.AGENT_SEARCHAPI_ENGINE || "google",
AgentSerperApiKey: !!process.env.AGENT_SERPER_DEV_KEY || null,
AgentBingSearchApiKey: !!process.env.AGENT_BING_SEARCH_API_KEY || null,
AgentBaiduSearchApiKey: !!process.env.AGENT_BAIDU_SEARCH_API_KEY || null,
AgentSerplyApiKey: !!process.env.AGENT_SERPLY_API_KEY || null,
AgentSearXNGApiUrl: process.env.AGENT_SEARXNG_API_URL || null,
AgentTavilyApiKey: !!process.env.AGENT_TAVILY_API_KEY || null,

View File

@@ -80,6 +80,9 @@ const webBrowsing = {
case "bing-search":
engine = "_bingWebSearch";
break;
case "baidu-search":
engine = "_baiduSearch";
break;
case "serply-engine":
engine = "_serplyEngine";
break;
@@ -604,6 +607,113 @@ const webBrowsing = {
);
return result;
},
_baiduSearch: async function (query) {
if (!process.env.AGENT_BAIDU_SEARCH_API_KEY) {
this.super.introspect(
`${this.caller}: I can't use Baidu Search because the user has not defined the required API key.\nVisit: https://cloud.baidu.com/doc/qianfan-api/s/Wmbq4z7e5 to create the API key.`
);
return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`;
}
this.super.introspect(
`${this.caller}: Using Baidu Search to search for "${
query.length > 100 ? `${query.slice(0, 100)}...` : query
}"`
);
const { response, error } = await fetch(
"https://qianfan.baidubce.com/v2/ai_search/web_search",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.AGENT_BAIDU_SEARCH_API_KEY}`,
"X-Appbuilder-Authorization": `Bearer ${process.env.AGENT_BAIDU_SEARCH_API_KEY}`,
},
body: JSON.stringify({
messages: [{ role: "user", content: query }],
resource_type_filter: [{ type: "web", top_k: 10 }],
}),
}
)
.then(async (res) => {
if (res.ok) return res.json();
const body = await res.text().catch(() => "");
throw new Error(
`${res.status} - ${res.statusText}. params: ${JSON.stringify({
auth: this.middleTruncate(
process.env.AGENT_BAIDU_SEARCH_API_KEY,
5
),
q: query,
body: body.slice(0, 300),
})}`
);
})
.then((data) => {
return { response: data, error: null };
})
.catch((e) => {
this.super.handlerProps.log(`Baidu Search Error: ${e.message}`);
return { response: null, error: e.message };
});
if (error)
return `There was an error searching for content. ${error}`;
if (
(response?.code || response?.message) &&
!response?.references
) {
return `There was an error searching for content. ${response?.message || response?.code}`;
}
/**
* Normalize Baidu Search References to the expected search results format
* @param {Array} references - The references to normalize
* @returns {Array} The normalized references
*/
function normalizeBaiduSearchReferences(references = []) {
if (!Array.isArray(references)) return [];
const seenLinks = new Set();
return references
.filter((reference) => {
if (!reference) return false;
const referenceType = String(
reference.type || reference.resource_type || "web"
).toLowerCase();
return referenceType === "web";
})
.map((reference) => {
const title = String(
reference.title || reference.web_anchor || ""
).trim();
const link = String(reference.url || "").trim();
const snippet = String(
reference.snippet || reference.content || ""
).trim();
if (!title || !link || seenLinks.has(link)) return null;
seenLinks.add(link);
return { title, link, snippet };
})
.filter(Boolean);
}
const data = normalizeBaiduSearchReferences(response?.references);
if (data.length === 0)
return `No information was found online for the search query.`;
this.reportSearchResultsCitations(data);
const result = JSON.stringify(data);
this.super.introspect(
`${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)`
);
return result;
},
_serplyEngine: async function (
query,
language = "en",

View File

@@ -586,6 +586,10 @@ const KEY_MAPPING = {
envKey: "AGENT_BING_SEARCH_API_KEY",
checks: [],
},
AgentBaiduSearchApiKey: {
envKey: "AGENT_BAIDU_SEARCH_API_KEY",
checks: [],
},
AgentSerplyApiKey: {
envKey: "AGENT_SERPLY_API_KEY",
checks: [],