前言

随着人工智能技术的不断进步,大语言模型(LLM)已成为技术前沿的热点。它们不仅能够理解和生成文本,还能在多种应用场景中实现复杂的交互。本文将深入探讨一段结合了大语言模型能力、意图识别和词槽实体抽取的Python代码,这段代码展示了如何实现智能的多轮问答能力,为用户提供更加丰富和个性化的交互体验。

正文

完整代码

这段代码的核心在于集成了最新的大语言模型(LLM)能力,结合了意图识别和词槽实体抽取的技术,以实现智能的多轮问答。

import json
import re
import requests
from datetime import datetime



url = 'https://api.openai.com/v1/chat/completions'

# 替换为您自己的chatGPT API密钥
api_key = 'sk-xxxxxx'

# 调试日志
debug = False

def check_values_not_empty(json_data):
    """
    检查是否所有元素value都不为空
    """
    # 遍历JSON数据中的每个元素
    for item in json_data:
        # 检查value字段是否为空字符串
        if item.get('value') == '':
            return False  # 如果发现空字符串,返回False
    return True  # 如果所有value字段都非空,返回True


def format_title_value_for_logging(json_data):
    """
    抽取参数名称和value值
    """
    log_strings = []
    for item in json_data:
        title = item.get('title', 'Unknown Title')  # 获取title,如果不存在则使用'Unknown Title'
        value = item.get('value', 'N/A')  # 获取value,如果不存在则使用'N/A'
        log_string = f"Title: {title}, Value: {value}"
        log_strings.append(log_string)
    return '\n'.join(log_strings)


def extract_json_from_string(input_string):
    """
    JSON抽取函数
    返回包含JSON对象的列表
    """
    try:
        # 正则表达式假设JSON对象由花括号括起来
        matches = re.findall(r'\{.*?\}', input_string, re.DOTALL)

        # 验证找到的每个匹配项是否为有效的JSON
        valid_jsons = []
        for match in matches:
            try:
                json_obj = json.loads(match)
                valid_jsons.append(json_obj)
            except json.JSONDecodeError:
                continue  # 如果不是有效的JSON,跳过该匹配项

        return valid_jsons
    except Exception as e:
        print(f"Error occurred: {e}")
        return []


def send_message(message, user_input):
    """
    请求LLM函数
    """
    print('--------------------------------------------------------------------')
    if debug:
        print('用户输入:', message)
    else:
        print('用户输入:', user_input)
    print('----------------------------------')
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }

    data = {
        "model": "gpt-3.5-turbo",
        "messages": [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": f"{message}"}
        ]
    }
    response = requests.post(url, headers=headers, json=data, verify=False)
    if response.status_code == 200:
        answer = response.json()["choices"][0]["message"]['content']
        print('LLM输出:', answer)
        print('--------------------------------------------------------------------')
        return answer
    else:
        print(f"Error: {response.status_code}")
        return None


# 问天气
# 参数:
# 1. 时间time(口语:今天明天、日期:10号、范围:未来一周)
# 2. 地点location(国家省市区)
# todo "transform": "时间格式转换工具?是自己做一个还是用大模型。建议自己开发一个",
dict_weather = [
    {
        "title": "时间",
        "desc": "口语表述:昨天、今天、明天;具体日期:10号、1月1号、2月3日;范围:未来一周、最近十四天",
        "transform": "时间统一转换格式,如果是时间点:yyyy-MM-dd;如果是时间段:yyyy-MM-dd#yyyy-MM-dd",
        "util": "",
        "value": "",
        "required": True
    },
    {
        "title": "地点",
        "desc": "口语表述:建邺区、南京、河北省、江苏南通、上海虹桥、北京朝阳区",
        "transform": "地点统一转换格式为省市区(如果有区)或省市,如南京市建邺区、南京市,如果提取不到市只有省份则不填写value",
        "util": "",
        "value": "",
        "required": True
    }
]

prompt_weather_info_update = """
JSON每个元素代表一个参数信息,我先给你提供一些基本介绍:
'''
title字段是参数名称,如果需要让用户填写该参数时你应该告诉用户你需要的参数名称
desc字段是参数描述,可以做为title字段的补充,更好的引导用户补充参数
transform字段是填写value字段值的格式要求说明
required字段为true表示该元素的value是必填参数,如果value为空则必须让用户补充该参数信息,如果required字段为false表示该参数不是必须补充的字段
'''

需求:
#01 根据用户输入信息提取有用的信息更新到JSON中的value字段并返回更新后的JSON
#02 仅更新value字段,其他字段都原样返回
#03 如果没有可更新的value则原样返回
#04 当前时间为{}

参考示例:
'''
JSON: [
    {{
        "title": "时间",
        "desc": "口语表述:昨天、今天、明天;具体日期:10号、1月1号、2月3日;范围:未来一周、最近十四天",
        "transform": "时间统一转换格式,如果是时间点:yyyy-MM-dd;如果是时间段:yyyy-MM-dd#yyyy-MM-dd",
        "util": "",
        "value": "",
        "required": true
    }},
    {{
        "title": "地点",
        "desc": "口语表述:建邺区、南京、河北省、江苏南通、上海虹桥、北京朝阳区",
        "transform": "地点统一转换格式为省市区(如果有区)或省市,如南京市建邺区、南京市,如果提取不到市只有省份则不填写value",
        "util": "",
        "value": "",
        "required": true
    }}
]
问:今天南京天气怎么样?
答:[
    {{
        "title": "时间",
        "desc": "口语表述:昨天、今天、明天;具体日期:10号、1月1号、2月3日;范围:未来一周、最近十四天",
        "transform": "时间统一转换格式,如果是时间点:yyyy-MM-dd;如果是时间段:yyyy-MM-dd#yyyy-MM-dd",
        "util": "",
        "value": "{}",
        "required": true
    }},
    {{
        "title": "地点",
        "desc": "口语表述:建邺区、南京、河北省、江苏南通、上海虹桥、北京朝阳区",
        "transform": "地点统一转换格式为省市区(如果有区)或省市,如南京市建邺区、南京市,如果提取不到市只有省份则不填写value",
        "util": "",
        "value": "南京",
        "required": true
    }}
]
'''

JSON:{}
问:{}
答:
"""

prompt_weather_query_user = """
JSON每个元素代表一个参数信息,我先给你提供一些基本介绍:
'''
title字段是参数名称,如果需要让用户填写该参数时你应该告诉用户你需要的参数名称
desc字段是参数描述,可以做为title字段的补充,更好的引导用户补充参数
required字段为true表示该元素的value是必填参数,如果value为空则必须让用户补充该参数信息,如果required字段为false表示该参数不是必须补充的字段
'''

需求:
#01 如果有多个未填写value的参数则可以一起向用户提问
#02 value已经填写的参数不用再次提问

参考示例:
'''
问:[
    {{
        "title": "时间",
        "desc": "口语表述:昨天、今天、明天;具体日期:10号、1月1号、2月3日;范围:未来一周、最近十四天",
        "transform": "时间统一转换格式,如果是时间点:yyyy-MM-dd;如果是时间段:yyyy-MM-dd#yyyy-MM-dd",
        "util": "",
        "value": "2022-01-01",
        "required": true
    }},
    {{
        "title": "地点",
        "desc": "口语表述:建邺区、南京、河北省、江苏南通、上海虹桥、北京朝阳区",
        "transform": "地点统一转换格式为省市区(如果有区)或省市,如南京市建邺区、南京市,如果提取不到市只有省份则不填写value",
        "util": "",
        "value": "",
        "required": true
    }}
]
答:请问你想查询天气的地点是什么?
'''

问:{}
答:
"""

# 公积金咨询
# 参数:
# 1. 事件(买房、租房、退休)
# 2. 购买房屋类型(买房事件时需要 经济适用房、新房、二手房)
dict_consult = {
    "event": "",
    "purchase_type": ""
}

prompt_global_purpose = """
有下面多种场景,需要你根据用户输入进行判断
1. 问天气
2. 公积金咨询
3. 其他场景

参考示例:
'''
问:今天天气如何
答:问天气
'''

问:{}
答:
"""

global_purpose = ''


def multi_question(user_input):
    global global_purpose
    global dict_weather
    current_purpose = send_message(prompt_global_purpose.format(user_input), user_input)
    if current_purpose != '其他场景':
        global_purpose = current_purpose
    if global_purpose == '问天气':
        # 先检查本次用户输入是否有信息补充,保存补充后的结果   编写程序进行字符串value值diff对比,判断是否有更新
        current_time = datetime.now().strftime("%Y-%m-%d")
        new_info_json_raw = send_message(prompt_weather_info_update.format(current_time, current_time, json.dumps(dict_weather, ensure_ascii=False), user_input), user_input)
        dict_weather = extract_json_from_string(new_info_json_raw)
        # 判断参数是否已经全部补全
        if check_values_not_empty(dict_weather):
            print('问天气 ------ 参数已完整,详细参数如下')
            print(format_title_value_for_logging(dict_weather))
            print('正在请求天气查询API,请稍后……')
        else:
            str = json.dumps(dict_weather, ensure_ascii=False)
            send_message(prompt_weather_query_user.format(str), user_input)
    elif global_purpose == '公积金咨询':
        pass
    else:
        pass
    pass


def user_input():
    while True:
        question = input("请输入您的问题:")
        multi_question(question)

# test
# multi_question("明天天气怎么样呢")
# multi_question("苏州天气怎么样呢")
# multi_question("苏州明天天气怎么样呢")

user_input()
代码解析

整合大语言模型(LLM):代码通过与一个语言模型API的交互,利用了大语言模型的强大文本生成和理解能力。这使得脚本能够处理复杂的用户查询,并生成合理的响应。

意图识别:通过分析用户的输入,脚本可以识别用户的意图(例如查询天气或获取公积金咨询),这是通过向LLM发送特定的提示(prompt)实现的。

词槽实体抽取:代码能够从用户输入中提取关键信息(如时间和地点),并将这些信息填充到预定义的JSON结构中。这是通过正则表达式和JSON操作实现的。

多轮交互:脚本支持多轮交互,能够根据用户输入的不同,动态地提问和响应,为用户提供连贯且自然的对话体验。

代码的实用性

这个脚本不仅展示了如何在Python中处理复杂的数据结构和执行HTTP请求,还展示了大语言模型在实际应用中的巨大潜力。它为开发者提供了一个实用的框架,用于构建可以理解和响应人类语言的智能应用程序。LLM+场景识别+词槽实体抽取,实现人机交互多轮会话,遥遥领先~

结尾

我们可以看到大语言模型在理解和生成自然语言方面的巨大潜力。结合意图识别和词槽实体抽取,它能够实现复杂且自然的多轮对话。这种技术的应用范围非常广泛,从简单的信息查询到复杂的交互式任务都能得到有效的支持。

希望这篇文章能够启发你探索大语言模型和智能问答系统的更多可能性。如果你觉得这篇文章有用,别忘了点赞和收藏!作者gallonyin,持续关注AI自动化。