LLM应用开发与落地:调用自定义函数

478 阅读8分钟

背景

LLM可以理解人们使用的自然语言了,但是做LLM应用开发,难免还是需要使用一些编程语言开发功能。

举一个智能客服中遇到的一个小场景。用户被禁言了,到智能客服询问禁言原因和发泄情绪。禁言原因都是存储在客户的内部系统中,这个时候我们就需要识别到用户的信息,然后调用客户的内部系统的接口获得禁言原因,然后再告诉用户禁言的原因并安抚用户情绪。

原理

LLM调用自定义函数的原理很简单,主要有四步:

一、把用户输入提交给LLM的时候,同时告诉LLM自定义函数的描述,函数的功能,参数和返回。

二、LLM根据输入和自定义函数的描述判断是否应该调用某个自定义函数来解决这个问题。如果LLM认为应该调用某个自定义函数,LLM就会返回该函数的名称和参数(参数从用户输入中提取)。

三、使用LLM返回函数名称和参数,调用本地函数,并获取函数的返回。

四、把本地调用的函数名称和返回提交给LLM,LLM会根据函数返回转化成自然语言输出。

举例

以用户禁言查询举例的话,上边的四步拆解下来就是:

一、把用户输入提交给LLM的时候,同时告诉LLM自定义函数的描述,函数的功能,参数和返回。

具体到这个例子中就是:

1.1 用户输入“为什么我被禁言了?我又没有干什么呀,我的用户id是666666”。

1.2 定义函数 get_user_banned_reason函数并进行描述。

1.3 把用户输入和 get_user_banned_reason函数 提交给LLM。

二、LLM根据输入和自定义函数的描述判断是否应该调用某个自定义函数来解决这个问题。如果LLM认为应该调用某个自定义函数,LLM就会返回该函数的名称和参数(参数从用户输入中提取)。

2.1 LLM 返回表示要调用函数 get_user_banned_reason 函数, 参数为 666666。

三、使用LLM返回函数名称和参数,调用本地函数,并获取函数的返回。

3.1 调用函数 get_user_banned_reason(666666) 获得禁言结果为 “涉黄”。

四、把本地调用的函数名称和返回提交给LLM,LLM会根据函数返回转化成自然语言输出。

4.1 把函数名 get_user_banned_reason 和返回的结果 “涉黄” 提交给LLM, LLM返回“您被禁言的原因是发言有涉黄嫌疑。请注意在平台上文明用语,遵守相关规定。如有疑问或需要帮助,请联系平台客服。”

实践

自定义函数如下:

def get_user_banned_reason(user_id:int):
    """获取用户被禁言的原因"""
    # 真实落地时,这里请求客户内部系统http接口,获取用户禁言原因
    user_banned_reason = {
        123456: "发言有涉黄嫌疑",
        666666: "涉嫌辱骂Ta人",
    }
​
    if user_id in user_banned_reason:
        return json.dumps({"reason":user_banned_reason[user_id]})
    else:
        return json.dumps({"reason":"您没有被禁言"})

测试例子1

输入:我为什么被禁言了?我的id是123456

输出:您被禁言的原因是发言有涉黄嫌疑。请注意在平台上文明用语,遵守相关规定。如有 疑问或需要帮助,请联系平台客服。

详细输出如下:

step 1. 把用户输入发送给llm,并且告诉llm有哪些函数可以调用
step 2. llm返回表示需要调用函数get_user_banned_reason,参数为{'user_id': 123456}
step 3. 调用自定义函数get_user_banned_reason(123456),结果:{"reason": "发言有涉黄嫌疑"}
[{'role': 'user', 'content': '我为什么被禁言了?我的id是123456'}, {'role': 'assistant', 'function_call': {'name': 'get_user_banned_reason', 'arguments': '{"user_id":123456}'}}, {'role': 'function', 'name': 'get_user_banned_reason', 'content': '{"reason": "发言有涉黄嫌疑"}'}]
step 4. 把函数调用和函数返回信息添加到消息中发送给llm,llm返回: 您被禁言的原因是发言有涉黄嫌疑。请注意在平台上文明用语,遵守相关规定。如有
疑问或需要帮助,请联系平台客服。

测试例子2

输入:我为什么被禁言了?我的id是666666

输出:您被禁言的原因是涉嫌辱骂他人。请注意文明用语,共同维护网络健康。如有疑问,请联系客服。

详细输出如下:

step 1. 把用户输入发送给llm,并且告诉llm有哪些函数可以调用
step 2. llm返回表示需要调用函数get_user_banned_reason,参数为{'user_id': 666666}
step 3. 调用自定义函数get_user_banned_reason(666666),结果:{"reason": "涉嫌辱骂Ta人"}
[{'role': 'user', 'content': '我为什么被禁言了?我的id是666666'}, {'role': 'assistant', 'function_call': {'name': 'get_user_banned_reason', 'arguments': '{"user_id":666666}'}}, {'role': 'function', 'name': 'get_user_banned_reason', 'content': '{"reason": "涉嫌辱骂Ta人"}'}]
step 4. 把函数调用和函数返回信息添加到消息中发送给llm,llm返回: 您被禁言的原因是涉嫌辱骂他人。请注意文明用语,共同维护网络健康。如有疑问,请联系客服。

测试例子3

输入:我为什么被禁言了?我的id是777777

输出:经过查询,您并没有被禁言。如果您在使用过程中遇到任何问题,请随时联系我们。

详细输出如下:

step 1. 把用户输入发送给llm,并且告诉llm有哪些函数可以调用
step 2. llm返回表示需要调用函数get_user_banned_reason,参数为{'user_id': 777777}
step 3. 调用自定义函数get_user_banned_reason(777777),结果:{"reason": "您没有被禁言"}
[{'role': 'user', 'content': '我为什么被禁言了?我的id是777777'}, {'role': 'assistant', 'function_call': {'name': 'get_user_banned_reason', 'arguments': '{"user_id":777777}'}}, {'role': 'function', 'name': 'get_user_banned_reason', 'content': '{"reason": "您没有被禁言"}'}]
step 4. 把函数调用和函数返回信息添加到消息中发送给llm,llm返回: 经过查询,您并没有被禁言。如果您在使用过程中遇到任何问题,请随时联系我们。

我目前正在开发一款基于自研的LLM agent framework 的智能客服产品,它具有私有知识问答,意图引导、信息收集、情绪安抚、内部系统融合、LUI与GUI 融合、人工接管、数据分析与洞察、异常监控等功能。

欢迎对prompt编写、LLM应用开发、智能客服产品、AI应用落地等等感兴趣的朋友加我微信,一起交流,共同前行。

想自己验证的,可以使用下边的demo代码,我使用的是百度的ernie-bot-4,替换自己百度千帆平台的ak,sk就可以运行:

import qianfan
import json
import os
​
# 使用安全认证AK/SK鉴权,通过环境变量方式初始化;替换下列示例中参数,安全认证Access Key替换your_iam_ak,Secret Key替换your_iam_sk
os.environ["QIANFAN_ACCESS_KEY"] = "your_iam_ak"
os.environ["QIANFAN_SECRET_KEY"] = "your_iam_sk"def get_user_banned_reason(user_id:int):
    """获取用户被禁言的原因"""
    # 真实落地时,这里请求客户内部系统http接口,获取用户禁言原因
    user_banned_reason = {
        123456: "发言有涉黄嫌疑",
        666666: "涉嫌辱骂Ta人",
    }
​
    if user_id in user_banned_reason:
        return json.dumps({"reason":user_banned_reason[user_id]})
    else:
        return json.dumps({"reason":"您没有被禁言"})
​
available_functions = [
    {
        "name": "get_user_banned_reason",
        "description": "获取用户被禁言的原因",
        "parameters": {
            "type": "object",
            "properties": {
                "user_id": {
                    "type": "int",
                    "description": "用户id",
                },
            },
            "required": ["user_account_id"],
        },
        "responses": {
            "type": "object",
            "properties": {
                "reason": {
                    "type": "string",
                    "description": "用户被禁言的原因",
                }
            }
        }
    }
]
​
print("step 1. 把用户输入发送给llm,并且告诉llm有哪些函数可以调用")
chat_comp = qianfan.ChatCompletion()
system_prompt = "你是一名善用工具解决问题的客服"
messages = [{"role": "user", "content": "我为什么被禁言了?我的id是777777"}] 
resp = chat_comp.do(model="ERNIE-Bot-4",
                    system=system_prompt,
                    temperature=0.0001,
                    messages=messages,
                    functions=available_functions
                    )
​
​
if resp.body.get("function_call") and resp.body.get("finish_reason") == "function_call":
    func_info = resp.body.get("function_call")
    func_name = func_info["name"]
    func_args = json.loads(func_info["arguments"])
    print(f"step 2. llm返回表示需要调用函数{func_name},参数为{func_args}")
​
    # message 的数量必须为奇数,role为user或者function的消息 与 role为asistant的消息交替出现,所以要把assistant的函数返回添加回去
    messages.append({"role":"assistant", "function_call":{"name":func_name, "arguments":func_info["arguments"]}})
​
    func_result = globals()[func_name](user_id=func_args.get("user_id"))    
    print(f'step 3. 调用自定义函数{func_name}({func_args.get("user_id")}),结果:{func_result}')
​
    messages.append({"role":"function", "name":func_name, "content":func_result})
    print(messages)
    resp = chat_comp.do(model="ERNIE-Bot-4",
                        system=system_prompt,
                        temperature=0.0001,
                        messages=messages,
                        functions=available_functions)
    print(f'step 4. 把函数调用和函数返回信息添加到消息中发送给llm,llm返回: {resp.body["result"]}')
​
else:
    print(resp.body["result"])
​
​