Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin(tool): 新增tool插件 #663

Merged
merged 11 commits into from Apr 4, 2023
2 changes: 1 addition & 1 deletion bot/session_manager.py
Expand Up @@ -50,7 +50,7 @@ def build_session(self, session_id, system_prompt=None):
'''
if session_id not in self.sessions:
self.sessions[session_id] = self.sessioncls(session_id, system_prompt, **self.session_args)
elif system_prompt is not None: # 如果有新的system_prompt,更新并重置session
elif system_prompt is not None: # 如果有新的system_prompt,更新并重置session
self.sessions[session_id].set_system_prompt(system_prompt)
session = self.sessions[session_id]
return session
Expand Down
55 changes: 55 additions & 0 deletions plugins/tool/README.md
@@ -0,0 +1,55 @@
## 插件描述
一个能让chatgpt联网,搜索,数字运算的插件,将赋予强大且丰富的扩展能力
使用该插件需在触发机器人回复条件时,在对话内容前加$tool
### 本插件所有工具同步存放至专用仓库:[chatgpt-tool-hub](https://github.com/goldfishh/chatgpt-tool-hub)


## 使用说明
使用该插件后将默认使用4个工具, 无需额外配置长期生效:
### 1. python_repl
###### python解释器,使用它来解释执行python指令

### 2. requests
###### 往往用来获取某个网站具体内容

### 3. terminal
###### 在你运行的电脑里执行shell命令

### 4. meteo-weather
###### 回答你有关天气的询问, 本工具使用了[meteo open api](https://open-meteo.com/)
注:该工具需提供时间,地点信息,且获取的数据不一定准确

## 使用本工具对话(prompt)技巧
### 1. 有指引的询问
#### 例如:
- 总结这个链接的内容 https://www.36kr.com/p/2186160784654466
- 使用Terminal执行curl cip.cc
- 使用python查询今天日期

### 2. 使用搜索引擎工具
- 如果有搜索工具就能让chatgpt获取到你的未传达清楚的上下文信息,chatgpt不知道你的地理位置,现在时间等,所以不可能给你正确回答


## 其他工具
###### 除上述以外还有其他工具,比如搜索联网、数学运算、百科、新闻需要获取api-key,
###### 由于这些工具使用方法暂时还在整理中,如果你不熟悉请不要尝试使用这些工具


## config.json 配置说明
###### 一个例子
```json
{
"tools": ["wikipedia"],
"kwargs": {
"top_k_results": 2
}
}
```
不创建该文件也能使用本tool
- `tools`:本插件初始化时加载的工具, 目前可选集:["wikipedia", "wolfram-alpha", "google-search", "news-api"]
- `kwargs`:工具执行时的配置,一般在这里存放api-key


## 备注
- 虽然我会有意加入一些限制,但请不要使用本插件做危害他人的事情,请提前了解清楚某些内容是否会违反相关规定,建议提前做好过滤
- 未来一段时间我会实现一些有意思的工具,比如stable diffusion 中文prompt翻译、cv方向的模型推理,欢迎有想法的朋友关注,一起扩展这个项目
Empty file added plugins/tool/__init__.py
Empty file.
116 changes: 116 additions & 0 deletions plugins/tool/tool.py
@@ -0,0 +1,116 @@
import json
import os

from chatgpt_tool_hub.apps import load_app
from chatgpt_tool_hub.apps.app import App

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


@plugins.register(name="tool", desc="Arming your ChatGPT bot with various tools", version="0.2", author="goldfishh", desire_priority=0)
class Tool(Plugin):
def __init__(self):
super().__init__()
self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context
os.environ["OPENAI_API_KEY"] = conf().get("open_ai_api_key", "")
os.environ["PROXY"] = conf().get("proxy", "")

self.app = self._reset_app()

logger.info("[tool] inited")

def get_help_text(self, **kwargs):
help_text = "这是一个能让chatgpt联网,搜索,数字运算的插件,将赋予强大且丰富的扩展能力"
return help_text

def on_handle_context(self, e_context: EventContext):
if e_context['context'].type != ContextType.TEXT:
return

# 暂时不支持未来扩展的bot
if Bridge().get_bot_type("chat") not in (const.CHATGPT, const.OPEN_AI, const.CHATGPTONAZURE):
return

content = e_context['context'].content
content_list = e_context['context'].content.split(maxsplit=1)

if not content or len(content_list) < 1:
e_context.action = EventAction.CONTINUE
return

logger.debug("[tool] on_handle_context. content: %s" % content)
reply = Reply()
reply.type = ReplyType.TEXT

# todo: 有些工具必须要api-key,需要修改config文件,所以这里没有实现query增删tool的功能
if content.startswith("$tool"):
if len(content_list) == 1:
logger.debug("[tool]: get help")
reply.content = self.get_help_text()
e_context['reply'] = reply
e_context.action = EventAction.BREAK_PASS
return
elif len(content_list) > 1:
if content_list[1].strip() == "reset":
logger.debug("[tool]: reset config")
self.app = self._reset_app()
reply.content = "重置工具成功"
e_context['reply'] = reply
e_context.action = EventAction.BREAK_PASS
return
elif content_list[1].startswith("reset"):
logger.debug("[tool]: remind")
reply.content = "请你随机用一种聊天风格,提醒用户:如果想重置tool插件,reset之后不要加任何字符"
e_context['reply'] = reply
e_context.action = EventAction.BREAK
return

query = content_list[1].strip()

# Don't modify bot name
all_sessions = Bridge().get_bot("chat").sessions
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")
try:
_reply = self.app.ask(query, user_session)
e_context.action = EventAction.BREAK_PASS
all_sessions.session_reply(_reply, e_context['context']['session_id'])
except ValueError as e:
logger.exception(e)
logger.error(str(e))

e_context['context'].content = "这个问题tool插件暂时无法处理"
reply.type = ReplyType.ERROR
Copy link
Collaborator

@lanvent lanvent Mar 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里需要改e_context['context'].content,将它做为新的问题。

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

之前表述有误,要让chatgpt来处理你更改后的query,需使用以下写法

e_context['context'].content = "请你随机用一种聊天风格,提醒用户:如果想重置tool插件,reset之后不要加任何字符"
e_context.action = EventAction.BREAK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我没仔细看插件具体实现,这样 e_context['reply']需要更改吗?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果是交付给chatgpt处理就不用改reply,它查询完会覆盖reply。

e_context.action = EventAction.CONTINUE
return

reply.content = _reply
e_context['reply'] = reply
return

def _read_json(self) -> dict:
curdir = os.path.dirname(__file__)
config_path = os.path.join(curdir, "config.json")
tool_config = {
"tools": [],
"kwargs": {}
}
if not os.path.exists(config_path):
return tool_config
else:
with open(config_path, "r") as f:
tool_config = json.load(f)
return tool_config

def _reset_app(self) -> App:
tool_config = self._read_json()
return load_app(tools_list=tool_config.get("tools"), **tool_config.get("kwargs"))
3 changes: 2 additions & 1 deletion requirements.txt
Expand Up @@ -13,4 +13,5 @@ wechaty>=0.10.7
wechaty_puppet>=0.4.23
chardet>=5.1.0
SpeechRecognition
tiktoken>=0.3.2
tiktoken>=0.3.2
chatgpt_tool_hub>=0.3.0