LangChain 模型调用实战:invoke、stream 与 batch
LangChain 在模型调用层提供了一套统一抽象,把不同供应商的对话 API 收敛为几个核心方法:invoke()、stream()、batch(),以及对应的异步版本 ainvoke()、astream()、abatch()。这套抽象的价值在于,切换模型时不必重写调用逻辑,只需更换初始化参数。但不同输入格式、返回值结构和流式支持程度,仍会影响工程实现。
说明:本文内容整理自一篇 CSDN 社区教程(community_signal),并非 LangChain 官方文档。文中出现的模型名、初始化类与参数写法均为该教程中的示例,仅用于说明调用形态,不代表官方推荐或产品能力背书,实际使用请以各供应商与 LangChain 官方文档为准。
核心调用方法一览
该社区教程对几种方法给出了如下定位。invoke() 是阻塞式调用,一次性返回完整结果,适合问答、批处理等无需实时反馈的场景。stream() 是流式输出,实时返回每个 token,适合聊天机器人、长文本生成等需要提升体验的交互应用。batch() 用于批量处理多个输入,模型会在后台并行处理,然后按原始输入顺序返回结果列表,适合文档摘要、批量问答、数据预处理等场景。异步版本 ainvoke()、astream()、abatch() 则面向高并发 Web 应用和 IO 密集型任务,用于提高系统吞吐量。需要注意的是,流式输出依赖模型供应商对流式输出的支持。
invoke 的三种输入形式
invoke() 支持文本输入、字典列表和消息对象列表。文本输入最简单,传入问题即可自动转化为 user message,但无法设置 system prompt,也无法传递对话历史。字典列表在该教程中被推荐用于生产环境,每条消息包含 role 和 content,可以设置 system、user、assistant 三种角色,JSON 兼容,易于序列化和网络传输。消息对象列表使用 SystemMessage、HumanMessage、AIMessage 等内置类,适合需要类型检查和 IDE 自动补全的大型项目,但代码较长,不如字典简洁,也难以直接序列化。
多轮会话与记忆传递
LangChain 本身不会自动保留会话历史。如果不传递历史消息,模型在后续提问中会“失忆”。该教程中的做法是:在字典列表或消息对象列表中显式加入 assistant 的历史回复。例如先发送 system 和 user 消息,拿到 AI 回复后,将其作为 assistant 消息追加到列表,再追加新的 user 消息,最后再次调用 invoke。这样模型就能记住第一轮对话内容。对于多轮对话,维护一个 conversation 列表是最简单的实现方式。
invoke 返回值解析
invoke() 返回一个 AIMessage 对象。核心字段是 content,即模型生成的文本回答,这是最常用的输出。id 是 LangChain 内部生成的运行标识符。additional_kwargs 包含特定供应商的额外参数,例如 refusal 字段会在模型拒绝回答时显示拒绝原因。response_metadata 中可能包含 token_usage 等信息,usage_metadata 则汇总了输入 token、输出 token 和总 token 数。tool_calls 和 invalid_tool_calls 用于工具调用场景。不同供应商的返回字段可能存在差异,解析时不宜硬编码。
流式输出实战
stream() 返回一个迭代器,通过循环逐块处理新生成的 chunk,通常读取 chunk.text。与 invoke() 等待完整输出不同,stream() 能让用户更快看到响应,交互体验更流畅,在长文本或复杂推理场景下尤其明显。该教程中的通义千问示例使用 ChatTongyi 初始化 qwen3-max,然后遍历 stream 输出。这里的关键点是:流式能力由模型供应商决定,如果供应商不支持,stream 行为可能不符合预期。
批量与异步调用
batch() 接收一组独立请求,一次性发送,等待所有请求处理完毕后返回结果列表。与逐个顺序 invoke 相比,它能减少网络往返开销和等待时间。该教程中的 DeepSeek 示例用 batch() 同时提问“你好,你是谁?”“2 + 3 * 5 = ?”“中国首都在哪里?”,返回结果按原始输入顺序排列。异步方法 ainvoke()、astream()、abatch() 的使用方式与同步版本对应,区别在于非阻塞执行,适合高并发场景。
美化输出与工程化封装建议
AIMessage 直接 print 可读性一般,该教程提到两种美化方式:使用 pretty_print(),或使用 rich 库的 rprint 打印对象。在工程化封装时,可以把模型初始化、消息拼装、调用方式选择、历史追加和异常处理收敛到一个统一入口。例如定义一个会话管理类,内部维护消息字典列表,对外提供 ask() 和 ask_stream() 方法,分别封装 invoke 与 stream。模型初始化则根据供应商选择不同方式:该教程中 DeepSeek 示例使用 init_chat_model 并传入 api_key 和 base_url,通义千问示例使用 ChatTongyi 并传入 api_key。
排错清单
第一,流式输出异常时,先确认模型供应商是否支持流式输出。第二,文本输入无法设置 system prompt,也无法携带历史,需要改用字典列表或消息对象列表。第三,多轮对话中模型“失忆”,通常是因为没有把 assistant 回复追加回消息列表。第四,不同模型的初始化类不同,不要混用参数结构。第五,返回值字段因供应商而异,解析 response_metadata 和 additional_kwargs 时要有容错。第六,需要统计成本时,可读取 usage_metadata 中的 token 计数。
总体来看,LangChain 的调用抽象降低了多模型切换成本,但工程落地仍要关注输入格式、流式支持、历史管理和返回值差异。把这些边界处理好,才能让统一接口真正稳定可用。