[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:
parent
55567239b0
commit
680d38a3ce
@ -378,6 +378,9 @@ GID='1000'
|
|||||||
#------ Bing Search ----------- https://portal.azure.com/
|
#------ Bing Search ----------- https://portal.azure.com/
|
||||||
# AGENT_BING_SEARCH_API_KEY=
|
# 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/
|
#------ Serply.io ----------- https://serply.io/
|
||||||
# AGENT_SERPLY_API_KEY=
|
# AGENT_SERPLY_API_KEY=
|
||||||
|
|
||||||
@ -448,4 +451,4 @@ GID='1000'
|
|||||||
|
|
||||||
# (optional) Comma-separated list of skills that are auto-approved.
|
# (optional) Comma-separated list of skills that are auto-approved.
|
||||||
# This will allow the skill to be invoked without user interaction.
|
# This will allow the skill to be invoked without user interaction.
|
||||||
# AGENT_AUTO_APPROVED_SKILLS=create-pdf-file,create-word-file
|
# AGENT_AUTO_APPROVED_SKILLS=create-pdf-file,create-word-file
|
||||||
|
|||||||
@ -248,6 +248,43 @@ export function BingSearchOptions({ settings }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function BaiduSearchOptions({ settings }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-sm text-white/60 my-2">
|
||||||
|
You can get an API key{" "}
|
||||||
|
<a
|
||||||
|
href="https://cloud.baidu.com/doc/qianfan-api/s/Wmbq4z7e5"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-blue-300 underline"
|
||||||
|
>
|
||||||
|
from Baidu AI Cloud Qianfan.
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-x-4">
|
||||||
|
<div className="flex flex-col w-60">
|
||||||
|
<label className="text-white text-sm font-semibold block mb-3">
|
||||||
|
API Key
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="env::AgentBaiduSearchApiKey"
|
||||||
|
className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
|
||||||
|
placeholder="Baidu Search API Key"
|
||||||
|
defaultValue={
|
||||||
|
settings?.AgentBaiduSearchApiKey ? "*".repeat(20) : ""
|
||||||
|
}
|
||||||
|
required={true}
|
||||||
|
autoComplete="off"
|
||||||
|
spellCheck={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function SerplySearchOptions({ settings }) {
|
export function SerplySearchOptions({ settings }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
@ -4,6 +4,7 @@ import SerpApiIcon from "./icons/serpapi.png";
|
|||||||
import SearchApiIcon from "./icons/searchapi.png";
|
import SearchApiIcon from "./icons/searchapi.png";
|
||||||
import SerperDotDevIcon from "./icons/serper.png";
|
import SerperDotDevIcon from "./icons/serper.png";
|
||||||
import BingSearchIcon from "./icons/bing.png";
|
import BingSearchIcon from "./icons/bing.png";
|
||||||
|
import BaiduSearchIcon from "./icons/baidu.png";
|
||||||
import SerplySearchIcon from "./icons/serply.png";
|
import SerplySearchIcon from "./icons/serply.png";
|
||||||
import SearXNGSearchIcon from "./icons/searxng.png";
|
import SearXNGSearchIcon from "./icons/searxng.png";
|
||||||
import TavilySearchIcon from "./icons/tavily.svg";
|
import TavilySearchIcon from "./icons/tavily.svg";
|
||||||
@ -24,6 +25,7 @@ import {
|
|||||||
SearchApiOptions,
|
SearchApiOptions,
|
||||||
SerperDotDevOptions,
|
SerperDotDevOptions,
|
||||||
BingSearchOptions,
|
BingSearchOptions,
|
||||||
|
BaiduSearchOptions,
|
||||||
SerplySearchOptions,
|
SerplySearchOptions,
|
||||||
SearXNGOptions,
|
SearXNGOptions,
|
||||||
TavilySearchOptions,
|
TavilySearchOptions,
|
||||||
@ -71,6 +73,14 @@ const SEARCH_PROVIDERS = [
|
|||||||
options: (settings) => <BingSearchOptions settings={settings} />,
|
options: (settings) => <BingSearchOptions settings={settings} />,
|
||||||
description: "Web search powered by the Bing Search API (paid service).",
|
description: "Web search powered by the Bing Search API (paid service).",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Baidu Search",
|
||||||
|
value: "baidu-search",
|
||||||
|
logo: BaiduSearchIcon,
|
||||||
|
options: (settings) => <BaiduSearchOptions settings={settings} />,
|
||||||
|
description:
|
||||||
|
"Web search powered by Baidu Search for stronger zh-CN retrieval.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Serply.io",
|
name: "Serply.io",
|
||||||
value: "serply-engine",
|
value: "serply-engine",
|
||||||
|
|||||||
@ -382,6 +382,9 @@ TTS_PROVIDER="native"
|
|||||||
#------ Bing Search ----------- https://portal.azure.com/
|
#------ Bing Search ----------- https://portal.azure.com/
|
||||||
# AGENT_BING_SEARCH_API_KEY=
|
# 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/
|
#------ Serply.io ----------- https://serply.io/
|
||||||
# AGENT_SERPLY_API_KEY=
|
# AGENT_SERPLY_API_KEY=
|
||||||
|
|
||||||
|
|||||||
@ -147,6 +147,7 @@ const SystemSettings = {
|
|||||||
"searchapi",
|
"searchapi",
|
||||||
"serper-dot-dev",
|
"serper-dot-dev",
|
||||||
"bing-search",
|
"bing-search",
|
||||||
|
"baidu-search",
|
||||||
"serply-engine",
|
"serply-engine",
|
||||||
"searxng-engine",
|
"searxng-engine",
|
||||||
"tavily-search",
|
"tavily-search",
|
||||||
@ -483,6 +484,7 @@ const SystemSettings = {
|
|||||||
AgentSearchApiEngine: process.env.AGENT_SEARCHAPI_ENGINE || "google",
|
AgentSearchApiEngine: process.env.AGENT_SEARCHAPI_ENGINE || "google",
|
||||||
AgentSerperApiKey: !!process.env.AGENT_SERPER_DEV_KEY || null,
|
AgentSerperApiKey: !!process.env.AGENT_SERPER_DEV_KEY || null,
|
||||||
AgentBingSearchApiKey: !!process.env.AGENT_BING_SEARCH_API_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,
|
AgentSerplyApiKey: !!process.env.AGENT_SERPLY_API_KEY || null,
|
||||||
AgentSearXNGApiUrl: process.env.AGENT_SEARXNG_API_URL || null,
|
AgentSearXNGApiUrl: process.env.AGENT_SEARXNG_API_URL || null,
|
||||||
AgentTavilyApiKey: !!process.env.AGENT_TAVILY_API_KEY || null,
|
AgentTavilyApiKey: !!process.env.AGENT_TAVILY_API_KEY || null,
|
||||||
|
|||||||
@ -80,6 +80,9 @@ const webBrowsing = {
|
|||||||
case "bing-search":
|
case "bing-search":
|
||||||
engine = "_bingWebSearch";
|
engine = "_bingWebSearch";
|
||||||
break;
|
break;
|
||||||
|
case "baidu-search":
|
||||||
|
engine = "_baiduSearch";
|
||||||
|
break;
|
||||||
case "serply-engine":
|
case "serply-engine":
|
||||||
engine = "_serplyEngine";
|
engine = "_serplyEngine";
|
||||||
break;
|
break;
|
||||||
@ -604,6 +607,113 @@ const webBrowsing = {
|
|||||||
);
|
);
|
||||||
return result;
|
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 (
|
_serplyEngine: async function (
|
||||||
query,
|
query,
|
||||||
language = "en",
|
language = "en",
|
||||||
|
|||||||
@ -586,6 +586,10 @@ const KEY_MAPPING = {
|
|||||||
envKey: "AGENT_BING_SEARCH_API_KEY",
|
envKey: "AGENT_BING_SEARCH_API_KEY",
|
||||||
checks: [],
|
checks: [],
|
||||||
},
|
},
|
||||||
|
AgentBaiduSearchApiKey: {
|
||||||
|
envKey: "AGENT_BAIDU_SEARCH_API_KEY",
|
||||||
|
checks: [],
|
||||||
|
},
|
||||||
AgentSerplyApiKey: {
|
AgentSerplyApiKey: {
|
||||||
envKey: "AGENT_SERPLY_API_KEY",
|
envKey: "AGENT_SERPLY_API_KEY",
|
||||||
checks: [],
|
checks: [],
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user