LangChain工具调用底层原理深度解析(进阶篇)——专为新手呈现!

系列文章目录

LangChain(一)构建本地数据检索问答Agent,新手向-CSDN博客

LangChain(二)基础问答大模型,纯新手向-CSDN博客

LangChain(三)基础问答大模型,从LLMchain开始了解chain!纯新手向-CSDN博客

LangChain(四)工具调用的底层原理!给大模型按上双手吧!(新手向)-CSDN博客


文章目录

前言

一、工具调用回顾

二、自己构建工具调用!

1.构建工具函数

2.定义大模型

3.设定工具选择模块

4.  构建chain以及最后格式化输出

5. 函数调用!最终步骤!

老规矩,全部代码展示!

总结


前言

随着第三篇的内容,我们跟进到了大模型调用工具的开发与原理,并浅尝辄止了一下大模型调用工具的原理!

本篇我们上接上文,继续此部分的内容,我们主要讲解有关大模型调用工具的原理!我们会尽可能的把LangChain高度抽象的代码还原,把每一个步骤都尽可能的说清楚!大家感兴趣的话可以看一下第四篇的内容~

其实不看也没事啦,看了理解起来会更快而已。本篇最大的用途是,对于一些不支持LangChain工具调用的大模型,我们依旧可以使用本方案工具调用!

一、工具调用回顾

        在上一篇的内容中我们开发了一个基础的工具调用代码,I know大家对于代码更感兴趣,所以我把代码先粘过来~

import os
from langchain_community.chat_models import QianfanChatEndpoint
from langchain_core.tools import tool
from langchain_core.messages import AIMessage
from langchain_core.runnables import Runnable
 
# 设定百度千帆大模型的AK和SK
os.environ["QIANFAN_AK"] = " your AK"
os.environ["QIANFAN_SK"] = " your SK"
 
# 定义千帆大模型
qianfan_chat = QianfanChatEndpoint(
    model="ERNIE-3.5-8K",
    temperature=0.2,
    timeout=30,
)
 
# 设定两个函数,记得描述函数功能,这很重要
@tool
def func1():
    ''' useless function '''
    return 0
 
@tool
def Multiply(a: int, b: int) -> int:
    """Multiplies a and b."""
    return a * b
 
# 工具集合
tools = [Multiply, func1]
# 工具与大模型绑定,构建函数选择模块
llm_with_tools = qianfan_chat.bind_tools(tools)
# 构建一一对应的map
tool_map = {tool.name: tool for tool in tools}
 
# 工具函数执行
def call_tools(msg: AIMessage) -> Runnable:
    """Simple sequential tool calling helper."""
    tool_calls = msg.tool_calls.copy()
    for tool_call in tool_calls:
        tool_call["output"] = tool_map[tool_call["name"]].invoke(tool_call["args"])
    return tool_calls
 
# 构建链
chain = llm_with_tools | call_tools
 
print(chain.invoke("What's 25 times 11?")[0]["output"])

总而言之,上诉的代码主要经过了以下几个步骤:

初始化内容

  • 定义工具函数与大模型初始化
  • 工具函数与大模型绑定构建函数选择模块:llm_with_tools
  • 构建工具调用模块:call_tools
  •  chain实际运行的数据流

  • 用户输入给到函数选择模块
  • 函数选择模块输出需要的函数和对应的参数
  • 工具调用模块进行工具调用并返回
  • 二、自己构建工具调用!

    不必担心,依旧很简单~

    1.构建工具函数

    from langchain_core.tools import tool
    @tool
    def multiply(first_int: int, second_int: int) -> int:
        """Multiply two integers together."""
        return first_int * second_int
    
    @tool
    def add(first_int: int, second_int: int) -> int:
        """add two integers together."""
        return first_int + second_int

    我们使用下面的代码查看一下具体的内容,这些内容就是真正需要给到大模型参考的函数描述 

    print(multiply.name)
    print(multiply.description)
    print(multiply.args)

     函数输出如下:

    multiply
    multiply(first_int: int, second_int: int) -> int - Multiply two integers together.
    {'first_int': {'title': 'First Int', 'type': 'integer'}, 'second_int': {'title': 'Second Int', 'type': 'integer'}}

     可以查看一下第三行这是之前没有讲到的部分,此部分定义了函数输入参数的规格,用以提醒大模型。

    2.定义大模型

    直接用LangChain集成好的,依旧是千帆大模型~

    import os
    from langchain_community.chat_models import QianfanChatEndpoint
     
    # 设定百度千帆大模型的AK和SK-去百度千帆官网的控制台新建一个应用即可
    os.environ["QIANFAN_AK"] = "your AK“"
    os.environ["QIANFAN_SK"] = "your SK"
     
    #创建千帆LLM模型
    qianfan_chat = QianfanChatEndpoint(
        model="ERNIE-3.5-8K",
        temperature=0.2,
        timeout=30,
    )

    3.设定工具选择模块

    这部分是本篇精华,本篇和上一篇最大的区别就在于函数选择器,LangChain帮助我们把此部分高度抽象成了 llm_with_tools 。我们本篇将其拓写!

    ①构建函数描述模块!

    from langchain.tools.render import render_text_description
    
    # 构建工具条件和调用描述
    rendered_tools = render_text_description([multiply, add])
    print("rendered_tools = ", rendered_tools)

     此部分需要好好讲解一下这是工具选择功能实现的核心!

    此部分render_text_description函数的作用是构建函数描述符,返回函数的头以及参数规格和函数的功能描述。其对应的输出如下:

    rendered_tools =  
    
    multiply(first_int: int, second_int: int) -> int - Multiply two integers together.
    add(first_int: int, second_int: int) -> int - add two integers together.

    有关该函数,详情可见如下: 

    LangChain中的render_text_description究竟做了什么操作?-CSDN博客

    ②构建工具选择输入prompt模板!

    from langchain_core.prompts import ChatPromptTemplate
    
    # 构建工具选择器的系统人设
    system_prompt = f"""You are an assistant that has access to the following set of tools. Here are the names and descriptions for each tool:
    
    {rendered_tools}
    
    # 构建输入的模板
    Given the user input, return the name and input of the tool to use. Return your response as a JSON blob with 'name' and 'arguments' keys."""
    
    prompt = ChatPromptTemplate.from_messages(
        [("system", system_prompt), ("user", "{input}")]
    )

            通过上诉文本的阅读,我想看客们此时应该已经反应过来了,大模型工具选择的原理就是根据之前传入的函数描述符和用户的输入,返回需要的函数名称和参数。实在是非常粗暴。很多看客想必此时心中一万匹草泥马奔腾而过……。 

    4.  构建chain以及最后格式化输出

    # 构建chain以及最后格式化输出
    chain = prompt | qianfan_chat
    print(chain.invoke({"input": "what's thirteen times 4"}))
    
    

    构建链的过程应该无需我过多解释,若不明白的看客可以查看一下上一篇的内容~。

    此时的输出如下:

    content='```json\n{\n    
    "name": "multiply",\n    
    "arguments": {\n        
    "first_int": 13,\n        
    "second_int": 4\n    
    }\n}\n
    ```' 
    additional_kwargs={
    'finish_reason': 'normal', 
    'request_id': 'as-mzhzznjcan', 
    'object': 'chat.completion', 
    'search_info': []} 
    response_metadata={
    'token_usage': {'prompt_tokens': 114, 'completion_tokens': 45, 'total_tokens': 159}, 'model_name': 'ERNIE-3.5-8K', 
    'finish_reason': 'normal', 
    'id': 'as-mzhzznjcan', 
    'object': 'chat.completion', 
    'created': 1720599923, 
    'result': '```json\n{\n    
        "name": "multiply",\n    
        "arguments": {\n        
            "first_int": 13,\n        
            "second_int": 4\n    }\n}\n```', 
    'is_truncated': False, 
    'need_clear_history': False, 
    'usage': {'prompt_tokens': 114, 'completion_tokens': 45, 'total_tokens': 159}} 
    
    id='run-b141fa71-065b-41c0-baa1-6f6797e3ae0f-0'

    太复杂了对不对?我们可以对结果进行处理下,LangChain很贴心的提供了相关的内容: JsonOutputParser() !我们在链的末尾增加该函数!

    # 构建chain以及最后格式化输出
    chain = prompt | qianfan_chat | JsonOutputParser()
    print(chain.invoke({"input": "what's thirteen times 4"}))
    
    

     此时输出如下:

    {'name': 'multiply', 'arguments': [13, 4]}

    完美!

    5. 函数调用!最终步骤!

    tools = [add, multiply]
    tool_map = {tool.name: tool for tool in tools}
    
    def tools_call(model_output):
        chosen_tool = tool_map[model_output["name"]]
        return chosen_tool.invoke(model_output["arguments"])
    
    
    chain = prompt | qianfan_chat | JsonOutputParser() | tools_call
    print(chain.invoke({"input": "what's thirteen times 4"}))

     如果理解了上一篇的内容,这部分毫无难度~。详情可以移步上一篇哦~


    老规矩,全部代码展示!

    from langchain_core.tools import tool
    import os
    from langchain_community.chat_models import QianfanChatEndpoint
    #创建工具调用
    from langchain.tools.render import render_text_description
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import JsonOutputParser
    from operator import itemgetter
    
    @tool
    def multiply(first_int: int, second_int: int) -> int:
        """Multiply two integers together."""
        return first_int * second_int
    
    @tool
    def add(first_int: int, second_int: int) -> int:
        """add two integers together."""
        return first_int + second_int
        
    tools = [add, multiply]
    tool_map = {tool.name: tool for tool in tools}
     
    # 设定百度千帆大模型的AK和SK-去百度千帆官网的控制台新建一个应用即可
    os.environ["QIANFAN_AK"] = "your AK"
    os.environ["QIANFAN_SK"] = "your SK"
     
    #创建千帆LLM模型
    qianfan_chat = QianfanChatEndpoint(
        model="ERNIE-3.5-8K",
        temperature=0.2,
        timeout=30,
    )
    
    # 构建工具条件和调用描述
    rendered_tools = render_text_description([multiply, add])
    print("rendered_tools = ", rendered_tools)
    
    # 构建工具选择器的系统人设
    system_prompt = f"""You are an assistant that has access to the following set of tools. Here are the names and descriptions for each tool:
    
    {rendered_tools}
    
    Given the user input, return the name and input of the tool to use. Return your response as a JSON blob with 'name' and 'arguments' keys."""
    
    # 构建输入的模板
    prompt = ChatPromptTemplate.from_messages(
        [("system", system_prompt), ("user", "{input}")]
    )
    
    # 构建chain以及最后格式化输出
    chain = prompt | qianfan_chat 
    print(chain.invoke({"input": "what's thirteen times 4"}))
    
    def tools_call(model_output):
        chosen_tool = tool_map[model_output["name"]]
        return chosen_tool.invoke(model_output["arguments"])
    
    
    chain = prompt | qianfan_chat | JsonOutputParser() | tools_call
    print(chain.invoke({"input": "what's thirteen times 4"}))
    

    总结

    本篇我们更深一层的剥开抽象的LangChain.对于LangChain的工具调用原理有了更深入的了解。

    下一篇我们将继续我们的Chain之路~构建和尝试更多的Chain!

    有何问题和想要了解的内容敬请留言哦

    作者:千天夜

    物联沃分享整理
    物联沃-IOTWORD物联网 » LangChain工具调用底层原理深度解析(进阶篇)——专为新手呈现!

    发表回复