使用 langchain 进行异步编程
基于 LLM 的应用程序通常涉及许多 I/O 绑定作,例如对语言模型、数据库或其他服务进行 API 调用。异步编程(或异步编程)是一种范例,它允许程序同时执行多个任务而不会阻止其他任务的执行,从而提高效率和响应能力,尤其是在 I/O 绑定作中。
在阅读本指南之前,您应该熟悉 Python 中的异步编程。如果您不是,请在线查找合适的资源以了解如何在 Python 中异步编程。 本指南特别关注在异步上下文中使用 LangChain 需要了解的内容,前提是您已经熟悉异步
Langchain 异步 API
许多 LangChain API 都是异步的,允许您构建高效且响应迅速的应用程序。
通常,任何可以执行 I/O作(例如,进行 API 调用、读取文件)的方法都将具有异步对应项。
在 LangChain 中,异步实现与同步实现位于相同的类中,异步方法具有 “a” 前缀。例如,同步的invokemethod 有一个名为ainvoke.
LangChain 的许多组件都实现了 Runnable Interface,其中包括对异步执行的支持。这意味着你可以使用awaitkeyword 的 Python 中。
await some_runnable.ainvoke(some_input)
其他未实现 Runnable 接口的组件(如 Embedding Models 和 VectorStore)通常仍遵循相同的规则,并在同一个类中包含带有 “a” 前缀的异步版本的方法。
例如
await some_vectorstore.aadd_documents(documents)
使用 LangChain 表达式语言 (LCEL) 创建的可运行对象也可以在实现时异步运行 完整的 Runnable Interface 中。
有关更多信息,请查看您正在使用的特定组件的 API 参考。
委托同步方法
大多数流行的 LangChain 集成都实现了对其 API 的异步支持。例如,ainvoke方法使用httpx.AsyncClient向模型提供程序的 API 发出异步 HTTP 请求。
当异步实现不可用时,LangChain 会尝试提供默认实现,即使它会产生 略有开销。
默认情况下,LangChain 会将未实现的异步方法的执行委托给同步的对应项。LangChain 几乎总是假设 synchronous 方法应该被视为阻塞作,并且应该在单独的线程中运行。
这是asyncio.loop.run_in_executor使用asyncio库。LangChain 使用asyncio库,它使用给定事件循环中重用的默认线程数惰性初始化线程池执行程序。虽然由于线程之间的上下文切换,此策略会产生轻微的开销,但它保证每个异步方法都有一个开箱即用的默认实现。
性能
LangChain 中的异步代码通常应该表现相对较好,开箱即用的开销最小,而且不太可能 成为大多数应用程序中的瓶颈。
开销的两个主要来源是:
- 委托给同步方法时线程之间的上下文切换成本。这可以通过提供本机异步实现来解决。
- 在 LCEL 中,任何作为链一部分出现的“廉价函数”要么被调度为事件循环上的任务(如果它们是异步的),要么在单独的线程中运行(如果它们是同步的),而不仅仅是内联运行。
您应该预期的延迟开销在几十微秒到几毫秒之间。
性能问题的更常见来源是用户在异步上下文中调用同步代码(例如,调用invoke而不是ainvoke).
兼容性
LangChain 仅与asyncio库,该库作为 Python 标准库的一部分分发。它不适用于其他异步库,例如trio或curio.
在 Python 3.9 和 3.10 中,asyncio 的任务没有
接受context参数。由于此限制,LangChain 无法自动传播RunnableConfig调用链下游
在某些情况下。
如果您在异步代码中遇到流式处理、回调或跟踪问题,并且使用的是 Python 3.9 或 3.10,则这可能是一个原因。
请阅读 传播 RunnableConfig 了解更多详细信息,了解如何传播RunnableConfig手动沿调用链向下(或升级到 Python 3.11,这不再是问题)。
如何在 ipython 和 jupyter 笔记本中使用
从 IPython 7.0 开始,IPython 支持异步 REPL。这意味着您可以使用await关键字,无需任何其他设置。有关更多信息,请参阅 IPython 博客文章。