feat(pi-web-search): 统一 web_search/web_fetch 并由主模型路由搜索厂商

7 个按厂商命名的搜索工具收敛为 2 个统一工具,schema 与提示词不再暴露
provider 概念:web_search 由当前主模型按内置规则在 Tavily/Exa/Keenable
间路由(会话内 LRU 缓存,失败按可用性降级),web_fetch 固定走 Exa 并
静默降级。三家均改为纯 fetch 直连——Exa 直接对 mcp.exa.ai 发 JSON-RPC
并缓存会话,彻底移除 pi-mcp-adapter、@tavily/pi-extension 与
@keenable/pi-search 依赖。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
云服务部-叶林立
2026-10-19 20:13:37 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 6fe6cb29ae
commit ee1e6999d7
32 changed files with 1690 additions and 1188 deletions
+13 -13
View File
@@ -174,8 +174,8 @@ test("builds and caches a validated catalog with the current model on first sear
const prompts: string[] = [];
const sourceTools = [
sourceTool("read", "Read a file"),
sourceTool("web_search", "Search the public web with filters and return sources"),
sourceTool("web_fetch", "Fetch the complete content of a selected web page"),
sourceTool("research_search", "Search the public web with filters and return sources"),
sourceTool("research_fetch", "Fetch the complete content of a selected web page"),
];
const api = {
getAllTools: () => sourceTools,
@@ -197,12 +197,12 @@ test("builds and caches a validated catalog with the current model on first sear
summary: "Search and fetch current web sources.",
useWhen: ["Current external facts are needed"],
avoidWhen: ["The answer is entirely local"],
tools: ["web_search", "web_fetch"],
tools: ["research_search", "research_fetch"],
},
],
tools: [
{ name: "web_search", summary: "Find web sources.", useWhen: ["Discover sources"], avoidWhen: [], keywords: ["search", "搜索"], primaryGroup: "web-research" },
{ name: "web_fetch", summary: "Read a selected source.", useWhen: ["A URL is known"], avoidWhen: [], keywords: ["fetch", "抓取"], primaryGroup: "web-research" },
{ name: "research_search", summary: "Find web sources.", useWhen: ["Discover sources"], avoidWhen: [], keywords: ["search", "搜索"], primaryGroup: "web-research" },
{ name: "research_fetch", summary: "Read a selected source.", useWhen: ["A URL is known"], avoidWhen: [], keywords: ["fetch", "抓取"], primaryGroup: "web-research" },
],
};
const usage = { input: 1, output: 1, cacheRead: 0, cacheWrite: 0, totalTokens: 2, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } };
@@ -250,8 +250,8 @@ test("uses the precomputed bundle groups without calling a model", async () => {
let modelCalls = 0;
const sourceTools = [
sourceTool("read", "Read a file"),
sourceTool("tavily_web_search", "Search broadly for current web sources"),
sourceTool("tavily_web_fetch", "Fetch a selected Tavily source"),
sourceTool("web_search", "Search broadly for current web sources"),
sourceTool("web_fetch", "Fetch a selected web source"),
sourceTool("ctx_execute", "Run commands in a context sandbox"),
sourceTool("ctx_execute_file", "Analyze a file in a context sandbox"),
sourceTool("ctx_batch_execute", "Run a bounded command batch"),
@@ -292,12 +292,12 @@ test("uses the precomputed bundle groups without calling a model", async () => {
const result = await registered.get("tool_search")?.execute(
"call-2",
{ query: "tavily_web_search" },
{ query: "web_search" },
undefined,
undefined,
ctx,
);
assert.equal(result?.details.loadedGroup, "web-tavily");
assert.equal(result?.details.loadedGroup, "web-search");
assert.equal(result?.details.catalogSource, "bundle");
assert.equal(modelCalls, 0);
await assert.rejects(readFile(join(agentDir, "my-pi", "tool-search", "catalog-v1.json"), "utf8"), /ENOENT/);
@@ -337,8 +337,8 @@ test("loads an exact known group without waiting for unknown-tool model enrichme
let modelCalls = 0;
const sourceTools = [
sourceTool("read", "Read a file"),
sourceTool("tavily_web_search", "Search broadly for current web sources"),
sourceTool("tavily_web_fetch", "Fetch a selected Tavily source"),
sourceTool("web_search", "Search broadly for current web sources"),
sourceTool("web_fetch", "Fetch a selected web source"),
sourceTool("unrecognized_extra", "An unrecognized third-party capability"),
];
const api = {
@@ -366,12 +366,12 @@ test("loads an exact known group without waiting for unknown-tool model enrichme
const result = await registered.get("tool_search")?.execute(
"call-1",
{ group: "web-tavily" },
{ group: "web-search" },
undefined,
undefined,
ctx,
);
assert.equal(result?.details.loadedGroup, "web-tavily");
assert.equal(result?.details.loadedGroup, "web-search");
assert.equal(result?.details.catalogSource, "hybrid");
assert.equal(modelCalls, 0);
} finally {