使用 SLM 从头开始​​构建 ReAct Agent

使用 slm 从头开始​​构建 react agent

在这篇文章中,我将演示如何使用小语言模型 (slm) 创建函数调用代理。利用 slm 可以带来一系列好处,特别是与 lora 适配器等工具配合使用时,可以实现高效的微调和执行。虽然大型语言模型 (llm) 功能强大,但它们可能会占用大量资源且速度缓慢。另一方面,slm 更加轻量级,使其非常适合硬件资源有限的环境或低延迟至关重要的特定用例。

通过将slmlora适配器结合使用,我们可以将推理和函数执行任务分开以优化性能。例如,模型可以使用适配器执行复杂的函数调用,并在没有适配器的情况下处理推理或思考任务,从而节省内存并提高速度。这种灵活性非常适合构建函数调用代理等应用程序,而无需大型模型所需的基础设施。

此外,slm 可以轻松扩展以在计算能力有限的设备上运行,这使其成为优先考虑成本和效率的生产环境的理想选择。在此示例中,我们将使用通过 unsloth 在 salesforce/xlam-function-calling-60k 数据集上训练的自定义模型,演示如何利用 slm 创建高性能、低资源的 ai 应用程序.

此外,这里讨论的方法可以扩展到更强大的模型,例如llama 3.1-8b,它具有内置的函数调用功能,在需要更大的模型时提供平滑的过渡。

1. 使用 unsloth 启动模型和分词器

我们首先使用 unsloth 设置模型和标记器。在这里,我们定义最大序列长度为 2048,尽管这个长度是可以调整的。我们还启用4 位量化来减少内存使用量,非常适合在内存较低的硬件上运行模型。

from unsloth import fastlanguagemodelmax_seq_length = 2048 # choose any! we auto support rope scaling internally!dtype = none # none for auto detection. float16 for tesla t4, v100, bfloat16 for ampere+load_in_4bit = true # use 4bit quantization to reduce memory usage. can be false.model, tokenizer = fastlanguagemodel.from_pretrained(    model_name = "akshayballal/phi-3.5-mini-xlam-function-calling",    max_seq_length = max_seq_length,    dtype = dtype,    load_in_4bit = load_in_4bit,)fastlanguagemodel.for_inference(model);

2. 实施受控发电的停止标准

为了确保代理在函数调用后暂停执行,我们定义了停止条件。当模型输出关键字“pause”时,这将停止生成,从而允许代理获取函数调用的结果。

from transformers import stoppingcriteria, stoppingcriterialistimport torchclass keywordsstoppingcriteria(stoppingcriteria):    def __init__(self, keywords_ids:list):        self.keywords = keywords_ids    def __call__(self, input_ids: torch.longtensor, _: torch.floattensor, **kwargs) -> bool:        if input_ids[0][-1] in self.keywords:            return true        return falsestop_ids = [17171]stop_criteria = keywordsstoppingcriteria(stop_ids)

3. 定义函数调用工具

接下来,我们定义代理在执行期间将使用的函数。这些python函数将充当代理可以调用​​的“工具”。返回类型必须明确,并且函数应包含描述性文档字符串,因为代理将依赖于此来选择正确的工具。

def add_numbers(a: int, b: int) -> int:    """    this function takes two integers and returns their sum.    parameters:    a (int): the first integer to add.    b (int): the second integer to add.    """    return a + b def square_number(a: int) -> int:    """    this function takes an integer and returns its square.    parameters:    a (int): the integer to be squared.    """    return a * adef square_root_number(a: int) -> int:    """    this function takes an integer and returns its square root.    parameters:    a (int): the integer to calculate the square root of.    """    return a ** 0.5

4. 为代理生成工具描述

这些函数描述将被构造成一个字典列表。代理将使用这些来了解可用的工具及其参数。

tool_descriptions = []for tool in tools:    spec = {        "name": tool.__name__,        "description": tool.__doc__.strip(),        "parameters": [            {                "name": param,                "type": arg.__name__ if hasattr(arg, '__name__') else str(arg),            } for param, arg in tool.__annotations__.items() if param != 'return'        ]    }    tool_descriptions.append(spec)tool_descriptions

这就是输出的样子

[{'name': 'add_numbers',  'description': 'this function takes two integers and returns their sum.    parameters:    a (int): the first integer to add.    b (int): the second integer to add.',  'parameters': [{'name': 'a', 'type': 'int'}, {'name': 'b', 'type': 'int'}]}, {'name': 'square_number',  'description': 'this function takes an integer and returns its square.    parameters:    a (int): the integer to be squared.',  'parameters': [{'name': 'a', 'type': 'int'}]}, {'name': 'square_root_number',  'description': 'this function takes an integer and returns its square root.    parameters:    a (int): the integer to calculate the square root of.',  'parameters': [{'name': 'a', 'type': 'int'}]}]

5. 创建代理类

然后我们创建代理类,它将系统提示、函数调用提示、工具和消息作为输入,并返回代理的响应。

__call__ 是使用消息调用代理时调用的函数。它将消息添加到消息列表并返回代理的响应。execute 是被调用以生成代理响应的函数。它使用模型来生成响应。function_call 是被调用以生成代理响应的函数。它使用函数调用模型来生成响应。

import astclass agent:    def __init__(        self, system: str = "", function_calling_prompt: str = "", tools=[]    ) -> none:        self.system = system        self.tools = tools        self.function_calling_prompt = function_calling_prompt        self.messages: list = []        if self.system:            self.messages.append({"role": "system", "content": system})    def __call__(self, message=""):        if message:            self.messages.append({"role": "user", "content": message})        result = self.execute()        self.messages.append({"role": "assistant", "content": result})        return result    def execute(self):        with model.disable_adapter():  # disable the adapter for thinking and reasoning            inputs = tokenizer.apply_chat_template(                self.messages,                tokenize=true,                add_generation_prompt=true,                return_tensors="pt",            )            output = model.generate(                input_ids=inputs,                max_new_tokens=128,                stopping_criteria=stoppingcriterialist([stop_criteria]),            )            return tokenizer.decode(                output[0][inputs.shape[-1] :], skip_special_tokens=true            )    def function_call(self, message):        inputs = tokenizer.apply_chat_template(            [                {                    "role": "user",                    "content": self.function_calling_prompt.format(                        tool_descriptions=tool_descriptions, query=message                    ),                }            ],            tokenize=true,            add_generation_prompt=true,            return_tensors="pt",        )        output = model.generate(input_ids=inputs, max_new_tokens=128, temperature=0.0)        prompt_length = inputs.shape[-1]        answer = ast.literal_eval(            tokenizer.decode(output[0][prompt_length:], skip_special_tokens=true)        )[            0        ]  # get the output of the function call model as a dictionary        print(answer)        tool_output = self.run_tool(answer["name"], **answer["arguments"])        return tool_output    def run_tool(self, name, *args, **kwargs):        for tool in self.tools:            if tool.__name__ == name:                return tool(*args, **kwargs)

6. 定义系统和函数调用提示

我们现在定义两个关键提示:

系统提示:智能体推理和工具使用的核心逻辑,遵循react模式。函数调用提示:通过传递相关工具描述和查询来启用函数调用。

system_prompt = f"""you run in a loop of thought, action, pause, observation.at the end of the loop you output an answeruse thought to describe your thoughts about the question you have been asked.use action to run one of the actions available to you - then return pause.observation will be the result of running those actions. stop when you have the answer. your available actions are:{tools}example session:question: what is the mass of earth times 2?thought: i need to find the mass of earthaction: get_planet_mass: earthpause observation: 5.972e24thought: i need to multiply this by 2action: calculate: 5.972e24 * 2pauseobservation: 1,1944×10e25if you have the answer, output it as the answer.answer: {{1,1944×10e25}}.pausenow it's your turn:""".strip()function_calling_prompt = """you are a helpful assistant. below are the tools that you have access to.  ### tools: {tool_descriptions} ### query: {query} """

7. 实现react循环

最后,我们定义了一个循环,使代理能够与用户交互,执行必要的函数调用,并返回正确的答案。

import redef loop_agent(agent: Agent, question, max_iterations=5):    next_prompt = question    i = 0    while i < max_iterations:        result = agent(next_prompt)        print(result)        if "Answer:" in result:            return result        action = re.findall(r"Action: (.*)", result)        if action:            tool_output= agent.function_call(action)            next_prompt = f"Observation: {tool_output}"            print(next_prompt)        else:            next_prompt = "Observation: tool not found"        i += 1    return resultagent = Agent( system=system_prompt, function_calling_prompt=function_calling_prompt, tools=tools)loop_agent(agent, "what is the square root of the difference between 32^2 and 54");

结论

按照此分步指南,您可以使用使用 unslothlora 适配器 训练的自定义模型创建函数调用代理。这种方法确保高效的内存使用,同时保持强大的推理和函数执行能力。

通过将此方法扩展到更大的模型或自定义代理可用的功能来进一步探索。

以上就是使用 SLM 从头开始​​构建 ReAct Agent的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1350130.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
医疗保健的未来:人工智能如何彻底改变个性化营养
上一篇 2025年12月13日 13:58:58
搜索-搜索插入位置
下一篇 2025年12月13日 13:59:08

相关推荐

  • Matplotlib 地图中多类型图例的创建与优化

    Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化

    本教程旨在解决matplotlib地图可视化中,如何在一个图例中同时展示颜色块(如区域分类)和自定义标记(如特定兴趣点)的问题。文章详细介绍了当传统`patch`对象无法正确显示标记时,如何利用`matplotlib.lines.line2d`创建标记图例句柄,并将其与颜色块图例句柄合并,从而生成一…

    2026年5月10日 用户投稿
    100
  • 利用海象运算符简化条件赋值:Python教程与最佳实践

    本文旨在探讨Python中海象运算符(:=)在条件赋值场景下的应用。通过对比传统if/else语句与海象运算符,以及条件表达式,分析海象运算符在简化代码、提高可读性方面的优势与局限性。并通过具体示例,展示如何在列表推导式等场景下合理使用海象运算符,同时强调其潜在的复杂性及替代方案,帮助开发者更好地掌…

    2026年5月10日
    100
  • RichHandler与Rich Progress集成:解决显示冲突的教程

    在使用rich库的`richhandler`进行日志输出并同时使用`progress`组件时,可能会遇到显示错乱或溢出问题。这通常是由于为`richhandler`和`progress`分别创建了独立的`console`实例导致的。解决方案是确保日志处理器和进度条组件共享同一个`console`实例…

    2026年5月10日
    000
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • Python递归函数追踪与性能考量:以序列打印为例

    本文深入探讨了Python中一种递归打印序列元素的方法,并着重演示了如何通过引入缩进参数来有效追踪递归函数的执行流程和参数变化。通过实际代码示例,文章揭示了递归调用可能带来的潜在性能开销,特别是对调用栈空间的需求,以及Python默认递归深度限制可能导致的错误,为读者提供了理解和优化递归算法的实用见…

    2026年5月10日
    000
  • python中zip函数详解 python多序列压缩zip函数应用场景

    zip函数的应用场景包括:1) 同时遍历多个序列,2) 合并多个列表的数据,3) 数据分析和科学计算中的元素运算,4) 处理csv文件,5) 性能优化。zip函数是一个强大的工具,能够简化代码并提高处理多个序列时的效率。 在Python中,zip函数是一个非常有用的工具,它能够将多个可迭代对象打包成…

    2026年5月10日
    000
  • Python中怎样使用pymongo?

    在python中使用pymongo可以轻松地与mongodb数据库进行交互。1)安装pymongo:pip install pymongo。2)连接到mongodb:from pymongo import mongoclient; client = mongoclient(‘mongod…

    2026年5月10日
    000
  • PHP多维数组到复杂XML结构的SOAP序列化实践

    本文旨在解决php多维数组向复杂soap xml结构序列化时遇到的“无法序列化结果”问题。通过深入理解soap xml的结构要求,包括命名空间和类型属性,文章将指导您如何构建符合特定xml schema的php关联数组。我们将利用`spatie/array-to-xml`库,详细演示其安装与使用方法…

    2026年5月10日
    000
  • Python 函数参数类型:如何使用可变参数和动态参数?

    python 中的参数类型:关键词参数、可变参数和动态参数 在 python 中,函数的参数可以分为以下几种类型: 关键词参数(kw)**:这些参数具有名称,并且在调用函数时明确指定。可变参数(*args):这些参数没有名称,允许函数接受任意数量的位置参数。它们将被收集到一个元组中。动态参数(kwa…

    2026年5月10日
    000
  • pycharm解析器怎么添加 解析器添加详细流程

    在pycharm中添加解析器的步骤包括:1) 打开pycharm并进入设置,2) 选择project interpreter,3) 点击齿轮图标并选择add,4) 选择解析器类型并配置路径,5) 点击ok完成添加。添加解析器后,选择合适的类型和版本,配置环境变量,并利用解析器的功能提高开发效率。 在…

    2026年5月10日
    000
  • python中numpy的用法

    NumPy是Python中用于科学计算的强大库,它提供了以下功能:多维数组处理矩阵运算快速傅里叶变换(FFT)线性代数随机数生成 NumPy在Python中的强大功能 NumPy是Python中用于科学计算的一个强大且灵活的库。它提供了用于处理多维数组和矩阵的一组高效工具,是数据分析和机器学习项目的…

    2026年5月10日
    100
  • python如何捕获所有类型的异常_python try except捕获所有异常的方法

    答案:捕获所有异常推荐使用except Exception as e,可捕获常规错误并记录日志,避免影响程序正常退出;需拦截系统信号时才用except BaseException as e。 在Python中,要捕获所有类型的异常,最常见且推荐的方法是使用 except Exception as e…

    2026年5月10日
    000
  • python中f怎么用

    f-字符串是 Python 3.6 中引入的格式化字符串语法糖,提供了简洁且安全的方式来插入表达式和变量。f-字符串以字符串前缀 f 为标志,使用大括号包含表达式或变量。f-字符串支持条件表达式和格式规范符,提供了更大的灵活性、安全性、可读性和易维护性。 在 Python 中使用 f-字符串 f-字…

    2026年5月10日
    100
  • CodeIgniter在IIS环境下实现URL重写与index.php移除指南

    本教程详细指导如何在IIS服务器上部署的CodeIgniter应用中,移除URL中不必要的index.php。核心解决方案涉及修改CodeIgniter的config.php文件,将$config[‘index_page’]设置为空,并辅以正确的IIS web.config重…

    2026年5月10日
    100
  • 怎么在手机上把XML文件转换为PDF?

    不可能直接在手机上用单一应用完成 XML 到 PDF 的转换。需要使用云端服务,通过两步走的方式实现:1. 在云端转换 XML 为 PDF,2. 在手机端访问或下载转换后的 PDF 文件。 怎么在手机上把XML文件转换为PDF? 这问题问得好,比直接问“怎么转换”有深度多了!因为它触及了移动端环境的…

    2026年5月10日
    000
  • ReCAPTCHA V3低分处理策略:结合V3与V2实现智能风险控制与用户验证

    本文旨在解决ReCAPTCHA V3在低分情况下无法直接触发验证码挑战的问题。我们将探讨如何通过巧妙地结合ReCAPTCHA V3的无感评分机制与ReCAPTCHA V2的交互式挑战,实现一套既能有效阻挡机器人流量,又能最大限度减少对合法用户干扰的智能验证系统。文章将详细阐述其实现原理、前端与后端集…

    2026年5月10日
    100
  • Python正则表达式:处理数字不同情况的替换

    本文旨在帮助读者理解和解决在使用Python正则表达式进行数字替换时遇到的问题。通过具体示例,详细解释了如何正确匹配和替换不同格式的数字,避免常见的匹配陷阱,并提供可直接使用的代码示例。掌握这些技巧,能有效提高处理文本数据的效率和准确性。 在使用Python的re模块进行字符串替换时,正则表达式的编…

    2026年5月10日
    000
  • python的tuple什么意思

    元组是Python中一种有序、不可变的序列数据结构。用于存储相关数据,例如坐标、个人信息或枚举值。创建方式:圆括号(),元素以逗号,分隔。访问元素:索引运算符;遍历元素:for循环。 什么是Python中的Tuple? Tuple,中文称为元组,是Python中一种有序、不可变的序列数据结构。 特点…

    2026年5月10日
    000
  • Python官网用户调查的参与方式_Python官网反馈提交详细教程

    答案是通过访问Python官网新闻页面、邮件邀请链接或GitHub仓库提交反馈。具体为:访问官网查找用户调查公告,或点击邮件中的专属链接参与,在GitHub的cpython仓库提交技术建议,并注意如实填写问卷与保护隐私。 如果您希望参与Python官网的用户调查并提交反馈,可以通过官方指定的渠道完成…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信