今天,我们非常激动地宣布,LangChain 0.1.0 版本正式发布了,这是我们推出的首个稳定版本。这个版本能够兼容以前的版本,提供了 Python 和 JavaScript 两种编程语言的支持,并通过改进功能和文档,使得我们的产品更加专注和高效。LangChain 的这个稳定版本不仅赢得了开发者们的信任,还为我们提供了一个系统且安全地更新和完善这个软件库的机会。
简介
LangChain 已经诞生一年多,它在成长为 LLM 应用的首选框架过程中,经历了诸多变化。就像我们一个月前预览的那样,我们近期对 LangChain 的包架构做了重大调整,目的是为了更好地组织这个项目,同时也加强了它的基础结构。
具体来说,我们进行了两项重要的架构变革:一是分离出了 langchain-core 核心模块;二是将合作伙伴的包分为 langchain-community 社区版或独立的合作伙伴包。
提醒一下,langchain-core 包含了主要的抽象概念、接口和核心功能。这部分代码已经非常稳定,并在过去一个多月里遵循了更加严格的版本管理政策。
但是,LangChain 本身之前一直停留在 0.0.x 版本。所有版本都处于初级阶段,这带来了一些挑战:
- 用户担心更新后可能会遇到兼容性问题。
- 为了尽量避免破坏性的更新和频繁的弃用通知,我们采取了全面维护的策略,结果却使得 LangChain 变得庞大且不稳定。
不过,从今天开始,随着 LangChain 0.1.0 版本的发布,所有未来的版本更新都将遵循新的版本标准。具体是:
- 任何对公共 API(Application Programming
Interface,应用程序编程接口)造成破坏性更改的操作,都会导致次要版本号的提升(即版本号的第二位数字)。 - 任何错误修复或新增功能都会导致补丁版本号的提升(即版本号的第三位数字)。
我们期望这些结合之前的架构变革,能够:
- 清晰地通报任何重大更改,让开发者能够放心更新
- 为我们提供正式淘汰及清理老旧代码的途径,减少代码冗余
- 更加负责任地处理集成问题(这些集成的 SDK 变化通常和 LangChain 一样迅速)
- 即便在我们推出 0.2 版本之后,我们依然承诺维护 0.1 版本的一个分支,但仅针对关键 bug 提供修补。
在我们将包结构调整为稳定的 0.1 版本的过程中,我们抓住机会与数百名开发者交流,了解他们选择使用 LangChain 的原因和喜欢的特点。这些反馈指导了我们的发展方向和重点。我们也借此机会在下文提到的核心领域,使 Python 和 JavaScript 版本达到一致。
💡 尽管某些集成和更特定的链可能针对特定语言,但核心抽象和关键功能在 Python 和 JavaScript 的包中均有同等实现。
我们希望分享我们的发现以及不断完善 LangChain 的计划。我们相信,分享这些经验将增加我们决策和思考方式的透明度,让更多人能更好地使用、理解并贡献于 LangChain。毕竟,LangChain 的一大部分是我们的社区,包括广大用户群和 2000 多名贡献者,我们希望每个人都能参与到这个旅程中来。
三方集成
LangChain 最受用户青睐的特点之一是其易用性,它让人们可以轻松地在任何技术栈上开始构建应用。我们提供了近 700 种集成,涵盖从 LLM 到向量存储,再到 Agent 使用的各种工具。
💡 LangChain 常被视为构建 LLM 应用所需各种组件的 “粘合剂”,因此,构建一个强大的集成生态系统对我们来说非常重要。
大约一个月前,我们开始实施一些改变,旨在提高集成的健壮性、稳定性、可扩展性以及整体开发者体验。我们把所有第三方集成都移到了 langchain-community 中,这样我们就能集中处理与集成相关的工作。同时,我们也开始将个别集成分离到它们自己的包中。
到目前为止,我们已经对约 10 个包进行了这样的处理,包括 OpenAI、Google 和 Mistral 等。这样做的好处之一是能更好地管理依赖关系 —— 之前所有依赖都是可选的,这在安装特定版本时可能会带来问题。现在,将集成放入各自的包中,我们可以对它们的要求进行更严格的版本控制,从而简化安装过程。另一个好处是版本控制。第三方集成有时会变更,可能需要进行重大更改。现在,这些更改可以在独立的集成包中得到适当的版本反映。
可观测性
构建 LLM 应用时,系统核心将会是一个非确定性组件。这些模型时常会产生出乎意料的结果,因此,清楚地了解系统中正在发生的情况是很重要的。
💡 我们希望使 LangChain 尽可能地易于观测和调试,无论是通过我们的架构设计,还是我们所开发的工具。
我们以几种方式实现了这一目标。
主要方法之一是开发了 LangSmith。LangSmith 的一个核心优势是为你的 LLM 应用提供一流的调试体验。我们详细记录了正在执行的每个步骤、每个步骤的输入、输出、所需时间等数据。我们以用户友好的方式展示这些信息,让你能够识别哪些步骤耗时最长,进入一个调试区域来处理 LLM 意外反馈的问题,追踪 Token 使用情况等。即便在私有测试阶段,LangSmith 就已经受到了极大的欢迎,我们正在大量投入资源以提高其可扩展性,以便未来几个月发布公共测试版,并最终普及它。我们还支持企业版本,为那些对数据隐私要求严格的企业提供虚拟专用云(VPC)内部署服务。
我们还通过其他途径增强了可观测性。长期以来,我们为整个处理流程提供了详细和调试模式,支持不同级别的日志记录。最近,我们还引入了一种新方法,可以可视化用户所创建的链条,并获取链条中使用的所有提示信息。
可组合性
尽管预先构建的链条有助于快速入门,但我们常见团队希望突破现有架构,定制属于自己的链条。这种定制不仅限于提示,还包括编排的各个部分。
💡 在过去几个月里,我们大力投资于 LangChain 表达式语言(LCEL)。LCEL 允许自由组合各种序列,为数据工程流程带来了类似于数据编排工具的诸多好处,如批处理、并行处理和回退策略。
LCEL 还提供了一些特定于 LLM 工作负载的独特好处,主要包括之前提到的 LLM 专用的可观测性,以及稍后将在本文中介绍的流处理功能。
LCEL 的核心组件位于 langchain-core 中。我们已经开始在 LangChain 中为特定链条创建更高级别的入口点。这些新的入口点将逐步取代现有的(现称为 “遗留”)链条,因为基于 LCEL 构建的链条将自带流处理、易于定制、可观测性、批处理和重试等功能。我们的目标是实现平滑过渡。以前你可能需要这样做:
ConversationalRetrievalChain.from_llm(llm, …)
而现在,我们希望简化为:
create_conversational_retrieval_chain(llm, …)
在底层,它将创建一个特定的 LCEL 链条并返回。如果你想修改逻辑,也没问题!因为一切都是用 LCEL 编写的,修改其中一部分而无需继承类或覆盖方法变得很容易。
LangChain 中有许多链条被广泛使用。在有替代构造函数存在且经过使用和充分测试之前,我们不会弃用遗留版本的链条。
流式处理
LLM 有时反应较慢。向最终用户展示正在进行的工作,而不是让他们盯着空白屏幕,十分重要。这可以通过从 LLM 流式传输 Token,或者流式传输更长时间运行的链条或 Agent 的中间步骤来实现。
我们在这两方面都进行了大量投资。所有用 LCEL 构建的链条均提供标准的 stream 和 astream 方法,我们还做了大量工作,确保流处理不仅限于 LLM 调用阶段(比如在输出解析器中也实现了流处理)。所有链条还提供了一个标准的 astream_log 方法,用于流式传输 LCEL 链条中的所有步骤。这样就可以轻松过滤这些步骤,获取中间步骤和其他信息。
💡 流式处理(包括 Token 和中间步骤)是大多数 LLM 应用中非常重要的用户体验部分,而通过 LangChain,你可以轻松实现这一功能。
输出解析
LangChain 的一个主要功能是 “工具使用”,即利用 LLM 来操作其他工具。
💡 确保 LLM 返回的信息格式适合后续应用并便于操作,这对于 LLM 的实际应用非常重要。
我们围绕这一需求,提出了 “输出解析器” 的概念,并致力于提升开发者的体验。
实现这一目标的关键方式之一是通过 OpenAI 的函数调用功能。我们不仅简化了指定输出格式的过程 —— 可以选择使用 Pydantic、JSON schema 或特定的函数,还方便了开发者处理模型的响应。
对于不支持 OpenAI 函数调用的模型,我们还支持多种编码方法(如 JSON、XML、Yaml),以便在使用提示进行操作时,能够更灵活地处理。使用提示时,还需要精确的指令来引导 LLM 作出恰当的回应 —— 所有的输出解析器都配备了一个 get_format_instructions 方法,用于获取这些指令。
我们还对输出解析器引入了更高级的功能,例如允许它们在生成过程中实时传输部分结果,以此提升用户体验。这包括从 JSON、XML 和 CSV 等结构化格式中实时传输部分结果。但输出解析有时颇具挑战性,例如要解析一个 JSON 数据块,大多数 JSON 解析器需要完整的数据块。我们的许多输出解析器都内置了处理这种部分解析的逻辑。
检索
我们发现开发者正在构建的主流应用类型之一是能与他们自己的私有数据进行交互的应用。
💡 将个人数据与 LLM 结合起来,是 LangChain 的一个核心特性。
这通常涉及两个环节:数据摄取(准备数据)和数据检索(获取数据),我们已经开发了相应的功能。
在数据摄取方面,关键步骤之一是将待处理的文本分割成小块。虽然这听起来简单,但实际上如何分割取决于文档的类型,这需要细致的考量。我们提供了 15 种不同的文本分割器,针对特定类型的文档(如 HTML 和 Markdown)进行了优化,使开发者能更好地控制这一过程。
需要注意的是,相关数据可能会频繁变化,我们的数据摄取系统适用于大规模的应用。我们设计了一个索引 API,允许重新摄取内容,同时忽略那些未发生变化的部分,这样可以为处理大量数据节省时间和成本。
在检索方面,我们不仅开发了更加先进、适合生产环境的检索方法,还实现了一系列来自学术界的高级检索策略,如 FLARE 和 Hyde,以及我们自创的方法,如 Parent Document 和 Self-Query。我们还参考了其他行业的解决方案,如 Multi-Query(这是一种在搜索中常用的查询扩展方法)。
此外,我们特别关注生产环境中的需求,比如为每个用户单独检索数据,这对于存储多用户文档的应用来说至关重要。
重要的是,尽管 LangChain 提供了构建高级检索系统所需的一切组件,但我们并不限制使用方式。这就激发了其他许多库在 LangChain 基础上发展,提供更加专业化的检索方法,例如 EmbedChain 和 GPTResearcher。
Agent
LangChain 最初因其在代理性工作负载方面的表现而受到关注。这主要体现在两个方面:
工具使用:让 LLM 调用某个函数或工具。
推理:优化 LLM 的使用方式,使其能够合理决定是否多次调用工具,以及调用的顺序。
在工具使用方面,我们重点覆盖了以下几个关键组件:
与众多第三方工具的集成。
确保 LLM 的响应能够匹配这些工具的输入要求。
提供灵活的方式来定制工具的调用方式(LCEL)。
在推理方面,我们开发了几种不同的 “Agent” 方法。这些方法大致上是让 LLM 在一个循环中运行,每次循环中决定是否需要调用某个工具,然后观察该工具的执行结果。我们从一开始就整合了 ReAct 这种早期的提示策略,并迅速加入了多种类型,包括使用 OpenAI 函数调用、使用新的工具调用 API、优化对话等等。
💡 通过灵活、可扩展的工具支持和先进的推理能力,LangChain 已成为 LLM 能够采取行动的首选方法。
与检索功能类似,虽然 LangChain 提供了构建 Agent 的基础模块,但我们也看到了一些更具主见的框架在其基础上发展,例如 CrewAI,它在 LangChain 基础上发展,为处理多 Agent 工作负载提供了更简便的界面。
总结
随着生态系统的发展,LangChain 也取得了明显的进步。我们非常感谢我们的社区和用户的推动和共建。通过这次 0.1 版本的发布,我们深入了解了用户在 LLM 框架中的需求,并保持致力于满足这些需求。随着社区需求的变化(或者如果我们遗漏了什么),我们期待听到你的反馈,以便做出改进。千里之行,始于足下,让我们从 0.1 版本开始。