什么是LangChain?
正式开始前还是有必要从定义What开始。LangChain是2022年10月底由哈佛大学的Harrison Chase发起的基于开源大语言模型的AI工程开发框架。当然也可以问一下AI我LangChain是什么GPT-4LangChain 是一个开源的语言模型工具链框架旨在使研究人员和开发人员能够更轻松地构建、实验和部署以自然语言处理NLP为中心的应用程序。它提供了多种组件和工具可帮助用户利用最近的语言模型进展如大型 Transformer 模型等并且可以与 Hugging Face 等平台集成。LangChain 的核心理念是将语言模型用作协作工具通过它开发者可以构建出处理复杂任务的系统并且可以高效地对接不同的数据源和应用程序接口APIs。这个框架试图简化连接不同的输入和输出流以及在流中实现语言模型的过程。顾名思义LangChain中的“Lang”自然是指大语言模型“Chain”即“链”也就是将大模型与其他组件连接成链借此构建AI工程应用。那么LangChain该如何How做到这一点的呢解答这个问题之前需要先回答什么是工程什么是AI工程1.2 什么是AI工程我们先Review一下“工程”的百科定义工程是指以某组设想的目标为依据应用有关的科学知识和技术手段通过有组织的一群人将某个或某些现有实体自然的或人造的转化为具有预期使用价值的人造产品过程。其中“目标”定义了要解决的问题域决定了工程的顶层设计和能力边界同时也定义了“产品”的最终形态。提升“人”的ROI是工程设计的价值归属。“实体”是工程的生产材料输入“科学 | 技术”是工程有序运行的基础对它们的合理利用可以提升工程的整体产出效率。于是我们可以这样解读“AI工程”中的关键概念目标待解决的特定AI用户需求。如内容创作、智能问答、文档摘要、图像识别等等。人实施AI工程的具体角色。可以是程序员或者AI应用的研发团队、创业公司。科学 | 技术显然是大模型与相关工具服务以及其后的计算科学理论。实体已有的文档、知识库、业务数据等生产材料。产品能满足目标需求的具体产品。如聊天机器人、内容生成工具等。1.3 如何设计LangChain因此如果我们是LangChain的设计者希望构建通用的AI工程框架。需要回答如下问题【目标 | 产品】LangChain的设计目标是什么能解决哪些AI工程问题【人】LangChain的编程接口如何定义才能提升AI工程师的研发效率【实体 | 科学 | 技术】LangChain的核心组件如何抽象以提升框架的扩展能力当然作为“事后诸葛”这些问题目前有比较明确的答案作为AI工程框架LangChain实际是对LLM能力的扩展和补充。如果把LLM比作人的大脑LangChain则是人的躯干和四肢协助LLM完成“思考”之外的“脏活累活”。它的能力边界只取决于LLM的智力水平和LangChain能提供的工具集的丰富程度。LangChain提供了LCELLangChain Expression Language声明式编程语言降低AI工程师的研发成本。LangChain提供了Models、Prompts、Indexes、Memory、Chains、Agents六大核心抽象用于构建复杂的AI应用同时保持了良好的扩展能力。很明显LLM作为LangChain能力的基础是了解LangChain工程化设计的前提。接下来我们就从最基础的LLM API使用谈起一步步了解LangChain的工程化构建过程及其背后的设计理念。2. 环境准备Python环境建议3.8版本以上。下载链接https://www.python.org/downloadsOpenAI SK自备。申请地址https://platform.openai.com/api-keys环境变量export OPENAI_API_KEYYour-OpenAI-SK安装LangChain执行命令pip install langchain langchain-openai3. 设计推演架构设计领域有个比较流行的术语——乐高架构当然也可以叫可插拔架构。说白就是通过对系统基本组件的合理抽象找到构造复杂系统的统一规律和可达路径从而实现在降低系统实现复杂度的同时提升系统整体的扩展性。非官方表达大家能Get到我的意思就好……LangChain实际上也遵循了乐高架构的思想。当然作为最关键的乐高组件之一LLM的能力自然是我们优先了解的对象那我们就从OpenAI的API开始吧3.1 造梦基础——API文本生成模型服务是OpenAI提供的最核心的API服务自ChatGPT发布后经历过几次版本迭代。3.1.1 Chat Completion API当下最新的是Chat Completion API是AI与LLM交互的核心入口。代码示例参考import os import requests # API Key api_key os.getenv(OPENAI_API_KEY) # 头部信息 headers { Content-Type: application/json, Authorization: fBearer {api_key} } # 准备数据 data { model: gpt-4, messages: [{role: user, content: 什么是图计算}], temperature: 0.7 } # 调用API url https://api.openai.com/v1/chat/completions response requests.post(url, jsondata, headersheaders) answer response.json()[choices][0][message][content] print(answer)代码示例输出图计算是一种计算模型用于处理大规模图形结构的数据并执行各种复杂的算法和计算。这种计算模型主要用于社交网络分析、Web搜索、生物信息学、网络路由优化、数据挖掘等领域。图计算模型的核心是将数据表示为图形结构节点和边这样可以更好地揭示数据之间的关系和互动。在图计算中算法通常以迭代的方式运行每次迭代都会更新图中节点的状态直到达到某种停止条件。3.1.2 Completion API早先的Completion API已经在2023年7月后不再维护和最新的Chat Completion API参数和结果格式有所不同最明显的是Prompt是以纯文本方式传递而非Message格式。# 准备数据 data { model: gpt-3.5-turbo-instruct, prompt: [什么是图计算], max_tokens: 1024 } # 调用API url https://api.openai.com/v1/completions response requests.post(url, jsondata, headersheaders) answer response.json()[choices][0][text] print(answer)除了文本生成服务OpenAI也提供了大量的LLM的周边服务以协助AI工程构建更复杂的应用能力。如函数调用、嵌入、微调、多模态等具体可参考OpenAI开发文档的内容。3.2 智能开端——Chat自2022年11月底ChatGPT发布以来AI的大门才真正地向人类打开其中给用户留下最深印象的功能自然是智能对话。OpenAI的Chat Completion API参数支持传入消息历史可以轻松地实现简单的对话服务。代码示例参考# 对话历史 messages [] def chat_with_ai(message): # 记录历史 messages.append({role: user, content: message}) print(fme: {message}) # 对话请求 data { model: gpt-4, messages: messages, temperature: 0.7 } url https://api.openai.com/v1/chat/completions response requests.post(url, jsondata, headersheaders) # 解析回答 if response.status_code 200: answer response.json()[choices][0][message][content] messages.append({role: assistant, content: answer}) print(fai: {answer}) else: print(fError: {response.status_code}, response.json()) # 多轮对话 chat_with_ai(什么是图计算) chat_with_ai(刚才我问了什么问题)代码示例输出me: 什么是图计算ai: 图计算是一种计算模型用于处理大规模图形结构数据的计算和分析。在这种计算模型中数据被表示为图形其中节点代表实体边代表实体之间的关系。图计算可以用于解决许多实际问题如社交网络分析、网络路由、生物信息学等。图计算的主要挑战是如何有效地处理大规模的图形数据并提供快速的计算和分析结果。me: 刚才我问了什么问题ai: 你问的问题是“什么是图计算”3.3 初步封装——SDK到目前为止我们还只是用OpenAI最原始的RESTful API构建LLM工程能力甚至连OpenAI提供的SDK都未使用。显然这不是一个高效的方式使用前边安装的LangChain-OpenAI集成包langchain-openai可以大大降低代码的开发成本。代码示例参考from langchain_openai import ChatOpenAI # 调用Chat Completion API llm ChatOpenAI(model_namegpt-4) response llm.invoke(什么是图计算) print(response)代码示例输出content图计算是一种计算模型主要用于处理图形结构数据的计算和分析。图计算的对象是图图由节点和边组成节点代表实体对象边代表实体对象之间的关系。图计算主要用于解决实体关系复杂、关系密集的问题如社交网络分析、网络拓扑分析、推荐系统等。图计算的主要任务是通过对图中节点和边的计算发现和提取出图中隐含的知识和信息。3.4 数据抽象——IO对于文本生成模型服务来说实际的输入和输出本质上都是字符串因此直接裸调用LLM服务带来的问题是要在输入格式化和输出结果解析上做大量的重复的文本处理工作。LangChain当然考虑到这一点提供了Prompt和OutputParser抽象用户可以根据自己的需要选择具体的实现类型使用。代码示例参考from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI # 创建LLM llm ChatOpenAI(model_namegpt-4) # 创建Prompt prompt ChatPromptTemplate.from_template({question}) # 创建输出解析器 output_parser StrOutputParser() # 调用LLM message prompt.invoke({question: 什么是图计算}) response llm.invoke(message) answer output_parser.invoke(response) print(answer)3.5 组装成链——Chain模型的IO组件确实可以减少重复的文本处理工作但形式上依然不够清晰这里就引入了LangChain中的关键概念链Chain。3.5.1 HelloWorldLangChain的表达式语言LCEL通过重载__or__运算符的思路构建了类似Unix管道运算符的设计实现更简洁的LLM调用形式。代码示例参考# 创建Chain chain prompt | llm | output_parser # 调用Chain answer chain.invoke({question: 什么是图计算}) print(answer)至此我们终于看到了LangChain版的“HelloWorld”……3.5.2 RunnablePassthrough当然为了简化Chain的参数调用格式也可以借助RunnablePassthrough透传上游参数输入。代码示例参考from langchain_core.runnables import RunnablePassthrough # 创建Chain chain {question: RunnablePassthrough()} | prompt | llm | output_parser # 调用Chain answer chain.invoke(什么是图计算) print(answer)3.5.3 DAG另外Chain也可以分叉、合并组合出更复杂的DAG计算图结构。代码示例参考from operator import itemgetter from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_openai import ChatOpenAI # 创建LLM llm ChatOpenAI(model_namegpt-4) # 创建输出解析器 output_parser StrOutputParser() # 创建Prompt topic_prompt ChatPromptTemplate.from_template(生成一种{input}的名称) good_prompt ChatPromptTemplate.from_template(列举{topic}的好处:) bad_prompt ChatPromptTemplate.from_template(列举{topic}的坏处:) summary_prompt ChatPromptTemplate.from_messages( [ (ai, {topic}), (human, 好处:\n{good}\n\n坏处:\n{bad}), (system, 生成最终结论), ] ) # 创建组合Chain topic_chain topic_prompt | llm | output_parser | {topic: RunnablePassthrough()} goods_chain good_prompt | llm | output_parser bads_chain bad_prompt | llm | output_parser summary_chain summary_prompt | llm | output_parser chain ( topic_chain | { good: goods_chain, bad: bads_chain, topic: itemgetter(topic), } | summary_chain ) # 调用chain answer chain.invoke({input: 常见水果}) print(answer)代码示例输出苹果是一种营养丰富的水果具有帮助消化、保护心脏、降低糖尿病风险、强化免疫系统、帮助减肥、保护视力、预防哮喘、抗癌和提升记忆力等多种好处。然而过度食用或者不适当的食用方式也可能带来一些不利影响如引发过敏、导致腹泻、对牙齿造成伤害、可能携带农药残留、影响正常饮食和钙质吸收、增加蛀牙风险和引发胃痛等。因此我们在享受苹果带来的好处的同时也需要注意适量和正确的食用方式。通过调用chain.get_graph().print_ascii()可以查看Chain的计算图结构。当然使用LangSmith能更清晰的跟踪每一步的计算结果。Tips开启LangSmith需要申请LangChain的AK并配置环境变量export LANGCHAIN_TRACING_V2trueexport LANGCHAIN_API_KEYYour-LangChain-AK3.5.4 LangGraph基于LCEL确实能描述比较复杂的LangChain计算图结构但依然有DAG天然的设计限制即不能支持“循环”。于是LangChain社区推出了一个新的项目——LangGraph期望基于LangChain构建支持循环和跨多链的计算图结构以描述更复杂的甚至具备自动化属性的AI工程应用逻辑比如智能体应用。其具体使用方式可以参考LangGraph文档。LangGraph声称其设计理念受Pregel/Beam的启发构建支持多步迭代的计算能力这部分设计理念和我们设计的支持“流/批/图”计算一体化的图计算引擎TuGraph也十分相似感兴趣的朋友可以访问TuGraph Analytics项目进行学习。3.6 开启记忆——Memory通过ChainLangChain相当于以“工作流”的形式将LLM与IO组件进行了有秩序的连接从而具备构建复杂AI工程流程的能力。而我们都知道LLM提供的文本生成服务本身不提供记忆功能需要用户自己管理对话历史。因此引入Memory组件可以很好地扩展AI工程的能力边界。3.6.1 Memory接口LangChain的BaseMemory接口提供了Memory的统一抽象截至v0.1.12还是Beta版本提供了多种类型的Memory组件的实现我们选用最简单的ConversationBufferMemory实现类型。需要注意的是要将Memory组件应用到Chain上需要使用子类LLMChain进行创建Chain。代码示例参考from langchain.chains import LLMChain from langchain.memory import ConversationBufferMemory from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \ HumanMessagePromptTemplate from langchain_openai import ChatOpenAI # 创建LLM llm ChatOpenAI(model_namegpt-4) # 创建Prompt prompt ChatPromptTemplate.from_messages([ MessagesPlaceholder(variable_namechat_history), HumanMessagePromptTemplate.from_template({question}) ]) # 创建Memory memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) # 创建LLMChain llm_chain LLMChain(llmllm, memorymemory, promptprompt) # 调用LLMChain print(llm_chain.predict(question什么是图计算)) print(llm_chain.predict(question刚才我问了什么问题))代码示例输出图计算是一种计算类型主要处理的数据结构是图。图是由节点或顶点和边组成的节点代表实体边代表实体之间的关系。在图计算中主要解决的问题是如何在图的结构上进行有效的计算和分析。你问的问题是“什么是图计算”这里可以看到创建带Memory功能的Chain并不能使用统一的LCEL语法。调用LLMChain使用的是predict而非invoke方法直接调用invoke会返回一个LLMResult类型的结果。因此LLMChain也不能使用管道运算符接StrOutputParser。这些设计上的问题个人推测也是目前Memory模块还是Beta版本的原因之一吧。3.6.2 History接口但是LangChain提供了工具类RunnableWithMessageHistory支持了为Chain追加History的能力从某种程度上缓解了上述问题。不过需要指定Lambda函数get_session_history以区分不同的会话并需要在调用时通过config参数指定具体的会话ID。SessionHistory必须是History接口类型BaseChatMessageHistory用户可以根据需要选择不同的存储实现。这里为了简化全局只用了一份内存类型的ChatMessageHistory。代码示例参考