🎯 课程主题
介绍LangChain中模型的三种调用方式:流式调用(stream)、批量调用(batch)与异步调用(ainvoke/astream/abatch),以及它们的使用场景和性能对比。
📝 核心知识点
1. 流式调用 (stream)
- 概念说明:
stream方法以迭代器方式实时返回模型响应的片段,用户可以逐段看到输出内容,而不必等待完整响应。 - 关键细节:
- 与
invoke的区别:invoke一次性返回完整结果,stream逐片段返回 - 本质是返回一个迭代器,通过
for chunk in model.stream("...")遍历 - 每个 chunk 通过
.content获取文本内容 - 依赖模型供应商对流式输出的支持(早期部分模型不支持,新模型基本都支持)
- 优势:响应速度更快、交互体验更流畅、适合长文本生成场景,可实时展示推理模型的思考过程
- 与
2. 批量调用 (batch)
- 概念说明:
batch方法允许一次性发送一组请求给模型,模型在后台并行处理,以列表形式返回所有结果。 - 关键细节:
- 传入参数是一个字符串/消息列表,返回也是一个结果列表
- 相比逐个
invoke调用,能大幅减少网络往返开销 - 两次子方法对比:
model.batch(messages)→ 一次性接收所有响应,结果顺序与请求顺序一致model.batch_as_completed(messages)→ 按任务完成顺序接收响应,结果可能是乱序的,每个元素为(index, result)元组
- 性能实测:3个问题 batch 调用耗时约2.24秒,顺序 invoke 调用耗时约5.10秒,节省约56%时间
3. 异步调用 (ainvoke / astream / abatch)
- 概念说明:与同步的三个方法对应,异步版本分别为
ainvoke、astream、abatch,避免阻塞主线程。 - 关键细节:
- 同步 vs 异步:同步调用中任务A等待任务B完成才继续执行(阻塞);异步调用中任务A发起任务B后不等待,继续执行自身逻辑
ainvoke返回协程对象,通过asyncio.create_task()包装为任务后在后台执行astream是一个异步生成器函数,调用后返回异步迭代器,通过async for遍历abatch同理,返回协程对象- 主要优势:避免阻塞主线程、优化CPU资源利用、减少整体等待时间
4. 异常处理
- 概念说明:调用模型时需使用 try-except 进行异常捕获。
- 关键细节:调用完成后打印结果,在异常分支中打印相关错误信息。
🏗️ 架构与工作流
调用方式对比:
┌─────────┬──────────────┬─────────────────┐
│ 类型 │ 同步 │ 异步 │
├─────────┼──────────────┼─────────────────┤
│ 单次调用 │ invoke │ ainvoke │
│ 流式调用 │ stream │ astream │
│ 批量调用 │ batch │ abatch │
│ │ batch_as_ │ │
│ │ completed │ │
└─────────┴──────────────┴─────────────────┘
💻 代码实战
# ===== 模型初始化 =====
from langchain_deepseek import ChatDeepSeek
model = ChatDeepSeek(model="deepseek-chat")
# ===== 1. 流式调用 =====
for chunk in model.stream("帮我解释一下什么是人工智能"):
print(chunk.content, end="", flush=True)
# ===== 2. 批量调用 - 一次性接收所有响应 =====
messages = [
"你是谁?",
"请介绍一下北京",
"中国的首都在哪里?"
]
results = model.batch(messages)
for res in results:
print(res.content)
# ===== 3. 批量调用 - 按完成顺序接收 =====
for idx, result in model.batch_as_completed(messages):
print(f"[索引{idx}] {result.content}")
# ===== 4. 性能对比:batch vs 顺序invoke =====
import time
start = time.time()
results = model.batch(messages)
batch_time = time.time() - start
print(f"batch耗时: {batch_time:.2f}秒")
start = time.time()
for msg in messages:
model.invoke(msg)
invoke_time = time.time() - start
print(f"顺序invoke耗时: {invoke_time:.2f}秒")
print(f"节省: {(1 - batch_time / invoke_time) * 100:.1f}%")
# ===== 5. 异步调用示例 =====
import asyncio
async def main():
task = asyncio.create_task(model.ainvoke("你好"))
for i in range(5):
print(f"主线程循环 {i}")
await asyncio.sleep(1)
response = await task
print(response.content)
asyncio.run(main())
# ===== 6. 异步流式调用 =====
async def stream_demo():
async for chunk in model.astream("用一句话介绍Python"):
print(chunk.content, end="", flush=True)
asyncio.run(stream_demo())
# ===== 7. 异步批量调用 =====
async def batch_demo():
task = asyncio.create_task(model.abatch(messages))
for i in range(3):
print(f"主线程循环 {i}")
await asyncio.sleep(1)
results = await task
for res in results:
print(res.content)
asyncio.run(batch_demo())
# ===== 8. 异常处理 =====
try:
response = model.invoke("你好")
print(response.content)
except Exception as e:
print(f"调用出错: {e}")
⚠️ 常见问题与避坑指南
stream调用依赖模型厂商对流式输出的支持,早期部分模型不支持batch_as_completed的结果顺序与请求顺序可能不一致,需注意索引- 异步调用在 LangChain v1.2 中对应的三个方法是
ainvoke、astream、abatch - 异步编程中
asyncio.create_task创建的任务会立即在后台执行
💡 个人总结与延伸
本节课涵盖了LangChain模型调用的全部六种方法(同步3种 + 异步3种),核心要点是理解各自适用场景:日常交互用 invoke,用户体验优先用 stream,批量任务用 batch,需并发非阻塞场景用异步版本。性能对比表明 batch 相比顺序 invoke 可节省约50%时间,在实际项目中应优先考虑批量调用以减少网络往返开销。