从API到Agent:万字长文洞悉LangChain工程化设计

   日期:2024-12-25    作者:hnxinming 移动:http://3jjewl.riyuangf.com/mobile/quote/20590.html

作者:范志东

我想做一个尝试,看看能不能用尽量清晰的逻辑,给“AI外行人士”(当然,我也是……)引入一下LangChain,试着从工程角度去理解LangChain的设计和使用。同时大家也可以将此文档作为LangChain的“10分钟快速上手”手册,本意是希望帮助需要的同学实现AI工程的Bootstrap。

文中所有的示例代码都已托管到GitHub:

喜欢实操的小伙伴可以边阅读,边复现。

正式开始前,还是有必要从定义(What)开始。LangChain是2022年10月底,由哈佛大学的Harrison Chase发起的基于开源大语言模型的AI工程开发框架。当然也可以问一下AI:

我:LangChain是什么? GPT-4:LangChain 是一个开源的语言模型工具链框架,旨在使研究人员和开发人员能够更轻松地构建、实验和部署以自然语言处理(NLP)为中心的应用程序。它提供了多种组件和工具,可帮助用户利用最近的语言模型进展,如大型 Transformer 模型等,并且可以与 Hugging Face 等平台集成。 LangChain 的核心理念是将语言模型用作协作工具,通过它,开发者可以构建出处理复杂任务的系统,并且可以高效地对接不同的数据源和应用程序接口(APIs)。这个框架试图简化连接不同的输入和输出流,以及在流中实现语言模型的过程。

顾名思义,LangChain中的“Lang”自然是指大语言模型,“Chain”即“链”,也就是将大模型与其他组件连接成链,借此构建AI工程应用。那么LangChain该如何(How)做到这一点的呢?解答这个问题之前,需要先回答什么是工程?什么是AI工程?

我们先Review一下“工程”的百科定义:

工程是指以某组设想的目标为依据,应用有关的科学知识和技术手段,通过有组织的一群将某个(或某些)现有实体(自然的或人造的)转化为具有预期使用价值的人造产品过程。

其中,“目标”定义了要解决的问题域,决定了工程的顶层设计和能力边界,同时也定义了“产品”的最终形态。提升“人”的ROI是工程设计的价值归属。“实体”是工程的生产材料输入,“科学 | 技术”是工程有序运行的基础,对它们的合理利用可以提升工程的整体产出效率。

于是,我们可以这样解读“AI工程”中的关键概念:

因此,如果我们是LangChain的设计者,希望构建通用的AI工程框架。需要回答如下问题:

当然,作为“事后诸葛”,这些问题目前有比较明确的答案:

很明显,LLM作为LangChain能力的基础,是了解LangChain工程化设计的前提。接下来我们就从最基础的LLM API使用谈起,一步步了解LangChain的工程化构建过程及其背后的设计理念。

架构设计领域有个比较流行的术语——乐高架构,当然也可以叫可插拔架构。说白就是通过对系统基本组件的合理抽象,找到构造复杂系统的统一规律和可达路径,从而实现在降低系统实现复杂度的同时,提升系统整体的扩展性。(非官方表达,大家能Get到我的意思就好……)

LangChain实际上也遵循了乐高架构的思想。当然,作为最关键的乐高组件之一,LLM的能力自然是我们优先了解的对象,那我们就从OpenAI的API开始吧!

文本生成模型服务是OpenAI提供的最核心的API服务,自ChatGPT发布后经历过几次版本迭代。

3.1.1 Chat Completion API

当下最新的是,是AI与LLM交互的核心入口。

代码示例参考:

代码示例输出:

图计算是一种计算模型,用于处理大规模图形结构的数据,并执行各种复杂的算法和计算。这种计算模型主要用于社交网络分析、Web搜索、生物信息学、网络路由优化、数据挖掘等领域。图计算模型的核心是将数据表示为图形结构(节点和边),这样可以更好地揭示数据之间的关系和互动。在图计算中,算法通常以迭代的方式运行,每次迭代都会更新图中节点的状态,直到达到某种停止条件。

3.1.2 Completion API

早先的已经在2023年7月后不再维护,和最新的Chat Completion API参数和结果格式有所不同,最明显的是Prompt是以纯文本方式传递,而非Message格式。

除了文本生成服务,OpenAI也提供了大量的LLM的周边服务,以协助AI工程构建更复杂的应用能力。如:函数调用、嵌入、微调、多模态等,具体可参考的内容。

自2022年11月底ChatGPT发布以来,AI的大门才真正地向人类打开,其中给用户留下最深印象的功能,自然是智能对话。OpenAI的Chat Completion API参数支持传入消息历史,可以轻松地实现简单的对话服务。

代码示例参考:

代码示例输出:

me: 什么是图计算? ai: 图计算是一种计算模型,用于处理大规模图形结构数据的计算和分析。在这种计算模型中,数据被表示为图形,其中节点代表实体,边代表实体之间的关系。图计算可以用于解决许多实际问题,如社交网络分析、网络路由、生物信息学等。图计算的主要挑战是如何有效地处理大规模的图形数据,并提供快速的计算和分析结果。 me: 刚才我问了什么问题? ai: 你问的问题是:“什么是图计算?”

到目前为止,我们还只是用OpenAI最原始的RESTful API构建LLM工程能力,甚至连OpenAI提供的SDK都未使用。显然这不是一个高效的方式,使用前边安装的LangChain-OpenAI集成包可以大大降低代码的开发成本。

代码示例参考:

代码示例输出:

content='图计算是一种计算模型,主要用于处理图形结构数据的计算和分析。图计算的对象是图,图由节点和边组成,节点代表实体对象,边代表实体对象之间的关系。图计算主要用于解决实体关系复杂、关系密集的问题,如社交网络分析、网络拓扑分析、推荐系统等。图计算的主要任务是通过对图中节点和边的计算,发现和提取出图中隐含的知识和信息。'

对于文本生成模型服务来说,实际的输入和输出本质上都是字符串,因此直接裸调用LLM服务带来的问题是要在输入格式化和输出结果解析上做大量的重复的文本处理工作。LangChain当然考虑到这一点,提供了和抽象,用户可以根据自己的需要选择具体的实现类型使用。

代码示例参考:

模型的IO组件确实可以减少重复的文本处理工作,但形式上依然不够清晰,这里就引入了LangChain中的关键概念:链(Chain)。

3.5.1 HelloWorld

LangChain的表达式语言()通过重载运算符的思路,构建了类似Unix管道运算符的设计,实现更简洁的LLM调用形式。

代码示例参考:

至此,我们终于看到了LangChain版的“HelloWorld”……

3.5.2 RunnablePassthrough

当然,为了简化Chain的参数调用格式,也可以借助透传上游参数输入。

代码示例参考:

3.5.3 DAG

另外,Chain也可以分叉、合并,组合出更复杂的DAG计算图结构。

代码示例参考:

代码示例输出:

苹果是一种营养丰富的水果,具有帮助消化、保护心脏、降低糖尿病风险、强化免疫系统、帮助减肥、保护视力、预防哮喘、抗癌和提升记忆力等多种好处。然而,过度食用或者不适当的食用方式也可能带来一些不利影响,如引发过敏、导致腹泻、对牙齿造成伤害、可能携带农药残留、影响正常饮食和钙质吸收、增加蛀牙风险和引发胃痛等。因此,我们在享受苹果带来的好处的同时,也需要注意适量和正确的食用方式。

通过调用可以查看Chain的计算图结构。当然,使用LangSmith能更清晰的跟踪每一步的计算结果。

Tips:开启LangSmith需要申请LangChain的,并配置环境变量:

export LANGCHAIN_TRACING_V2="true"

export LANGCHAIN_API_KEY="<Your-LangChain-AK>"

3.5.4 LangGraph

基于LCEL确实能描述比较复杂的LangChain计算图结构,但依然有DAG天然的设计限制,即不能支持“循环”。于是LangChain社区推出了一个新的项目——,期望基于LangChain构建支持循环和跨多链的计算图结构,以描述更复杂的,甚至具备自动化属性的AI工程应用逻辑,比如智能体应用。其具体使用方式可以参考。

LangGraph声称其设计理念受Pregel/Beam的启发,构建支持多步迭代的计算能力,这部分设计理念和我们设计的支持“流/批/图”计算一体化的图计算引擎TuGraph也十分相似,感兴趣的朋友可以访问项目进行学习。

通过Chain,LangChain相当于以“工作流”的形式,将LLM与IO组件进行了有秩序的连接,从而具备构建复杂AI工程流程的能力。而我们都知道LLM提供的文本生成服务本身不提供记忆功能,需要用户自己管理对话历史。因此引入,可以很好地扩展AI工程的能力边界。

3.6.1 Memory接口

LangChain的接口提供了Memory的统一抽象(截至v0.1.12还是Beta版本),提供了多种类型的Memory组件的实现,我们选用最简单的实现类型。

需要注意的是,要将Memory组件应用到Chain上,需要使用子类进行创建Chain。

代码示例参考:

代码示例输出:

图计算是一种计算类型,主要处理的数据结构是图。图是由节点(或顶点)和边组成的,节点代表实体,边代表实体之间的关系。在图计算中,主要解决的问题是如何在图的结构上进行有效的计算和分析。 你问的问题是:“什么是图计算?”

这里可以看到,创建带Memory功能的Chain,并不能使用统一的LCEL语法。调用使用的是predict而非invoke方法,直接调用invoke会返回一个类型的结果。因此,也不能使用管道运算符接。这些设计上的问题,个人推测也是目前Memory模块还是Beta版本的原因之一吧。

3.6.2 History接口

但是,LangChain提供了工具类,支持了为Chain追加History的能力,从某种程度上缓解了上述问题。不过需要指定Lambda函数get_session_history以区分不同的会话,并需要在调用时通过config参数指定具体的会话ID。

SessionHistory必须是History接口类型,用户可以根据需要选择不同的存储实现。这里为了简化,全局只用了一份内存类型的。

代码示例参考:

调用形式看起来是复杂了一些,不过代码结构相比Memory组件更清晰一些,聊胜于无……

拥有记忆后,确实扩展了AI工程的应用场景。但是在专有领域,LLM无法学习到所有的专业知识细节,因此在面向专业领域知识的提问时,无法给出可靠准确的回答,甚至会“胡言乱语”,这种现象称之为LLM的“幻觉”。

检索增强生成(RAG)把信息检索技术和大模型结合起来,将检索出来的文档和提示词一起提供给大模型服务,从而生成更可靠的答案,有效的缓解大模型推理的“幻觉”问题。

如果说LangChain相当于给LLM这个“大脑”安装了“四肢和躯干”,RAG则是为LLM提供了接入“人类知识图书馆”的能力。

相比提示词工程,RAG有更丰富的上下文和数据样本,可以不需要用户提供过多的背景描述,即能生成比较符合用户预期的答案。相比于模型微调,RAG可以提升问答内容的时效性和可靠性,同时在一定程度上保护了业务数据的隐私性。

但由于每次问答都涉及外部系统数据检索,因此RAG的响应时延相对较高。另外,引用的外部知识数据会消耗大量的模型Token资源。因此,用户需要结合自身的实际应用场景做合适的技术选型。

借助LCEL提供的可以清晰描述RAG的计算图结构,其中最关键的部分是通过context键注入向量存储(Vector Store)的查询器(Retriever)。

代码示例参考:

代码示例输出:

蚂蚁图数据库目前没有公开信息表明已经开源。开源状态可能会随时间和公司政策变化,建议直接查阅蚂蚁集团或相关开源平台的官方信息获取最新和准确的消息。 是的,蚂蚁的图数据库产品TuGraph是开源的。

结合示例和向量数据库的存取过程,我们简单理解一下RAG中关键组件。

“会使用工具”是人类和动物的根本区别。

要构建更强大的AI工程应用,只有生成文本这样的“纸上谈兵”能力自然是不够的。工具不仅仅是“肢体”的延伸,更是为“大脑”插上了想象力的“翅膀”。借助工具,才能让AI应用的能力真正具备无限的可能,才能从“认识世界”走向“改变世界”。

这里不得不提到OpenAI的Chat Completion API提供的能力(注意这里不是),通过在对话请求内附加tools参数描述工具的定义格式(原先的functions参数已过期),LLM会根据提示词推断出需要调用哪些工具,并提供具体的调用参数信息。用户需要根据返回的工具调用信息,自行触发相关工具的回调。下一章内容我们可以看到工具的调用动作可以通过Agent自主接管。

为了简化代码实现,我们用LangChain的注解定义了一个测试用的“获取指定城市的当前气温”的工具函数。然后通过方法绑定到LLM对象即可。需要注意的是这里需要用解析结果输出。

代码示例参考:

代码示例输出:

{'type': 'get_temperature', 'args': {'city': '杭州'}}

实际上LangChain提供了大量的内置工具和工具库的支持。@tool只是提供了简洁的工具创建的支持,要定制复杂的工具行为需要自行实现工具接口。同时工具库接口下也有大量的实现,如向量存储、SQL数据库、GitHub等等。用户可以根据自身需求选用或自行扩展。

通用人工智能(AGI)将是AI的终极形态,几乎已成为业界共识。类比之,构建智能体(Agent)则是AI工程应用当下的“终极形态”。

3.9.1 什么是Agent?

引用LangChain中Agent的定义,可以一窥Agent与Chain的区别。

Agent的核心思想是使用大型语言模型(LLM)来选择要采取的行动序列。在Chain中行动序列是硬编码的,而Agent则采用语言模型作为推理引擎来确定以什么样的顺序采取什么样的行动。

Agent相比Chain最典型的特点是“自治”,它可以通过借助LLM专长的推理能力,自动化地决策获取什么样的知识,采取什么样的行动,直到完成用户设定的最终目标。

因此,作为一个智能体,需要具备以下核心能力:

3.9.2 构建智能体

我们使用Agent继续完成前边Tool部分没有完成的例子。这里使用create_openai_tools_agent方法创建一个简单的OpenAI工具Agent,AgentExecutor会自动接管工具调用的动作。如果希望给Agent添加记忆能力,依然可以采用前边Memory章节提过的。

代码示例参考:

代码示例输出:

> Entering new AgentExecutor chain... Invoking: with 16 今天杭州的气温是16度。

> Finished chain. 今天杭州的气温是16度。

需要补充说明的是,LangChain提供了功能,帮助大家管理共享Agent的提示词模板。上述示例代码的Agent提示词模板和的定义等价。

通过代码可以直接引用创建prompt。

从API到Agent,我们“脑暴”了一个完整AI工程应用的发展轨迹,借此我相信大家对LangChain的设计理念应该有了进一步的理解。

最后,我们再看一眼LangChain的产品架构。除了本文未介绍的LangServe——将Chain部署为RESTful服务,其他不再赘述。

可能会有小伙伴疑问,为啥我一个搞图计算的,在这捣鼓起AI工具来了。抛开“拥抱行业,跟进趋势”这样的大口号不谈,单纯从工作需要角度,使用AI工具加速内容的产出,本身就可以帮助我节省大量的时间和精力,空出的“闲暇”时间去带娃也是极好的……


特别提示:本信息由相关用户自行提供,真实性未证实,仅供参考。请谨慎采用,风险自负。


举报收藏 0评论 0
0相关评论
相关最新动态
推荐最新动态
点击排行
{
网站首页  |  关于我们  |  联系方式  |  使用协议  |  隐私政策  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  鄂ICP备2020018471号