Skip to main content

如何为大型语言模型(LLMs)和聊天模型添加临时工具调用功能

前提条件

在本指南中,我们将构建一个链(Chain),它不依赖任何特殊的模型 API(例如工具调用,我们在快速入门中展示过),而是直接通过提示词引导模型调用工具。

环境配置

我们需要安装以下包:

yarn add @langchain/core zod

设置环境变量

# 可选,使用 LangSmith 获得最佳可观测性体验
LANGSMITH_API_KEY=your-api-key
LANGSMITH_TRACING=true

# 如果你不在无服务器环境中运行,可减少追踪延迟
# 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

创建我们的提示词

我们需要编写一个提示词,指定模型可以访问的工具、这些工具的参数以及模型的期望输出格式。在这种情况下,我们会指示模型输出一个形式为 {"name": "...", "arguments": {...}} 的 JSON 数据块。

:::提示 从 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 格式。

Pick your chat model:

Install dependencies

yarn add @langchain/groq 

Add environment variables

GROQ_API_KEY=your-api-key

Instantiate the model

import { ChatGroq } from "@langchain/groq";

const model = new ChatGroq({
model: "llama-3.3-70b-versatile",
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 工具输出轻松实现这一点。这将获取传递给 RunnablePassthrough 组件的任何输入(假设为字典类型),并在仍然传递当前输入中所有内容的同时,向其添加一个键:

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' }

接下来是什么?

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

实际上,如果你使用更复杂的工具,你将开始遇到模型返回的错误,特别是对于那些未经过工具调用微调的模型以及能力较弱的模型。

你需要准备添加一些策略来改进模型的输出;例如:

  • 提供少量示例(few shot examples)。
  • 添加错误处理机制(例如,捕获异常并将其反馈给 LLM,要求它修正之前的输出)。

Was this page helpful?


You can also leave detailed feedback on GitHub.