Bladeren bron

Merge branch 'hanfangyuan4396-master'

master
zhayujie 11 maanden geleden
bovenliggende
commit
e04a12a8f4
4 gewijzigde bestanden met toevoegingen van 121 en 30 verwijderingen
  1. +55
    -26
      bot/ali/ali_qwen_bot.py
  2. +62
    -0
      bot/ali/ali_qwen_session.py
  3. +2
    -2
      bot/bot_factory.py
  4. +2
    -2
      plugins/godcmd/godcmd.py

bot/tongyi/tongyi_qwen_bot.py → bot/ali/ali_qwen_bot.py Bestand weergeven

@@ -10,31 +10,48 @@ import broadscope_bailian
from broadscope_bailian import ChatQaMessage from broadscope_bailian import ChatQaMessage


from bot.bot import Bot from bot.bot import Bot
from bot.baidu.baidu_wenxin_session import BaiduWenxinSession
from bot.ali.ali_qwen_session import AliQwenSession
from bot.session_manager import SessionManager from bot.session_manager import SessionManager
from bridge.context import ContextType from bridge.context import ContextType
from bridge.reply import Reply, ReplyType from bridge.reply import Reply, ReplyType
from common.log import logger from common.log import logger
from common import const
from config import conf, load_config from config import conf, load_config


class TongyiQwenBot(Bot):
class AliQwenBot(Bot):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.access_key_id = conf().get("qwen_access_key_id")
self.access_key_secret = conf().get("qwen_access_key_secret")
self.agent_key = conf().get("qwen_agent_key")
self.app_id = conf().get("qwen_app_id")
self.node_id = conf().get("qwen_node_id") or ""
self.api_key_client = broadscope_bailian.AccessTokenClient(access_key_id=self.access_key_id, access_key_secret=self.access_key_secret)
self.api_key_expired_time = self.set_api_key() self.api_key_expired_time = self.set_api_key()
self.sessions = SessionManager(BaiduWenxinSession, model=conf().get("model") or "qwen")
self.temperature = conf().get("temperature", 0.2) # 值在[0,1]之间,越大表示回复越具有不确定性
self.top_p = conf().get("top_p", 1)
self.sessions = SessionManager(AliQwenSession, model=conf().get("model", const.QWEN))

def api_key_client(self):
return broadscope_bailian.AccessTokenClient(access_key_id=self.access_key_id(), access_key_secret=self.access_key_secret())

def access_key_id(self):
return conf().get("qwen_access_key_id")

def access_key_secret(self):
return conf().get("qwen_access_key_secret")

def agent_key(self):
return conf().get("qwen_agent_key")

def app_id(self):
return conf().get("qwen_app_id")

def node_id(self):
return conf().get("qwen_node_id", "")

def temperature(self):
return conf().get("temperature", 0.2 )

def top_p(self):
return conf().get("top_p", 1)


def reply(self, query, context=None): def reply(self, query, context=None):
# acquire reply content # acquire reply content
if context.type == ContextType.TEXT: if context.type == ContextType.TEXT:
logger.info("[TONGYI] query={}".format(query))
logger.info("[QWEN] query={}".format(query))


session_id = context["session_id"] session_id = context["session_id"]
reply = None reply = None
@@ -51,11 +68,11 @@ class TongyiQwenBot(Bot):
if reply: if reply:
return reply return reply
session = self.sessions.session_query(query, session_id) session = self.sessions.session_query(query, session_id)
logger.debug("[TONGYI] session query={}".format(session.messages))
logger.debug("[QWEN] session query={}".format(session.messages))


reply_content = self.reply_text(session) reply_content = self.reply_text(session)
logger.debug( logger.debug(
"[TONGYI] new_query={}, session_id={}, reply_cont={}, completion_tokens={}".format(
"[QWEN] new_query={}, session_id={}, reply_cont={}, completion_tokens={}".format(
session.messages, session.messages,
session_id, session_id,
reply_content["content"], reply_content["content"],
@@ -69,14 +86,14 @@ class TongyiQwenBot(Bot):
reply = Reply(ReplyType.TEXT, reply_content["content"]) reply = Reply(ReplyType.TEXT, reply_content["content"])
else: else:
reply = Reply(ReplyType.ERROR, reply_content["content"]) reply = Reply(ReplyType.ERROR, reply_content["content"])
logger.debug("[TONGYI] reply {} used 0 tokens.".format(reply_content))
logger.debug("[QWEN] reply {} used 0 tokens.".format(reply_content))
return reply return reply


else: else:
reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type)) reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
return reply return reply


def reply_text(self, session: BaiduWenxinSession, retry_count=0) -> dict:
def reply_text(self, session: AliQwenSession, retry_count=0) -> dict:
""" """
call bailian's ChatCompletion to get the answer call bailian's ChatCompletion to get the answer
:param session: a conversation session :param session: a conversation session
@@ -86,9 +103,9 @@ class TongyiQwenBot(Bot):
try: try:
prompt, history = self.convert_messages_format(session.messages) prompt, history = self.convert_messages_format(session.messages)
self.update_api_key_if_expired() self.update_api_key_if_expired()
# NOTE 阿里百炼的call()函数参数比较奇怪, top_k参数表示top_p, top_p参数表示temperature, 可以参考文档 https://help.aliyun.com/document_detail/2587502.htm
response = broadscope_bailian.Completions().call(app_id=self.app_id, prompt=prompt, history=history, top_k=self.top_p, top_p=self.temperature)
completion_content = self.get_completion_content(response, self.node_id)
# NOTE 阿里百炼的call()函数未提供temperature参数,考虑到temperature和top_p参数作用相同,取两者较小的值作为top_p参数传入,详情见文档 https://help.aliyun.com/document_detail/2587502.htm
response = broadscope_bailian.Completions().call(app_id=self.app_id(), prompt=prompt, history=history, top_p=min(self.temperature(), self.top_p()))
completion_content = self.get_completion_content(response, self.node_id())
completion_tokens, total_tokens = self.calc_tokens(session.messages, completion_content) completion_tokens, total_tokens = self.calc_tokens(session.messages, completion_content)
return { return {
"total_tokens": total_tokens, "total_tokens": total_tokens,
@@ -99,39 +116,40 @@ class TongyiQwenBot(Bot):
need_retry = retry_count < 2 need_retry = retry_count < 2
result = {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"} result = {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"}
if isinstance(e, openai.error.RateLimitError): if isinstance(e, openai.error.RateLimitError):
logger.warn("[TONGYI] RateLimitError: {}".format(e))
logger.warn("[QWEN] RateLimitError: {}".format(e))
result["content"] = "提问太快啦,请休息一下再问我吧" result["content"] = "提问太快啦,请休息一下再问我吧"
if need_retry: if need_retry:
time.sleep(20) time.sleep(20)
elif isinstance(e, openai.error.Timeout): elif isinstance(e, openai.error.Timeout):
logger.warn("[TONGYI] Timeout: {}".format(e))
logger.warn("[QWEN] Timeout: {}".format(e))
result["content"] = "我没有收到你的消息" result["content"] = "我没有收到你的消息"
if need_retry: if need_retry:
time.sleep(5) time.sleep(5)
elif isinstance(e, openai.error.APIError): elif isinstance(e, openai.error.APIError):
logger.warn("[TONGYI] Bad Gateway: {}".format(e))
logger.warn("[QWEN] Bad Gateway: {}".format(e))
result["content"] = "请再问我一次" result["content"] = "请再问我一次"
if need_retry: if need_retry:
time.sleep(10) time.sleep(10)
elif isinstance(e, openai.error.APIConnectionError): elif isinstance(e, openai.error.APIConnectionError):
logger.warn("[TONGYI] APIConnectionError: {}".format(e))
logger.warn("[QWEN] APIConnectionError: {}".format(e))
need_retry = False need_retry = False
result["content"] = "我连接不到你的网络" result["content"] = "我连接不到你的网络"
else: else:
logger.exception("[TONGYI] Exception: {}".format(e))
logger.exception("[QWEN] Exception: {}".format(e))
need_retry = False need_retry = False
self.sessions.clear_session(session.session_id) self.sessions.clear_session(session.session_id)


if need_retry: if need_retry:
logger.warn("[TONGYI] 第{}次重试".format(retry_count + 1))
logger.warn("[QWEN] 第{}次重试".format(retry_count + 1))
return self.reply_text(session, retry_count + 1) return self.reply_text(session, retry_count + 1)
else: else:
return result return result


def set_api_key(self): def set_api_key(self):
api_key, expired_time = self.api_key_client.create_token(agent_key=self.agent_key)
api_key, expired_time = self.api_key_client().create_token(agent_key=self.agent_key())
broadscope_bailian.api_key = api_key broadscope_bailian.api_key = api_key
return expired_time return expired_time

def update_api_key_if_expired(self): def update_api_key_if_expired(self):
if time.time() > self.api_key_expired_time: if time.time() > self.api_key_expired_time:
self.api_key_expired_time = self.set_api_key() self.api_key_expired_time = self.set_api_key()
@@ -140,6 +158,7 @@ class TongyiQwenBot(Bot):
history = [] history = []
user_content = '' user_content = ''
assistant_content = '' assistant_content = ''
system_content = ''
for message in messages: for message in messages:
role = message.get('role') role = message.get('role')
if role == 'user': if role == 'user':
@@ -149,11 +168,21 @@ class TongyiQwenBot(Bot):
history.append(ChatQaMessage(user_content, assistant_content)) history.append(ChatQaMessage(user_content, assistant_content))
user_content = '' user_content = ''
assistant_content = '' assistant_content = ''
elif role =='system':
system_content += message.get('content')
if user_content == '': if user_content == '':
raise Exception('no user message') raise Exception('no user message')
if system_content != '':
# NOTE 模拟系统消息,测试发现人格描述以"你需要扮演ChatGPT"开头能够起作用,而以"你是ChatGPT"开头模型会直接否认
system_qa = ChatQaMessage(system_content, '好的,我会严格按照你的设定回答问题')
history.insert(0, system_qa)
logger.debug("[QWEN] converted qa messages: {}".format([item.to_dict() for item in history]))
logger.debug("[QWEN] user content as prompt: {}".format(user_content))
return user_content, history return user_content, history


def get_completion_content(self, response, node_id): def get_completion_content(self, response, node_id):
if not response['Success']:
return f"[ERROR]\n{response['Code']}:{response['Message']}"
text = response['Data']['Text'] text = response['Data']['Text']
if node_id == '': if node_id == '':
return text return text

+ 62
- 0
bot/ali/ali_qwen_session.py Bestand weergeven

@@ -0,0 +1,62 @@
from bot.session_manager import Session
from common.log import logger

"""
e.g.
[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who won the world series in 2020?"},
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"}
]
"""

class AliQwenSession(Session):
def __init__(self, session_id, system_prompt=None, model="qianwen"):
super().__init__(session_id, system_prompt)
self.model = model
self.reset()

def discard_exceeding(self, max_tokens, cur_tokens=None):
precise = True
try:
cur_tokens = self.calc_tokens()
except Exception as e:
precise = False
if cur_tokens is None:
raise e
logger.debug("Exception when counting tokens precisely for query: {}".format(e))
while cur_tokens > max_tokens:
if len(self.messages) > 2:
self.messages.pop(1)
elif len(self.messages) == 2 and self.messages[1]["role"] == "assistant":
self.messages.pop(1)
if precise:
cur_tokens = self.calc_tokens()
else:
cur_tokens = cur_tokens - max_tokens
break
elif len(self.messages) == 2 and self.messages[1]["role"] == "user":
logger.warn("user message exceed max_tokens. total_tokens={}".format(cur_tokens))
break
else:
logger.debug("max_tokens={}, total_tokens={}, len(messages)={}".format(max_tokens, cur_tokens, len(self.messages)))
break
if precise:
cur_tokens = self.calc_tokens()
else:
cur_tokens = cur_tokens - max_tokens
return cur_tokens

def calc_tokens(self):
return num_tokens_from_messages(self.messages, self.model)

def num_tokens_from_messages(messages, model):
"""Returns the number of tokens used by a list of messages."""
# 官方token计算规则:"对于中文文本来说,1个token通常对应一个汉字;对于英文文本来说,1个token通常对应3至4个字母或1个单词"
# 详情请产看文档:https://help.aliyun.com/document_detail/2586397.html
# 目前根据字符串长度粗略估计token数,不影响正常使用
tokens = 0
for msg in messages:
tokens += len(msg["content"])
return tokens

+ 2
- 2
bot/bot_factory.py Bestand weergeven

@@ -45,6 +45,6 @@ def create_bot(bot_type):
return ClaudeAIBot() return ClaudeAIBot()


elif bot_type == const.QWEN: elif bot_type == const.QWEN:
from bot.tongyi.tongyi_qwen_bot import TongyiQwenBot
return TongyiQwenBot()
from bot.ali.ali_qwen_bot import AliQwenBot
return AliQwenBot()
raise RuntimeError raise RuntimeError

+ 2
- 2
plugins/godcmd/godcmd.py Bestand weergeven

@@ -313,7 +313,7 @@ class Godcmd(Plugin):
except Exception as e: except Exception as e:
ok, result = False, "你没有设置私有GPT模型" ok, result = False, "你没有设置私有GPT模型"
elif cmd == "reset": elif cmd == "reset":
if bottype in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI, const.BAIDU, const.XUNFEI]:
if bottype in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI, const.BAIDU, const.XUNFEI, const.QWEN]:
bot.sessions.clear_session(session_id) bot.sessions.clear_session(session_id)
if Bridge().chat_bots.get(bottype): if Bridge().chat_bots.get(bottype):
Bridge().chat_bots.get(bottype).sessions.clear_session(session_id) Bridge().chat_bots.get(bottype).sessions.clear_session(session_id)
@@ -339,7 +339,7 @@ class Godcmd(Plugin):
ok, result = True, "配置已重载" ok, result = True, "配置已重载"
elif cmd == "resetall": elif cmd == "resetall":
if bottype in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI, if bottype in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI,
const.BAIDU, const.XUNFEI]:
const.BAIDU, const.XUNFEI, const.QWEN]:
channel.cancel_all_session() channel.cancel_all_session()
bot.sessions.clear_all_session() bot.sessions.clear_all_session()
ok, result = True, "重置所有会话成功" ok, result = True, "重置所有会话成功"


Laden…
Annuleren
Opslaan