Press "Enter" to skip to content

FitBot – 一款健身聊天机器人代理

如何创建一个利用OpenAI函数调用的聊天机器人代理

Gary Butterfield在Unsplash上的照片

介绍

在健康意识日益增强、追求平衡生活方式成为普遍愿望的时代,营养无疑是一个中心支柱。

然而,饮食计划的复杂性和庞大的营养数据常常成为我们实现平衡的障碍。一个常见的情况是糖尿病患者面临的情况,他们需要持续准确的营养指导来有效管理血糖水平。如果我们能够在数字化的世界中获得个性化的营养助手,这将是一种变革性的体验。

在这种背景下,利用技术来辅助营养指导不仅有益,而且是必要的。通过将尖端人工智能(AI)与全面的营养数据库整合,可以创建一个强大的工具,可以协助个人在他们的健康之旅中。

**本项目的代码在这个GitHub存储库中: 链接**

项目概述

该项目的核心是构建一个名为FitBot的聊天机器人,它由OpenAI的函数驱动,并基于ReAct(推理和行动)框架(图1)。

它通过解释用户的饮食习惯并整合营养数据的API来提供营养信息和建议。

技术方法

通过利用ReAct框架,FitBot保持了对话的互动性质,可以为每条建议提供详细的解释。它还连接了一个外部的营养数据库,确保准确和最新的饮食建议。

图1:ReAct结合推理(例如思考链)和行动。图片来源:链接

FitBot的幕后

FitBot将OpenAI的GPT-4功能与ReAct的动态处理相结合,以理解饮食查询,提供合适的替代方案,并提供个性化的建议。它在保持对话语调的同时,使营养建议易于理解和吸引人。

FitBot的独特之处在于它能够与外部的营养数据库进行接口交互。这使得FitBot能够向用户提供精确和更新的信息,确保给出的建议可靠且基于准确的数据。

在接下来的部分中,我们将深入了解代码,并查看FitBot的各个组件是如何开发和相互交互的,全面了解这个创新项目的内部工作原理。

构建FitBot: 代码解释

该项目有四个主要的脚本,用于处理数据并在用户界面中显示:

  1. fitness_agent.py:该文件包含FitnessAgent类,利用OpenAI函数实现FitBot所需的功能。
  2. chatbot.py:该文件包含FitBot的用户界面代码,使用Gradio库实现。
  3. agents.py:该文件包含Agent类,用于处理与OpenAI API的对话。这段代码基于James Briggs在funkagent库中开发的脚本。
  4. parser.py:该文件包含将函数docstring解析为OpenAI函数描述的代码。

定义我们的函数

为了创建一个能够提供准确和有用的营养和健身建议的聊天机器人,我们需要考虑对最终用户最有价值的信息。这就是之前实现的函数背后的原因。

1. get_nutritional_info:这个函数对于任何以健身为导向的聊天机器人来说至关重要。人们经常缺乏关于他们所吃食物的营养含量的清晰信息。通过使用API Ninjas的营养端点来获取各种食物的实时营养数据,我们可以帮助用户做出明智的饮食决策。返回的数据可以包括卡路里、蛋白质、碳水化合物、脂肪等详细信息,全面了解食物的营养特征。

def get_nutritional_info(self, query: str) -> dict:    """获取特定食物的营养信息    :param query: 需要获取营养信息的食物    :return: 食物的营养信息    """    api_url = 'https://api.api-ninjas.com/v1/nutrition?query={}'.format(query)    response = requests.get(api_url, headers={'X-Api-Key': self.nut_api_key})    if response.status_code == requests.codes.ok:        return response.json()  # 使用json而不是文本,以获得更结构化的数据    else:        return {"错误": response.status_code, "消息": response.text}

2. calculate_bmr: 基础代谢率(BMR)是了解个体新陈代谢的关键数据。它是人在休息时消耗的能量,与年龄、体重、身高和性别密切相关。能够计算BMR可以为聊天机器人提供一个基准,帮助用户了解他们的身体需要多少卡路里来正常运作,即使没有任何体力活动。

def calculate_bmr(weight: float, height: float, age: int, gender: str, equation: str = 'mifflin_st_jeor') -> float:    """计算个体的基础代谢率(BMR)    :param weight: 以千克为单位的体重    :param height: 以厘米为单位的身高    :param age: 以年为单位的年龄    :param gender: 个体的性别('男'或'女')    :param equation: 用于BMR计算的方程('harris_benedict'或'mifflin_st_jeor')    :return: 个体的BMR    """    if equation.lower() == 'mifflin_st_jeor':        if gender.lower() == 'male':            return (10 * weight) + (6.25 * height) - (5 * age) + 5        else:  # 'female'            return (10 * weight) + (6.25 * height) - (5 * age) - 161    else:  # 'harris_benedict'        if gender.lower() == 'male':            return 88.362 + (13.397 * weight) + (4.799 * height) - (5.677 * age)        else:  # 'female'            return 447.593 + (9.247 * weight) + (3.098 * height) - (4.330 * age)

3. calculate_tdee: 了解个体的每日总能量消耗量(TDEE)对于制定个性化的饮食或运动计划至关重要。TDEE不仅考虑了BMR,还考虑了日常活动和运动中消耗的卡路里。了解他们的TDEE可以帮助用户更有效地规划饮食和运动计划,无论是保持体重、减肥还是增肥。

def calculate_tdee(bmr: float, activity_level: str) -> float:    """计算个体的每日总能量消耗量(TDEE)    :param bmr: 个体的BMR    :param activity_level: 个体的活动水平('久坐', '轻度活动', '中度活动', '高度活动', '超级活动')    :return: 个体的TDEE    """    activity_factors = {        '久坐': 1.2,        '轻度活动': 1.375,        '中度活动': 1.55,        '高度活动': 1.725,        '超级活动': 1.9,    }    return bmr * activity_factors.get(activity_level, 1)

4. calculate_ibw: 知道理想体重(IBW)可以为用户提供一个被认为是健康的身高和性别的目标体重。虽然IBW不是一个完美的指标(它没有考虑肌肉量等因素),但它确实给用户一个关于他们的体重应该是多少以达到最佳健康状况的大致概念。

def calculate_ibw(height: float, gender: str) -> float:    """计算理想体重(IBW)    :param height: 以英寸为单位的身高    :param gender: 个体的性别('男'或'女')    :return: 以千克为单位的理想体重    """    if gender.lower() == 'male':        if height <= 60:  # 5英尺 = 60英寸            return 50        else:            return 50 + 2.3 * (height - 60)    elif gender.lower() == 'female':        if height <= 60:            return 45.5        else:            return 45.5 + 2.3 * (height - 60)    else:        raise ValueError("无效的性别。期望值为'男'或'女'。")

4. calculate_bmi: 体重指数(BMI)是使用一个人的身高和体重进行简单计算的。公式为BMI = kg/m^2,其中kg是一个人的体重(千克),m^2是他们的身高(以米为单位)的平方。BMI不能直接测量体脂肪,但研究表明BMI与直接测量体脂肪的方法有适度的相关性。它提供了一个有助于理解一个人是否体重过轻、体重正常、超重或肥胖的参考。

def calculate_bmi(weight: float, height: float) -> float:    """计算一个人的体重指数(BMI)    :param weight: 人的体重(千克)    :param height: 人的身高(厘米)    :return: 人的BMI    """    height_meters = height / 100  # 将厘米转换为米    bmi = weight / (height_meters ** 2)    return round(bmi, 2)  # 保留两位小数以提高可读性

构建代理:封装函数

在建立了基本的函数之后,我们的下一步是将它们集成到我们的聊天机器人代理中。这种封装使得机器人能够利用这些函数,并根据用户的查询提供相关和准确的响应。

下面是创建代理的方法:

# 实例化代理fitness_agent = FitnessAgent(openai_api_key, nut_api_key)

您可以查看它所拥有的函数:

# 您可以查看已处理的函数指令print(fitness_agent.functions)

输出:

[   {      "name":"get_nutritional_info",      "description":"获取特定食物项目的营养信息",      "parameters":{         "type":"object",         "properties":{            "query":{               "description":"要获取营养信息的食物项目",               "type":"string"            }         }      },      "required":[         "query"      ]   },   {      "name":"calculate_bmr",      "description":"计算一个人的基础代谢率(BMR)",      "parameters":{         "type":"object",         "properties":{            "weight":{               "description":"人的体重(千克)",               "type":"number"            },            "height":{               "description":"人的身高(厘米)",               "type":"number"            },            "age":{               "description":"人的年龄(岁)",               "type":"integer"            },            "gender":{               "description":"人的性别('男'或'女')",               "type":"string"            },            "equation":{               "description":"用于BMR计算的方程('harris_benedict'或'mifflin_st_jeor')",               "type":"string"            }         }      },      "required":[         "weight",         "height",         "age",         "gender",         "equation"      ]   },   {      "name":"calculate_tdee",      "description":"计算一个人的每日总能量消耗(TDEE)",      "parameters":{         "type":"object",         "properties":{            "bmr":{               "description":"人的BMR",               "type":"number"            },            "activity_level":{               "description":"人的活动水平",               "type":"string"            }         }      },      "required":[         "bmr",         "activity_level"      ]   }]

与代理互动:用户对话

在将我们定义的函数封装到FitnessAgent中后,我们现在可以模拟与我们的机器人对话了。使用FitnessAgent类的ask()方法,我们可以轻松地将用户查询输入到机器人中,并打印出它生成的响应。

例如,让我们向机器人询问一些关于常见食物项目的营养信息:

# 定义一个问题user_input = "香蕉的营养价值是多少?"# 获取原始聊天响应response = fitness_agent.ask(user_input)# 打印最终响应print(response['choices'][0]['message']['content'])

输出:

一个100克的香蕉通常含有:- 热量:89.4卡路里- 总脂肪:0.3克,其中饱和脂肪为0.1克- 蛋白质:1.1克- 钠:1毫克- 钾:22毫克- 胆固醇:0毫克- 总碳水化合物:23.2克,其中膳食纤维为2.6克,糖类为12.3克这些数值可能会根据香蕉的确切大小和成熟度而有所变化。还请注意,香蕉是膳食钾和维生素C的良好来源。

在这个例子中,FitBot有效地使用get_nutritional_info()函数来获取并显示香蕉的营养价值。

FitBot还可以处理更复杂的请求。例如,如果提供了所需的数据,它可以计算一个人的总日能量消耗(TDEE):

# 定义一个问题user_input = "一个30岁男性,身高180厘米,体重80公斤,每周运动3次的人的TDEE是多少?"# 获取原始的聊天回复response = fitness_agent.ask(user_input)# 打印最终的回复print(response['choices'][0]['message']['content'])

输出:

一个30岁男性,身高180厘米,体重80公斤,每周运动3次的人根据Harris-Benedict方程的“适度”运动,每天的总日能量消耗(TDEE)大约为2574卡路里。这只是一个粗略的估计,个体结果可能会因代谢速度、具体体力活动等多个因素而有所不同。以下是该过程的简单概述:1. 计算基础代谢率(BMR)- 这是一个人在没有任何活动的情况下仅通过存在而燃烧的卡路里数。对于男性,计算BMR的Harris-Benedict方程为BMR = 88.362 +(13.397 *体重(公斤))+(4.799 *身高(厘米))-(5.677 *年龄(岁))根据给定的值,BMR大约为1783卡路里。2. 调整活动量 - 可以使用以下乘数将BMR调整为体力活动水平:- 久坐(几乎没有运动):BMR * 1.2- 轻度活动(每周1-3天轻度运动/体育运动):BMR * 1.375- 中度活动(每周3-5天中度运动/体育运动):BMR * 1.55- 非常活跃(每周6-7天剧烈运动/体育运动):BMR * 1.725- 超级活跃(非常剧烈的运动/体力工作和每天2次运动):BMR * 1.9对于每周运动3次的人,我们可以将乘数取为1.55(中度活动)。因此,TDEE = BMR * 活动乘数 = 1783 * 1.55 = 大约2764卡路里。注意:实际的总热量需求可能会稍高或稍低,因为此计算未考虑肌肉与脂肪比例、特定类型的活动等。

让我们将此输出与基于GPT-4的原始ChatGPT进行比较:

TDEE(总日能量消耗)表示一个人一天需要摄入的总卡路里数,以维持其当前体重,考虑其体力活动水平。我们首先需要计算基础代谢率(BMR),即在休息状态下消耗的能量量。有几个方程可以计算BMR,其中一个常用的方程是Mifflin-St. Jeor方程:对于男性:BMR = 10 * 体重(公斤)+ 6.25 * 身高(厘米)- 5 * 年龄(岁)+ 5因此,对于你的情况:BMR = 10 * 80公斤 + 6.25 * 180厘米 - 5 * 30岁 + 5BMR = 800 + 1125 - 150 + 5 = 1780千卡/天这是保持身体在休息状态下运作所需的卡路里数。要将体力活动纳入方程中,我们将BMR乘以体力活动水平(PAL)。对于每周运动3次的人,常见的PAL为1.375(适度运动)。因此,TDEE = BMR * PAL TDEE = 1780千卡/天 * 1.375 = 2447.5千卡/天因此,一个30岁男性,身高180厘米,体重80公斤,每周运动3次,需要每天摄入大约2448卡路里来维持他的当前体重。请注意,这只是一个估计值;实际的卡路里需求可能因许多因素而有所不同,包括基因和体成分。始终咨询医疗保健提供者或注册的营养师以获得个性化建议。

将Chatbot与用户界面集成

现在,我们已经构建了我们的健身代理并为其配备了有用的功能,我们希望以易于使用的界面呈现它。

为此,我们使用Gradio,这是一个Python库,可以快速方便地创建可共享的基于Web的用户界面。在本节中,我们将向您介绍如何将我们的聊天机器人与Gradio用户界面集成。

这是我们界面的整体结构:

def main():    openai_api_key = gr.components.Textbox(        lines=1,        label="输入OpenAI API密钥",        type="password",    )    nut_api_key = gr.components.Textbox(        lines=1,        label="输入Nutrition API密钥",        type="password",    )    question = gr.components.Textbox(        lines=3,        label="输入您的消息",    )    output_history = gr.outputs.HTML(        label="更新的对话",    )    inputs = [        openai_api_key,        nut_api_key,        question,    ]    iface = gr.Interface(        fn=partial(get_response),        inputs=inputs,        outputs=[output_history],        title="健身代理",        description="使用健身代理和Gradio的简单聊天机器人,带有对话历史记录",        allow_flagging=False,    )    iface.launch()if __name__ == "__main__":    main()

这是我们的main函数,也是我们脚本的入口点。我们首先创建文本框,用于用户输入他们的OpenAI API密钥和Nutrition API密钥。这些密钥的类型设置为password以隐藏输入。接下来,我们为用户提供一个文本框,让他们提问。聊天机器人的响应将以HTML形式显示在一个标有“更新的对话”的区域中。

然后,将输入和输出传递给Gradio界面,在脚本运行时启动该界面。

get_response函数与健身代理进行交互:

def get_response(openai_api_key, nut_api_key, user_input, action=None):    set_openai_api_key(openai_api_key)    set_nut_api_key(nut_api_key)    fitness_agent = FitnessAgent(openai_api_key, nut_api_key)    # 获取原始聊天响应    fitness_agent.ask(user_input)    memory = fitness_agent.agent.chat_history    # 遍历ChatMessageHistory中的消息,并格式化输出    updated_conversation = '<div style="background-color: hsl(30, 100%, 30%); color: white; padding: 5px; margin-bottom: 10px; text-align: center; font-size: 1.5em;">聊天历史</div>'    logger.info(memory)    for i, message in enumerate(memory):        if i != 0:            if message['role'] == 'user':                prefix = "用户: "                background_color = "hsl(0, 0%, 40%)"  # 深灰色背景                text_color = "hsl(0, 0%, 100%)"  # 白色文字            else:                prefix = "聊天机器人: "                background_color = "hsl(0, 0%, 95%)"  # 白色背景                text_color = "hsl(0, 0%, 0%)"  # 黑色文字            updated_conversation += f'<div style="color: {text_color}; background-color: {background_color}; margin: 5px; padding: 5px;">{prefix}{message["content"]}</div>'    return updated_conversation

get_response中,我们使用set_openai_api_keyset_nut_api_key函数设置OpenAI和Nutrition API密钥,然后初始化我们的健身代理。接下来,我们使用用户的问题调用代理的ask方法,并存储对话历史记录。然后,将每条对话历史记录格式化为HTML字符串,并添加到updated_conversation中。该HTML字符串将被返回并显示在Gradio界面中。

最终界面的一瞥

在将关键计算和对话逻辑集成到具有令人愉悦的用户界面中,并使用Gradio进行封装后,我们的FitBot已准备好进行交互!

以下是最终界面的外观:

图2:聊天机器人的Gradio用户界面。图片由作者提供。

在界面上,您会注意到三个输入框,您可以在其中输入OpenAI和API Ninjas Nutrition端点的必要密钥,以及用户消息发送给Fitness Agent。

摘要

本文详细介绍了使用OpenAI的GPT-4创建FitBot,这是一个全面的Fitness Agent,它是一个强大的AI模型,能够理解和回答复杂的用户查询。

我们首先构建了用于计算重要健康指标如基础代谢率(BMR)、每日总能量消耗量(TDEE)和身体质量指数(BMI)的函数。这些计算为Fitness Agent提供准确和个性化的健身和营养建议打下了基础。

接下来,我们与API Ninjas的Nutrition端点进行了集成。这使Fitness Agent能够访问和提供准确的营养信息,这是任何全面的健身和饮食计划的关键组成部分。

然后,我们展示了如何构建对话逻辑,使Fitness Agent更具互动性。它能够处理对话流程,使其能够回答各种用户查询并有效地指导用户的健身之旅。

最后,我们使用Gradio将所有这些功能封装成一个外观吸引人的用户界面。结果是一个既智能又用户友好的Fitness Agent,以易于理解的格式提供清晰和全面的建议。

简而言之:在本文中,我们使用OpenAI的GPT-4构建了FitBot,一个能够提供个性化健身和营养建议的Fitness Agent。我们实现了计算重要健康指标(BMR、TDEE、BMI)的函数,与Nutrition API进行了集成以获取准确的饮食信息,并使用Gradio将所有功能封装在一个用户友好的界面中。这个项目展示了人工智能在健康和健身中的力量,简化了复杂的计算,提供个性化的建议,并通过一个吸引人的用户界面进行提供。

感谢阅读!

  • 关注我的Linkedin!
Leave a Reply

Your email address will not be published. Required fields are marked *