@aalis/plugin-websearch-serper 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/i18n.d.ts +4 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +18 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +348 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @aalis/plugin-websearch-serper
|
|
2
|
+
|
|
3
|
+
运行时插件
|
|
4
|
+
|
|
5
|
+
## 角色
|
|
6
|
+
|
|
7
|
+
运行时插件
|
|
8
|
+
|
|
9
|
+
## 安装
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @aalis/plugin-websearch-serper
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 文档
|
|
16
|
+
|
|
17
|
+
详见 [docs/plugins/plugin-websearch-serper.md](../../docs/plugins/plugin-websearch-serper.md)。
|
|
18
|
+
|
|
19
|
+
## 许可
|
|
20
|
+
|
|
21
|
+
见仓库根目录 LICENSE。
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { I18nResources } from '@aalis/core';
|
|
2
|
+
export declare const DEFAULT_COMPRESSION_PROMPT_ZH = "\u8BF7\u6839\u636E\u7528\u6237\u7684\u641C\u7D22\u610F\u56FE\uFF0C\u5C06\u4EE5\u4E0B\u641C\u7D22\u7ED3\u679C\u538B\u7F29\u6574\u5408\u4E3A\u4E00\u6BB5\u7B80\u6D01\u3001\u51C6\u786E\u3001\u6709\u6761\u7406\u7684\u6458\u8981\u3002\u4FDD\u7559\u5173\u952E\u4E8B\u5B9E\u3001\u6570\u636E\u548C\u6765\u6E90\uFF0C\u53BB\u9664\u91CD\u590D\u548C\u65E0\u5173\u4FE1\u606F\u3002\u7528\u4E2D\u6587\u56DE\u7B54\u3002\n\n\u641C\u7D22\u5173\u952E\u8BCD: {query}\n\n\u641C\u7D22\u7ED3\u679C:\n{results}";
|
|
3
|
+
export declare const i18n: I18nResources;
|
|
4
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,6BAA6B,0eAA8G,CAAC;AAEzJ,eAAO,MAAM,IAAI,EAAE,aAelB,CAAC"}
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const DEFAULT_COMPRESSION_PROMPT_ZH = '请根据用户的搜索意图,将以下搜索结果压缩整合为一段简洁、准确、有条理的摘要。保留关键事实、数据和来源,去除重复和无关信息。用中文回答。\n\n搜索关键词: {query}\n\n搜索结果:\n{results}';
|
|
2
|
+
export const i18n = {
|
|
3
|
+
'zh-CN': {
|
|
4
|
+
'plugin.websearchSerper.displayName': 'Serper 网络搜索',
|
|
5
|
+
'config.websearchSerper.promptLocale.label': 'Prompt 语言',
|
|
6
|
+
'config.websearchSerper.promptLocale.description': '默认搜索结果压缩提示词使用的语言。自定义压缩提示词不受此项影响。',
|
|
7
|
+
'config.websearchSerper.promptLocale.auto': '跟随全局设置',
|
|
8
|
+
'prompt.websearchSerper.compression': DEFAULT_COMPRESSION_PROMPT_ZH,
|
|
9
|
+
},
|
|
10
|
+
'en-US': {
|
|
11
|
+
'plugin.websearchSerper.displayName': 'Serper Web Search',
|
|
12
|
+
'config.websearchSerper.promptLocale.label': 'Prompt Language',
|
|
13
|
+
'config.websearchSerper.promptLocale.description': 'Language used by the default search-result compression prompt. Custom compression prompts are left unchanged.',
|
|
14
|
+
'config.websearchSerper.promptLocale.auto': 'Follow Global Setting',
|
|
15
|
+
'prompt.websearchSerper.compression': 'Based on the user\'s search intent, compress and integrate the following search results into a concise, accurate, well-structured summary. Preserve key facts, data, and sources, and remove duplicate or irrelevant information. Answer in English.\n\nSearch query: {query}\n\nSearch results:\n{results}',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=i18n.js.map
|
package/dist/i18n.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,6BAA6B,GAAG,2GAA2G,CAAC;AAEzJ,MAAM,CAAC,MAAM,IAAI,GAAkB;IACjC,OAAO,EAAE;QACP,oCAAoC,EAAE,aAAa;QACnD,2CAA2C,EAAE,WAAW;QACxD,iDAAiD,EAAE,kCAAkC;QACrF,0CAA0C,EAAE,QAAQ;QACpD,oCAAoC,EAAE,6BAA6B;KACpE;IACD,OAAO,EAAE;QACP,oCAAoC,EAAE,mBAAmB;QACzD,2CAA2C,EAAE,iBAAiB;QAC9D,iDAAiD,EAAE,+GAA+G;QAClK,0CAA0C,EAAE,uBAAuB;QACnE,oCAAoC,EAAE,6SAA6S;KACpV;CACF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ConfigSchema, Context } from '@aalis/core';
|
|
2
|
+
import '@aalis/plugin-tools-api';
|
|
3
|
+
export type { WebSearchCapability, WebSearchCapabilityRegistry, WebSearchRequest, WebSearchResponse, WebSearchResult, WebSearchService, } from './types.js';
|
|
4
|
+
export { WebSearchCapabilities } from './types.js';
|
|
5
|
+
export declare const name = "@aalis/plugin-websearch-serper";
|
|
6
|
+
export declare const displayName = "Serper \u7F51\u7EDC\u641C\u7D22";
|
|
7
|
+
export declare const subsystem = "tools";
|
|
8
|
+
export declare const provides: string[];
|
|
9
|
+
export declare const inject: {
|
|
10
|
+
optional: string[];
|
|
11
|
+
};
|
|
12
|
+
export declare const configSchema: ConfigSchema;
|
|
13
|
+
export declare const defaultConfig: {
|
|
14
|
+
maxPerMinute: number;
|
|
15
|
+
maxPerDay: number;
|
|
16
|
+
maxConcurrent: number;
|
|
17
|
+
defaultNumResults: number;
|
|
18
|
+
enableCompression: boolean;
|
|
19
|
+
compressionPrompt: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function apply(ctx: Context, config: Record<string, unknown>): void;
|
|
22
|
+
declare module '@aalis/core' {
|
|
23
|
+
interface ServiceTypeMap {
|
|
24
|
+
'web-search': import('./types.js').WebSearchService;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAOzD,OAAO,yBAAyB,CAAC;AAEjC,YAAY,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAInD,eAAO,MAAM,IAAI,mCAAmC,CAAC;AACrD,eAAO,MAAM,WAAW,oCAAgB,CAAC;AACzC,eAAO,MAAM,SAAS,UAAU,CAAC;AACjC,eAAO,MAAM,QAAQ,UAAiB,CAAC;AACvC,eAAO,MAAM,MAAM;;CAElB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,YAuB1B,CAAC;AAEF,eAAO,MAAM,aAAa;;;;;;;CAOzB,CAAC;AAoKF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAkNzE;AAGD,OAAO,QAAQ,aAAa,CAAC;IAC3B,UAAU,cAAc;QACtB,YAAY,EAAE,OAAO,YAAY,EAAE,gBAAgB,CAAC;KACrD;CACF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { resolveLLMModel } from '@aalis/plugin-llm-api';
|
|
2
|
+
import { useToolService } from '@aalis/plugin-tools-api';
|
|
3
|
+
import { WebSearchCapabilities } from './types.js';
|
|
4
|
+
import '@aalis/plugin-tools-api';
|
|
5
|
+
export { WebSearchCapabilities } from './types.js';
|
|
6
|
+
// ===== 插件元数据 =====
|
|
7
|
+
export const name = '@aalis/plugin-websearch-serper';
|
|
8
|
+
export const displayName = 'Serper 网络搜索';
|
|
9
|
+
export const subsystem = 'tools';
|
|
10
|
+
export const provides = ['web-search'];
|
|
11
|
+
export const inject = {
|
|
12
|
+
optional: ['llm'],
|
|
13
|
+
};
|
|
14
|
+
export const configSchema = {
|
|
15
|
+
apiKey: { type: 'string', label: 'Serper API Key', required: true, secret: true, description: 'Serper.dev API 密钥' },
|
|
16
|
+
maxPerMinute: { type: 'number', label: '每分钟最大次数', default: 10, description: '频率限制:每分钟最多搜索次数' },
|
|
17
|
+
maxPerDay: { type: 'number', label: '每天最大次数', default: 100, description: '频率限制:每天最多搜索次数' },
|
|
18
|
+
maxConcurrent: { type: 'number', label: '最大并发', default: 3, description: '同时进行的搜索请求数上限' },
|
|
19
|
+
defaultNumResults: { type: 'number', label: '默认结果数', default: 5, description: '每次搜索返回的结果条数' },
|
|
20
|
+
enableCompression: {
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
label: '启用搜索结果压缩',
|
|
23
|
+
default: false,
|
|
24
|
+
description: '启用后,搜索结果将先经过 LLM 压缩整合后再返回给 Agent,减少 Token 消耗并提升信息质量。',
|
|
25
|
+
},
|
|
26
|
+
compressionLLM: {
|
|
27
|
+
type: 'llm-ref',
|
|
28
|
+
label: '压缩模型',
|
|
29
|
+
description: '选择用于压缩搜索结果的模型。留空则使用默认 LLM 提供者。',
|
|
30
|
+
},
|
|
31
|
+
compressionPrompt: {
|
|
32
|
+
type: 'textarea',
|
|
33
|
+
label: '压缩提示词',
|
|
34
|
+
default: '',
|
|
35
|
+
description: '自定义压缩搜索结果的提示词。留空使用默认提示。提示词中可使用 {query} 代表搜索关键词。',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
export const defaultConfig = {
|
|
39
|
+
maxPerMinute: 10,
|
|
40
|
+
maxPerDay: 100,
|
|
41
|
+
maxConcurrent: 3,
|
|
42
|
+
defaultNumResults: 5,
|
|
43
|
+
enableCompression: false,
|
|
44
|
+
compressionPrompt: '',
|
|
45
|
+
};
|
|
46
|
+
// ===== 速率限制器 =====
|
|
47
|
+
class RateLimiter {
|
|
48
|
+
maxPerMinute;
|
|
49
|
+
maxPerDay;
|
|
50
|
+
maxConcurrent;
|
|
51
|
+
minuteTimestamps = [];
|
|
52
|
+
dayTimestamps = [];
|
|
53
|
+
concurrent = 0;
|
|
54
|
+
constructor(config) {
|
|
55
|
+
this.maxPerMinute = config.maxPerMinute;
|
|
56
|
+
this.maxPerDay = config.maxPerDay;
|
|
57
|
+
this.maxConcurrent = config.maxConcurrent;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 检查是否可以发起请求,返回 null 表示允许,否则返回拒绝原因
|
|
61
|
+
*/
|
|
62
|
+
check() {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
// 清理过期记录
|
|
65
|
+
const oneMinuteAgo = now - 60_000;
|
|
66
|
+
const oneDayAgo = now - 86_400_000;
|
|
67
|
+
this.minuteTimestamps = this.minuteTimestamps.filter(t => t > oneMinuteAgo);
|
|
68
|
+
this.dayTimestamps = this.dayTimestamps.filter(t => t > oneDayAgo);
|
|
69
|
+
if (this.concurrent >= this.maxConcurrent) {
|
|
70
|
+
return `已达最大并发数 (${this.maxConcurrent})`;
|
|
71
|
+
}
|
|
72
|
+
if (this.minuteTimestamps.length >= this.maxPerMinute) {
|
|
73
|
+
return `已达每分钟调用上限 (${this.maxPerMinute}/min)`;
|
|
74
|
+
}
|
|
75
|
+
if (this.dayTimestamps.length >= this.maxPerDay) {
|
|
76
|
+
return `已达每日调用上限 (${this.maxPerDay}/day)`;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
acquire() {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
this.minuteTimestamps.push(now);
|
|
83
|
+
this.dayTimestamps.push(now);
|
|
84
|
+
this.concurrent++;
|
|
85
|
+
}
|
|
86
|
+
release() {
|
|
87
|
+
this.concurrent = Math.max(0, this.concurrent - 1);
|
|
88
|
+
}
|
|
89
|
+
getStatus() {
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
const oneMinuteAgo = now - 60_000;
|
|
92
|
+
const oneDayAgo = now - 86_400_000;
|
|
93
|
+
return {
|
|
94
|
+
minuteUsed: this.minuteTimestamps.filter(t => t > oneMinuteAgo).length,
|
|
95
|
+
minuteLimit: this.maxPerMinute,
|
|
96
|
+
dayUsed: this.dayTimestamps.filter(t => t > oneDayAgo).length,
|
|
97
|
+
dayLimit: this.maxPerDay,
|
|
98
|
+
concurrent: this.concurrent,
|
|
99
|
+
concurrentLimit: this.maxConcurrent,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function serperSearch(query, apiKey, numResults) {
|
|
104
|
+
const response = await fetch('https://google.serper.dev/search', {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: {
|
|
107
|
+
'X-API-KEY': apiKey,
|
|
108
|
+
'Content-Type': 'application/json',
|
|
109
|
+
},
|
|
110
|
+
body: JSON.stringify({
|
|
111
|
+
q: query,
|
|
112
|
+
num: numResults,
|
|
113
|
+
}),
|
|
114
|
+
signal: AbortSignal.timeout(15_000),
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const text = await response.text();
|
|
118
|
+
throw new Error(`Serper API 错误 (${response.status}): ${text}`);
|
|
119
|
+
}
|
|
120
|
+
return (await response.json());
|
|
121
|
+
}
|
|
122
|
+
function formatSearchResults(data) {
|
|
123
|
+
const parts = [];
|
|
124
|
+
if (data.answerBox) {
|
|
125
|
+
const ab = data.answerBox;
|
|
126
|
+
parts.push(`【直接回答】${ab.answer ?? ab.snippet ?? ab.title ?? ''}`);
|
|
127
|
+
}
|
|
128
|
+
if (data.knowledgeGraph) {
|
|
129
|
+
const kg = data.knowledgeGraph;
|
|
130
|
+
if (kg.description) {
|
|
131
|
+
parts.push(`【知识图谱】${kg.title ?? ''}: ${kg.description}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (data.organic && data.organic.length > 0) {
|
|
135
|
+
parts.push('【搜索结果】');
|
|
136
|
+
for (const item of data.organic) {
|
|
137
|
+
parts.push(`${item.position ?? '-'}. ${item.title}\n ${item.link}\n ${item.snippet}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return parts.length > 0 ? parts.join('\n\n') : '未找到搜索结果。';
|
|
141
|
+
}
|
|
142
|
+
/** 将 Serper 原始响应转换为标准 WebSearchResult 数组 */
|
|
143
|
+
function toStandardResults(data) {
|
|
144
|
+
return (data.organic ?? []).map(item => ({
|
|
145
|
+
title: item.title,
|
|
146
|
+
url: item.link,
|
|
147
|
+
snippet: item.snippet,
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
// ===== 插件入口 =====
|
|
151
|
+
export function apply(ctx, config) {
|
|
152
|
+
const cfg = {
|
|
153
|
+
apiKey: config.apiKey ?? '',
|
|
154
|
+
maxPerMinute: config.maxPerMinute ?? 10,
|
|
155
|
+
maxPerDay: config.maxPerDay ?? 100,
|
|
156
|
+
maxConcurrent: config.maxConcurrent ?? 3,
|
|
157
|
+
defaultNumResults: config.defaultNumResults ?? 5,
|
|
158
|
+
enableCompression: config.enableCompression ?? false,
|
|
159
|
+
compressionLLM: config.compressionLLM &&
|
|
160
|
+
typeof config.compressionLLM === 'object' &&
|
|
161
|
+
config.compressionLLM.provider &&
|
|
162
|
+
config.compressionLLM.model
|
|
163
|
+
? config.compressionLLM
|
|
164
|
+
: undefined,
|
|
165
|
+
compressionPrompt: config.compressionPrompt ?? '',
|
|
166
|
+
};
|
|
167
|
+
if (!cfg.apiKey) {
|
|
168
|
+
throw new Error('未配置 Serper API Key,网络搜索不可用');
|
|
169
|
+
}
|
|
170
|
+
const limiter = new RateLimiter(cfg);
|
|
171
|
+
const DEFAULT_COMPRESSION_PROMPT = '请根据用户的搜索意图,将以下搜索结果压缩整合为一段简洁、准确、有条理的摘要。' +
|
|
172
|
+
'保留关键事实、数据和来源,去除重复和无关信息。用中文回答。\n\n' +
|
|
173
|
+
'搜索关键词: {query}\n\n搜索结果:\n{results}';
|
|
174
|
+
/** 使用 LLM 压缩搜索结果 */
|
|
175
|
+
async function compressResults(query, rawResults) {
|
|
176
|
+
const entry = resolveLLMModel(ctx, cfg.compressionLLM, ['chat']);
|
|
177
|
+
if (!entry)
|
|
178
|
+
return rawResults;
|
|
179
|
+
const promptTemplate = cfg.compressionPrompt || DEFAULT_COMPRESSION_PROMPT;
|
|
180
|
+
const prompt = promptTemplate.replace('{query}', query).replace('{results}', rawResults);
|
|
181
|
+
const messages = [{ role: 'user', content: prompt }];
|
|
182
|
+
try {
|
|
183
|
+
const response = await entry.instance.chat({
|
|
184
|
+
messages,
|
|
185
|
+
maxTokens: 1024,
|
|
186
|
+
});
|
|
187
|
+
return response.content?.trim() || rawResults;
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
191
|
+
ctx.logger.warn(`搜索结果压缩失败,返回原始结果: ${msg}`);
|
|
192
|
+
return rawResults;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
ctx.logger.info(`网络搜索已启用 (provider: serper, ` +
|
|
196
|
+
`限制: ${cfg.maxPerMinute}/min, ${cfg.maxPerDay}/day, ` +
|
|
197
|
+
`并发: ${cfg.maxConcurrent}` +
|
|
198
|
+
`${cfg.enableCompression ? ', 压缩: 已启用' : ''})`);
|
|
199
|
+
// 注册为 web-search 服务,供其他插件消费
|
|
200
|
+
const serperService = {
|
|
201
|
+
providerName: 'serper',
|
|
202
|
+
async search(request) {
|
|
203
|
+
const num = Math.min(10, Math.max(1, request.numResults ?? cfg.defaultNumResults));
|
|
204
|
+
const data = await serperSearch(request.query, cfg.apiKey, num);
|
|
205
|
+
return {
|
|
206
|
+
query: request.query,
|
|
207
|
+
results: toStandardResults(data),
|
|
208
|
+
answer: data.answerBox?.answer ?? data.answerBox?.snippet,
|
|
209
|
+
raw: data,
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
const { Web, Compression } = WebSearchCapabilities;
|
|
214
|
+
ctx.provide('web-search', serperService, {
|
|
215
|
+
capabilities: cfg.enableCompression ? [Web, Compression] : [Web],
|
|
216
|
+
label: 'Serper',
|
|
217
|
+
});
|
|
218
|
+
// 注册工具分组
|
|
219
|
+
useToolService(ctx).registerGroup({
|
|
220
|
+
name: 'search',
|
|
221
|
+
label: '网页搜索',
|
|
222
|
+
description: '通过 Serper API 搜索互联网获取最新信息',
|
|
223
|
+
});
|
|
224
|
+
// 注册搜索工具
|
|
225
|
+
useToolService(ctx).register({
|
|
226
|
+
groups: ['search'],
|
|
227
|
+
definition: {
|
|
228
|
+
type: 'function',
|
|
229
|
+
function: {
|
|
230
|
+
name: 'web_search',
|
|
231
|
+
strict: true,
|
|
232
|
+
description: '搜索互联网获取最新信息。以下情况应主动使用,不要等被明确要求才搜索:' +
|
|
233
|
+
'(1) 遇到不熟悉的梗、网络用语、表情包、段子、缩写;' +
|
|
234
|
+
'(2) 时事新闻、热点事件、最新数据;' +
|
|
235
|
+
'(3) 任何拿不准或想核实的事实、人物、作品;' +
|
|
236
|
+
'(4) 用户明确要求查询的内容。',
|
|
237
|
+
parameters: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
query: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
description: '搜索关键词,应当简洁且有针对性',
|
|
243
|
+
},
|
|
244
|
+
numResults: {
|
|
245
|
+
type: 'number',
|
|
246
|
+
description: `返回结果数量,默认 ${cfg.defaultNumResults},范围 1-10`,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
required: ['query'],
|
|
250
|
+
additionalProperties: false,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
handler: async (args) => {
|
|
255
|
+
const query = args.query;
|
|
256
|
+
const numResults = Math.min(10, Math.max(1, args.numResults ?? cfg.defaultNumResults));
|
|
257
|
+
// 速率限制检查
|
|
258
|
+
const rejectReason = limiter.check();
|
|
259
|
+
if (rejectReason) {
|
|
260
|
+
ctx.logger.warn(`搜索被限流: ${rejectReason}`);
|
|
261
|
+
return JSON.stringify({
|
|
262
|
+
error: `搜索请求被限流: ${rejectReason}`,
|
|
263
|
+
status: limiter.getStatus(),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
limiter.acquire();
|
|
267
|
+
try {
|
|
268
|
+
ctx.logger.debug(`执行搜索: "${query}" (${numResults} 条结果)`);
|
|
269
|
+
const data = await serperSearch(query, cfg.apiKey, numResults);
|
|
270
|
+
let result = formatSearchResults(data);
|
|
271
|
+
// 压缩整合
|
|
272
|
+
if (cfg.enableCompression) {
|
|
273
|
+
ctx.logger.debug(`压缩搜索结果 (原始长度: ${result.length})`);
|
|
274
|
+
result = await compressResults(query, result);
|
|
275
|
+
ctx.logger.debug(`压缩完成 (压缩后长度: ${result.length})`);
|
|
276
|
+
}
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
281
|
+
ctx.logger.error(`搜索失败: ${message}`);
|
|
282
|
+
return JSON.stringify({ error: `搜索失败: ${message}` });
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
limiter.release();
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
// 图片搜索工具:返回候选图片 URL 列表,配合 send_attachment 使用
|
|
290
|
+
useToolService(ctx).register({
|
|
291
|
+
groups: ['search'],
|
|
292
|
+
definition: {
|
|
293
|
+
type: 'function',
|
|
294
|
+
function: {
|
|
295
|
+
name: 'search_images',
|
|
296
|
+
description: '搜索互联网图片,返回候选图片 URL 列表。常与 send_attachment 配合:' +
|
|
297
|
+
'先 search_images 拿候选 → 评估缩略图 → 调 send_attachment(kind="image", url) 发出。' +
|
|
298
|
+
'适用场景:用户要求"发个 xxx 表情包"、"找张 xxx 的图发我"。',
|
|
299
|
+
parameters: {
|
|
300
|
+
type: 'object',
|
|
301
|
+
properties: {
|
|
302
|
+
query: { type: 'string', description: '图片搜索关键词。' },
|
|
303
|
+
numResults: {
|
|
304
|
+
type: 'number',
|
|
305
|
+
description: `返回数量,默认 ${cfg.defaultNumResults},范围 1-10`,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
required: ['query'],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
handler: async (args) => {
|
|
313
|
+
const query = args.query;
|
|
314
|
+
const numResults = Math.min(10, Math.max(1, args.numResults ?? cfg.defaultNumResults));
|
|
315
|
+
const rejectReason = limiter.check();
|
|
316
|
+
if (rejectReason)
|
|
317
|
+
return JSON.stringify({ error: `图片搜索被限流: ${rejectReason}` });
|
|
318
|
+
limiter.acquire();
|
|
319
|
+
try {
|
|
320
|
+
const res = await fetch('https://google.serper.dev/images', {
|
|
321
|
+
method: 'POST',
|
|
322
|
+
headers: { 'X-API-KEY': cfg.apiKey, 'Content-Type': 'application/json' },
|
|
323
|
+
body: JSON.stringify({ q: query, num: numResults }),
|
|
324
|
+
});
|
|
325
|
+
if (!res.ok)
|
|
326
|
+
return JSON.stringify({ error: `Serper images API 错误 ${res.status}` });
|
|
327
|
+
const data = (await res.json());
|
|
328
|
+
const images = (data.images ?? []).slice(0, numResults).map(it => ({
|
|
329
|
+
title: it.title,
|
|
330
|
+
imageUrl: it.imageUrl,
|
|
331
|
+
thumbnailUrl: it.thumbnailUrl,
|
|
332
|
+
source: it.source,
|
|
333
|
+
link: it.link,
|
|
334
|
+
width: it.imageWidth,
|
|
335
|
+
height: it.imageHeight,
|
|
336
|
+
}));
|
|
337
|
+
return JSON.stringify({ query, count: images.length, images });
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
limiter.release();
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,yBAAyB,CAAC;AAUjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,oBAAoB;AAEpB,MAAM,CAAC,MAAM,IAAI,GAAG,gCAAgC,CAAC;AACrD,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AACzC,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC;AACjC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,YAAY,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,QAAQ,EAAE,CAAC,KAAK,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAiB;IACxC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,mBAAmB,EAAE;IACnH,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE;IAC9F,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,eAAe,EAAE;IAC1F,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE;IACzF,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,aAAa,EAAE;IAC7F,iBAAiB,EAAE;QACjB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,UAAU;QACjB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,sDAAsD;KACpE;IACD,cAAc,EAAE;QACd,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,gCAAgC;KAC9C;IACD,iBAAiB,EAAE;QACjB,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,OAAO;QACd,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,iDAAiD;KAC/D;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,YAAY,EAAE,EAAE;IAChB,SAAS,EAAE,GAAG;IACd,aAAa,EAAE,CAAC;IAChB,iBAAiB,EAAE,CAAC;IACpB,iBAAiB,EAAE,KAAK;IACxB,iBAAiB,EAAE,EAAE;CACtB,CAAC;AAeF,oBAAoB;AAEpB,MAAM,WAAW;IACE,YAAY,CAAS;IACrB,SAAS,CAAS;IAClB,aAAa,CAAS;IAE/B,gBAAgB,GAAa,EAAE,CAAC;IAChC,aAAa,GAAa,EAAE,CAAC;IAC7B,UAAU,GAAG,CAAC,CAAC;IAEvB,YAAY,MAA6E;QACvF,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QACxC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,KAAK;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,SAAS;QACT,MAAM,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC;QAClC,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;QACnC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC;QAC5E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;QAEnE,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YAC1C,OAAO,YAAY,IAAI,CAAC,aAAa,GAAG,CAAC;QAC3C,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtD,OAAO,cAAc,IAAI,CAAC,YAAY,OAAO,CAAC;QAChD,CAAC;QACD,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAChD,OAAO,aAAa,IAAI,CAAC,SAAS,OAAO,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,SAAS;QACP,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,YAAY,GAAG,GAAG,GAAG,MAAM,CAAC;QAClC,MAAM,SAAS,GAAG,GAAG,GAAG,UAAU,CAAC;QACnC,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,MAAM;YACtE,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,MAAM;YAC7D,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,eAAe,EAAE,IAAI,CAAC,aAAa;SACpC,CAAC;IACJ,CAAC;CACF;AA0BD,KAAK,UAAU,YAAY,CAAC,KAAa,EAAE,MAAc,EAAE,UAAkB;IAC3E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,kCAAkC,EAAE;QAC/D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,WAAW,EAAE,MAAM;YACnB,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,CAAC,EAAE,KAAK;YACR,GAAG,EAAE,UAAU;SAChB,CAAC;QACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmB,CAAC;AACnD,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAoB;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC;QAC/B,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,IAAI,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AAC5D,CAAC;AAED,4CAA4C;AAC5C,SAAS,iBAAiB,CAAC,IAAoB;IAC7C,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,GAAG,EAAE,IAAI,CAAC,IAAI;QACd,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,mBAAmB;AAEnB,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAA+B;IACjE,MAAM,GAAG,GAAoB;QAC3B,MAAM,EAAG,MAAM,CAAC,MAAiB,IAAI,EAAE;QACvC,YAAY,EAAG,MAAM,CAAC,YAAuB,IAAI,EAAE;QACnD,SAAS,EAAG,MAAM,CAAC,SAAoB,IAAI,GAAG;QAC9C,aAAa,EAAG,MAAM,CAAC,aAAwB,IAAI,CAAC;QACpD,iBAAiB,EAAG,MAAM,CAAC,iBAA4B,IAAI,CAAC;QAC5D,iBAAiB,EAAG,MAAM,CAAC,iBAA6B,IAAI,KAAK;QACjE,cAAc,EACZ,MAAM,CAAC,cAAc;YACrB,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ;YACxC,MAAM,CAAC,cAAyC,CAAC,QAAQ;YACzD,MAAM,CAAC,cAAsC,CAAC,KAAK;YAClD,CAAC,CAAE,MAAM,CAAC,cAAsD;YAChE,CAAC,CAAC,SAAS;QACf,iBAAiB,EAAG,MAAM,CAAC,iBAA4B,IAAI,EAAE;KAC9D,CAAC;IAEF,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;IAErC,MAAM,0BAA0B,GAC9B,wCAAwC;QACxC,mCAAmC;QACnC,oCAAoC,CAAC;IAEvC,oBAAoB;IACpB,KAAK,UAAU,eAAe,CAAC,KAAa,EAAE,UAAkB;QAC9D,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,KAAK;YAAE,OAAO,UAAU,CAAC;QAE9B,MAAM,cAAc,GAAG,GAAG,CAAC,iBAAiB,IAAI,0BAA0B,CAAC;QAC3E,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAEzF,MAAM,QAAQ,GAAc,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAEhE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACzC,QAAQ;gBACR,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAC;YAC3C,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,6BAA6B;QAC3B,OAAO,GAAG,CAAC,YAAY,SAAS,GAAG,CAAC,SAAS,QAAQ;QACrD,OAAO,GAAG,CAAC,aAAa,EAAE;QAC1B,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,CACjD,CAAC;IAEF,4BAA4B;IAC5B,MAAM,aAAa,GAAqB;QACtC,YAAY,EAAE,QAAQ;QACtB,KAAK,CAAC,MAAM,CAAC,OAAyB;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACnF,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAChE,OAAO;gBACL,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,iBAAiB,CAAC,IAAI,CAAC;gBAChC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,OAAO;gBACzD,GAAG,EAAE,IAA0C;aAChD,CAAC;QACJ,CAAC;KACF,CAAC;IACF,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,qBAAqB,CAAC;IACnD,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,aAAa,EAAE;QACvC,YAAY,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAChE,KAAK,EAAE,QAAQ;KAChB,CAAC,CAAC;IAEH,SAAS;IACT,cAAc,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC;QAChC,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,2BAA2B;KACzC,CAAC,CAAC;IAEH,SAAS;IACT,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAC3B,MAAM,EAAE,CAAC,QAAQ,CAAC;QAClB,UAAU,EAAE;YACV,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE;gBACR,IAAI,EAAE,YAAY;gBAClB,MAAM,EAAE,IAAI;gBACZ,WAAW,EACT,oCAAoC;oBACpC,6BAA6B;oBAC7B,qBAAqB;oBACrB,yBAAyB;oBACzB,kBAAkB;gBACpB,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,KAAK,EAAE;4BACL,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,iBAAiB;yBAC/B;wBACD,UAAU,EAAE;4BACV,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,aAAa,GAAG,CAAC,iBAAiB,UAAU;yBAC1D;qBACF;oBACD,QAAQ,EAAE,CAAC,OAAO,CAAC;oBACnB,oBAAoB,EAAE,KAAK;iBAC5B;aACF;SACF;QACD,OAAO,EAAE,KAAK,EAAC,IAAI,EAAC,EAAE;YACpB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAC;YACnC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAG,IAAI,CAAC,UAAqB,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAEnG,SAAS;YACT,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,YAAY,EAAE,CAAC;gBACjB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,YAAY,EAAE,CAAC,CAAC;gBAC1C,OAAO,IAAI,CAAC,SAAS,CAAC;oBACpB,KAAK,EAAE,YAAY,YAAY,EAAE;oBACjC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE;iBAC5B,CAAC,CAAC;YACL,CAAC;YAED,OAAO,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,MAAM,UAAU,OAAO,CAAC,CAAC;gBACzD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;gBAC/D,IAAI,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;gBAEvC,OAAO;gBACP,IAAI,GAAG,CAAC,iBAAiB,EAAE,CAAC;oBAC1B,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;oBACpD,MAAM,GAAG,MAAM,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBAC9C,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;gBACrD,CAAC;gBAED,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;gBACrC,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,CAAC,CAAC;YACvD,CAAC;oBAAS,CAAC;gBACT,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,CAAC;QACH,CAAC;KACF,CAAC,CAAC;IAEH,6CAA6C;IAC7C,cAAc,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAC3B,MAAM,EAAE,CAAC,QAAQ,CAAC;QAClB,UAAU,EAAE;YACV,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE;gBACR,IAAI,EAAE,eAAe;gBACrB,WAAW,EACT,8CAA8C;oBAC9C,wEAAwE;oBACxE,sCAAsC;gBACxC,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE;wBAClD,UAAU,EAAE;4BACV,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,WAAW,GAAG,CAAC,iBAAiB,UAAU;yBACxD;qBACF;oBACD,QAAQ,EAAE,CAAC,OAAO,CAAC;iBACpB;aACF;SACF;QACD,OAAO,EAAE,KAAK,EAAC,IAAI,EAAC,EAAE;YACpB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAC;YACnC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAG,IAAI,CAAC,UAAqB,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACnG,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YACrC,IAAI,YAAY;gBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,YAAY,YAAY,EAAE,EAAE,CAAC,CAAC;YAC/E,OAAO,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,kCAAkC,EAAE;oBAC1D,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBACxE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;iBACpD,CAAC,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,EAAE;oBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,wBAAwB,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACpF,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAgD,CAAC;gBAC/E,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;oBACjE,KAAK,EAAE,EAAE,CAAC,KAAe;oBACzB,QAAQ,EAAE,EAAE,CAAC,QAAkB;oBAC/B,YAAY,EAAE,EAAE,CAAC,YAAsB;oBACvC,MAAM,EAAE,EAAE,CAAC,MAAgB;oBAC3B,IAAI,EAAE,EAAE,CAAC,IAAc;oBACvB,KAAK,EAAE,EAAE,CAAC,UAAoB;oBAC9B,MAAM,EAAE,EAAE,CAAC,WAAqB;iBACjC,CAAC,CAAC,CAAC;gBACJ,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YACjE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrF,CAAC;oBAAS,CAAC;gBACT,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** 单条搜索结果 */
|
|
2
|
+
export interface WebSearchResult {
|
|
3
|
+
/** 条目标题 */
|
|
4
|
+
title: string;
|
|
5
|
+
/** 条目 URL */
|
|
6
|
+
url: string;
|
|
7
|
+
/** 摘要/片段(可能为空) */
|
|
8
|
+
snippet?: string;
|
|
9
|
+
/** 来源站点(可选,如 "wikipedia.org") */
|
|
10
|
+
source?: string;
|
|
11
|
+
/** 发布或更新日期(可选,ISO 字符串) */
|
|
12
|
+
publishedAt?: string;
|
|
13
|
+
}
|
|
14
|
+
/** 搜索请求参数 */
|
|
15
|
+
export interface WebSearchRequest {
|
|
16
|
+
/** 搜索关键词 */
|
|
17
|
+
query: string;
|
|
18
|
+
/** 期望返回的结果数(后端决定最大值) */
|
|
19
|
+
numResults?: number;
|
|
20
|
+
/** 语言偏好(如 'zh' / 'en'),后端可忽略 */
|
|
21
|
+
language?: string;
|
|
22
|
+
/** 搜索类型筛选(后端按 capability 声明的支持范围处理) */
|
|
23
|
+
kind?: 'web' | 'news' | 'images';
|
|
24
|
+
}
|
|
25
|
+
/** 搜索响应 */
|
|
26
|
+
export interface WebSearchResponse {
|
|
27
|
+
/** 搜索关键词(回显) */
|
|
28
|
+
query: string;
|
|
29
|
+
/** 结果列表 */
|
|
30
|
+
results: WebSearchResult[];
|
|
31
|
+
/** 后端给出的摘要/答案框(如 Serper 的 answerBox) */
|
|
32
|
+
answer?: string;
|
|
33
|
+
/** 相关问题(若后端支持) */
|
|
34
|
+
relatedQuestions?: string[];
|
|
35
|
+
/** 后端元信息(如耗时、总条数等) */
|
|
36
|
+
raw?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 网络搜索服务
|
|
40
|
+
*
|
|
41
|
+
* 实现者需至少提供 `search()`。`describeProvider()` 用于日志/WebUI 展示。
|
|
42
|
+
*/
|
|
43
|
+
export interface WebSearchService {
|
|
44
|
+
/** 执行一次搜索 */
|
|
45
|
+
search(request: WebSearchRequest): Promise<WebSearchResponse>;
|
|
46
|
+
/** 后端标识(供日志/排错用),如 'serper' */
|
|
47
|
+
readonly providerName: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 网络搜索服务能力注册表
|
|
51
|
+
*
|
|
52
|
+
* 第三方插件可通过 declaration merging 追加:
|
|
53
|
+
* ```ts
|
|
54
|
+
* declare module '@aalis/core' {
|
|
55
|
+
* interface WebSearchCapabilityRegistry {
|
|
56
|
+
* Scholar: 'scholar';
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export interface WebSearchCapabilityRegistry {
|
|
62
|
+
/** 支持常规网页搜索(基本能力) */
|
|
63
|
+
Web: 'web';
|
|
64
|
+
/** 支持新闻搜索 */
|
|
65
|
+
News: 'news';
|
|
66
|
+
/** 支持图片搜索 */
|
|
67
|
+
Images: 'images';
|
|
68
|
+
/** 支持对结果进行 LLM 压缩/摘要 */
|
|
69
|
+
Compression: 'compression';
|
|
70
|
+
/** 返回关联问题 */
|
|
71
|
+
RelatedQuestions: 'related-questions';
|
|
72
|
+
}
|
|
73
|
+
/** 网络搜索能力字符串 union(自动包含第三方扩展) */
|
|
74
|
+
export type WebSearchCapability = WebSearchCapabilityRegistry[keyof WebSearchCapabilityRegistry];
|
|
75
|
+
/** 网络搜索内置能力常量 */
|
|
76
|
+
export declare const WebSearchCapabilities: {
|
|
77
|
+
readonly Web: "web";
|
|
78
|
+
readonly News: "news";
|
|
79
|
+
readonly Images: "images";
|
|
80
|
+
readonly Compression: "compression";
|
|
81
|
+
readonly RelatedQuestions: "related-questions";
|
|
82
|
+
};
|
|
83
|
+
declare module '@aalis/core' {
|
|
84
|
+
interface ServiceCapabilityMap {
|
|
85
|
+
'web-search': WebSearchCapability;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAUA,aAAa;AACb,MAAM,WAAW,eAAe;IAC9B,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,aAAa;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,aAAa;AACb,MAAM,WAAW,gBAAgB;IAC/B,YAAY;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;CAClC;AAED,WAAW;AACX,MAAM,WAAW,iBAAiB;IAChC,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW;IACX,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB;IAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,sBAAsB;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa;IACb,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9D,+BAA+B;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAID;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,2BAA2B;IAC1C,qBAAqB;IACrB,GAAG,EAAE,KAAK,CAAC;IACX,aAAa;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa;IACb,MAAM,EAAE,QAAQ,CAAC;IACjB,wBAAwB;IACxB,WAAW,EAAE,aAAa,CAAC;IAC3B,aAAa;IACb,gBAAgB,EAAE,mBAAmB,CAAC;CACvC;AAED,iCAAiC;AACjC,MAAM,MAAM,mBAAmB,GAAG,2BAA2B,CAAC,MAAM,2BAA2B,CAAC,CAAC;AAEjG,iBAAiB;AACjB,eAAO,MAAM,qBAAqB;;;;;;CAMc,CAAC;AAEjD,OAAO,QAAQ,aAAa,CAAC;IAC3B,UAAU,oBAAoB;QAC5B,YAAY,EAAE,mBAAmB,CAAC;KACnC;CACF"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// ----- 网络搜索服务接口 -----
|
|
2
|
+
//
|
|
3
|
+
// 由 plugin-websearch-serper 等插件实现。Agent 或其他插件可通过
|
|
4
|
+
// `ctx.getService<WebSearchService>('web-search')` 获得该服务来执行搜索,
|
|
5
|
+
// 而无需关心具体后端(Serper / DuckDuckGo / Bing 等)。
|
|
6
|
+
//
|
|
7
|
+
// 多个搜索后端可同时注册,框架按 priority 选择默认实现;
|
|
8
|
+
// 若需指定特定后端(如只使用带新闻能力的后端),可通过 capability 过滤:
|
|
9
|
+
// `ctx.getService<WebSearchService>('web-search', ['news'])`
|
|
10
|
+
/** 网络搜索内置能力常量 */
|
|
11
|
+
export const WebSearchCapabilities = {
|
|
12
|
+
Web: 'web',
|
|
13
|
+
News: 'news',
|
|
14
|
+
Images: 'images',
|
|
15
|
+
Compression: 'compression',
|
|
16
|
+
RelatedQuestions: 'related-questions',
|
|
17
|
+
};
|
|
18
|
+
import { registerCapabilityProbe } from '@aalis/core';
|
|
19
|
+
registerCapabilityProbe('web-search', WebSearchCapabilities.Web, inst => typeof inst.search === 'function'
|
|
20
|
+
? true
|
|
21
|
+
: 'WebSearchService.search() is required for capability "web"');
|
|
22
|
+
// News / Images / Compression / RelatedQuestions 均由请求参数/实现内决定,不做方法探测。
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,EAAE;AACF,iDAAiD;AACjD,+DAA+D;AAC/D,2CAA2C;AAC3C,EAAE;AACF,mCAAmC;AACnC,4CAA4C;AAC5C,+DAA+D;AAoF/D,iBAAiB;AACjB,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,aAAa;IAC1B,gBAAgB,EAAE,mBAAmB;CACS,CAAC;AAQjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,uBAAuB,CAAC,YAAY,EAAE,qBAAqB,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,CACtE,OAAQ,IAA6B,CAAC,MAAM,KAAK,UAAU;IACzD,CAAC,CAAC,IAAI;IACN,CAAC,CAAC,4DAA4D,CACjE,CAAC;AAEF,sEAAsE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aalis/plugin-websearch-serper",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"aalis-plugin"
|
|
7
|
+
],
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@aalis/plugin-llm-api": "^0.1.0",
|
|
15
|
+
"@aalis/plugin-tools-api": "^0.1.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^22.0.0",
|
|
19
|
+
"typescript": "^5.7.0",
|
|
20
|
+
"@aalis/plugin-webui-api": "0.1.0",
|
|
21
|
+
"@aalis/core": "0.1.0",
|
|
22
|
+
"@aalis/plugin-message-api": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@aalis/core": "^0.1.0"
|
|
26
|
+
},
|
|
27
|
+
"aalis": {
|
|
28
|
+
"service": {
|
|
29
|
+
"optional": [
|
|
30
|
+
"llm"
|
|
31
|
+
],
|
|
32
|
+
"provides": [
|
|
33
|
+
"web-search"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"dev": "tsc --watch"
|
|
40
|
+
}
|
|
41
|
+
}
|