LangChain是一个用来开发大型语言模型(LLM)应用的框架,为了简化构建基于LLM的应用,它能够为开发LLM应用带来如下能力:
LangChain是什么
LangChainLibraries是一个Python和JavaScript库,是LangChain中最核心的部分,在图中对应于下方的深色块。LangChainLibraries被分为3个核心的部分,也是代码组织方式的表现:LangChain、LangChain-Community、LangChain-Core,简要介绍如下:
包含LangChain的基本抽象和LCEL(LangChainExpressionLanguage)。
提供了一组用于简化部署的参考架构,方便构建基于LLM应用的中的各种任务。
提供了用来将LangChain部署为RESTAPI服务的工具,以供应用的其他模块调用。LangChain使用了FastAPI来实现这种服务能力。
LangSmith是LangChain提供的开发平台,方便开发人员进行调试、测试、评估、监控等。
LangChain模块(Modules)概览
LangChain提供了一个标准的可扩展的接口,可以方便地与外部其他组件进行交互以满足面向特定领域LLM应用的需求,它也能够与外部其它组件/中间件连接和交互。这些模块根据功能及行为方式,分为6个类别,如下表所示:
通过上面表格,我们基本了解了LangChain提供的一些方便构建LLM应用的模块及其对应的功能,下面,针对每个模块进行更详细的说明。
LangChain模块:ModelI/O
使用Ollama在本地就可以启动一个模型服务,然后我们就可以使用LangChain创建对应的LLM和ChatModel,示例代码如下:
使用LangChain与LLM进行对话,示例代码如下:
fromlangchain.schemaimportHumanMessagetext="Whatwouldbeagoodcompanynameforacompanythatmakescolorfulsocks"messages=[HumanMessage(content=text)]llm.invoke(text)#>>FeetfulofFunchat_model.invoke(messages)#>>AIMessage(content="SocksO'Color")PromptTemplate包含了我们要查询LLM的请求内容,以及我们所基于的上下文,这样就限制了预训练的LLM能够基于我们提供的上下文来给出更准确的回答。示例代码如下:
LangChain提供了不同类型的OutputParser,能够将LLM输出的消息进行格式化,方便下游应用任务使用。比如,下面示例中使用CommaSeparatedListOutputParser,将消息按照逗号分割,并返回一个列表:
fromlangchain.output_parsersimportCommaSeparatedListOutputParseroutput_parser=CommaSeparatedListOutputParser()output_parser.parse("hi,bye")#>>['hi','bye']使用LCEL将上面的PromptTemplate、ChatModel、OutputParser组合到一起,并实现了与LLM的交互,示例代码如下:
template="Generatealistof5{text}.\n\n{format_instructions}"chat_prompt=ChatPromptTemplate.from_template(template)chat_prompt=chat_prompt.partial(format_instructions=output_parser.get_format_instructions())chain=chat_prompt|chat_model|output_parserchain.invoke({"text":"colors"})#>>['red','blue','green','yellow','orange']LangChain模块:Retrieval
VectorStore也叫VectorDB,用来存储我们自己数据的向量表示内容(Embedding),以支持Retrieval模块的检索匹配。
Retriever称为检索器,从我们构建好的VectorStore中进行检索。LangChain提供了各种支持检索的算法,比如:ParentDocumentRetriever、SelfQueryRetriever、EnsembleRetriever、MultiVectorRetriever,等等。下面以SelfQueryRetriever为例说明使用方法,示例代码如下所示:
LangChain模块:Agents
Agent通过LLM来推理,能够自然地、持续多轮地与LLM交互,这也是智能体所具备与支持的基本能力。为了说明Agent框架的使用方法,使用Tavily和Retriever两个工具来构建Agent,其中Tavily是一个支持Online搜索的工具。
agent_executor.invoke({"input":"hi!"})这样的交互不会记忆上下文,是无状态的。所以,如果考虑上下文,需要把以往的记录加入到Memory,示例代码如下:
fromlangchain_core.messagesimportAIMessage,HumanMessageagent_executor.invoke({"chat_history":[HumanMessage(content="hi!mynameisbob"),AIMessage(content="HelloBob!HowcanIassistyoutoday"),],"input":"what'smyname",})也可以直接使用LangChain提供的ChatMessageHistory、RunnableWithMessageHistory来实现自动跟踪历史消息的功能,可以参考LangChain文档。
LangChain模块:Chains
Chains是一个调用/处理/计算任务序列的抽象,可以根据我们自己的需求来构建不同的调用链、处理链,等等。Chains是通过LangChain提供的LCEL来构建的。下面是LainChain提供的一些LCELConstructor,可以方便构建一个Chain,如下所示:
通过名称,就可以大致了解每个LCELConstructor实现的Chain的功能。
LangChain模块:Memory
fromlangchain_openaiimportOpenAIfromlangchain.promptsimportPromptTemplatefromlangchain.chainsimportLLMChainfromlangchain.memoryimportConversationBufferMemoryllm=OpenAI(temperature=0)prompt=PromptTemplate.from_template(template)memory=ConversationBufferMemory(memory_key="chat_history")#needtoalignthe`memory_key`conversation=LLMChain(llm=llm,prompt=prompt,verbose=True,memory=memory)当然也可以直接使用ChatModel来实现整个对话过程,具体例子可以参考LangChain官网文档。Memory模块提供了多个不同的实现,具体也可以根据自己实际应用需求来实现,如下所示是LangChain提供的一些实现:
具体功能和使用方法,可以点击对应链接查看官网文档的详细说明。
LangChain模块:Callbacks
LangChain支持使用Callbacks,通过Hook到基于LLM的应用的任何位置,来实现类似日志记录、监控、Streaming处理以及其它任务等等。要想实现自定义的Callback能力,可以直接实现CallbackHandler接口,开发满足自己应用需求的CallbackHandler。LangChain实现的StdOutCallbackHandler是一个非常基础的Callback,使用示例如下:
fromlangchain.callbacksimportStdOutCallbackHandlerfromlangchain.chainsimportLLMChainfromlangchain_openaiimportOpenAIfromlangchain.promptsimportPromptTemplatehandler=StdOutCallbackHandler()llm=OpenAI()prompt=PromptTemplate.from_template("1+{number}=")chain=LLMChain(llm=llm,prompt=prompt,callbacks=[handler])#Constructorcallbackchain.invoke({"number":2})chain=LLMChain(llm=llm,prompt=prompt,verbose=True)#Useverboseflagtoachievecallbackschain.invoke({"number":2})chain=LLMChain(llm=llm,prompt=prompt)#Requestcallbackschain.invoke({"number":2},{"callbacks":[handler]})LCEL(LangChainExpressionLanguage)
下面通过一个实现prompt+model+outputparser的例子,用来说明使用LCEL带来的直观性和便利性,示例代码如下:
LangServe工具介绍
LangServe提供了将LangChain的runnables和chains部署为RESTAPI的能力,它是使用FastAPI来实现的。使用LangServe部署基于LLM的应用,会包含两个部分:Server(提供LLMChatModel服务)、Client(调用模型服务)。下面通过一个例子来说明如何构建。
Server包括OpenAIChatModel和AnthropicChatModel,它们可以提供对话服务能力,示例代码如下:
#!/usr/bin/envpythonfromfastapiimportFastAPIfromlangchain.promptsimportChatPromptTemplatefromlangchain.chat_modelsimportChatAnthropic,ChatOpenAIfromlangserveimportadd_routesimportuvicornapp=FastAPI(title="LangChainServer",version="1.0",description="AsimpleapiserverusingLangchain'sRunnableinterfaces",)add_routes(app,ChatOpenAI(),path="/openai",)add_routes(app,ChatAnthropic(),path="/anthropic",)model=ChatAnthropic()prompt=ChatPromptTemplate.from_template("tellmeajokeabout{topic}")add_routes(app,prompt|model,path="/joke",)if__name__=="__main__":uvicorn.run(app,host="localhost",port=8000)如果没有报错,打开浏览器浏览localhost:8080/docs可以看到对应的页面,说明Server端部署成功。
Client端用来与Server端进行交互:调用Server端提供的RESTAPI就可以实现。Client端示例的Python代码如下:
LangSmith工具介绍
LangGraph介绍
LangGraph是一个用来构建有状态的、多Actor的LLM应用的库,它是在LangChain框架的基础之上构建实现的。LangGraph的灵感来自Pregel和ApacheBeam,支持基于LCEL的能力构建一个带有循环的(Cyclic)的应用模式,从而实现协调多个Chain跨多个计算步骤(ComputationSteps)的能力。可见,LangGraph是支持带环的计算图模式的,并不是简单的DAG(有向无环图)。下面以官网的入门例子为例,说明如何使用LangGraph,如下所示:
fromlangchain_community.tools.tavily_searchimportTavilySearchResultstools=[TavilySearchResults(max_results=1)]fromlanggraph.prebuiltimportToolExecutortool_executor=ToolExecutor(tools)#wrapTavilytoolusingLangGraphToolExecutorfromlangchain_openaiimportChatOpenAI#Wewillsetstreaming=Truesothatwecanstreamtokensmodel=ChatOpenAI(temperature=0,streaming=True)fromlangchain.tools.renderimportformat_tool_to_openai_functionfunctions=[format_tool_to_openai_function(t)fortintools]model=model.bind_functions(functions)#Bindtoolstothemodellanggraph提供的图主要是指StatefulGraph,它可以被一个状态对象参数化(Parameterized)。状态对象在图中的每个节点之间传递,每一个节点会返回更新状态对象的操作,从而实现状态的更新。这里的状态会跟踪对话过程中产生的消息的列表,构建的图中的每个节点需要把消息添加到列表中,所以使用TypedDict来实现Agent状态,如下所示:
fromtypingimportTypedDict,Annotated,Sequenceimportoperatorfromlangchain_core.messagesimportBaseMessageclassAgentState(TypedDict):messages:Annotated[Sequence[BaseMessage],operator.add]在langgraph中,图节点是一个function对象或runnable对象;然后通过边来连接各个节点,在langgraph中有两种类型的边:ConditionalEdge和NormalEdge,可以通过定义函数来实现,确定从一个节点到另一个节点的路径。示例代码如下:
fromlanggraph.graphimportStateGraph,ENDworkflow=StateGraph(AgentState)#Definethetwonodeswewillcyclebetweenworkflow.add_node("agent",call_model)workflow.add_node("action",call_tool)workflow.set_entry_point("agent")#Settheentrypointas`agent`#Addaconditionaledgeworkflow.add_conditional_edges("agent",#First,wedefinethestartnode.Weuse`agent`.should_continue,#Next,thefunctionwilldeterminewhichnodeiscallednext.#Finallywepassinamapping,andbasedonwhichoneitmatches,thatnodewillthenbecalled.{"continue":"action",#If`tools`,thenwecallthetoolnode."end":END#Otherwisewefinish.})#Addanormaledgefrom`tools`to`agent`:after`tools`iscalled,`agent`nodeiscallednext.workflow.add_edge('action','agent')app=workflow.compile()##CompilesitintoaLangChainRunnable,fromlangchain_core.messagesimportHumanMessageinputs={"messages":[HumanMessage(content="whatistheweatherinsf")]}app.invoke(inputs)通过上面1~6步骤,就可以构建并执行一个基于LangGraph的简单LLM应用。