|
|
@ -317,6 +317,7 @@ class DouyinLiveWebReply:
|
|
|
|
self.live_chat_config = LiveChatConfig()
|
|
|
|
self.live_chat_config = LiveChatConfig()
|
|
|
|
self.punctuation = ",.!;:,。!?:;"
|
|
|
|
self.punctuation = ",.!;:,。!?:;"
|
|
|
|
self.system_message_index = 0
|
|
|
|
self.system_message_index = 0
|
|
|
|
|
|
|
|
self.response_queue = PromptQueue(10)
|
|
|
|
|
|
|
|
|
|
|
|
def _llm(self, prompt, stream=False):
|
|
|
|
def _llm(self, prompt, stream=False):
|
|
|
|
payload = {
|
|
|
|
payload = {
|
|
|
@ -385,6 +386,41 @@ class DouyinLiveWebReply:
|
|
|
|
timeout=5
|
|
|
|
timeout=5
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def post_to_human_sync(self, text: str):
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
同步调用post_to_human
|
|
|
|
|
|
|
|
:param text: 要发送的文本内容
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = requests.post(
|
|
|
|
|
|
|
|
f'{self.live_chat_config.livetalking_address}/human',
|
|
|
|
|
|
|
|
json={
|
|
|
|
|
|
|
|
"type": "echo",
|
|
|
|
|
|
|
|
"sessionid": self.live_chat_config.livetalking_sessionid,
|
|
|
|
|
|
|
|
"text": text
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
timeout=5
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
if response.status_code != 200:
|
|
|
|
|
|
|
|
logger.error(f'Failed to post to human: {response.text}')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_messages(self, batch_number=0):
|
|
|
|
|
|
|
|
message_count = self.live_chat_config.messages(batch_number)
|
|
|
|
|
|
|
|
if message_count == 0:
|
|
|
|
|
|
|
|
logger.info(f'生成系统文案,batch_number: {batch_number}')
|
|
|
|
|
|
|
|
# 结合原始样例话术,拼接提示词,调用Ollama,生成洗稿后的话术
|
|
|
|
|
|
|
|
system_messages = self.live_chat_config.system_messages
|
|
|
|
|
|
|
|
llm_prompt = self.live_chat_config.refine_system_message_prompt.format(
|
|
|
|
|
|
|
|
content=system_messages)
|
|
|
|
|
|
|
|
reply_messages = self._llm(llm_prompt, False)
|
|
|
|
|
|
|
|
# 处理reply_messages,先转换为json对象,将key和value分别对应type和message存入sqlite message表中,并统一给batch_number赋值为0
|
|
|
|
|
|
|
|
# 正则匹配处理reply_messages,只保留大括号及其范围内的字符串
|
|
|
|
|
|
|
|
reply_messages = re.findall(r'\{.*?\}', reply_messages, re.DOTALL)[0]
|
|
|
|
|
|
|
|
reply_messages = json.loads(reply_messages)
|
|
|
|
|
|
|
|
# 遍历reply_messages对象,insert message
|
|
|
|
|
|
|
|
for _type, message in reply_messages.items():
|
|
|
|
|
|
|
|
self.live_chat_config.insert_message(message, _type, batch_number)
|
|
|
|
|
|
|
|
logger.info(f'入库备用系统文案:{_type} | {message}')
|
|
|
|
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
def __call__(self):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
优先从用户交互队列中取提示词,如果没有用户交互的数据,则输出系统提示词
|
|
|
|
优先从用户交互队列中取提示词,如果没有用户交互的数据,则输出系统提示词
|
|
|
@ -392,6 +428,8 @@ class DouyinLiveWebReply:
|
|
|
|
live_chat_config.update_chat_enable_status('已启动')
|
|
|
|
live_chat_config.update_chat_enable_status('已启动')
|
|
|
|
logger.info(f'livetalking address -> {self.live_chat_config.livetalking_address}')
|
|
|
|
logger.info(f'livetalking address -> {self.live_chat_config.livetalking_address}')
|
|
|
|
logger.info(f'ollama_address -> {self.live_chat_config.ollama_address}')
|
|
|
|
logger.info(f'ollama_address -> {self.live_chat_config.ollama_address}')
|
|
|
|
|
|
|
|
# 加一个计数器,统计is_speaking连续为False的次数,如果超过10次,才算真正的未在说话
|
|
|
|
|
|
|
|
is_not_speaking_count = 0
|
|
|
|
while True:
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
is_speaking = requests.post(f'{self.live_chat_config.livetalking_address}/is_speaking',
|
|
|
|
is_speaking = requests.post(f'{self.live_chat_config.livetalking_address}/is_speaking',
|
|
|
@ -399,76 +437,92 @@ class DouyinLiveWebReply:
|
|
|
|
timeout=5).json()['data']
|
|
|
|
timeout=5).json()['data']
|
|
|
|
if is_speaking:
|
|
|
|
if is_speaking:
|
|
|
|
time.sleep(0.1)
|
|
|
|
time.sleep(0.1)
|
|
|
|
continue
|
|
|
|
prompt_data = self.queue.get(False)
|
|
|
|
|
|
|
|
if prompt_data is not None:
|
|
|
|
|
|
|
|
product_name, product_specification, product_description = self.live_chat_config.product_info
|
|
|
|
|
|
|
|
# live_chat: 弹幕
|
|
|
|
|
|
|
|
message_type, prompt, live_chat = prompt_data
|
|
|
|
|
|
|
|
if message_type == MessageType.ENTER_LIVE_ROOM.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.enter_live_room_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.FOLLOW.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.follow_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.LIKE.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.like_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.GIFT.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.gift_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.CHAT.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.chat_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
if live_chat is not None:
|
|
|
|
|
|
|
|
logger.info(f'弹幕: {live_chat}')
|
|
|
|
|
|
|
|
llm_output = self._llm(
|
|
|
|
|
|
|
|
self.live_chat_config.product_related_prompt.format(content=live_chat))
|
|
|
|
|
|
|
|
logger.info(f'判断弹幕是否违反中国大陆法律和政策: {llm_output}')
|
|
|
|
|
|
|
|
if llm_output != '否':
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
reply_message = self._llm(prompt, False)
|
|
|
|
|
|
|
|
self.response_queue.put(reply_message)
|
|
|
|
|
|
|
|
# is_speaking此时是False,需要等一段时间再查询
|
|
|
|
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# 用户交互队列为空,输出系统文案和备用系统文案
|
|
|
|
|
|
|
|
if not self.live_chat_config.next_reply_message:
|
|
|
|
|
|
|
|
logger.info('备用系统文案已用完,重新生成备用系统文案')
|
|
|
|
|
|
|
|
self.live_chat_config.flush_message()
|
|
|
|
|
|
|
|
self.generate_messages(1)
|
|
|
|
|
|
|
|
|
|
|
|
prompt_data = self.queue.get(False)
|
|
|
|
continue
|
|
|
|
if prompt_data is not None:
|
|
|
|
|
|
|
|
product_name, product_specification, product_description = self.live_chat_config.product_info
|
|
|
|
|
|
|
|
# live_chat: 弹幕
|
|
|
|
|
|
|
|
message_type, prompt, live_chat = prompt_data
|
|
|
|
|
|
|
|
if message_type == MessageType.ENTER_LIVE_ROOM.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.enter_live_room_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.FOLLOW.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.follow_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.LIKE.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.like_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.GIFT.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.gift_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
elif message_type == MessageType.CHAT.value:
|
|
|
|
|
|
|
|
if random.random() >= self.live_chat_config.chat_prob / 100:
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
prompt = prompt.format(product_name=product_name,
|
|
|
|
|
|
|
|
product_specification=product_specification,
|
|
|
|
|
|
|
|
product_description=product_description)
|
|
|
|
|
|
|
|
if live_chat is not None:
|
|
|
|
|
|
|
|
logger.info(f'弹幕: {live_chat}')
|
|
|
|
|
|
|
|
llm_output = self._llm(self.live_chat_config.product_related_prompt.format(content=live_chat))
|
|
|
|
|
|
|
|
logger.info(f'判断弹幕是否违反中国大陆法律和政策: {llm_output}')
|
|
|
|
|
|
|
|
if llm_output != '否':
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
reply_messages = self._llm(prompt, True)
|
|
|
|
|
|
|
|
for reply_message in reply_messages:
|
|
|
|
|
|
|
|
reply_message = self.reply_message_postprocess(reply_message)
|
|
|
|
|
|
|
|
asyncio.run(self.post_to_human(reply_message))
|
|
|
|
|
|
|
|
logger.info(f'输出回复: {reply_message}')
|
|
|
|
|
|
|
|
# is_speaking此时是False,需要等一段时间再查询
|
|
|
|
|
|
|
|
time.sleep(0.5)
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
# 用户交互队列为空,输出系统文案
|
|
|
|
time.sleep(0.1)
|
|
|
|
# time.sleep(1)
|
|
|
|
is_not_speaking_count += 1
|
|
|
|
system_messages = self.live_chat_config.system_messages
|
|
|
|
if is_not_speaking_count == 20:
|
|
|
|
reply_message = system_messages[self.system_message_index]
|
|
|
|
logger.info('连续20次请求Livetalking未在说话,开始回复')
|
|
|
|
self.system_message_index += 1
|
|
|
|
# 调用Livetalking说话
|
|
|
|
if self.system_message_index >= len(system_messages):
|
|
|
|
# 判断response_queue是否为空,如果不为空,则取出回复内容并调用livetalking,否则从数据库中取出备用系统文案
|
|
|
|
self.system_message_index = 0
|
|
|
|
reply_message = ''
|
|
|
|
llm_prompt = self.live_chat_config.refine_system_message_prompt.format(content=reply_message)
|
|
|
|
if not self.response_queue.empty():
|
|
|
|
reply_messages = self._llm(llm_prompt, True)
|
|
|
|
reply_message = self.response_queue.get()
|
|
|
|
for reply_message in reply_messages:
|
|
|
|
reply_message = self.reply_message_postprocess(reply_message)
|
|
|
|
reply_message = self.reply_message_postprocess(reply_message)
|
|
|
|
else:
|
|
|
|
asyncio.run(self.post_to_human(reply_message))
|
|
|
|
reply_message_data = self.live_chat_config.next_reply_message
|
|
|
|
logger.info(f'输出系统文案: {reply_message}')
|
|
|
|
if not reply_message_data:
|
|
|
|
time.sleep(1)
|
|
|
|
logger.info('备用系统文案已用完,重新生成备用系统文案')
|
|
|
|
|
|
|
|
self.generate_messages(0)
|
|
|
|
|
|
|
|
self.generate_messages(1)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
reply_message, _id = reply_message_data
|
|
|
|
|
|
|
|
# 说完之后把状态改为1
|
|
|
|
|
|
|
|
logger.info(f'更新备用系统文案id:{_id}状态为: 1')
|
|
|
|
|
|
|
|
self.live_chat_config.update_next_reply_status(1, _id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# asyncio.run(self.post_to_human(reply_message))
|
|
|
|
|
|
|
|
logger.info(f'开始播放: {reply_message}')
|
|
|
|
|
|
|
|
self.post_to_human_sync(reply_message)
|
|
|
|
|
|
|
|
is_not_speaking_count = 0
|
|
|
|
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
except Exception:
|
|
|
|
# 发生异常,输出系统文案
|
|
|
|
# 发生异常,输出系统文案
|
|
|
|