缝缝补补

6
This commit is contained in:
gaoao-3 2025-01-03 10:02:22 +08:00
parent 4d48f2be10
commit d0d78113bc
3 changed files with 185 additions and 262 deletions

View file

@ -18,7 +18,7 @@ import { KickOutTool } from '../utils/tools/KickOutTool.js'
import { SetTitleTool } from '../utils/tools/SetTitleTool.js'
import { SerpTool } from '../utils/tools/SerpTool.js'
import { SendMessageToSpecificGroupOrUserTool } from '../utils/tools/SendMessageToSpecificGroupOrUserTool.js'
import { MultiTool } from '../utils/tools/MultiTool.js'
import { GoogleSearchTool } from '../utils/tools/GoogleSearchTool.js'
import { UrlExtractionTool } from '../utils/tools/UrlExtractionTool.js'
export class bym extends plugin {
@ -119,7 +119,7 @@ export class bym extends plugin {
new SendVideoTool(),
new SendMusicTool(),
new SendPictureTool(),
new MultiTool(),
new GoogleSearchTool(),
new UrlExtractionTool(),
new WebsiteTool(),
new WeatherTool(),

View 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
};
}
}

View file

@ -1,260 +0,0 @@
import { AbstractTool } from './AbstractTool.js';
import fetch from 'node-fetch';
import { Config } from '../config.js';
import common from '../../../../lib/common/common.js';
/**
* 多功能工具类 - 支持搜索和代码执行
* @class MultiTool
* @extends {AbstractTool}
*/
export class MultiTool extends AbstractTool {
name = 'MultiTool';
parameters = {
properties: {
query: {
type: 'string',
description: '要处理的内容(可以是搜索查询或代码)',
},
type: {
type: 'string',
description: '操作类型search搜索或 code代码执行',
enum: ['search', 'code']
},
language: {
type: 'string',
description: '当type为code时的编程语言',
},
length: {
type: 'integer',
description: '当type为search时的摘要长度句子数默认为3',
}
},
required: ['query', 'type'],
};
description = '多功能工具:支持使用 Gemini API 进行智能搜索和代码执行。';
/**
* 工具执行函数
* @param {Object} opt - 工具参数
* @param {string} opt.query - 处理内容
* @param {string} opt.type - 操作类型
* @param {string} [opt.language] - 编程语言
* @param {number} [opt.length] - 摘要长度
* @param {Object} e - 事件对象
*/
func = async function (opt, e) {
const { query, type, language, length = 3 } = opt;
if (!query?.trim()) {
throw new Error('处理内容不能为空');
}
try {
const result = await this.processRequest(query, type, language, length);
console.debug(`[MultiTool] 处理结果:`, result);
// 构建转发消息
const forwardMsg = this.constructForwardMessage(result, type);
e.reply(await common.makeForwardMsg(e, forwardMsg, `${e.sender.card || e.sender.nickname || e.user_id}${type === 'search' ? '搜索' : '代码执行'}结果`));
return result;
} catch (error) {
console.error('[MultiTool] 处理失败:', error);
throw new Error(`操作失败: ${error.message}`);
}
};
/**
* 处理请求
* @param {string} query - 处理内容
* @param {string} type - 操作类型
* @param {string} language - 编程语言
* @param {number} length - 摘要长度
*/
async processRequest(query, type, language, 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": this.getSystemInstruction(type)
}]
},
"contents": [{
"parts": [{
"text": this.constructPrompt(query, type, language, length)
}],
"role": "user"
}],
"tools": [{
"googleSearch": {}
}, {
"code_execution": {}
}],
"generationConfig": {
"temperature": 0.1,
"topK": 1,
"topP": 1,
"maxOutputTokens": 2048,
}
};
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, type);
} catch (error) {
console.error('[MultiTool] API调用失败:', error);
throw error;
}
}
/**
* 获取系统指令
* @param {string} type - 操作类型
*/
getSystemInstruction(type) {
if (type === 'search') {
return "你是一个有用的助手,你更喜欢说中文。你会根据用户的问题,通过搜索引擎获取最新的信息来回答问题。你的回答会尽可能准确、客观。";
} else {
return "你是一个代码执行助手。请执行用户提供的代码并返回结果。同时提供代码分析和改进建议。如果有错误,请提供详细的错误信息和修复方案。";
}
}
/**
* 构建提示词
* @param {string} query - 处理内容
* @param {string} type - 操作类型
* @param {string} language - 编程语言
* @param {number} length - 摘要长度
*/
constructPrompt(query, type, language, length) {
if (type === 'search') {
return `请对以下内容进行搜索并提供${length}句话的详细总结。
需要搜索的内容: ${query}
请确保回答准确客观并包含相关事实和信息`;
} else {
return `请执行以下${language}代码并提供执行结果和分析:\n\`\`\`${language}\n${query}\n\`\`\`\n请提供:\n1. 代码执行结果\n2. 代码分析和可能的改进建议`;
}
}
/**
* 处理 Gemini API 响应
* @param {Object} data - API 响应数据
* @param {string} type - 操作类型
*/
processGeminiResponse(data, type) {
if (!data?.candidates?.[0]?.content?.parts) {
throw new Error('无效的 API 响应');
}
const response = data.candidates[0].content.parts
.map(part => part.text)
.filter(Boolean)
.join('\n');
if (type === 'search') {
let sources = [];
if (data.candidates?.[0]?.groundingMetadata?.groundingChunks) {
sources = data.candidates[0].groundingMetadata.groundingChunks
.filter(chunk => chunk.web)
.map(chunk => ({
title: chunk.web.title || '未知标题',
url: this.processUrl(chunk.web.uri)
}))
.filter((v, i, a) =>
a.findIndex(t => (t.title === v.title && t.url === v.url)) === i
);
}
return { answer: response, sources };
} else {
let output = '';
let explanation = '';
let error = null;
if (response.includes('执行结果:')) {
const parts = response.split(/(?=执行结果:|代码分析:)/);
parts.forEach(part => {
if (part.startsWith('执行结果:')) {
output = part.replace('执行结果:', '').trim();
} else if (part.startsWith('代码分析:')) {
explanation = part.replace('代码分析:', '').trim();
}
});
} else if (response.includes('错误:')) {
error = response;
} else {
output = response;
}
return { output, explanation, error, executionTime: Date.now() };
}
}
/**
* 处理URL
* @param {string} url - 原始URL
*/
processUrl(url) {
if (url.includes('https://vertexaisearch.cloud.google.com/grounding-api-redirect')) {
return url.replace(
'https://vertexaisearch.cloud.google.com/grounding-api-redirect',
'https://miao.news'
);
}
return url;
}
/**
* 构建转发消息
* @param {Object} result - 处理结果
* @param {string} type - 操作类型
*/
constructForwardMessage(result, type) {
const forwardMsg = [];
if (type === 'search') {
const { answer, sources } = result;
forwardMsg.push(answer);
if (sources && sources.length > 0) {
forwardMsg.push('信息来源:');
sources.forEach((source, index) => {
forwardMsg.push(`${index + 1}. ${source.title}\n${source.url}`);
});
}
} else {
const { output, explanation, error } = result;
if (error) {
forwardMsg.push(`执行出错:\n${error}`);
} else {
forwardMsg.push(`执行结果:\n${output}`);
if (explanation) {
forwardMsg.push(`\n代码分析:\n${explanation}`);
}
}
}
return forwardMsg;
}
}