如何为 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(),
}),
}
);
使用函数调用,我们可以这样做
如果我们想要运行模型选择的工具,我们可以使用一个函数,该函数根据模型输出返回工具。具体来说,我们的函数将操作返回它自己的子链,该子链获取模型输出的“参数”部分并将其传递给所选工具。
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
返回工具输入
返回工具输出和工具输入都非常有用。我们可以轻松地使用 LCEL 通过 RunnablePassthrough.assign
分配工具输出来实现。这将接受 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 以请求其更正其之前的输出)。