@@ -129,10 +129,13 @@ pip3 install --upgrade openai | |||||
**4.其他配置** | **4.其他配置** | ||||
+ `model`: 模型名称,目前支持 `gpt-3.5-turbo`, `text-davinci-003`, `gpt-4`, `gpt-4-32k` (其中gpt-4 api暂未开放) | + `model`: 模型名称,目前支持 `gpt-3.5-turbo`, `text-davinci-003`, `gpt-4`, `gpt-4-32k` (其中gpt-4 api暂未开放) | ||||
+ `temperature`,`frequency_penalty`,`presence_penalty`: Chat API接口参数,详情参考[OpenAI官方文档。](https://platform.openai.com/docs/api-reference/chat) | |||||
+ `proxy`:由于目前 `openai` 接口国内无法访问,需配置代理客户端的地址,详情参考 [#351](https://github.com/zhayujie/chatgpt-on-wechat/issues/351) | + `proxy`:由于目前 `openai` 接口国内无法访问,需配置代理客户端的地址,详情参考 [#351](https://github.com/zhayujie/chatgpt-on-wechat/issues/351) | ||||
+ 对于图像生成,在满足个人或群组触发条件外,还需要额外的关键词前缀来触发,对应配置 `image_create_prefix ` | + 对于图像生成,在满足个人或群组触发条件外,还需要额外的关键词前缀来触发,对应配置 `image_create_prefix ` | ||||
+ 关于OpenAI对话及图片接口的参数配置(内容自由度、回复字数限制、图片大小等),可以参考 [对话接口](https://beta.openai.com/docs/api-reference/completions) 和 [图像接口](https://beta.openai.com/docs/api-reference/completions) 文档直接在 [代码](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/bot/openai/open_ai_bot.py) `bot/openai/open_ai_bot.py` 中进行调整。 | + 关于OpenAI对话及图片接口的参数配置(内容自由度、回复字数限制、图片大小等),可以参考 [对话接口](https://beta.openai.com/docs/api-reference/completions) 和 [图像接口](https://beta.openai.com/docs/api-reference/completions) 文档直接在 [代码](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/bot/openai/open_ai_bot.py) `bot/openai/open_ai_bot.py` 中进行调整。 | ||||
+ `conversation_max_tokens`:表示能够记忆的上下文最大字数(一问一答为一组对话,如果累积的对话字数超出限制,就会优先移除最早的一组对话) | + `conversation_max_tokens`:表示能够记忆的上下文最大字数(一问一答为一组对话,如果累积的对话字数超出限制,就会优先移除最早的一组对话) | ||||
+ `clear_memory_commands`: 对话内指令,主动清空前文记忆,字符串数组可自定义指令别名。 | |||||
+ `hot_reload`: 程序退出后,暂存微信扫码状态,默认关闭。 | |||||
+ `character_desc` 配置中保存着你对机器人说的一段话,他会记住这段话并作为他的设定,你可以为他定制任何人格 (关于会话上下文的更多内容参考该 [issue](https://github.com/zhayujie/chatgpt-on-wechat/issues/43)) | + `character_desc` 配置中保存着你对机器人说的一段话,他会记住这段话并作为他的设定,你可以为他定制任何人格 (关于会话上下文的更多内容参考该 [issue](https://github.com/zhayujie/chatgpt-on-wechat/issues/43)) | ||||
@@ -27,7 +27,8 @@ class ChatGPTBot(Bot): | |||||
if not context or not context.get('type') or context.get('type') == 'TEXT': | if not context or not context.get('type') or context.get('type') == 'TEXT': | ||||
logger.info("[OPEN_AI] query={}".format(query)) | logger.info("[OPEN_AI] query={}".format(query)) | ||||
session_id = context.get('session_id') or context.get('from_user_id') | session_id = context.get('session_id') or context.get('from_user_id') | ||||
if query == '#清除记忆': | |||||
clear_memory_commands = conf().get('clear_memory_commands', ['#清除记忆']) | |||||
if query in clear_memory_commands: | |||||
Session.clear_session(session_id) | Session.clear_session(session_id) | ||||
return '记忆已清除' | return '记忆已清除' | ||||
elif query == '#清除所有': | elif query == '#清除所有': | ||||
@@ -65,11 +66,11 @@ class ChatGPTBot(Bot): | |||||
response = openai.ChatCompletion.create( | response = openai.ChatCompletion.create( | ||||
model= conf().get("model") or "gpt-3.5-turbo", # 对话模型的名称 | model= conf().get("model") or "gpt-3.5-turbo", # 对话模型的名称 | ||||
messages=session, | messages=session, | ||||
temperature=0.9, # 值在[0,1]之间,越大表示回复越具有不确定性 | |||||
temperature=conf().get('temperature', 0.9), # 值在[0,1]之间,越大表示回复越具有不确定性 | |||||
#max_tokens=4096, # 回复最大的字符数 | #max_tokens=4096, # 回复最大的字符数 | ||||
top_p=1, | top_p=1, | ||||
frequency_penalty=0.0, # [-2,2]之间,该值越大则更倾向于产生不同的内容 | |||||
presence_penalty=0.0, # [-2,2]之间,该值越大则更倾向于产生不同的内容 | |||||
frequency_penalty=conf().get('frequency_penalty', 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容 | |||||
presence_penalty=conf().get('presence_penalty', 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容 | |||||
) | ) | ||||
# logger.info("[ChatGPT] reply={}, total_tokens={}".format(response.choices[0]['message']['content'], response["usage"]["total_tokens"])) | # logger.info("[ChatGPT] reply={}, total_tokens={}".format(response.choices[0]['message']['content'], response["usage"]["total_tokens"])) | ||||
return {"total_tokens": response["usage"]["total_tokens"], | return {"total_tokens": response["usage"]["total_tokens"], | ||||
@@ -42,7 +42,7 @@ class WechatChannel(Channel): | |||||
def startup(self): | def startup(self): | ||||
# login by scan QRCode | # login by scan QRCode | ||||
itchat.auto_login(enableCmdQR=2) | |||||
itchat.auto_login(enableCmdQR=2, hotReload=conf().get('hot_reload', False)) | |||||
# start message listener | # start message listener | ||||
itchat.run() | itchat.run() | ||||
@@ -1,50 +1,26 @@ | |||||
import time | import time | ||||
from collections import OrderedDict | |||||
class ExpiredDict(OrderedDict): | |||||
class ExpiredDict(dict): | |||||
def __init__(self, expires_in_seconds): | def __init__(self, expires_in_seconds): | ||||
super().__init__() | super().__init__() | ||||
# 存储键值对的生存时间 | |||||
self.expires_in_seconds = expires_in_seconds | self.expires_in_seconds = expires_in_seconds | ||||
# 获取当前时间的函数 | |||||
self.now = time.monotonic | |||||
def __getitem__(self, key): | def __getitem__(self, key): | ||||
# 检查键是否存在 | |||||
if key not in self: | |||||
raise KeyError(key) | |||||
# 获取值和过期时间 | |||||
value, expiry_time = self[key] | |||||
# 如果过期时间早于当前时间,删除该键值对并引发 KeyError | |||||
if expiry_time is not None and self.now() > expiry_time: | |||||
value, expiry_time = super().__getitem__(key) | |||||
# 如果元素已过期,则从字典中删除该元素并抛出 KeyError 异常 | |||||
if time.monotonic() > expiry_time: | |||||
del self[key] | del self[key] | ||||
raise KeyError(key) | |||||
# 如果存活时间不为 None,更新该键值对的过期时间 | |||||
if self.expires_in_seconds is not None: | |||||
self[key] = value, self.now() + self.expires_in_seconds | |||||
# 删除过期的键值对 | |||||
self._delete_expired_items() | |||||
# 返回值 | |||||
raise KeyError("expired {}".format(key)) | |||||
self.__setitem__(key, value) | |||||
return value | return value | ||||
def __setitem__(self, key, value): | def __setitem__(self, key, value): | ||||
# 如果存活时间不为 None,设置该键值对的过期时间 | |||||
if self.expires_in_seconds is not None: | |||||
self[key] = value, self.now() + self.expires_in_seconds | |||||
else: | |||||
self[key] = value, None | |||||
# 删除过期的键值对 | |||||
self._delete_expired_items() | |||||
# 刷新元素缓存时间 | |||||
super().__setitem__(key, (value, time.monotonic() + self.expires_in_seconds)) | |||||
def get(self, key, default=None): | def get(self, key, default=None): | ||||
try: | try: | ||||
return self[key] | return self[key] | ||||
except KeyError: | except KeyError: | ||||
return default | return default | ||||
def _delete_expired_items(self): | |||||
# 遍历所有键值对,删除过期的键值对 | |||||
for key, (value, expiry_time) in list(self.items()): | |||||
if expiry_time is not None and self.now() > expiry_time: | |||||
del self[key] |
@@ -1,15 +1,16 @@ | |||||
{ | |||||
"open_ai_api_key": "YOUR API KEY", | |||||
"model": "gpt-3.5-turbo", | |||||
"proxy": "", | |||||
"single_chat_prefix": ["bot", "@bot"], | |||||
"single_chat_reply_prefix": "[bot] ", | |||||
"group_chat_prefix": ["@bot"], | |||||
"group_name_white_list": ["ChatGPT测试群", "ChatGPT测试群2"], | |||||
"image_create_prefix": ["画", "看", "找"], | |||||
"speech_recognition": false, | |||||
"voice_reply_voice": false, | |||||
"conversation_max_tokens": 1000, | |||||
"expires_in_seconds": 3600, | |||||
"character_desc": "你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。" | |||||
} | |||||
{ | |||||
"open_ai_api_key": "YOUR API KEY", | |||||
"model": "gpt-3.5-turbo", | |||||
"proxy": "", | |||||
"single_chat_prefix": ["bot", "@bot"], | |||||
"single_chat_reply_prefix": "[bot] ", | |||||
"group_chat_prefix": ["@bot"], | |||||
"group_name_white_list": ["ChatGPT测试群", "ChatGPT测试群2"], | |||||
"image_create_prefix": ["画", "看", "找"], | |||||
"speech_recognition": false, | |||||
"voice_reply_voice": false, | |||||
"conversation_max_tokens": 1000, | |||||
"expires_in_seconds": 3600, | |||||
"character_desc": "你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。" | |||||
} | |||||