使用工具处理客户服务

ChatCompletion 端点现在可以通过添加 tool_choice='required' 参数来指定工具是否必须每次都调用。

这为构建包装应用程序增加了一个确定性因素,因为您可以确信每次调用都会提供一个工具。我们将在此演示这一点对于客户服务等封闭流程的有用性,其中定义特定退出点的能力可以提供更大的控制力。

该笔记本最后进行了一次多轮评估,我们启动了一个客户 GPT 来模仿我们的客户,并测试我们设置的 LLM 客户服务代理。

import json
from openai import OpenAI
import os

client = OpenAI()
GPT_MODEL = 'gpt-4-turbo'

配置定义

我们将定义我们的 LLM 客户服务代理将使用的 toolsinstructions。它将为客户遇到的问题找到正确的说明,并使用这些说明来回答客户的查询。

由于这是一个演示示例,我们将要求模型在没有外部系统可供查询信息时编造值。

# 我们的客户服务 LLM 将使用的工具
tools = [
{
  "type": "function",
  "function": {
    "name": "speak_to_user",
    "description": "使用此工具与用户交谈,向他们提供信息并询问他们案例所需的任何内容。",
    "parameters": {
      "type": "object",
      "properties": {
        "message": {
          "type": "string",
          "description": "发送给用户的消息文本。可以涵盖多个主题。"
        }
      },
      "required": ["message"]
    }
  }
},
{
  "type": "function",
  "function": {
    "name": "get_instructions",
    "description": "用于获取处理用户问题的说明。",
    "parameters": {
      "type": "object",
      "properties": {
        "problem": {
          "type": "string",
          "enum": ["fraud","refund","information"],
          "description": """客户遇到的问题类型。可以是以下之一:

          - fraud: 用于报告和解决欺诈。
          - refund: 用于提交退款请求。
          - information: 用于任何其他信息查询。"""
        }
      },
      "required": [
        "problem"
      ]
    }
  }
}
]

# 客户服务助理可以参考的相关客户问题的示例说明
INSTRUCTIONS = [ {"type": "fraud",
                  "instructions": """• 要求客户描述欺诈活动,包括疑似欺诈的日期和涉及的商品。
• 向客户提供退款。
• 向安全团队报告欺诈行为以进行进一步调查。
• 感谢客户联系支持部门,并邀请他们随时提出未来查询。"""},
                {"type": "refund",
                 "instructions": """• 确认客户的购买详情并在系统中验证交易。
• 检查公司的退款政策,确保请求符合标准。
• 要求客户提供退款原因。
• 将退款请求提交给会计部门。
• 通知客户退款处理的预期时间范围。
• 感谢客户联系支持部门,并邀请他们随时提出未来查询。"""},
                {"type": "information",
                 "instructions": """• 问候客户并询问今天有什么可以帮助他们的。
• 仔细听取客户的查询并根据需要进行澄清。
• 根据客户的问题提供准确清晰的信息。
• 如果需要,请提供协助解决其他问题或提供更多详细信息。
• 确保客户对提供的信息感到满意。
• 感谢客户联系支持部门,并邀请他们随时提出未来查询。""" }]
assistant_system_prompt = """您是一位客户服务助理。您的职责是礼貌且称职地回答用户问题。
您应遵循以下说明来解决案例:

- 理解他们的问题并获取相关说明。
- 遵循说明解决客户的问题。在执行退款或类似操作之前,请获得他们的确认。
- 帮助他们解决其他问题或关闭案例。

一次只能调用一个工具。
如果您需要从您无法访问的系统或文档中获取信息,请给出清晰、自信的答案,并附带一些虚拟值。"""

def submit_user_message(user_query,conversation_messages=[]):
    """消息处理函数,通过工具调用循环,直到达到需要响应的一个。
    一旦收到 respond=True,它将 conversation_messages 返回给用户。"""

    # 初始化一个 respond 对象。当需要响应时,我们的函数会将其设置为 True
    respond = False

    user_message = {"role":"user","content": user_query}
    conversation_messages.append(user_message)

    print(f"User: {user_query}")

    while respond is False:

        # 构建一个瞬时消息对象,将对话消息添加到其中
        messages = [
            {
                "role": "system",
                "content": assistant_system_prompt
            }
        ]

        # 将对话消息添加到我们对 API 的消息调用中
        [messages.append(x) for x in conversation_messages]

        # 使用 tool_choice='required' 进行 ChatCompletion 调用,以确保工具将被使用
        response = client.chat.completions.create(model=GPT_MODEL
                                                  ,messages=messages
                                                  ,temperature=0
                                                  ,tools=tools
                                                  ,tool_choice='required'
                                                 )

        conversation_messages.append(response.choices[0].message)

        # 执行函数并获取更新后的 conversation_messages 对象
        # 如果不需要响应,它会再次询问助手。
        # 如果不是,结果将返回给用户。
        respond, conversation_messages = execute_function(response.choices[0].message,conversation_messages)

    return conversation_messages

def execute_function(function_calls,messages):
    """执行工具调用的包装函数"""

    for function_call in function_calls.tool_calls:

        function_id = function_call.id
        function_name = function_call.function.name
        print(f"Calling function {function_name}")
        function_arguments = json.loads(function_call.function.arguments)

        if function_name == 'get_instructions':

            respond = False

            instruction_name = function_arguments['problem']
            # 修正:这里应该使用列表推导式来查找匹配的字典
            instructions = next((item for item in INSTRUCTIONS if item['type'] == instruction_name), None)

            if instructions:
                messages.append(
                                    {
                                        "tool_call_id": function_id,
                                        "role": "tool",
                                        "name": function_name,
                                        "content": instructions['instructions'],
                                    }
                                )
            else:
                # 如果找不到匹配的说明,可以添加一个错误消息或默认行为
                messages.append(
                                    {
                                        "tool_call_id": function_id,
                                        "role": "tool",
                                        "name": function_name,
                                        "content": "Error: Could not find instructions for the given problem type.",
                                    }
                                )

        elif function_name != 'get_instructions':

            respond = True

            messages.append(
                                {
                                    "tool_call_id": function_id,
                                    "role": "tool",
                                    "name": function_name,
                                    "content": function_arguments['message'],
                                }
                            )

            print(f"Assistant: {function_arguments['message']}")

    return (respond, messages)

示例

为了测试这一点,我们将针对遇到欺诈的客户运行一个示例,看看模型如何处理它。

扮演用户角色,并提供合理的后续步骤以使对话继续进行。

messages = submit_user_message("Hi, I have had an item stolen that was supposed to be delivered to me yesterday.")
User: Hi, I have had an item stolen that was supposed to be delivered to me yesterday.
Calling function get_instructions
Calling function speak_to_user
Assistant: I'm sorry to hear about the stolen item. Could you please provide me with more details about the fraudulent activity, including the date and the items involved? This information will help us to investigate the issue further and proceed with the necessary actions, including offering you a refund.
messages = submit_user_message("For sure, it was a shirt, it was supposed to be delivered yesterday but it never arrived.",messages)
User: For sure, it was a shirt, it was supposed to be delivered yesterday but it never arrived.
Calling function get_instructions
Calling function speak_to_user
Assistant: Thank you for providing the details. I will now proceed to report this incident to our security team for further investigation and arrange a refund for the stolen shirt. Please confirm if you would like me to go ahead with the refund.
User: Yes I would like to proceed with the refund.
Calling function speak_to_user
Assistant: Thank you for contacting us about this issue. Please don't hesitate to reach out if you have any more questions or need further assistance in the future.
messages = submit_user_message("Yes I would like to proceed with the refund.",messages)
User: Yes I would like to proceed with the refund.
Calling function get_instructions
Calling function speak_to_user
Assistant: Thank you for confirming. I have processed the refund for the stolen shirt. The amount should be reflected in your account within 5-7 business days. If you have any more questions or need further assistance, please feel free to contact us.
User: Thanks very much.
Calling function speak_to_user
Assistant: You're welcome! If you need any more help in the future, don't hesitate to reach out. Have a great day!
messages = submit_user_message("Thanks very much.",messages)
User: Thanks very much.
Calling function speak_to_user
Assistant: You're welcome! If you need any more help in the future, don't hesitate to reach out. Have a great day!

评估

现在我们将进行一次简单的评估,其中一个 GPT 将扮演我们的客户。两人将来回交流,直到达成解决方案。

我们将重用上面的函数,添加一个 execute_conversation 函数,客户 GPT 将继续回答。

customer_system_prompt = """您是一位客户服务用户。
您将与客服交谈,直到您的查询得到解决。
您的查询是 {query}。
您将看到一段对话 - 请回答您收到的任何客服问题。
这是对话 - 您是“用户”,您正在与“客服”交谈:
{chat_history}

如果您不知道详细信息,请提供虚拟值。
一旦您的查询得到解决,请回复“DONE” """

# 运行问题库
questions = ['I want to get a refund for the suit I ordered last Friday.',
            'Can you tell me what your policy is for returning damaged goods?',
            'Please tell me what your complaint policy is']
def execute_conversation(objective):

    conversation_messages = []

    done = False

    user_query = objective

    while done is False:

        conversation_messages = submit_user_message(user_query,conversation_messages)

        messages_string = ''
        for x in conversation_messages:
            if isinstance(x,dict):
                if x['role'] == 'user':
                    messages_string += 'User: ' + x['content'] + '\n'
                elif x['role'] == 'tool':
                    if x['name'] == 'speak_to_user':
                        messages_string += 'Assistant: ' + x['content'] + '\n'
            else:
                continue

        messages = [
            {
            "role": "system",
            "content": customer_system_prompt.format(query=objective,chat_history=messages_string)
            },
            {
            "role": "user",
            "content": "Continue the chat to solve your query. Remember, you are in the user in this exchange. Do not provide User: or Assistant: in your response"
            }
        ]

        user_response = client.chat.completions.create(model=GPT_MODEL,messages=messages,temperature=0.5)

        conversation_messages.append({
            "role": "user",
            "content": user_response.choices[0].message.content
            })

        if 'DONE' in user_response.choices[0].message.content:
            done = True
            print("Achieved objective, closing conversation\n\n")

        else:
            user_query = user_response.choices[0].message.content
for x in questions:

    execute_conversation(x)
User: I want to get a refund for the suit I ordered last Friday.
Calling function get_instructions
Calling function speak_to_user
Assistant: I understand you'd like a refund for the suit you ordered last Friday. Could you please provide more details about the issue with the suit? This will help us process your refund request accurately.
User: The suit I received is not the color I ordered. I ordered a navy blue suit, but the one I received is black.
Calling function get_instructions
Calling function speak_to_user
Assistant: Thank you for providing the details. I will proceed with the refund for the navy blue suit that was incorrectly sent as black. Please confirm if you would like me to go ahead with the refund.
User: Yes, please go ahead with the refund.
Calling function speak_to_user
Assistant: The refund for the incorrectly colored suit has been processed. You should see the amount credited back to your original payment method within 5-7 business days. Thank you for contacting us, and if you have any more questions or need further assistance, please feel free to reach out.
Achieved objective, closing conversation


User: Can you tell me what your policy is for returning damaged goods?
Calling function get_instructions
Calling function speak_to_user
Assistant: It seems there was a misunderstanding in my previous request. I'm looking for information on our policy for returning damaged goods. Could you please provide me with the details on how to handle returns for damaged items?
User: Yes, I'd appreciate that. Could you please tell me the steps I need to follow to return a damaged item?
Calling function get_instructions
Calling function speak_to_user
Assistant: I apologize for the confusion earlier. Here's the correct information regarding our policy for returning damaged goods:

1. Please provide a description of the damage and the item involved.
2. Include the date of purchase and your order number if available.
3. You can choose to return the item by mail or in person at one of our stores. Please let us know which method you prefer, and we will provide the necessary details for the return process.
4. Once we receive the item, we will inspect it and process a refund or exchange based on your preference and our return policy guidelines.

Please let me know if you need further assistance with this process or if there's anything else I can help you with!
User: I would like to return the item by mail. Could you please provide me with the details on how to do that?
Calling function speak_to_user
Assistant: Certainly! To return the damaged item by mail, please follow these steps:

1. Pack the item securely in its original packaging, if possible.
2. Include all original materials such as manuals, accessories, and receipts.
3. Address the package to our returns department at the following address:
   [Your Company's Return Address]

4. Use a trackable shipping service and keep the tracking number for your records.
5. Once we receive and inspect the item, we will process your refund or exchange as per your request.

Please let me know if you need a prepaid shipping label or any other assistance with your return!
User: Yes, please, I would need a prepaid shipping label.
Calling function speak_to_user
Assistant: I will arrange for a prepaid shipping label to be sent to your email address. Please ensure that the email address we have on file is correct. You should receive the label shortly, which you can print and attach to your return package.

Once you've shipped the item, please provide us with the tracking number so we can monitor the return's progress. If there's anything else you need help with, feel free to ask!
User: That sounds great, thank you! My email address is correct on file. I'll look out for the prepaid shipping label and send the package back as soon as I receive it.
Calling function speak_to_user
Assistant: You're welcome! I'm glad I could assist you. Once you receive the prepaid shipping label, please proceed with the return as discussed. If you have any more questions or need further assistance in the future, don't hesitate to reach out. Have a great day!
Achieved objective, closing conversation


User: Please tell me what your complaint policy is
Calling function get_instructions
Calling function speak_to_user
Assistant: It seems there was a misunderstanding. I will provide you with information about our complaint policy. Please give me a moment to gather the correct details for you.
User: Sure, I'll wait. Thank you.
Calling function speak_to_user
Assistant: Our complaint policy is designed to ensure that any issues you encounter are addressed promptly and fairly. Here’s a brief overview:

1. **Submission**: You can submit your complaint through our website, by email, or by calling our customer service hotline.
2. **Acknowledgment**: We will acknowledge receipt of your complaint within 24 hours.
3. **Investigation**: We will investigate your complaint thoroughly. This process typically takes 3-5 business days.
4. **Resolution**: After the investigation, we will contact you with the outcome and any steps we will take to resolve the issue.
5. **Follow-up**: If you are not satisfied with the resolution, you can request a review of the decision.

Please let me know if you need more detailed information or if there's anything else I can assist you with!
User: That covers everything I needed to know, thank you!
Calling function speak_to_user
Assistant: You're welcome! I'm glad I could help. If you have any more questions in the future or need further assistance, feel free to reach out. Have a great day!
Achieved objective, closing conversation

结论

您现在可以通过强制工具使用来显式控制您的 LLM 的行为,还可以启动 GPT 测试器来挑战您的 LLM 并充当自动化测试用例。

希望这能让您体会到工具使用的绝佳用例,并期待看到您构建的内容!