如何从工具中流式传输事件
本指南假定您熟悉以下概念:
如果您有调用聊天模型、检索器或其他可运行对象的工具,您可能希望访问这些可运行对象的内部事件或使用额外属性配置它们。本指南将向您展示如何正确手动传递参数,以便您可以使用 .streamEvents() 方法实现此目的。
为了支持更广泛的 JavaScript 环境,基础 LangChain 包默认不会自动将配置传播到子可运行对象。这包括 .streamEvents() 所需的回调。这是您可能无法看到自定义可运行对象或工具发出事件的常见原因。
您需要手动将 RunnableConfig 对象传播到子可运行对象。有关手动传播配置的示例,请参见下面 RunnableLambda 的 bar 实现。
本指南还要求 @langchain/core>=0.2.16。
假设您有一个自定义工具,它调用了一个链,该链通过提示聊天模型仅返回 10 个单词,然后反转输出来压缩其输入。首先,以一种简单的方式定义它:
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 { ChatAnthropic } from "@langchain/anthropic";
const model = new ChatAnthropic({
model: "claude-3-5-sonnet-20240620",
temperature: 0,
});
import { z } from "zod";
import { tool } from "@langchain/core/tools";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
const specialSummarizationTool = tool(
async (input) => {
const prompt = ChatPromptTemplate.fromTemplate(
"You are an expert writer. Summarize the following text in 10 words or less:\n\n{long_text}"
);
const reverse = (x: string) => {
return x.split("").reverse().join("");
};
const chain = prompt
.pipe(model)
.pipe(new StringOutputParser())
.pipe(reverse);
const summary = await chain.invoke({ long_text: input.long_text });
return summary;
},
{
name: "special_summarization_tool",
description: "A tool that summarizes input text using advanced techniques.",
schema: z.object({
long_text: z.string(),
}),
}
);
直接调用工具也能正常工作:
const LONG_TEXT = `
NARRATOR:
(Black screen with text; The sound of buzzing bees can be heard)
According to all known laws of aviation, there is no way a bee should be able to fly. Its wings are too small to get its fat little body off the ground. The bee, of course, flies anyway because bees don't care what humans think is impossible.
BARRY BENSON:
(Barry is picking out a shirt)
Yellow, black. Yellow, black. Yellow, black. Yellow, black. Ooh, black and yellow! Let's shake it up a little.
JANET BENSON:
Barry! Breakfast is ready!
BARRY:
Coming! Hang on a second.`;
await specialSummarizationTool.invoke({ long_text: LONG_TEXT });
.yad noitaudarg rof tiftuo sesoohc yrraB ;scisyhp seifed eeB
但如果你想访问聊天模型的原始输出而非完整工具,你可以尝试使用
streamEvents()
方法,并查找 on_chat_model_end 事件。以下是其工作原理:
const stream = await specialSummarizationTool.streamEvents(
{ long_text: LONG_TEXT },
{ version: "v2" }
);
for await (const event of stream) {
if (event.event === "on_chat_model_end") {
// Never triggers!
console.log(event);
}
}
请注意,子运行中没有发出聊天模型事件!
这是因为上面的示例未将工具的配置对象传递到内部链中。要解决此问题,请重新定义你的工具,使其接收一个特殊参数,该参数的类型为
RunnableConfig(更多详细信息请参见此指南)。在执行内部链时,还需要将该参数传递进去:
const specialSummarizationToolWithConfig = tool(
async (input, config) => {
const prompt = ChatPromptTemplate.fromTemplate(
"You are an expert writer. Summarize the following text in 10 words or less:\n\n{long_text}"
);
const reverse = (x: string) => {
return x.split("").reverse().join("");
};
const chain = prompt
.pipe(model)
.pipe(new StringOutputParser())
.pipe(reverse);
// Pass the "config" object as an argument to any executed runnables
const summary = await chain.invoke({ long_text: input.long_text }, config);
return summary;
},
{
name: "special_summarization_tool",
description: "A tool that summarizes input text using advanced techniques.",
schema: z.object({
long_text: z.string(),
}),
}
);
现在使用你的新工具尝试与之前相同的 .streamEvents() 调用:
const stream = await specialSummarizationToolWithConfig.streamEvents(
{ long_text: LONG_TEXT },
{ version: "v2" }
);
for await (const event of stream) {
if (event.event === "on_chat_model_end") {
// Never triggers!
console.log(event);
}
}
{
event: 'on_chat_model_end',
data: {
output: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: 'Bee defies physics; Barry chooses outfit for graduation day.',
name: undefined,
additional_kwargs: [Object],
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: [Object]
},
input: { messages: [Array] }
},
run_id: '27ac7b2e-591c-4adc-89ec-64d96e233ec8',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
太棒了!这次有一个事件被触发了。
对于流式传输,如果可能的话,.streamEvents()
会自动调用链中启用流式传输的内部可运行对象,因此如果你希望获得聊天模型生成的
token 流,只需筛选 on_chat_model_stream 事件即可,无需其他更改:
const stream = await specialSummarizationToolWithConfig.streamEvents(
{ long_text: LONG_TEXT },
{ version: "v2" }
);
for await (const event of stream) {
if (event.event === "on_chat_model_stream") {
// Never triggers!
console.log(event);
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: 'Bee',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: ' def',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: 'ies physics',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: ';',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: ' Barry',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: ' cho',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: 'oses outfit',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: ' for',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: ' graduation',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: ' day',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
{
event: 'on_chat_model_stream',
data: {
chunk: AIMessageChunk {
lc_serializable: true,
lc_kwargs: [Object],
lc_namespace: [Array],
content: '.',
name: undefined,
additional_kwargs: {},
response_metadata: {},
id: undefined,
tool_calls: [],
invalid_tool_calls: [],
tool_call_chunks: [],
usage_metadata: undefined
}
},
run_id: '938c0469-83c6-4dbd-862e-cd73381165de',
name: 'ChatAnthropic',
tags: [ 'seq:step:2' ],
metadata: {
ls_provider: 'anthropic',
ls_model_name: 'claude-3-5-sonnet-20240620',
ls_model_type: 'chat',
ls_temperature: 0,
ls_max_tokens: 2048,
ls_stop: undefined
}
}
自动传递配置(高级)
如果你使用过
LangGraph,你可能已经注意到在嵌套调用中无需手动传递配置。这是因为
LangGraph 利用了一个名为
async_hooks 的
API,但在许多(但并非所有)环境中并不支持该功能。
如果需要,你可以通过运行以下代码来全局导入并启用
AsyncLocalStorage,以启用自动配置传递功能:
import { AsyncLocalStorageProviderSingleton } from "@langchain/core/singletons";
import { AsyncLocalStorage } from "async_hooks";
AsyncLocalStorageProviderSingleton.initializeGlobalInstance(
new AsyncLocalStorage()
);
下一步
现在你已经了解了如何在工具内部流式传输事件。接下来,请查看以下指南以了解更多关于使用工具的内容:
你还可以查看一些更具体的工具调用用法: