{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 函数调用(Function Calling)\n", "\n", "文心一言提供函数调用功能,模型根据用户需求以及对函数的描述确定何时以及如何调用函数。函数调用功能的典型使用流程如下:\n", "\n", "(1) 用户提供对一组函数的名称、功能、请求参数(输入参数)和响应参数(返回值)的描述; \n", "\n", "(2) 模型根据用户需求以及函数描述信息,智能确定是否应该调用函数、调用哪一个函数、以及在调用该函数时需要如何设置输入参数; \n", "\n", "(3) 用户根据模型的提示调用函数,并将函数的响应传递给模型; \n", "\n", "(4) 模型综合对话上下文信息,以自然语言形式给出满足用户需求的回答。\n", "\n", "借由函数调用,用户可以从大模型获取结构化数据,进而利用编程手段将大模型与已有的内外部API结合以构建应用。\n", "\n", "在ERNIE Bot SDK中,erniebot.ChatCompletion.create接口提供函数调用功能。关于该接口的更多详情请参考[ChatCompletion API文档](../../docs/api_reference/chat_completion.md)。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "| 字段名称 | 数据类型 | 字段描述 |\n", "| :-------------- | ---------- | ---------------------------------- |\n", "| name | str | API(函数)名称 |\n", "| description | str | API(函数)功能描述 |\n", "| parameters | dict | API(函数)输入参数(以dict输入) |\n", "| responses | dict | API(函数)输出结果(以dict返回) |\n", "\n", "
\n", "\n", "parameter 和 responses 的相关字段情况如下\n", "\n", "\n", "| 字段名 | 数据类型 | 描述 |\n", "| :------------- | ----------- | ----------------------------------------------------- |\n", "| type | str | 此层级一般为object |\n", "| properties | dict | 变量的详细情况,键为变量名,值为变量的相关信息(dict),该字典详见下表 |\n", "| required | List[str] | 函数的输入参数中必须含有的变量,responses不含此字段 |\n", "\n", "\n", "对于properties中的变量的字典,相关的键值对如下\n", "\n", "\n", "| properties中的变量字典的元素 | 是否必须 | 描述 |\n", "| :----------------------------- | ---------- | ---------------------- |\n", "| type | Required | 此层级一般为string,也可以使用integral等,但是因为整体为文本返回,差别不是特别大,使用时请注意函数内部转换 |\n", "| description | Optional | 该变量的描述 |\n", "| enum | Optional | 该变量的可选参数 |\n", "| default | Optional | 该变量的默认参数 |\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "以下是一个简单的使用示例" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 1 \n", "用户提供对一组函数的名称、功能、请求参数(输入参数)和响应参数(返回值)的描述(可使用以下类进行统一管理)。 " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "functions = [\n", " {\n", " 'name': 'add_function',\n", " 'description': '计算两个数的和',\n", " 'parameters': {\n", " 'type': 'object',\n", " 'properties': {\n", " 'a': {'type': 'string', 'description': '第一个数'},\n", " 'b': {'type': 'string', 'description': '第二个数'}\n", " },\n", " 'required': ['a', 'b']\n", " },\n", " 'responses': {'type': 'object', 'properties': {}}\n", " },\n", " {\n", " 'name': 'minus_function',\n", " 'description': '计算两个数的差',\n", " 'parameters': {\n", " 'type': 'object',\n", " 'properties': {\n", " 'a': {'type': 'string', 'description': '第一个数'},\n", " 'b': {'type': 'string', 'default': '1.0'}\n", " },\n", " 'required': ['a']\n", " },\n", " 'responses': {'type': 'object', 'properties': {}}\n", " }\n", " ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 2\n", "模型根据用户需求以及函数描述信息,智能确定是否应该调用函数、调用哪一个函数、以及在调用该函数时需要如何设置输入参数; \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在有了添加完function之后,即可调用function calling功能让大模型进行思考以及决策\n", "function call功能中返回的response的相关字段为\n", "\n", "| name | thoughts | arguments |\n", "| :----------- | -------------- | ------------ |\n", "| 函数名称 | 模型思考过程 | 函数的输入 |\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'name': 'add_function', 'thoughts': '用户的问题是关于1加2的计算,我可以使用add_function工具来计算这两个数的和。', 'arguments': '{\"a\":\"1\",\"b\":\"2\"}'}\n" ] } ], "source": [ "import erniebot\n", "from typing import List\n", "\n", "erniebot.api_type = 'aistudio'\n", "erniebot.access_token = ''\n", "\n", "response = erniebot.ChatCompletion.create(\n", " model='ernie-bot-4',\n", " messages=[{\n", " 'role': 'user',\n", " 'content': \"1加2等于多少\",\n", " }, ],\n", " functions = functions,\n", ")\n", "function_call = response.function_call\n", "print(function_call)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 3\n", "用户根据模型的提示调用函数,并将函数的响应传递给模型; " ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.0" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function_return = eval(response.get_result()['name'])(**eval(response.get_result()['arguments']))\n", "function_return" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Step 4[Option]\n", "模型综合对话上下文信息,以自然语言形式给出满足用户需求的回答。\n", "\n", "这一步不是必须的,用户可以不经过此步骤直接输出答案,这一步主要针对有需要再使用大模型进行进一步提炼、总结插件返回结果的场景,比如搜索插件、代码执行插件、计算器插件等别的插件。" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1加2的和是3.0。如果您还有其他问题或需要我进行其他计算,请随时告诉我。\n" ] } ], "source": [ "chat_messages = [\n", " {\n", " 'role': 'user',\n", " 'content': \"1加2等于多少\",\n", " },\n", " {\n", " 'role': 'assistant',\n", " 'content': None,\n", " 'function_call': function_call,\n", " },\n", " {\n", " 'role': 'function',\n", " 'name': function_call['name'],\n", " 'content': function_return,\n", " } \n", "]\n", "\n", "response = erniebot.ChatCompletion.create(\n", " model='ernie-bot-4',\n", " messages=chat_messages,\n", " functions=functions,\n", ")\n", "print(response.get_result())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Final\n", "最终整体展示以及调用(整合了Step2-Step4),可以通过以下函数进行整体的串型调用执行,获取最终想要的结果。" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[Thought]: 用户想要知道512和798的和。我可以使用add_function工具来计算这两个数的和。\n", "[函数调用]结果计算中...\n", "[回传]最终结果生成中...\n", "[最终回答] 512加798的结果是1310。如果你还有其他问题,随时告诉我。\n" ] }, { "data": { "text/plain": [ "'512加798的结果是1310。如果你还有其他问题,随时告诉我。'" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import time\n", "\n", "def function_call(\n", " query: str,\n", " functions: List[str],\n", ") -> str:\n", " messages = [\n", " {\n", " 'role': 'user',\n", " 'content': query,\n", " }\n", " ]\n", " # Step 2: 模型选择使用哪个函数/API\n", " response = erniebot.ChatCompletion.create(\n", " model='ernie-bot-4',\n", " messages = messages,\n", " functions = functions,\n", " )\n", "\n", " assert hasattr(response, 'function_call')\n", " function_call = response.function_call\n", "\n", " assert hasattr(response.get_result(), 'thoughts')\n", " print(\"[Thought]:\",response.get_result()['thoughts'])\n", "\n", " messages.append(\n", " {\n", " 'role': 'assistant',\n", " 'content': None,\n", " 'function_call': function_call,\n", " }\n", " )\n", " # Step 3: 执行函数调用\n", " print('[函数调用]结果计算中...')\n", " function_return = eval(response.get_result()['name'])(**eval(response.get_result()['arguments']))\n", "\n", " messages.append(\n", " {\n", " 'role': 'function',\n", " 'name': function_call['name'],\n", " 'content': function_return,\n", " }\n", " )\n", " \n", " time.sleep(1) # 防止频繁访问报错\n", "\n", " # Step 4: 回传模型进行修饰[Option]\n", " print('[回传]最终结果生成中...')\n", " response = erniebot.ChatCompletion.create(\n", " model='ernie-bot-4',\n", " messages=messages,\n", " functions=functions,\n", " )\n", " print('[最终回答]',response.result)\n", " return response.result\n", "\n", "function_call(\"512加798等于多少\",functions=functions)" ] } ], "metadata": { "kernelspec": { "display_name": "ernie", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.13" } }, "nbformat": 4, "nbformat_minor": 2 }