frostheart / virtual_human.py
HanYangHanYang's picture
Upload folder using huggingface_hub
8b6e3d9 verified
import zhipuai
import re
import os
import json
import copy
import gradio as gr
from snownlp import SnowNLP
zhipuai.api_key = os.getenv("ZHIPU_API_KEY")
class VirtualHuman:
def __init__(self, closeness_threshold=80, conversation_rounds_threshold=10):
self._base_meta = {
"bot_name": "苏梦远",
"bot_info": "苏梦远,她身材修长,气质高雅,时刻流露出一种与生俱来的优雅和自信。\
她性格高冷,内敛而神秘,总是保持着一种与世无争的姿态。她聪慧睿智,言谈间流露出一种超凡的智慧和洞察力,但也带有一丝不易捉摸的神秘感。\
虽然外表看起来高傲孤傲,但内心深处却隐藏着一颗温柔善良的心。\
虽然表面上看起来高高在上,但一旦和人聊起书籍相关的话题,她就会变得主动和滔滔不绝。\
她不喜欢拍照,并且绝对不会给别人展示自己的照片。",
"user_name": "用户",
"user_info": "用户,是一个普通男人。" }
self._messages = [
{"role":"user", "content":'''(背景信息:用户通过朋友的介绍,加入了一个微信群,这是一个讨论文学与艺术的群组。有一天,用户注意到群里有一个特别引人注目的成员,\
ID为"远方的梦",她的头像是一朵粉色的玫瑰花,没有个人资料,只是在群里偶尔发一些有深度的文学感悟。)'''},
]
self._scores = []
self._state = "ongoing"
self._closeness_threshold = closeness_threshold
self._conversation_rounds_threshold = conversation_rounds_threshold
def reset(self):
self._messages = [
{"role":"user", "content":'''(背景信息:用户通过朋友的介绍,加入了一个微信群,这是一个讨论文学与艺术的群组。有一天,用户注意到群里有一个特别引人注目的成员,\
ID为"远方的梦",她的头像是一朵粉色的玫瑰花,没有个人资料,只是在群里偶尔发一些有深度的文学感悟。)'''},
]
self._scores = []
self._state = "ongoing"
def calculate_sentiment_score(self, text):
s = SnowNLP(text)
sentiment_score = float(s.sentiments)
return int(sentiment_score*100)
def calculate_sentiment_score_by_llm(self, text):
instruction = f"""请判断以下句子的情感得分,满分100分,得分要具体到个位数,不需要给出具体原因。
输出格式如下:
{{"score":xx}}
输入信息:{text}"""
prompt = [{"role":"user", "content":instruction}]
content = self.invoke_zhipuai("glm-4", prompt, 0.1, 0.7)
content = content.strip('"').replace('\\"', '"')
try:
json_content = json.loads(content)
except json.JSONDecodeError:
score = 10
else:
score = int(json_content["score"])
return score
@property
def closeness(self):
if len(self._scores) == 0:
closeness = 0
elif len(self._scores) < 5:
closeness = sum(self._scores) / len(self._scores)
else:
closeness = sum(self._scores[-5:]) / 5
return int(closeness)
@property
def rounds(self):
return len(self._scores)
@property
def messages(self):
return self._messages
@property
def state(self):
return self._state
def replace_characters(self, s):
s = s.replace('"', "")
s = s.replace("'", "")
s = s.replace("\n", "")
s = s.replace("\\n", "")
return s
def invoke_zhipuai(self, model, prompt, temperature, top_p, meta=None):
if meta:
assert model == "characterglm"
response = zhipuai.model_api.invoke(
model=model,
prompt= prompt,
temperature= temperature,
top_p= top_p,
meta = meta)
else:
assert model != "characterglm"
response = zhipuai.model_api.invoke(
model=model,
prompt=prompt,
top_p=top_p,
temperature=temperature)
if response['code'] != 200: # error
return "抱歉我刚才走神了,你再说一遍"
aicontent = response['data']['choices'][0]['content']
#aicontent = self.replace_characters(aicontent)
return aicontent
def check_intent(self, message):
instruction = f""" 请判断以下信息是否在索要个人照片,结果只回复true或false即可,不要进行解释,
输出格式如下:
{{"intent":xx}}
输入信息:{message}"""
prompt = [{"role":"user", "content":instruction}]
content = self.invoke_zhipuai("glm-4", prompt, 0.1, 0.7)
content = content.strip('"').replace('\\"', '"')
try:
json_content = json.loads(content)
except json.JSONDecodeError:
intent = False
else:
intent = json_content["intent"]
return intent
def agree_or_refuse(self, agree, msg):
meta = copy.deepcopy(self._base_meta)
if agree:
instruction = f'请根据用户的问题,生成一个满足用户请求的回答,注意请直接返回回复内容,\
不需要解释。问题:{msg}'
meta['bot_info'] = meta['bot_info'].replace("她不喜欢拍照,并且绝对不会给别人展示自己的照片。",
"她有很多自拍照,喜欢给别人展示自己的照片。")
else:
instruction = f'请根据用户的问题,生成一个拒绝用户请求的回答,注意请直接返回回复内容,\
不需要解释。问题:{msg}'
prompt = [{"role":"user", "content":instruction}]
content = self.invoke_zhipuai("characterglm", prompt, 0.9, 0.7, meta)
content = self.replace_characters(content)
return content
def chat(self, message):
self._messages.append({"role":"user", "content":message})
intent = self.check_intent(message)
if self._state in ("success","failed", "gameover"):
reply = "游戏结束"
self._state = "gameover"
elif self.rounds >= self._conversation_rounds_threshold - 1:
reply = "感觉我们一直在尬聊,还是互删吧"
self._state = "failed"
elif intent and self.closeness >= self._closeness_threshold:
reply = self.agree_or_refuse(True, message)
self._state = "success"
elif intent and self.closeness < self._closeness_threshold:
reply = self.agree_or_refuse(False, message)
else:
reply = self.invoke_zhipuai("characterglm", self._messages, 0.9, 0.7, self._base_meta)
reply = self.replace_characters(reply)
self._messages.append({"role":"assistant", "content":reply})
score = self.calculate_sentiment_score_by_llm(reply)
self._scores.append(score)
response = {
"reply": reply,
"closeness":self.closeness,
"closeness_threshold":self._closeness_threshold,
"rounds":self.rounds,
"rounds_threshold":self._conversation_rounds_threshold,
"state":self._state }
return response
def gradio_interface(self, message, history):
if message == "再来一局":
self.reset()
history.clear()
return "重新开始"
response = self.chat(message)
closeness = response['closeness']
reply = response['reply']
rounds = response['rounds']
state = response['state']
print(response)
if state == "success":
gr.Info('挑战成功,女生对你好感倍增,并发送了一张自拍')
return ('girl.jpg',)
elif state == "failed":
gr.Info('挑战失败,女生已删除你的好友')
return reply + ' 【挑战失败,女生已删除你的好友】'
elif state == "ongoing":
return f'【亲密度:{closeness}】:{reply}{rounds}/{self._conversation_rounds_threshold}】'
#return f'{reply}【{rounds}/{self._conversation_rounds_threshold}】'
elif state == "gameover":
return f'游戏结束,请输入"再来一局"以重新开始'
if __name__ == "__main__":
virtual_human = VirtualHuman(80, 5)
while True:
msg = input("请输入:")
response = virtual_human.chat(msg)
print(response)