mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-17 13:57:10 +00:00
缝缝补补
6
This commit is contained in:
parent
4d48f2be10
commit
d0d78113bc
3 changed files with 185 additions and 262 deletions
183
utils/tools/GoogleSearchTool.js
Normal file
183
utils/tools/GoogleSearchTool.js
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
import { AbstractTool } from './AbstractTool.js';
|
||||
import fetch from 'node-fetch';
|
||||
import { Config } from '../config.js';
|
||||
import common from '../../../../lib/common/common.js';
|
||||
|
||||
/**
|
||||
* 自定义搜索工具类 - 使用 Gemini API
|
||||
* @class GoogleSearchTool
|
||||
* @extends {AbstractTool}
|
||||
*/
|
||||
export class GoogleSearchTool extends AbstractTool {
|
||||
name = 'GoogleSearchTool';
|
||||
|
||||
parameters = {
|
||||
properties: {
|
||||
query: {
|
||||
type: 'string',
|
||||
description: '要搜索的内容或关键词',
|
||||
},
|
||||
length: {
|
||||
type: 'integer',
|
||||
description: '期望的摘要长度(句子数),默认为3',
|
||||
}
|
||||
},
|
||||
required: ['query'],
|
||||
};
|
||||
|
||||
description = '使用 Gemini API 进行智能搜索,根据输入的内容或关键词提供全面的搜索结果和摘要。支持自定义摘要长度。';
|
||||
|
||||
/**
|
||||
* 工具执行函数
|
||||
* @param {Object} opt - 工具参数
|
||||
* @param {string} opt.query - 搜索内容或关键词
|
||||
* @param {number} [opt.length=3] - 摘要长度
|
||||
* @param {Object} e - 事件对象
|
||||
* @returns {Promise<Object>} - 包含答案和来源的对象
|
||||
*/
|
||||
func = async function (opt, e) {
|
||||
const { query, length = 3 } = opt;
|
||||
|
||||
if (!query?.trim()) {
|
||||
throw new Error('搜索内容或关键词不能为空');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.searchWithGemini(query, length);
|
||||
console.debug(`[GoogleSearchTool] 搜索结果:`, result);
|
||||
|
||||
// 构建转发消息
|
||||
const { answer, sources } = result;
|
||||
const forwardMsg = [answer];
|
||||
if (sources && sources.length > 0) {
|
||||
forwardMsg.push('信息来源:');
|
||||
sources.forEach((source, index) => {
|
||||
forwardMsg.push(`${index + 1}. ${source.title}\n${source.url}`);
|
||||
});
|
||||
}
|
||||
e.reply(await common.makeForwardMsg(e, forwardMsg, `${e.sender.card || e.sender.nickname || e.user_id}的搜索结果`));
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[GoogleSearchTool] 搜索失败:', error);
|
||||
throw new Error(`搜索失败: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用 Gemini API 进行搜索
|
||||
* @param {string} query - 搜索内容或关键词
|
||||
* @param {number} length - 摘要长度
|
||||
* @returns {Promise<Object>} - 包含答案和来源的对象
|
||||
* @private
|
||||
*/
|
||||
async searchWithGemini(query, length) {
|
||||
const apiKey = Config.geminiKey;
|
||||
const apiBaseUrl = Config.geminiBaseUrl;
|
||||
const apiUrl = `${apiBaseUrl}/v1beta/models/gemini-2.0-flash-exp:generateContent?key=${apiKey}`;
|
||||
|
||||
if (!apiKey || !apiBaseUrl) {
|
||||
throw new Error('Gemini API 配置缺失');
|
||||
}
|
||||
|
||||
const requestBody = {
|
||||
"systemInstruction": {
|
||||
"parts": [{
|
||||
"text": "你是一个有用的助手,你更喜欢说中文。你会根据用户的问题,通过搜索引擎获取最新的信息来回答问题。你的回答会尽可能准确、客观。"
|
||||
}]
|
||||
},
|
||||
"contents": [{
|
||||
"parts": [{
|
||||
"text": this.constructPrompt(query, length)
|
||||
}],
|
||||
"role": "user"
|
||||
}],
|
||||
"tools": [{
|
||||
"googleSearch": {}
|
||||
}]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestBody)
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API 请求失败: ${data.error?.message || '未知错误'}`);
|
||||
}
|
||||
|
||||
return this.processGeminiResponse(data);
|
||||
} catch (error) {
|
||||
console.error('[GoogleSearchTool] API调用失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建提示词
|
||||
* @param {string} query - 搜索内容或关键词
|
||||
* @param {number} length - 摘要长度
|
||||
* @returns {string} - 格式化的提示词
|
||||
* @private
|
||||
*/
|
||||
constructPrompt(query, length) {
|
||||
return `请对以下内容进行搜索并提供${length}句话的详细总结。
|
||||
需要搜索的内容: ${query}
|
||||
请确保回答准确、客观,并包含相关事实和信息。`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 Gemini API 响应
|
||||
* @param {Object} data - API 响应数据
|
||||
* @returns {Object} - 处理后的结果对象
|
||||
* @private
|
||||
*/
|
||||
processGeminiResponse(data) {
|
||||
if (!data?.candidates?.[0]?.content?.parts) {
|
||||
throw new Error('无效的 API 响应');
|
||||
}
|
||||
|
||||
// 合并所有文本部分作为答案
|
||||
const answer = data.candidates[0].content.parts
|
||||
.map(part => part.text)
|
||||
.filter(Boolean)
|
||||
.join('\n');
|
||||
|
||||
// 处理来源信息
|
||||
let sources = [];
|
||||
if (data.candidates?.[0]?.groundingMetadata?.groundingChunks) {
|
||||
sources = data.candidates[0].groundingMetadata.groundingChunks
|
||||
.filter(chunk => chunk.web)
|
||||
.map(chunk => {
|
||||
let url = chunk.web.uri;
|
||||
// 替换特定的URL前缀
|
||||
if (url.includes('https://vertexaisearch.cloud.google.com/grounding-api-redirect')) {
|
||||
url = url.replace(
|
||||
'https://vertexaisearch.cloud.google.com/grounding-api-redirect',
|
||||
'https://miao.news'
|
||||
);
|
||||
}
|
||||
return {
|
||||
title: chunk.web.title || '未知标题',
|
||||
url: url
|
||||
};
|
||||
})
|
||||
.filter((v, i, a) =>
|
||||
a.findIndex(t => (t.title === v.title && t.url === v.url)) === i
|
||||
);
|
||||
}
|
||||
|
||||
console.debug('[GoogleSearchTool] 处理后的来源信息:', sources);
|
||||
|
||||
return {
|
||||
answer,
|
||||
sources
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue