Browse Source

tool ver0.5

1. 新增工具pure模式,支持单个工具调用
2. 新增消息转发工具:email, sms, wechat, 可以根据规则向其他平台发送消息
3. 替换visual-dl(更名为visual)实现,目前识别图片链接效果较好。
4. 修复了0.4版本大部分工具返回结果不可靠问题
master
goldfishh 10 months ago
parent
commit
55e9064307
3 changed files with 121 additions and 55 deletions
  1. +17
    -18
      plugins/tool/README.md
  2. +2
    -2
      plugins/tool/config.json.template
  3. +102
    -35
      plugins/tool/tool.py

+ 17
- 18
plugins/tool/README.md View File

@@ -3,11 +3,19 @@
使用说明(默认trigger_prefix为$): 使用说明(默认trigger_prefix为$):
```text ```text
#help tool: 查看tool帮助信息,可查看已加载工具列表 #help tool: 查看tool帮助信息,可查看已加载工具列表
$tool 命令: 根据给出的{命令}使用一些可用工具尽力为你得到结果。
$tool 工具名 命令: [pure模式]:根据给出的{命令}使用指定 一个 可用工具尽力为你得到结果。
$tool 命令: [多工具模式]:根据给出的{命令}使用 一些 可用工具尽力为你得到结果。
$tool reset: 重置工具。 $tool reset: 重置工具。
``` ```
### 本插件所有工具同步存放至专用仓库:[chatgpt-tool-hub](https://github.com/goldfishh/chatgpt-tool-hub) ### 本插件所有工具同步存放至专用仓库:[chatgpt-tool-hub](https://github.com/goldfishh/chatgpt-tool-hub)


2024.01.16更新
1. 新增工具pure模式,支持单个工具调用
2. 新增消息转发工具:email, sms, wechat, 可以根据规则向其他平台发送消息
3. 替换visual-dl(更名为visual)实现,目前识别图片链接效果较好。
4. 修复了0.4版本大部分工具返回结果不可靠问题

新版本工具名共19个,不一一列举,相应工具需要环境参数见`tool.py`里的`_build_tool_kwargs`函数


## 使用说明 ## 使用说明
使用该插件后将默认使用4个工具, 无需额外配置长期生效: 使用该插件后将默认使用4个工具, 无需额外配置长期生效:
@@ -22,21 +30,19 @@ $tool reset: 重置工具。
#### 2.2 browser #### 2.2 browser
###### 浏览器,功能与2.1类似,但能更好模拟,不会被识别为爬虫影响获取网站内容 ###### 浏览器,功能与2.1类似,但能更好模拟,不会被识别为爬虫影响获取网站内容


> 注1:url-get默认配置、browser需额外配置,browser依赖google-chrome,你需要提前安装好

> 注2:当检测到长文本时会进入summary tool总结长文本,tokens可能会大量消耗!
> 注1:url-get默认配置、browser已能自动下载好依赖


这是debian端安装google-chrome教程,其他系统请自行查找
> https://www.linuxjournal.com/content/how-can-you-install-google-browser-debian
> 注2:(可通过`browser_use_summary`或 `url_get_use_summary`开关)当检测到长文本时会进入summary tool总结长文本,tokens可能会大量消耗!


### 3. terminal ### 3. terminal
###### 在你运行的电脑里执行shell命令,可以配合你想要chatgpt生成的代码使用,给予自然语言控制手段 ###### 在你运行的电脑里执行shell命令,可以配合你想要chatgpt生成的代码使用,给予自然语言控制手段


> terminal调优记录:https://github.com/zhayujie/chatgpt-on-wechat/issues/776#issue-1659347640 > terminal调优记录:https://github.com/zhayujie/chatgpt-on-wechat/issues/776#issue-1659347640


### 4. meteo-weather
### 4. meteo
###### 回答你有关天气的询问, 需要获取时间、地点上下文信息,本工具使用了[meteo open api](https://open-meteo.com/) ###### 回答你有关天气的询问, 需要获取时间、地点上下文信息,本工具使用了[meteo open api](https://open-meteo.com/)
注:该工具需要较高的对话技巧,不保证你问的任何问题均能得到满意的回复 注:该工具需要较高的对话技巧,不保证你问的任何问题均能得到满意的回复
注2:当前版本可只使用这个工具,返回结果较可控。


> meteo调优记录:https://github.com/zhayujie/chatgpt-on-wechat/issues/776#issuecomment-1500771334 > meteo调优记录:https://github.com/zhayujie/chatgpt-on-wechat/issues/776#issuecomment-1500771334


@@ -65,18 +71,12 @@ $tool reset: 重置工具。
#### 6.2. morning-news * #### 6.2. morning-news *
###### 每日60秒早报,每天凌晨一点更新,本工具使用了[alapi-每日60秒早报](https://alapi.cn/api/view/93) ###### 每日60秒早报,每天凌晨一点更新,本工具使用了[alapi-每日60秒早报](https://alapi.cn/api/view/93)


```text
可配置参数:
1. morning_news_use_llm: 是否使用LLM润色结果,默认false(可能会慢)
```

> 该tool每天返回内容相同 > 该tool每天返回内容相同


#### 6.3. finance-news #### 6.3. finance-news
###### 获取实时的金融财政新闻 ###### 获取实时的金融财政新闻


> 该工具需要解决browser tool 的google-chrome依赖安装

> 该工具需要用到browser工具解决反爬问题




### 7. bing-search * ### 7. bing-search *
@@ -99,12 +99,12 @@ $tool reset: 重置工具。
> 0.4.2更新,例子:帮我找一篇吴恩达写的论文 > 0.4.2更新,例子:帮我找一篇吴恩达写的论文


### 11. summary ### 11. summary
###### 总结工具,该工具必须输入一个本地文件的绝对路径
###### 总结工具,该工具可以支持输入url


> 该工具目前是和其他工具配合使用,暂未测试单独使用效果 > 该工具目前是和其他工具配合使用,暂未测试单独使用效果


### 12. image2text
###### 将图片转换成文字,底层调用imageCaption模型,该工具必须输入一个本地文件的绝对路径
### 12. visual
###### 将图片转换成文字,底层调用ali dashscope `qwen-vl-plus`模型


### 13. searxng-search * ### 13. searxng-search *
###### 一个私有化的搜索引擎工具 ###### 一个私有化的搜索引擎工具
@@ -137,7 +137,6 @@ $tool reset: 重置工具。
- `debug`: 输出chatgpt-tool-hub额外信息用于调试 - `debug`: 输出chatgpt-tool-hub额外信息用于调试
- `request_timeout`: 访问openai接口的超时时间,默认与wechat-on-chatgpt配置一致,可单独配置 - `request_timeout`: 访问openai接口的超时时间,默认与wechat-on-chatgpt配置一致,可单独配置
- `no_default`: 用于配置默认加载4个工具的行为,如果为true则仅使用tools列表工具,不加载默认工具 - `no_default`: 用于配置默认加载4个工具的行为,如果为true则仅使用tools列表工具,不加载默认工具
- `top_k_results`: 控制所有有关搜索的工具返回条目数,数字越高则参考信息越多,但无用信息可能干扰判断,该值一般为2
- `model_name`: 用于控制tool插件底层使用的llm模型,目前暂未测试3.5以外的模型,一般保持默认 - `model_name`: 用于控制tool插件底层使用的llm模型,目前暂未测试3.5以外的模型,一般保持默认


--- ---


+ 2
- 2
plugins/tool/config.json.template View File

@@ -3,10 +3,10 @@
"python", "python",
"url-get", "url-get",
"terminal", "terminal",
"meteo-weather"
"meteo"
], ],
"kwargs": { "kwargs": {
"top_k_results": 2,
"debug": true,
"no_default": false, "no_default": false,
"model_name": "gpt-3.5-turbo" "model_name": "gpt-3.5-turbo"
} }


+ 102
- 35
plugins/tool/tool.py View File

@@ -1,23 +1,20 @@
import json
import os

from chatgpt_tool_hub.apps import AppFactory from chatgpt_tool_hub.apps import AppFactory
from chatgpt_tool_hub.apps.app import App from chatgpt_tool_hub.apps.app import App
from chatgpt_tool_hub.tools.all_tool_list import get_all_tool_names
from chatgpt_tool_hub.tools.tool_register import main_tool_register


import plugins import plugins
from bridge.bridge import Bridge from bridge.bridge import Bridge
from bridge.context import ContextType from bridge.context import ContextType
from bridge.reply import Reply, ReplyType from bridge.reply import Reply, ReplyType
from common import const from common import const
from config import conf
from config import conf, get_appdata_dir
from plugins import * from plugins import *




@plugins.register( @plugins.register(
name="tool", name="tool",
desc="Arming your ChatGPT bot with various tools", desc="Arming your ChatGPT bot with various tools",
version="0.4",
version="0.5",
author="goldfishh", author="goldfishh",
desire_priority=0, desire_priority=0,
) )
@@ -26,6 +23,8 @@ class Tool(Plugin):
super().__init__() super().__init__()
self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context


self.tool_config = self._read_json()
self.app_kwargs = self._build_tool_kwargs(self.tool_config.get("kwargs", {}))
self.app = self._reset_app() self.app = self._reset_app()


logger.info("[tool] inited") logger.info("[tool] inited")
@@ -80,6 +79,8 @@ class Tool(Plugin):
elif len(content_list) > 1: elif len(content_list) > 1:
if content_list[1].strip() == "reset": if content_list[1].strip() == "reset":
logger.debug("[tool]: reset config") logger.debug("[tool]: reset config")
self.tool_config = self._read_json()
self.app_kwargs = self._build_tool_kwargs(self.tool_config.get("kwargs", {}))
self.app = self._reset_app() self.app = self._reset_app()
reply.content = "重置工具成功" reply.content = "重置工具成功"
e_context["reply"] = reply e_context["reply"] = reply
@@ -91,17 +92,28 @@ class Tool(Plugin):


e_context.action = EventAction.BREAK e_context.action = EventAction.BREAK
return return

query = content_list[1].strip() query = content_list[1].strip()
use_one_tool = False
for tool_name in main_tool_register.get_registered_tool_names():
if query.startswith(tool_name):
use_one_tool = True
query = query[len(tool_name):]
break


# Don't modify bot name # Don't modify bot name
all_sessions = Bridge().get_bot("chat").sessions all_sessions = Bridge().get_bot("chat").sessions
user_session = all_sessions.session_query(query, e_context["context"]["session_id"]).messages user_session = all_sessions.session_query(query, e_context["context"]["session_id"]).messages


# chatgpt-tool-hub will reply you with many tools
logger.debug("[tool]: just-go") logger.debug("[tool]: just-go")
try: try:
_reply = self.app.ask(query, user_session)
if use_one_tool:
_func, _ = main_tool_register.get_registered_tool()[tool_name]
tool = _func(**self.app_kwargs)
_reply = tool.run(query)
else:
# chatgpt-tool-hub will reply you with many tools
_reply = self.app.ask(query, user_session)
e_context.action = EventAction.BREAK_PASS e_context.action = EventAction.BREAK_PASS
all_sessions.session_reply(_reply, e_context["context"]["session_id"]) all_sessions.session_reply(_reply, e_context["context"]["session_id"])
except Exception as e: except Exception as e:
@@ -126,53 +138,108 @@ class Tool(Plugin):
request_timeout = kwargs.get("request_timeout") request_timeout = kwargs.get("request_timeout")


return { return {
"debug": kwargs.get("debug", False),
"openai_api_key": conf().get("open_ai_api_key", ""),
"open_ai_api_base": conf().get("open_ai_api_base", "https://api.openai.com/v1"),
"deployment_id": conf().get("azure_deployment_id", ""),
"proxy": conf().get("proxy", ""),
# 全局配置相关
"log": True, # tool 日志开关
"debug": kwargs.get("debug", False), # 输出更多日志
"no_default": kwargs.get("no_default", False), # 不要默认的工具,只加载自己导入的工具
"think_depth": kwargs.get("think_depth", 2), # 一个问题最多使用多少次工具
"proxy": conf().get("proxy", ""), # 科学上网
"request_timeout": request_timeout if request_timeout else conf().get("request_timeout", 120), "request_timeout": request_timeout if request_timeout else conf().get("request_timeout", 120),
"temperature": kwargs.get("temperature", 0), # llm 温度,建议设置0
# LLM配置相关
"llm_api_key": conf().get("open_ai_api_key", ""), # 如果llm api用key鉴权,传入这里
"llm_api_base_url": conf().get("open_ai_api_base", "https://api.openai.com/v1"), # 支持openai接口的llm服务地址前缀
"deployment_id": conf().get("azure_deployment_id", ""), # azure openai会用到
# note: 目前tool暂未对其他模型测试,但这里仍对配置来源做了优先级区分,一般插件配置可覆盖全局配置 # note: 目前tool暂未对其他模型测试,但这里仍对配置来源做了优先级区分,一般插件配置可覆盖全局配置
"model_name": tool_model_name if tool_model_name else conf().get("model", "gpt-3.5-turbo"),
"no_default": kwargs.get("no_default", False),
"top_k_results": kwargs.get("top_k_results", 3),
# for news tool
"news_api_key": kwargs.get("news_api_key", ""),
"model_name": tool_model_name if tool_model_name else conf().get("model", const.GPT35),
# 工具配置相关
# for arxiv tool
"arxiv_simple": kwargs.get("arxiv_simple", True), # 返回内容更精简
"arxiv_top_k_results": kwargs.get("arxiv_top_k_results", 2), # 只返回前k个搜索结果
"arxiv_sort_by": kwargs.get("arxiv_sort_by", "relevance"), # 搜索排序方式 ["relevance","lastUpdatedDate","submittedDate"]
"arxiv_sort_order": kwargs.get("arxiv_sort_order", "descending"), # 搜索排序方式 ["ascending", "descending"]
"arxiv_output_type": kwargs.get("arxiv_output_type", "text"), # 搜索结果类型 ["text", "pdf", "all"]
# for bing-search tool # for bing-search tool
"bing_subscription_key": kwargs.get("bing_subscription_key", ""), "bing_subscription_key": kwargs.get("bing_subscription_key", ""),
"bing_search_url": kwargs.get("bing_search_url", "https://api.bing.microsoft.com/v7.0/search"), # 必应搜索的endpoint地址,无需修改
"bing_search_top_k_results": kwargs.get("bing_search_top_k_results", 2), # 只返回前k个搜索结果
"bing_search_simple": kwargs.get("bing_search_simple", True), # 返回内容更精简
"bing_search_output_type": kwargs.get("bing_search_output_type", "text"), # 搜索结果类型 ["text", "json"]
# for email tool
"email_nickname_mapping": kwargs.get("email_nickname_mapping", "{}"), # 关于人的代号对应的邮箱地址,可以不输入邮箱地址发送邮件。键为代号值为邮箱地址
"email_smtp_host": kwargs.get("email_smtp_host", ""), # 例如 'smtp.qq.com'
"email_smtp_port": kwargs.get("email_smtp_port", ""), # 例如 587
"email_sender": kwargs.get("email_sender", ""), # 发送者的邮件地址
"email_authorization_code": kwargs.get("email_authorization_code", ""), # 发送者验证秘钥(可能不是登录密码)
# for google-search tool # for google-search tool
"google_api_key": kwargs.get("google_api_key", ""), "google_api_key": kwargs.get("google_api_key", ""),
"google_cse_id": kwargs.get("google_cse_id", ""), "google_cse_id": kwargs.get("google_cse_id", ""),
"google_simple": kwargs.get("google_simple", True), # 返回内容更精简
"google_output_type": kwargs.get("google_output_type", "text"), # 搜索结果类型 ["text", "json"]
# for finance-news tool
"finance_news_filter": kwargs.get("finance_news_filter", False), # 是否开启过滤
"finance_news_filter_list": kwargs.get("finance_news_filter_list", []), # 过滤词列表
"finance_news_simple": kwargs.get("finance_news_simple", True), # 返回内容更精简
"finance_news_repeat_news": kwargs.get("finance_news_repeat_news", False), # 是否过滤不返回。该tool每次返回约50条新闻,可能有重复新闻
# for morning-news tool
"morning_news_api_key": kwargs.get("morning_news_api_key", ""), # api-key
"morning_news_simple": kwargs.get("morning_news_simple", True), # 返回内容更精简
"morning_news_output_type": kwargs.get("morning_news_output_type", "text"), # 搜索结果类型 ["text", "image"]
# for news-api tool
"news_api_key": kwargs.get("news_api_key", ""),
# for searxng-search tool # for searxng-search tool
"searx_search_host": kwargs.get("searx_search_host", ""),
"searxng_search_host": kwargs.get("searxng_search_host", ""),
"searxng_search_top_k_results": kwargs.get("searxng_search_top_k_results", 2), # 只返回前k个搜索结果
"searxng_search_output_type": kwargs.get("searxng_search_output_type", "text"), # 搜索结果类型 ["text", "json"]
# for sms tool
"sms_nickname_mapping": kwargs.get("sms_nickname_mapping", "{}"), # 关于人的代号对应的手机号,可以不输入手机号发送sms。键为代号值为手机号
"sms_username": kwargs.get("sms_username", ""), # smsbao用户名
"sms_apikey": kwargs.get("sms_apikey", ""), # smsbao
# for stt tool
"stt_api_key": kwargs.get("stt_api_key", ""), # azure
"stt_api_region": kwargs.get("stt_api_region", ""), # azure
"stt_recognition_language": kwargs.get("stt_recognition_language", "zh-CN"), # 识别的语言类型 部分:en-US ja-JP ko-KR yue-CN zh-CN
# for tts tool
"tts_api_key": kwargs.get("tts_api_key", ""), # azure
"tts_api_region": kwargs.get("tts_api_region", ""), # azure
"tts_auto_detect": kwargs.get("tts_auto_detect", True), # 是否自动检测语音的语言
"tts_speech_id": kwargs.get("tts_speech_id", "zh-CN-XiaozhenNeural"), # 输出语音ID
# for summary tool
"summary_max_segment_length": kwargs.get("summary_max_segment_length", 2500), # 每2500tokens分段,多段触发总结tool
# for terminal tool
"terminal_nsfc_filter": kwargs.get("terminal_nsfc_filter", True), # 是否过滤llm输出的危险命令
"terminal_return_err_output": kwargs.get("terminal_return_err_output", True), # 是否输出错误信息
"terminal_timeout": kwargs.get("terminal_timeout", 20), # 允许命令最长执行时间
# for visual tool
"caption_api_key": kwargs.get("caption_api_key", ""), # ali dashscope apikey
# for browser tool
"browser_use_summary": kwargs.get("browser_use_summary", True), # 是否对返回结果使用tool功能
# for url-get tool
"url_get_use_summary": kwargs.get("url_get_use_summary", True), # 是否对返回结果使用tool功能
# for wechat tool
"wechat_hot_reload": kwargs.get("wechat_hot_reload", True), # 是否使用热重载的方式发送wechat
"wechat_cpt_path": kwargs.get("wechat_cpt_path", os.path.join(get_appdata_dir(), "itchat.pkl")), # wechat 配置文件(`itchat.pkl`)
"wechat_send_group": kwargs.get("wechat_send_group", False), # 是否向群组发送消息
"wechat_nickname_mapping": kwargs.get("wechat_nickname_mapping", "{}"), # 关于人的代号映射关系。键为代号值为微信名(昵称、备注名均可)
# for wikipedia tool
"wikipedia_top_k_results": kwargs.get("wikipedia_top_k_results", 2), # 只返回前k个搜索结果
# for wolfram-alpha tool # for wolfram-alpha tool
"wolfram_alpha_appid": kwargs.get("wolfram_alpha_appid", ""), "wolfram_alpha_appid": kwargs.get("wolfram_alpha_appid", ""),
# for morning-news tool
"morning_news_api_key": kwargs.get("morning_news_api_key", ""),
# for visual_dl tool
"cuda_device": kwargs.get("cuda_device", "cpu"),
"think_depth": kwargs.get("think_depth", 3),
"arxiv_summary": kwargs.get("arxiv_summary", True),
"morning_news_use_llm": kwargs.get("morning_news_use_llm", False),
} }


def _filter_tool_list(self, tool_list: list): def _filter_tool_list(self, tool_list: list):
valid_list = [] valid_list = []
for tool in tool_list: for tool in tool_list:
if tool in get_all_tool_names():
if tool in main_tool_register.get_registered_tool_names():
valid_list.append(tool) valid_list.append(tool)
else: else:
logger.warning("[tool] filter invalid tool: " + repr(tool)) logger.warning("[tool] filter invalid tool: " + repr(tool))
return valid_list return valid_list


def _reset_app(self) -> App: def _reset_app(self) -> App:
tool_config = self._read_json()
app_kwargs = self._build_tool_kwargs(tool_config.get("kwargs", {}))

app = AppFactory() app = AppFactory()
app.init_env(**app_kwargs)

app.init_env(**self.app_kwargs)
# filter not support tool # filter not support tool
tool_list = self._filter_tool_list(tool_config.get("tools", []))
tool_list = self._filter_tool_list(self.tool_config.get("tools", []))


return app.create_app(tools_list=tool_list, **app_kwargs)
return app.create_app(tools_list=tool_list, **self.app_kwargs)

Loading…
Cancel
Save