如何为大型语言模型(LLMs)和聊天模型添加临时工具调用功能
本指南假定您已经熟悉以下概念:
在本指南中,我们将构建一个链(Chain),它不依赖任何特殊的模型 API(例如工具调用,我们在快速入门中展示过),而是直接通过提示词引导模型调用工具。
环境配置
我们需要安装以下包:
- npm
- yarn
- pnpm
npm i @langchain/core zod
yarn add @langchain/core zod
pnpm 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:
- Groq
- OpenAI
- Anthropic
- Google Gemini
- FireworksAI
- MistralAI
- VertexAI
Install dependencies
- npm
- yarn
- pnpm
npm i @langchain/groq
yarn add @langchain/groq
pnpm 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
});
Install dependencies
- npm
- yarn
- pnpm
npm i @langchain/openai
yarn add @langchain/openai
pnpm add @langchain/openai
Add environment variables
OPENAI_API_KEY=your-api-key
Instantiate the model
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0
});
Install dependencies
- npm
- yarn
- pnpm
npm i @langchain/anthropic
yarn add @langchain/anthropic
pnpm add @langchain/anthropic
Add environment variables
ANTHROPIC_API_KEY=your-api-key
Instantiate the model
import { ChatAnthropic } from "@langchain/anthropic";
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
temperature: 0
});
Install dependencies
- npm
- yarn
- pnpm
npm i @langchain/google-genai
yarn add @langchain/google-genai
pnpm add @langchain/google-genai
Add environment variables
GOOGLE_API_KEY=your-api-key
Instantiate the model
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
const model = new ChatGoogleGenerativeAI({
model: "gemini-2.0-flash",
temperature: 0
});
Install dependencies
- npm
- yarn
- pnpm
npm i @langchain/community
yarn add @langchain/community
pnpm add @langchain/community
Add environment variables
FIREWORKS_API_KEY=your-api-key
Instantiate the model
import { ChatFireworks } from "@langchain/community/chat_models/fireworks";
const model = new ChatFireworks({
model: "accounts/fireworks/models/llama-v3p1-70b-instruct",
temperature: 0
});
Install dependencies
- npm
- yarn
- pnpm
npm i @langchain/mistralai
yarn add @langchain/mistralai
pnpm add @langchain/mistralai
Add environment variables
MISTRAL_API_KEY=your-api-key
Instantiate the model
import { ChatMistralAI } from "@langchain/mistralai";
const model = new ChatMistralAI({
model: "mistral-large-latest",
temperature: 0
});
Install dependencies
- npm
- yarn
- pnpm
npm i @langchain/google-vertexai
yarn add @langchain/google-vertexai
pnpm add @langchain/google-vertexai
Add environment variables
GOOGLE_APPLICATION_CREDENTIALS=credentials.json
Instantiate the model
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 工具输出轻松实现这一点。这将获取传递给
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,要求它修正之前的输出)。