【使用LLM搭建系统】7 搭建一个带评估的端到端问答系统
一、环境配置
pip install channels==3.0.5
# 配置 OpenAI KEY
import os
from zhipuai import ZhipuAI
import sys
sys.path.append('../..')
# 使用英文 Prompt 的工具包
import utils_en
# 使用中文 Prompt 的工具包
import utils_zhkey = "f5cd91f2528fed334b9dfd75015791c3.GuLdvM9tXWrGQnAg"
client = ZhipuAI(api_key = key)
def get_completion_from_messages(messages, model="glm-3-turbo", temperature=0, max_tokens=500):'''封装一个访问 OpenAI GPT3.5 的函数参数: messages: 这是一个消息列表,每个消息都是一个字典,包含 role(角色)和 content(内容)。角色可以是'system'、'user' 或 'assistant’,内容是角色的消息。model: 调用的模型,默认为 gpt-3.5-turbo(ChatGPT),有内测资格的用户可以选择 gpt-4temperature: 这决定模型输出的随机程度,默认为0,表示输出将非常确定。增加温度会使输出更随机。max_tokens: 这决定模型输出的最大的 token 数。'''response = client.chat.completions.create(model=model,messages=messages,temperature=temperature, # 这决定模型输出的随机程度max_tokens=max_tokens, # 这决定模型输出的最大的 token 数)return response.choices[0].message.content
'''
注意:限于模型对中文理解能力较弱,中文 Prompt 可能会随机出现不成功,可以多次运行;也非常欢迎同学探究更稳定的中文 Prompt
'''
def process_user_message_ch(user_input, all_messages, debug=True):"""对用户信息进行预处理参数:user_input : 用户输入all_messages : 历史信息debug : 是否开启 DEBUG 模式,默认开启"""# 分隔符delimiter = "```"# 第一步: 使用 OpenAI 的 Moderation API 检查用户输入是否合规或者是一个注入的 Prompt
# response = openai.Moderation.create(input=user_input)
# moderation_output = response["results"][0]# # 经过 Moderation API 检查该输入不合规
# if moderation_output["flagged"]:
# print("第一步:输入被 Moderation 拒绝")
# return "抱歉,您的请求不合规"# 如果开启了 DEBUG 模式,打印实时进度if debug: print("第一步:输入通过 Moderation 检查")# 第二步:抽取出商品和对应的目录,类似于之前课程中的方法,做了一个封装category_and_product_response = utils_zh.find_category_and_product_only(user_input, utils_zh.get_products_and_category())#print(category_and_product_response)# 将抽取出来的字符串转化为列表category_and_product_list = utils_zh.read_string_to_list(category_and_product_response)#print(category_and_product_list)if debug: print("第二步:抽取出商品列表")# 第三步:查找商品对应信息product_information = utils_zh.generate_output_string(category_and_product_list)if debug: print("第三步:查找抽取出的商品信息")# 第四步:根据信息生成回答system_message = f"""您是一家大型电子商店的客户服务助理。\请以友好和乐于助人的语气回答问题,并提供简洁明了的答案。\请确保向用户提出相关的后续问题。"""# 插入 messagemessages = [{'role': 'system', 'content': system_message},{'role': 'user', 'content': f"{delimiter}{user_input}{delimiter}"},{'role': 'assistant', 'content': f"相关商品信息:\n{product_information}"}]# 获取 GPT3.5 的回答# 通过附加 all_messages 实现多轮对话final_response = get_completion_from_messages(all_messages + messages)if debug:print("第四步:生成用户回答")# 将该轮信息加入到历史信息中all_messages = all_messages + messages[1:]# 第五步:基于 Moderation API 检查输出是否合规
# response = openai.Moderation.create(input=final_response)
# moderation_output = response["results"][0]# # 输出不合规
# if moderation_output["flagged"]:
# if debug: print("第五步:输出被 Moderation 拒绝")
# return "抱歉,我们不能提供该信息"# if debug: print("第五步:输出经过 Moderation 检查")# 第六步:模型检查是否很好地回答了用户问题user_message = f"""用户信息: {delimiter}{user_input}{delimiter}代理回复: {delimiter}{final_response}{delimiter}回复是否足够回答问题如果足够,回答 Y如果不足够,回答 N仅回答上述字母即可"""# print(final_response)messages = [{'role': 'system', 'content': system_message},{'role': 'user', 'content': user_message}]# 要求模型评估回答evaluation_response = get_completion_from_messages(messages)# print(evaluation_response)if debug: print("第六步:模型评估该回答")# 第七步:如果评估为 Y,输出回答;如果评估为 N,反馈将由人工修正答案if "Y" in evaluation_response: # 使用 in 来避免模型可能生成 Yesif debug: print("第七步:模型赞同了该回答.")return final_response, all_messageselse:if debug: print("第七步:模型不赞成该回答.")neg_str = "很抱歉,我无法提供您所需的信息。我将为您转接到一位人工客服代表以获取进一步帮助。"return neg_str, all_messagesuser_input = "请告诉我关于 smartx pro phone 和 the fotosnap camera 的信息。另外,请告诉我关于你们的tvs的情况。"
response,_ = process_user_message_ch(user_input,[])
print(response)
2.2 持续收集用户和助手消息的函数
实现一个可视化界面
import panel as pn # 用于图形化界面
pn.extension()
# 调用中文 Prompt 版本
def collect_messages_ch(debug=False):"""用于收集用户的输入并生成助手的回答参数:debug: 用于觉得是否开启调试模式"""user_input = inp.value_inputif debug: print(f"User Input = {user_input}")if user_input == "":returninp.value = ''global context# 调用 process_user_message 函数#response, context = process_user_message(user_input, context, utils.get_products_and_category(),debug=True)response, context = process_user_message_ch(user_input, context, debug=False)context.append({'role':'assistant', 'content':f"{response}"})panels.append(pn.Row('User:', pn.pane.Markdown(user_input, width=600)))panels.append(pn.Row('Assistant:', pn.pane.Markdown(response, width=600, style={'background-color': '#F6F6F6'})))return pn.Column(*panels) # 包含了所有的对话信息
panels = [] # collect display # 系统信息
context = [ {'role':'system', 'content':"You are Service Assistant"} ] inp = pn.widgets.TextInput( placeholder='Enter text here…')
button_conversation = pn.widgets.Button(name="Service Assistant")interactive_conversation = pn.bind(collect_messages, button_conversation)dashboard = pn.Column(inp,pn.Row(button_conversation),pn.panel(interactive_conversation, loading_indicator=True, height=300),
)dashboard