跳至主要内容

如何在 LLM 和聊天模型中添加即时工具调用功能

先决条件

在本指南中,我们将构建一个链,该链不依赖于任何特殊的模型 API(例如工具调用,我们在 快速入门 中展示了这种方式),而是直接提示模型调用工具。

设置

我们需要安装以下软件包

yarn add @langchain/core zod

设置环境变量

# Optional, use LangSmith for best-in-class observability
LANGSMITH_API_KEY=your-api-key
LANGCHAIN_TRACING_V2=true

# Reduce tracing latency if you are not in a serverless environment
# LANGCHAIN_CALLBACKS_BACKGROUND=true

创建工具

首先,我们需要创建一个要调用的工具。在本例中,我们将从函数创建一个自定义工具。有关创建自定义工具的所有详细信息,请参阅 本指南

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

const multiplyTool = tool(
(input) => {
return (input.first_int * input.second_int).toString();
},
{
name: "multiply",
description: "Multiply two integers together.",
schema: z.object({
first_int: z.number(),
second_int: z.number(),
}),
}
);
console.log(multiplyTool.name);
console.log(multiplyTool.description);
multiply
Multiply two integers together.
await multiplyTool.invoke({ first_int: 4, second_int: 5 });
20

创建我们的提示

我们需要编写一个提示,该提示指定模型可以访问的工具、这些工具的参数以及模型所需的输出格式。在本例中,我们将指示它输出以下形式的 JSON 块 {"name": "...", "arguments": {...}}

提示

langchain 版本 0.2.8 开始,renderTextDescription 函数现在支持 OpenAI 格式的工具

import { renderTextDescription } from "langchain/tools/render";

const renderedTools = renderTextDescription([multiplyTool]);
import { ChatPromptTemplate } from "@langchain/core/prompts";

const systemPrompt = `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.`;

const prompt = ChatPromptTemplate.fromMessages([
["system", systemPrompt],
["user", "{input}"],
]);

添加输出解析器

我们将使用 JsonOutputParser 来将我们的模型输出解析为 JSON。

选择你的聊天模型

安装依赖项

yarn add @langchain/openai 

添加环境变量

OPENAI_API_KEY=your-api-key

实例化模型

import { ChatOpenAI } from "@langchain/openai";

const model = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0
});
import { JsonOutputParser } from "@langchain/core/output_parsers";
const chain = prompt.pipe(model).pipe(new JsonOutputParser());
await chain.invoke({
input: "what's thirteen times 4",
rendered_tools: renderedTools,
});
{ name: 'multiply', arguments: [ 13, 4 ] }

调用工具

我们可以通过将模型生成的“参数”传递给它来将工具作为链的一部分调用

import { RunnableLambda, RunnablePick } from "@langchain/core/runnables";

const chain = prompt
.pipe(model)
.pipe(new JsonOutputParser())
.pipe(new RunnablePick("arguments"))
.pipe(
new RunnableLambda({
func: (input) =>
multiplyTool.invoke({
first_int: input[0],
second_int: input[1],
}),
})
);
await chain.invoke({
input: "what's thirteen times 4",
rendered_tools: renderedTools,
});
52

从多个工具中选择

假设我们有多个工具,我们希望链能够从中选择

const addTool = tool(
(input) => {
return (input.first_int + input.second_int).toString();
},
{
name: "add",
description: "Add two integers together.",
schema: z.object({
first_int: z.number(),
second_int: z.number(),
}),
}
);

const exponentiateTool = tool(
(input) => {
return Math.pow(input.first_int, input.second_int).toString();
},
{
name: "exponentiate",
description: "Exponentiate the base to the exponent power.",
schema: z.object({
first_int: z.number(),
second_int: z.number(),
}),
}
);

使用函数调用,我们可以这样做

如果我们想运行选择的模型工具,我们可以使用一个返回基于模型输出的工具的函数。具体来说,我们的函数将返回它自己的子链,该子链获取模型输出的“arguments”部分并将其传递给选择的工具。

import { StructuredToolInterface } from "@langchain/core/tools";

const tools = [addTool, exponentiateTool, multiplyTool];

const toolChain = (modelOutput) => {
const toolMap: Record<string, StructuredToolInterface> = Object.fromEntries(
tools.map((tool) => [tool.name, tool])
);
const chosenTool = toolMap[modelOutput.name];
return new RunnablePick("arguments").pipe(
new RunnableLambda({
func: (input) =>
chosenTool.invoke({
first_int: input[0],
second_int: input[1],
}),
})
);
};
const toolChainRunnable = new RunnableLambda({
func: toolChain,
});

const renderedTools = renderTextDescription(tools);
const systemPrompt = `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.`;

const prompt = ChatPromptTemplate.fromMessages([
["system", systemPrompt],
["user", "{input}"],
]);
const chain = prompt
.pipe(model)
.pipe(new JsonOutputParser())
.pipe(toolChainRunnable);
await chain.invoke({
input: "what's 3 plus 1132",
rendered_tools: renderedTools,
});
1135

返回工具输入

返回工具输出和工具输入都很有帮助。我们可以通过 RunnablePassthrough.assign 将工具输出分配给 LCEL 来轻松实现。这将获取传入 RunnablePassrthrough 组件的任何输入(假设为字典),并向其中添加一个键,同时仍然传递当前输入中的所有内容。

import { RunnablePassthrough } from "@langchain/core/runnables";

const chain = prompt
.pipe(model)
.pipe(new JsonOutputParser())
.pipe(RunnablePassthrough.assign({ output: toolChainRunnable }));
await chain.invoke({
input: "what's 3 plus 1132",
rendered_tools: renderedTools,
});
{ name: 'add', arguments: [ 3, 1132 ], output: '1135' }

下一步

本指南展示了当模型正确输出所有必需的工具信息时的“理想路径”。

实际上,如果您使用更复杂的工具,您将开始遇到来自模型的错误,尤其是对于那些没有针对工具调用进行微调的模型以及能力较低的模型而言。

您需要准备好添加策略来改进模型的输出,例如:

  • 提供少样本示例。
  • 添加错误处理(例如,捕获异常并将异常反馈给 LLM 以要求其更正其之前的输出)。

此页面是否有帮助?


您也可以留下详细的反馈 在 GitHub 上.