langchain之function calling

561 阅读3分钟

Function calling

OpenAI中叫作function calling,在langchain中被称为tool calling,用于连接模型和外部数据系统。

function calling是一个过程(function + calling),首先定义一系列函数即function,之后模型基于对话内容,判断是否需要使用定义好的函数calling,手动执行函数,将函数返回的结果提供给模型,模型输出结果。 函数可以有多种形式和用途,常见的如

  • fetch data,如检索数据
  • take actions,如根据邮件内容触发记录代办、安排会议等
  • build rich workflow,如数据提取、数据标注
  • intercte with application ui,如根据输入内容,更新页面

OpenAI官网提供的function calling流程图 function-calling-diagram.png

function definition

function definition目的是向模型描述需要使用的函数,包括函数的用途和执行函数需要的入参,格式使用JSON Schema,模型会根据提供的schema生成调用function时所需的参数。

// 分为三部分,函数名、函数描述、函数入参
{
    "name": "get_delivery_date",
    "description": "Get the delivery date for a customer's order. Call this whenever you need to know the delivery date, for example when a customer asks 'Where is my package'",
    // 入参中包含类型、是否可选等内容
    "parameters": {
        "type": "object",
        "properties": {
            "order_id": {
                "type": "string",
                "description": "The customer's order ID."
            }
        },
        "required": ["order_id"],
        "additionalProperties": false
    }
}

langchain中可以使用zod库进行类型定义,描述function的params,使用tool定义function

import { z } from 'zod';
import { tool } from '@langchain/core/tools';

const calculatorSchema = z.object({
  operation: z
    .enum(['add', 'subtract', 'multiply', 'divide'])
    .describe('The type of operation to execute.'),
  number1: z.number().describe('The first number to operate on.'),
  number2: z.number().describe('The second number to operate on.'),
});

const calculatorTool = tool(
  async ({ operation, number1, number2 }) => {
    // Functions must return strings
    if (operation === 'add') {
      return `${number1 + number2}`;
    } else if (operation === 'subtract') {
      return `${number1 - number2}`;
    } else if (operation === 'multiply') {
      return `${number1 * number2}`;
    } else if (operation === 'divide') {
      return `${number1 / number2}`;
    } else {
      throw new Error('Invalid operation.');
    }
  },
  {
    name: 'calculator',
    description: 'Can perform mathematical operations.',
    schema: calculatorSchema,
  }
);

function bind

langchain中使用bindTools将function和模型进行绑定,用于明确哪些function对于模型来说,是可以访问的。

import { ChatOllama } from '@langchain/ollama';
const model = new ChatOllama({
    baseUrl: 'http://localhost:11434',
    model: 'llama3.1',
});
const modelWithTools = model.bindTools([calculatorTool]);

function call

是否决定调用function是由模型根据对话内容决定的,会有不需要调用的情况,区分是否需要调用,可以根据模型返回数据的result来判断,需要调用的情况下,result会有tool_calls变量。

const messages = [new HumanMessage('What is 2 + 2? and 3 - 1?')];
const aiMessage = await modelWithTools.invoke(messages);
console.log(aiMessage);
// 
AIMessage {
  "content": "",
  "additional_kwargs": {},
  "response_metadata": {...},
  "tool_calls": [
    {
      "name": "calculator",
      "args": {
        "number1": 2,
        "number2": 2,
        "operation": "add"
      },
      "id": "dc6acf31-cf47-4467-9ecc-c203c12a2270",
      "type": "tool_call"
    },
    {
      "name": "calculator",
      "args": {
        "number1": 3,
        "number2": 1,
        "operation": "subtract"
      },
      "id": "2277c37a-8ff2-41e3-b365-a3eb15051559",
      "type": "tool_call"
    }
  ],
  "invalid_tool_calls": [],
  "usage_metadata": {...}
}

function execution

根据模型返回的tool_calls内容,执行并获取function返回结果,并将返回结果传递给模型。

messages.push(aiMessage);
for (const toolCall of aiMessage.tool_calls) {
    const toolMessage = await calculatorTool.invoke(toolCall);
    // 将toolMessage收录到对话历史中
    messages.push(toolMessage);
}
// tool.invoke后会生成toolMessage,content中包含执行结果
ToolMessage {
    "content": "4",
    "name": "calculator",
    "additional_kwargs": {},
    "response_metadata": {},
    "tool_call_id": "dc6acf31-cf47-4467-9ecc-c203c12a2270"
  },
  ToolMessage {
    "content": "2",
    "name": "calculator",
    "additional_kwargs": {},
    "response_metadata": {},
    "tool_call_id": "2277c37a-8ff2-41e3-b365-a3eb15051559"
  }
// 进行最后的调用
const res = await modelWithTools.invoke(messages);
console.log(res);
// 
AIMessage {
  "content": "The output of the ipython tool call shows that 2 + 2 = 4, and 3 - 1 = 2. Therefore, the answer to your question is:\n\nYes, you are correct that 2 + 2 = 4 and 3 - 1 = 2.",
  "additional_kwargs": {},
  "response_metadata": {。。。},
  "tool_calls": [],
  "invalid_tool_calls": [],
  "usage_metadata": {
    "input_tokens": 86,
    "output_tokens": 63,
    "total_tokens": 149
  }

TODO more