Skip to main content

如何将运行时值传递给工具

预备知识
支持的模型

本指南使用具有原生工具调用功能的模型。 您可以查看支持工具调用的所有模型列表

您可能需要将仅在运行时才知道的值绑定到某个工具。例如,工具的逻辑可能需要使用发起请求的用户 ID。

大多数情况下,这些值不应由 LLM 控制。实际上,允许 LLM 控制用户 ID 可能会导致安全风险。

相反,LLM 应该仅控制那些设计上应由 LLM 控制的工具参数,而其他参数(如用户 ID)应由应用程序逻辑固定。

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 llm = new ChatGroq({
model: "llama-3.3-70b-versatile",
temperature: 0
});

使用上下文变量

兼容性

此功能是在 @langchain/core>=0.3.10 中添加的。如果您在项目中单独使用 LangSmith SDK,我们也建议升级到 langsmith>=0.1.65。请确保您的软件包已更新。

它还需要 async_hooks 支持,在所有环境中并非都支持此功能。

解决此问题的一种方法是使用上下文变量。上下文变量是一个强大的功能,允许您在应用程序的更高级别设置值,然后在从该级别调用的子 runnable(例如工具)中访问它们。

它们的工作方式超出了传统的范围规则,因此您无需直接引用已声明的变量即可访问其值。

下面,我们声明了一个工具,该工具根据名为 userId 的上下文变量更新中心 userToPets 状态。请注意,此 userId 不属于工具的 schema 或输入:

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

let userToPets: Record<string, string[]> = {};

const updateFavoritePets = tool(
async (input) => {
const userId = getContextVariable("userId");
if (userId === undefined) {
throw new Error(
`No "userId" found in current context. Remember to call "setContextVariable('userId', value)";`
);
}
userToPets[userId] = input.pets;
return "update_favorite_pets called.";
},
{
name: "update_favorite_pets",
description: "add to the list of favorite pets.",
schema: z.object({
pets: z.array(z.string()),
}),
}
);

如果在更高层级设置上下文变量之前调用上述工具,userId 将会是 undefined

await updateFavoritePets.invoke({ pets: ["cat", "dog"] });
Error: No "userId" found in current context. Remember to call "setContextVariable('userId', value)";
at updateFavoritePets.name (evalmachine.<anonymous>:14:15)
at /Users/jacoblee/langchain/langchainjs/langchain-core/dist/tools/index.cjs:329:33
at AsyncLocalStorage.run (node:async_hooks:346:14)
at AsyncLocalStorageProvider.runWithConfig (/Users/jacoblee/langchain/langchainjs/langchain-core/dist/singletons/index.cjs:58:24)
at /Users/jacoblee/langchain/langchainjs/langchain-core/dist/tools/index.cjs:325:68
at new Promise (<anonymous>)
at DynamicStructuredTool.func (/Users/jacoblee/langchain/langchainjs/langchain-core/dist/tools/index.cjs:321:20)
at DynamicStructuredTool._call (/Users/jacoblee/langchain/langchainjs/langchain-core/dist/tools/index.cjs:283:21)
at DynamicStructuredTool.call (/Users/jacoblee/langchain/langchainjs/langchain-core/dist/tools/index.cjs:111:33)
at async evalmachine.<anonymous>:3:22

相反,请设置一个上下文变量,作为调用工具的位置的父级:

import { setContextVariable } from "@langchain/core/context";
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
import { RunnableLambda } from "@langchain/core/runnables";

const handleRunTimeRequestRunnable = RunnableLambda.from(
async (params: { userId: string; query: string; llm: BaseChatModel }) => {
const { userId, query, llm } = params;
if (!llm.bindTools) {
throw new Error("Language model does not support tools.");
}
// Set a context variable accessible to any child runnables called within this one.
// You can also set context variables at top level that act as globals.
setContextVariable("userId", userId);
const tools = [updateFavoritePets];
const llmWithTools = llm.bindTools(tools);
const modelResponse = await llmWithTools.invoke(query);
// For simplicity, skip checking the tool call's name field and assume
// that the model is calling the "updateFavoritePets" tool
if (modelResponse.tool_calls.length > 0) {
return updateFavoritePets.invoke(modelResponse.tool_calls[0]);
} else {
return "No tool invoked.";
}
}
);

当我们的方法调用工具时,你将看到工具正确访问了先前设置的 userId 上下文变量,并成功运行:

await handleRunTimeRequestRunnable.invoke({
userId: "brace",
query: "my favorite animals are cats and parrots.",
llm: llm,
});
ToolMessage {
"content": "update_favorite_pets called.",
"name": "update_favorite_pets",
"additional_kwargs": {},
"response_metadata": {},
"tool_call_id": "call_vsD2DbSpDquOtmFlOtbUME6h"
}

并且还更新了 userToPets 对象,添加了一个与我们传入的 userId(即 "brace")匹配的键:

console.log(userToPets);
{ brace: [ 'cats', 'parrots' ] }

没有上下文变量的情况下

如果你使用的是较早版本的 core,或者所处的环境不支持 async_hooks,你可以使用以下设计模式,在运行时动态创建工具,并将其绑定到相应的值。

其思路是在请求时动态创建工具,并将其绑定到相应的信息。例如, 这些信息可以是从请求本身解析出的用户 ID。

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

userToPets = {};

function generateToolsForUser(userId: string) {
const updateFavoritePets = tool(
async (input) => {
userToPets[userId] = input.pets;
return "update_favorite_pets called.";
},
{
name: "update_favorite_pets",
description: "add to the list of favorite pets.",
schema: z.object({
pets: z.array(z.string()),
}),
}
);
// You can declare and return additional tools as well:
return [updateFavoritePets];
}

验证工具是否正常工作

const [updatePets] = generateToolsForUser("cobb");

await updatePets.invoke({ pets: ["tiger", "wolf"] });

console.log(userToPets);
{ cobb: [ 'tiger', 'wolf' ] }
import { BaseChatModel } from "@langchain/core/language_models/chat_models";

async function handleRunTimeRequest(
userId: string,
query: string,
llm: BaseChatModel
): Promise<any> {
if (!llm.bindTools) {
throw new Error("Language model does not support tools.");
}
const tools = generateToolsForUser(userId);
const llmWithTools = llm.bindTools(tools);
return llmWithTools.invoke(query);
}

这段代码将允许 LLM 调用工具,但 LLM并不知道存在用户 ID这一事实。你可以看到,user_id 并不在 LLM 生成的参数列表中:

const aiMessage = await handleRunTimeRequest(
"cobb",
"my favorite pets are tigers and wolves.",
llm
);
console.log(aiMessage.tool_calls[0]);
{
name: 'update_favorite_pets',
args: { pets: [ 'tigers', 'wolves' ] },
type: 'tool_call',
id: 'call_FBF4D51SkVK2clsLOQHX6wTv'
}

:::提示 点击 这里 查看上述运行的 LangSmith 跟踪。 :::

:::提示 聊天模型仅输出调用工具的请求。它们不会实际调用底层工具。

如需了解如何调用工具,请参阅 如何使用模型调用工具。 :::


Was this page helpful?


You can also leave detailed feedback on GitHub.