• AI系列:大语言模型的function calling(下)- 使用LangChain


    前言

    AI系列:大语言模型的function calling(上) 中我们实现了OpenAI原生的function calling。这篇文章将继续探讨如何使用LangChain实现大语言模型(LLM)的function calling。

    LangChain Tool/Function calling

    LangChain提供了对LLM function calling的支持。前提是底层大模型必须支持function calling。

    1. Tool/function加强功能

    LangChain的tool装饰器

    LangChain在langchain_core模块中的tools子模块中提供了名为tool的装饰器,将根据函数定义和注释自动生成不同LLM function calling功能需要的schema,然后传递给LLM。后续对于LLM的调用将包括这些function/tool schema。

    在Python中可以通过下面这种方式为自己定义的函数导入tool装饰器:

    from langchain_core.tools import tool
    @tool
    def multiply(first_int: int, second_int: int) -> int:
        """两个整数相乘"""
        return first_int * second_int
    
    @tool
    def add(first_add: int, second_add: int) -> int:
        """两个整数相加"""
        return first_add + second_add
    
    tools=[multiply, add]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    其他方式: Pydantic

    除了tool解释器,LangChain还支持用Pydantic来定义schema的方式。比如:

    from langchain_core.pydantic_v1 import BaseModel, Field
    
    # 注释很重要,会被用来生成schema。
    class Add(BaseModel):
        """Add two integers together."""
    
        a: int = Field(..., description="First integer")
        b: int = Field(..., description="Second integer")
    
    tools=[Add]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2. 绑定tools/functions

    绑定tool组成的列表后,LangChain将根据函数定义和注释自动生成底层LLM的function calling功能需要的schema,并传递给LLM。以OpenAI为例,

    from langchain_openai import ChatOpenAI
    llm = ChatOpenAI(model="gpt-3.5-turbo")
    llm_with_tools = llm.bind_tools(tools)
    
    • 1
    • 2
    • 3

    3. 调用大模型(LLM)

    LangChain调用LLM的接口输入参数为Message的列表。它抽象了几个不同的Message类型。以OpenAI为例,它们分别对应OpenAI的不同role属性的对话项。比如HumanMessage, AIMessage, ToolMessage分别对应OpenAI的user, assistant和tool的角色。

    from langchain_core.messages import HumanMessage, ToolMessage
    prompt = "一共有3个人,每个人有15个苹果,10个鸭梨,一共有多少苹果?"
    
    messagesLC = [
        HumanMessage(prompt)
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    调用的方法也比较直接,直接调用invoke方法即可。注意我们已经将tools绑定在LLM中,调用时,LangChain将自动将tools的schema传递给LLM。

    #通过LangChain调用LLM接口,将LLM回复加入对话上下文
    response = llm_with_tools.invoke(messagesLC) 
    messagesLC.append(response)
    
    • 1
    • 2
    • 3

    返回类型为AIMessage。

    4. function calling处理流程

    function calling整个处理的逻辑与上篇中的介绍完全一致,这里就不累述了。具体可以参考AI系列:大语言模型的function calling(上)

    整个对话上下文如下,抽象成这样,是不是感觉比OpenAI原生的更清楚一点:

    [
    HumanMessage(content='一共有3个人,每个人有15个苹果,10个鸭梨,一共有多少苹果?'), 
    AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LsvajoCqf1G6ZNNS8M1gildQ', 'function': {'arguments': '{"first_int":3,"second_int":15}', 'name': 'multiply'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'logprobs': None}), 
    ToolMessage(content='45', tool_call_id='call_LsvajoCqf1G6ZNNS8M1gildQ'), 
    AIMessage(content='一共有45个苹果。', response_metadata={'finish_reason': 'stop', 'logprobs': None})
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    LangChain版代码

    以下是LangChain版function calling的实现代码,可以对比大语言模型的function calling(上) OpenAI原生版的实现代码。

    __author__ = 'liyane'
    
    import json
    
    # 初始化环境
    from dotenv import load_dotenv, find_dotenv
    _ = load_dotenv(find_dotenv())
    
    #定义function/tool 1: multiply,应用tool装饰器
    from langchain_core.tools import tool
    @tool
    def multiply(first_int: int, second_int: int) -> int:
        """两个整数相乘"""
        return first_int * second_int
    
    #定义function/tool 2: add,应用tool装饰器
    @tool
    def add(first_add: int, second_add: int) -> int:
        """两个整数相加"""
        return first_add + second_add
    tools=[multiply, add]
    
    #定义大模型并绑定tools
    from langchain_openai import ChatOpenAI
    llm = ChatOpenAI(model="gpt-3.5-turbo")
    llm_with_tools = llm.bind_tools(tools)
    
    #构建对话
    from langchain_core.messages import HumanMessage, ToolMessage
    prompt = "一共有3个人,每个人有15个苹果,10个鸭梨,一共有多少苹果?"
    
    messagesLC = [
        HumanMessage(prompt)
    ]
    
    #通过LangChain调用LLM接口,将LLM回复加入对话上下文
    response = llm_with_tools.invoke(messagesLC) 
    messagesLC.append(response)
    
    #如果LLM需要function calling,调用相应的函数,并将函数结果数据加入对话上下文,继续调用LLM。
    while (response.additional_kwargs.get("tool_calls") is not None):
        for tool_call in response.additional_kwargs["tool_calls"]:
            selected_tool = {"add": add, "multiply": multiply}[tool_call["function"]["name"]]
            args = json.loads(tool_call["function"]["arguments"])
            tool_output = selected_tool(args)
            messagesLC.append(ToolMessage(tool_output, tool_call_id=tool_call["id"]))
    
        response = llm_with_tools.invoke(messagesLC) 
        messagesLC.append(response)
    
    print("=====最终结果=====")
    print(response.content)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    与原生LLM调用的比较

    对于Function calling这部分功能,使用LangChain可以节省大段的手工定义tool schema的代码,也避免了未来有变动时会产生的维护问题。

    同时,LangChain屏蔽了底层LLM。当LLM改变时,无需重写代码,只需要替换LangChain对应LLM的库文件即可。

    目前LangChain快速迭代过程中,代码大功能上可以用,在细节上可能存在问题,遇到具体问题时可能需要看它的代码。

    参考

    OpenAI / function calling
    LangChain / Tool/function calling
    LangChain Message Types

  • 相关阅读:
    MySQl有哪些索引(种类)?索引特点?为什么要使用索引?
    从零开始C语言精讲篇5:指针
    通信基础之天线知识梳理---2022/12/05
    初识设计模式 - 观察者模式
    小节3:数据类型
    【C++】【Opencv】cv::GaussianBlur、cv::filter2D()函数详解和示例
    科技视界杂志科技视界杂志社科技视界编辑部2022年第18期目录
    Maven optional的作用
    【docker系列】docker compose的v1\v2版本安装及使用上的区别
    云原生数据库TDSQL-C
  • 原文地址:https://blog.csdn.net/liyane/article/details/137868786