如何在 LLM 和聊天模型中添加即时工具调用功能
本指南假设你熟悉以下概念
在本指南中,我们将构建一个链,该链不依赖于任何特殊的模型 API(例如工具调用,我们在 快速入门 中展示了这种方式),而是直接提示模型调用工具。
设置
我们需要安装以下软件包
- npm
- yarn
- pnpm
npm i @langchain/core zod
yarn add @langchain/core zod
pnpm 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。
选择你的聊天模型
- OpenAI
- Anthropic
- FireworksAI
- MistralAI
- Groq
- VertexAI
安装依赖项
- npm
- yarn
- pnpm
npm i @langchain/openai
yarn add @langchain/openai
pnpm add @langchain/openai
添加环境变量
OPENAI_API_KEY=your-api-key
实例化模型
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0
});
安装依赖项
- npm
- yarn
- pnpm
npm i @langchain/anthropic
yarn add @langchain/anthropic
pnpm add @langchain/anthropic
添加环境变量
ANTHROPIC_API_KEY=your-api-key
实例化模型
import { ChatAnthropic } from "@langchain/anthropic";
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
temperature: 0
});
安装依赖项
- npm
- yarn
- pnpm
npm i @langchain/community
yarn add @langchain/community
pnpm add @langchain/community
添加环境变量
FIREWORKS_API_KEY=your-api-key
实例化模型
import { ChatFireworks } from "@langchain/community/chat_models/fireworks";
const model = new ChatFireworks({
model: "accounts/fireworks/models/llama-v3p1-70b-instruct",
temperature: 0
});
安装依赖项
- npm
- yarn
- pnpm
npm i @langchain/mistralai
yarn add @langchain/mistralai
pnpm add @langchain/mistralai
添加环境变量
MISTRAL_API_KEY=your-api-key
实例化模型
import { ChatMistralAI } from "@langchain/mistralai";
const model = new ChatMistralAI({
model: "mistral-large-latest",
temperature: 0
});
安装依赖项
- npm
- yarn
- pnpm
npm i @langchain/groq
yarn add @langchain/groq
pnpm add @langchain/groq
添加环境变量
GROQ_API_KEY=your-api-key
实例化模型
import { ChatGroq } from "@langchain/groq";
const model = new ChatGroq({
model: "mixtral-8x7b-32768",
temperature: 0
});
安装依赖项
- npm
- yarn
- pnpm
npm i @langchain/google-vertexai
yarn add @langchain/google-vertexai
pnpm add @langchain/google-vertexai
添加环境变量
GOOGLE_APPLICATION_CREDENTIALS=credentials.json
实例化模型
import { ChatVertexAI } from "@langchain/google-vertexai";
const model = new ChatVertexAI({
model: "gemini-1.5-flash",
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 以要求其更正其之前的输出)。