AI项目博客
记录 AI 工程实践与大模型应用

LangChain 1.2 入门(十):invoke、stream、batch 与异步调用

模型建好之后,就是调用。LangChain 提供六种核心调用方法,区别主要在「等不等结果」和「一次发几个」:

方法 特点 适用场景
invoke() 阻塞式,一次性返回完整结果 问答、批处理任务、无需实时反馈
ainvoke() 非阻塞式,提高系统吞吐量 高并发 Web 应用、IO 密集型任务
stream() 流式输出,实时返回每个 token 聊天机器人、长文本生成、重视体验的交互
astream() 非阻塞的流式输出 高并发场景下的流式输出
batch() 批量处理多个输入 需要同时处理大量请求
abatch() 非阻塞的批量处理 高并发批量任务

10.1 invoke()

invoke 是最核心的方法,工作模式是阻塞式的:程序等待模型完全生成整个响应后,再一次性返回结果。它做三件事:接收输入(问题、指令、对话历史)→ 发送给 LLM → 返回响应(文本加元数据)。

response = model.invoke(input, config=None)
参数 类型 说明 必需 默认值
input str / list[dict] / list[Message] 等 要发送给模型的内容 必需 无
config dict 高级配置(回调函数、元数据、标签等) 可选 None

10.1.1 三种输入形式

1、文本输入(最简单)。直接传一个问题或指令,适合快速测试,不需要保留对话历史的简单生成任务。缺点是无法设置系统提示,也无法传递对话历史。

prompt = "翻译成英文:你好世界"
response = model.invoke(prompt)
print(response)

2、字典列表(推荐,最灵活)。用 role 和 content 组成消息列表,可以设置系统提示、表达多轮对话历史,JSON 兼容、易于序列化和网络传输,生产环境推荐。

messages = [
    {"role": "system", "content": "系统提示"},
    {"role": "user", "content": "用户消息"},
    {"role": "assistant", "content": "AI 回复"},  # 可选,用于对话历史
    {"role": "user", "content": "继续提问"},
]
response = model.invoke(messages)
角色 英文 作用 示例
system System 设定 AI 的行为、角色、规则 「你是一个专业的 Python 导师」
user Human/User 用户的输入或问题 「什么是装饰器?」
assistant AI/Assistant AI 的历史回复,用于对话上下文 「装饰器是一种设计模式……」

user 和 human 有时可以互换,但遵循主流模型提供商(如 OpenAI)的惯例,使用 user 最稳妥。

from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os

load_dotenv(override=True)

model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=os.getenv("CLOSEAI_API_KEY"),
    base_url=os.getenv("CLOSEAI_BASE_URL"),
)

messages = [
    {"role": "system", "content": "你是一个专业的数学老师。"},
    {"role": "user", "content": "什么是斐波那契数列?"},
]
response = model.invoke(messages)
print(f"AI的回复:{response.content}")

3、消息对象列表。使用内置的 SystemMessage、HumanMessage、AIMessage,适合需要类型检查、IDE 自动补全的大型项目;缺点是代码更长、不如字典简洁、难以序列化。

from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

messages = [
    SystemMessage("你是一个专业的数学老师。"),
    HumanMessage("2 + 3 * 2 = ?"),
    AIMessage("8"),
    HumanMessage("我刚才问什么问题了?"),
]
response = model.invoke(messages)
print(response.content)
消息类 对应字典格式 作用
SystemMessage {“role”: “system”, …} 系统提示
HumanMessage {“role”: “user”, …} 用户输入
AIMessage {“role”: “assistant”, …} AI 回复

10.1.2 不带历史就会「失忆」

第一次问「你好,我叫小明」,第二次问「我叫什么名字」。如果第二次只发新问题,模型会回答「我不知道你的名字」——两个请求之间没有任何记忆。把上一轮的 assistant 回复和本轮问题追加进同一个列表,模型就能答对。

conversation = [
    {"role": "system", "content": "你是一个非常友好的AI助手"},
    {"role": "user", "content": "你好,我叫小明"},
]
response1 = model.invoke(conversation)
print(f"AI的回复1:{response1.content}")

# 把 AI 回复和下一轮问题追加进历史
conversation.append({"role": "assistant", "content": response1.content})
conversation.append({"role": "user", "content": "我叫什么名字?"})

response2 = model.invoke(conversation)
print(f"AI的回复2:{response2.content}")  # 你叫小明。

10.1.3 返回值:AIMessage

invoke 返回一个 AIMessage 对象,源码签名如下:

def invoke(
    self,
    input: LanguageModelInput,
    config: RunnableConfig | None = None,
    *,
    stop: list[str] | None = None,
    **kwargs: Any,
) -> AIMessage:

AIMessage 的字段可以分成五类:

  1. 核心内容:content 是模型生成的文本;id 是本次运行在 LangChain 内部的唯一标识;additional_kwargs 存厂商附加参数,refusal 表示拒答原因。
  2. 消耗统计:prompt_tokens/input_tokens 是输入 Token,completion_tokens/output_tokens 是输出 Token,total_tokens 是总消耗;推理模型还有 reasoning_tokens,命中缓存时有 cached_tokens。
  3. 响应元数据:model_name、model_provider、finish_reason(stop 正常结束、length 达到长度限制)、system_fingerprint。
  4. 性能与延迟:total_duration_ms 总耗时、user_visible_ttft_ms 用户看到首字的等待时间、engine_ttft_ms 引擎首字时间、service_tbt_ms 字与字之间的间隔(决定打字机效果是否丝滑)、pre_inference_ms 推理前处理耗时。
  5. 工具调用:tool_calls 是模型决定调用的工具列表,invalid_tool_calls 是格式错误的调用尝试。
AIMessage(
    content='2 + 3 * 2 = **8**',
    additional_kwargs={'refusal': None},
    response_metadata={
        'token_usage': {
            'completion_tokens': 15,
            'prompt_tokens': 16,
            'total_tokens': 31,
            'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0},
            'latency_checkpoint': {
                'engine_tbt_ms': 4,
                'engine_ttft_ms': 36,
                'engine_ttlt_ms': 100,
                'pre_inference_ms': 86,
                'service_tbt_ms': 4,
                'service_ttft_ms': 280,
                'service_ttlt_ms': 338,
                'total_duration_ms': 259,
                'user_visible_ttft_ms': 194,
            },
        },
        'model_provider': 'openai',
        'model_name': 'gpt-5.4-mini-2026-03-17',
        'system_fingerprint': None,
        'id': 'chatcmpl-DgWobsxhDOqzjqVFwbZYKRnovpEiV',
        'service_tier': 'default',
        'finish_reason': 'stop',
        'logprobs': None,
    },
    id='lc_run--019e3659-5ee2-7b62-bc8a-741e27374b43-0',
    tool_calls=[],
    invalid_tool_calls=[],
    usage_metadata={
        'input_tokens': 16,
        'output_tokens': 15,
        'total_tokens': 31,
        'input_token_details': {'audio': 0, 'cache_read': 0},
        'output_token_details': {'audio': 0, 'reasoning': 0},
    },
)

按需访问这些字段:

response = model.invoke("用一句话解释什么是 AI")
print("AI 回复:", response.content)

meta = response.response_metadata
print(f"使用的模型: {meta['model_name']}")
print(f"结束原因: {meta['finish_reason']}")
print(f"模型提供商:{meta['model_provider']}")

usage = meta.get('token_usage', {})
print(f"输入 tokens: {usage.get('prompt_tokens')}")
print(f"输出 tokens: {usage.get('completion_tokens')}")
print(f"总计 tokens: {usage.get('total_tokens')}")
print(f"消息 ID: {response.id}")

10.2 流式调用 stream()

invoke 在模型输出完成后才一次性返回,输出文本很长时用户体验不好。stream 返回一个迭代器,边生成边返回 chunk,可以逐 token 渲染。注意:流式输出依赖模型供应商对流式的支持。

for chunk in model.stream("写一首七言律诗,总结大模型的发展"):
    print(chunk.text, end="", flush=True)

输出不再是一整段,而是逐字出现。stream 的优点:响应更快,用户不必等待完整输出;交互更流畅,尤其在长文本或复杂推理场景;可以实时展示模型的生成过程。

10.3 批量调用 batch()

batch 一次发送一组请求,模型在后台并行处理,然后按原始输入顺序返回结果列表。相比逐个调用 invoke,能大幅减少网络往返和等待时间,适合文档摘要、批量问答、数据预处理、多样本分类。

messages = [
    "你好,你是谁?",
    "2 + 3 * 5 = ?",
    "中国首都在哪里?",
]
responses = model.batch(messages)
for response in responses:
    print(response)

如果输入列表很大,或单个请求耗时差异明显,用 batch_as_completed():谁先完成就先返回,结果可能乱序。每个元素是 (原始输入索引, AIMessage) 元组,可以按索引重新排序。

for index, response in model.batch_as_completed(messages):
    print(index, response.content)

性能对比:4 条翻译请求,batch 耗时约 1.91 秒,循环 invoke 耗时约 3.87 秒,批量调用节省约 50.7%。

import time

inputs = [
    "翻译成英文:春天来了",
    "翻译成英文:夏天很热",
    "翻译成英文:秋天落叶",
    "翻译成英文:冬天下雪",
]

start = time.time()
responses = model.batch(inputs)
batch_time = time.time() - start
print(f"批量调用耗时: {batch_time:.2f}秒")

10.4 异步调用

同步:发起任务后必须等待任务完成,才能继续执行后续任务,当前执行流会被阻塞。异步:发起任务后不必等待,可以继续执行其他任务;任务完成后仍可通过特定方式获取结果,执行流不会被阻塞。

同步与异步的执行流程对比

LangChain 的异步方法 ainvoke、astream、abatch 与同步版本一一对应,价值在于避免阻塞主线程、提高资源利用率。以下示例建议放在 .py 文件中执行,不要在 Jupyter 里跑。

ainvoke():

import asyncio
import time
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os

load_dotenv(override=True)
model = init_chat_model(
    model="openai:gpt-5.4-mini",
    api_key=os.getenv("CLOSEAI_API_KEY"),
    base_url=os.getenv("CLOSEAI_BASE_URL"),
)

async def demo_async_invoke():
    start = time.perf_counter()
    print(">>> 发起异步模型调用 (ainvoke)...")
    task = asyncio.create_task(model.ainvoke("用一句话解释人工智能。"))

    for i in range(3):
        await asyncio.sleep(1)
        print(f">>> 正在执行第{i + 1}个任务... (已耗时 {time.perf_counter() - start:.2f}s)")

    response = await task
    print(f">>> 模型返回: {response.content}")
    print(f"=== 总运行耗时: {time.perf_counter() - start:.2f}s ===")

asyncio.run(demo_async_invoke())

三个本地任务各 1 秒,模型请求同时在后台执行,总耗时约 3.02 秒;如果串行,模型调用时间还要再叠加。

astream():

async def demo_async_stream():
    start = time.perf_counter()
    stream_resp = model.astream("请用一句话解释机器学习的基本概念。")

    for i in range(3):
        await asyncio.sleep(1)
        print(f">>> 正在执行第{i + 1}个任务... (已耗时 {time.perf_counter() - start:.2f}s)")

    print(">>> 流式输出: ", end="", flush=True)
    async for chunk in stream_resp:
        content = chunk.content if hasattr(chunk, "content") else str(chunk)
        print(content, end="", flush=True)
    print(f"\n=== 总运行耗时: {time.perf_counter() - start:.2f}s ===")

asyncio.run(demo_async_stream())

abatch():

async def demo_async_batch():
    start = time.perf_counter()
    questions = [
        "用一句话说明深度学习与传统机器学习的区别",
        "中国首都在哪里?",
    ]
    batch_task = asyncio.create_task(model.abatch(questions))

    for i in range(3):
        await asyncio.sleep(1)
        print(f">>> 正在执行第{i + 1}个任务... (已耗时 {time.perf_counter() - start:.2f}s)")

    responses = await batch_task
    for response in responses:
        print(f">>> 响应内容: {response.content}")
    print(f"=== 总运行耗时: {time.perf_counter() - start:.2f}s ===")

asyncio.run(demo_async_batch())

10.5 如何处理 API 调用失败

网络、限流、配置错误都可能让调用失败,线上代码至少要用 try-except 包住:

try:
    response = model.invoke("Hello")
    print(response.content)
except ValueError as e:
    print(f"配置错误: {e}")
except ConnectionError as e:
    print(f"网络错误: {e}")
except Exception as e:
    print(f"未知错误: {e}")

10.6 小结

优先用字典列表传消息,能带 system 和历史;长文本用 stream;批量任务用 batch,并可用 max_concurrency 控制并发;高并发服务用异步方法;线上务必加异常处理。

下一篇:LangChain 1.2 入门(十一):输出美化、profile 与 config;上一篇:Ollama 本地模型部署与调用。

本文根据公开课程课件整理,并结合 LangChain 官方文档核对与补充。

赞(0)
未经允许不得转载:AI项目博客 » LangChain 1.2 入门(十):invoke、stream、batch 与异步调用

评论 抢沙发

AI项目博客

专注 AI 工程化落地 —— 覆盖智能体、大模型、Java AI 与 Python 的学习笔记,以及 AI 平台的项目实战记录。

关于本站文章归档

登录

找回密码

注册