在 LangChain 里,模型初始化好之后,真正“提问”的动作有三种常见方式:invoke、stream、batch。它们面对的是同一类问题,但交互形态、适用场景、乃至参数和返回值都不一样。这篇文章把三者的差异讲清楚,重点说清每种方式的参数和输出。
先厘清一个容易踩的坑:JS 里没有 ainvoke / astream / abatch
如果你是从 Python 版 LangChain 过来的,可能会习惯 ainvoke、astream、abatch 这类带 a 前缀的写法。但在 LangChain JS 中并不存在这些方法。
原因很直接:JS 里的 invoke / stream / batch 本身就是异步的(返回 Promise 或异步迭代器),不需要再用 a 前缀区分同步和异步。所以:
// JS 正确写法
await model.invoke("...");
for await (const chunk of await model.stream("...")) {}
await model.batch(["...", "..."]);
一、invoke:一问一答
invoke 是最基础的调用方式:给一个输入,拿到一个完整回复。
参数
await model.invoke(input, options);
input:可以是多种形式——
- 字符串:
"用一句话介绍你自己" - 单条消息:
new HumanMessage("...") - 消息数组(用于角色区分 / 多轮):
[new SystemMessage(...), new HumanMessage(...)] PromptValue(由 PromptTemplate 格式化而来)
options:可选的单次调用配置,会覆盖构造模型时的默认值,常用字段包括:
temperature、topP、maxTokens、stop—— 本次调用的生成参数;callbacks—— 本次调用的回调(日志、追踪);signal——AbortSignal,用于取消请求;maxRetries—— 覆盖重试次数;configurable—— 配合initChatModel做运行时字段覆盖。
输出
返回 Promise<AIMessage>。这是一个消息对象,主要字段有:
content:模型回复的文本(或内容块数组);tool_calls:模型请求调用的工具(若有);usage_metadata:token 用量(输入 / 输出 / 总计);response_metadata:厂商原始响应元信息;additional_kwargs:附加参数。
示例
const res = await model.invoke("用一句话介绍你自己", { temperature: 0.3 });
console.log(res.content); // 回复文本
console.log(res.usage_metadata); // token 用量
适用场景:绝大多数“一次请求、一次完整回复”的场景,比如问答、摘要、分类。
二、stream:边生成边返回
stream 适合需要“打字机效果”或长文本输出的场景,模型每生成一小段就返回一次。
参数
签名与 invoke 一致:
const stream = await model.stream(input, options);
input 和 options 的含义与 invoke 相同。
输出
返回 Promise<IterableReadableStream<AIMessageChunk>>——一个异步迭代器,需要用 for await...of 逐个消费。每个元素是 AIMessageChunk,主要字段:
content:本批次新增的文本片段;tool_call_chunks:工具调用的分片(若流式返回工具调用);usage_metadata:通常只在最后一个 chunk 上携带(取决于服务端)。
提示:多个 chunk 可以用 .concat() 拼接成一个完整的 AIMessageChunk。
示例
const stream = await model.stream("写一首关于秋天的短诗");
for await (const chunk of stream) {
process.stdout.write(chunk.content); // 逐块打印
}
适用场景:聊天界面、长文生成、需要实时反馈的交互。
三、batch:一次处理多个输入
batch 把多个输入打包,并发调用模型,返回一组结果,适合批量任务。
参数
await model.batch(inputs, options?, batchOptions?);
- inputs:输入数组,每个元素的形式和
invoke的input一样,例如["问题1", "问题2", "问题3"]; - options:可以是单个配置(应用到所有输入),也可以是与 inputs 等长的数组(为每个输入单独配置);
- batchOptions:批处理选项,字段有:
maxConcurrency:最大并发数,控制请求压力(注意在较新版本中更推荐通过 runnable config 传入);returnExceptions:为true时,失败的任务不抛错,而是在结果数组对应位置返回Error对象。
输出
返回 Promise<结果数组>,顺序与输入一致:
- 默认情况下每个元素是
AIMessage(遇到错误则整体抛错); - 当
returnExceptions: true时,元素类型为AIMessage | Error,需要自行判断。
示例
const results = await model.batch(
["用一句话解释 JavaScript", "用一句话解释 Python", "用一句话解释 Go"],
{ maxConcurrency: 2 }
);
results.forEach((r, i) => console.log(`[${i + 1}] ${r.content}`));
容错写法:
const results = await model.batch(inputs, {}, { returnExceptions: true });
results.forEach((r) =>
r instanceof Error ? console.error("失败:", r.message) : console.log(r.content)
);
适用场景:批量翻译、批量打标、数据集预处理、对比多个提示词效果。
四、三者对比
| 维度 | invoke | stream | batch |
|---|---|---|---|
| 输入 | 单个输入 | 单个输入 | 输入数组 |
| 是否异步 | 是(Promise) | 是(异步迭代器) | 是(Promise) |
| 返回类型 | Promise<AIMessage> | AsyncIterable<AIMessageChunk> | Promise<AIMessage[]> |
| 拿到结果的方式 | await | for await...of | await |
| 额外参数 | options | options | options + batchOptions |
| 典型场景 | 一问一答 | 打字机效果 / 长文本 | 批量处理 |
五、怎么选
- 只要一个完整回复 → invoke
- 需要实时输出、提升交互体验 → stream
- 有一批输入要一起跑、且能接受并发 → batch
- 想要流式 + 批量 → 可以
batch之后再对每个结果stream,或用链式组合,但通常分开实现更清晰
另外,options 在这三种方式里含义一致,只是 batch 多了一层 batchOptions 用于控制并发和异常。理解这一点,就能在三者之间自如切换。
六、小结
invoke、stream、batch 分别对应“单次、流式、批量”三种交互形态,返回值依次是完整的 AIMessage、分片的 AIMessageChunk 流、以及结果数组。它们的调用参数基本共享同一套 options,batch 额外支持 batchOptions 控制并发与容错。最后再强调一句:在 LangChain JS 中,这些方法本身就是异步的,不存在 ainvoke / astream / abatch。