IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    LangChain 框架介绍及入门指南

    Yanjun发表于 2024-02-17 11:45:13
    love 0

    LangChain 是一个用来开发大型语言模型(LLM)应用的框架,为了简化构建基于 LLM 的应用,它能够为开发 LLM 应用带来如下能力:

    • 根据给定的 Prompt 方便构建上下文,并连接到 LLM, 得到更加符合查询的回答结果
    • 在构建整个基于 LLM 的应用提供各种工具,如各种模块(Modules)、LCEL、LangGraph 等
    • 提供工具支持,使用户自己的 LLM 应用从原型版本到上线到生产环境过程中,一站式的调试、测试、评估等迭代功能

    当前,已经发布了最新的 v0.10.0 稳定版本,可以参考这里 LangChain v0.1.0。本文我们通过介绍 LangChain 框架的方方面面(算是对官方文档的一个入门的内容浓缩)使我们对它有一个更全面的认识,以帮助我们使用 LangChain 构建基于 LLM 的应用,具体包括如下几个方面的内容:

    • LangChain 是什么
    • LangChain 模块(Modules)概览
    • LangChain 模块:Model I/O
    • LangChain 模块:Retrieval
    • LangChain 模块:Agents
    • LangChain 模块:Chains
    • LangChain 模块:Memory
    • LangChain 模块:Callbacks
    • LCEL(LangChain Expression Language)
    • LangServe 工具介绍
    • LangGraph 介绍

    LangChain 是什么

    LangChain 是一个开源框架,它提供了构建基于 LLM 的 AI 应用所需的各种模块(Modules)与工具,大大降低了开发这一类相关应用的门槛,使得任何人都可以轻松构建基于 OpenAI、Cohere、GPT4All、Huggingface 等提供的大模型的应用。
    首先,我们看一下 LangChain 框架的组成,如下图所示:
    langchain_stack
    可见,LangChain 包含如下 4 个主要部分:

    • LangChain Libraries

    LangChain Libraries 是一个 Python 和 JavaScript 库,是 LangChain 中最核心的部分,在图中对应于下方的深色块。LangChain Libraries 被分为 3 个核心的部分,也是代码组织方式的表现:LangChain、LangChain-Community、LangChain-Core,简要介绍如下:

    • 1.LangChain

    包括组成应用认知架构(Application’s Cognitive Architecture)的各个部分,如 Chains、Agents 以及 检索策略(Retrieval Strategies)等。

    • 2.LangChain-Community

    所有用来与第三方集成的功能,都会放到 LangChain-Community 中,方便构建 LLM 应用的人更集中关注集成的能力,比如提供 LLM 的各个 Provider。

    • 3.LangChain-Core

    包含 LangChain 的基本抽象和 LCEL(LangChain Expression Language)。

    • LangChain Templates

    提供了一组用于简化部署的参考架构,方便构建基于 LLM 应用的中的各种任务。

    • LangServe

    提供了用来将 LangChain 部署为 REST API 服务的工具,以供应用的其他模块调用。LangChain 使用了 FastAPI 来实现这种服务能力。

    • LangSmith

    LangSmith 是 LangChain 提供的开发平台,方便开发人员进行调试、测试、评估、监控等。

    LangChain 模块(Modules)概览

    LangChain 提供了一个标准的可扩展的接口,可以方便地与外部其他组件进行交互以满足面向特定领域 LLM 应用的需求,它也能够与外部其它组件/中间件连接和交互。这些模块根据功能及行为方式,分为 6 个类别,如下表所示:

    模块名称 模块功能
    Model I/O 用于与 LLM 语言模型交互
    Retrieval 用于与应用特定的数据进行交互
    Agents 用于确定 LLM 需要采用的 Action 及其顺序的决策,即智能体应用
    Chains 用于构建顺序调用的多个构建块(Building Block),主要支持通过 LCEL 的方式构建
    Memory 用于在多轮对话过程中保存应用状态
    Callbacks 回调系统,用来支持 LLM 应用的日志记录、监控、Streaming 处理,等等

    通过上面表格,我们基本了解了 LangChain 提供的一些方便构建 LLM 应用的模块及其对应的功能,下面,针对每个模块进行更详细的说明。

    LangChain 模块:Model I/O

    Model I/O 模块是基于 LangChain 框架构建 LLM 应用的最核心的模块,因为我们必须要与 LLM 进行交互,没有它其他什么都做不了。使用 Model I/O 模块的基本流程,如下图所示:
    model_io
    我们以使用开源 LLM 为例,基于 Llama 2 模型来说明如何快速使用 Model I/O 的方法,分为如下几个步骤:

    • 1.选择并连接到 LLM

    使用 Ollama 在本地就可以启动一个模型服务,然后我们就可以使用 LangChain 创建对应的 LLM 和 ChatModel,示例代码如下:

    from langchain_community.llms import Ollama
    from langchain_community.chat_models import ChatOllama
    
    llm = Ollama(model="llama2")
    chat_model = ChatOllama()
    

    通过 llm 和 chat_model 我们就可以与 LLM 进行交互。我们需要知道,llm 和 chat_model 是不同的,但是都能通过它们与 LLM 对话,其中 chat_model 是基于 llm 的,而且进一步进行了调优,使整个持续会话过程更加丰富和完善。

    • 2.调用与 LLM 会话

    使用 LangChain 与 LLM 进行会话,示例代码如下:

    from langchain.schema import HumanMessage
    
    text = "What would be a good company name for a company that makes colorful socks?"
    messages = [HumanMessage(content=text)]
    llm.invoke(text) # >> Feetful of Fun
    chat_model.invoke(messages) # >> AIMessage(content="Socks O'Color")
    
    • 3.创建 Prompt Template

    Prompt Template 包含了我们要查询 LLM 的请求内容,以及我们所基于的上下文,这样就限制了预训练的 LLM 能够基于我们提供的上下文来给出更准确的回答。示例代码如下:

    from langchain.prompts.chat import ChatPromptTemplate
    
    template = "You are a helpful assistant that translates {input_language} to {output_language}."
    human_template = "{text}"
    chat_prompt = ChatPromptTemplate.from_messages([
        ("system", template),
        ("human", human_template),
    ])
    chat_prompt.format_messages(input_language="English", output_language="French", text="I love programming.")
    

    这样就能够把当前要查询的请求内容,以及相关的上线文消息信息整合到一起,然后调用 LLM 进行会话。

    • 4.创建并配置 OutputParser,指定 LLM 返回消息格式

    LangChain 提供了不同类型的 OutputParser,能够将 LLM 输出的消息进行格式化,方便下游应用任务使用。比如,下面示例中使用 CommaSeparatedListOutputParser,将消息按照逗号分割,并返回一个列表:

    from langchain.output_parsers import CommaSeparatedListOutputParser
    
    output_parser = CommaSeparatedListOutputParser()
    output_parser.parse("hi, bye") # >> ['hi', 'bye']
    
    • 5.使用 LCEL 组装上面的各种配置

    使用 LCEL 将上面的 Prompt Template、ChatModel、OutputParser 组合到一起,并实现了与 LLM 的交互,示例代码如下:

    template = "Generate a list of 5 {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_parser
    chain.invoke({"text": "colors"}) # >> ['red', 'blue', 'green', 'yellow', 'orange']
    

    LangChain 模块:Retrieval

    我们在使用 LLM 构建应用时,往往会根据自己的应用场景使用自己的数据,而不是直接使用预训练的 LLM 提供的内容,所以我们就会从自己的系统中输入数据来影响与 LLM 对话返回的结果内容。LangChain 通过 Retrieval 模块来处理有关数据连接的功能,比如常用的 RAG(Retrieval Augmented Generation)架构能够增强 LLM 的表达。LangChain 提供了 RAG 架构所需要的各种 Building Block,简化我们开发 LLM 应用的难度。
    LangChain 基于 RAG 实现的 Retrieval 模块的基本流程,如下图所示:
    retrieval_data_connection
    下面简要说明流程中涉及到的各个部分,有关 RAG 更详细内容可以参考其它资料。

    • Document Loaders

    从指定的数据源加载数据,通过 LangChain 的 Document Loader 实现,并转换成对应的 Document 对象(一个文本块及其相关信息),示例如下:

    from langchain_community.document_loaders import TextLoader
    
    loader = TextLoader("./index.md")
    loader.load()
    
    • Text Splitting

    将一个 Document 分割成多个文本块(Text Chunk),根据输入 LLM 的 Prompt 得到与会话查询最相关的文本块,以此作为会话上下文,提高会话返回结果的相关性和精确度。

    • Text Embedding Model

    对输入 LLM 的 Prompt,以及我们上面得到的文本块,需要通过调用选定的 Text Embedding Model 模型来得到对应的 Embedding,以此获取丰富的语义表达,这样就能够得到与 Prompt 最相关的文本块作为与 LLM 此次会话的上下文。

    • Vector Store

    Vector Store 也叫 Vector DB,用来存储我们自己数据的向量表示内容(Embedding),以支持 Retrieval 模块的检索匹配。

    • Retriever

    Retriever 称为检索器,从我们构建好的 Vector Store 中进行检索。LangChain 提供了各种支持检索的算法,比如:ParentDocumentRetriever、SelfQueryRetriever、EnsembleRetriever、MultiVectorRetriever,等等。
    下面以 SelfQueryRetriever 为例说明使用方法,示例代码如下所示:

    from langchain.schema import Document
    from langchain_community.vectorstores import Chroma
    from langchain_openai import OpenAIEmbeddings
    docs = [
        Document(
            page_content="A bunch of scientists bring back dinosaurs and mayhem breaks loose",
            metadata={"year": 1993, "rating": 7.7, "genre": "science fiction"},
        ),
        Document(
            page_content="Leo DiCaprio gets lost in a dream within a dream within a dream within a ...",
            metadata={"year": 2010, "director": "Christopher Nolan", "rating": 8.2},
        ),
    ]
    vectorstore = Chroma.from_documents(docs, OpenAIEmbeddings())
    
    from langchain.chains.query_constructor.base import AttributeInfo
    from langchain.retrievers.self_query.base import SelfQueryRetriever
    from langchain_openai import ChatOpenAI
    
    metadata_field_info = [
        AttributeInfo(
            name="genre",
            description="The genre of the movie. One of ['science fiction', 'comedy', 'drama', 'thriller', 'romance', 'action', 'animated']",
            type="string",
        ),
        AttributeInfo(
            name="year",
            description="The year the movie was released",
            type="integer",
        ),
    ]
    document_content_description = "Brief summary of a movie"
    llm = ChatOpenAI(temperature=0)
    retriever = SelfQueryRetriever.from_llm(
        llm, vectorstore, document_content_description, metadata_field_info,
    )
    
    # This example only specifies a filter
    retriever.invoke("I want to watch a movie rated higher than 8.5")
    

    上面代码,首先,构建 Document,生成对应的 Embedding 保存到 Vector Store 中;然后,创建 SelfQueryRetriever,需要指定 LLM、Vector Store、Document 相关的元数据信息(如果对搜索引擎索引数据比较熟悉,就会理解这么做的原因:是为了构建倒排索引);最后,就可以通过 retriever 来进行查询得到与输入 Prompt 最相关的 Document,并基于这些 Document 作为会话上下文,输入给 LLM 得到最终的会话输出结果。

    LangChain 模块:Agents

    Agent 通过 LLM 来推理,能够自然地、持续多轮地与 LLM 交互,这也是智能体所具备与支持的基本能力。
    为了说明 Agent 框架的使用方法,使用 Tavily 和 Retriever 两个工具来构建 Agent,其中 Tavily 是一个支持 Online 搜索的工具。

    • 1.准备 Tavily 和 Retriever
    from langchain_community.tools.tavily_search import TavilySearchResults
    search = TavilySearchResults()
    
    from langchain.text_splitter import RecursiveCharacterTextSplitter
    from langchain_community.document_loaders import WebBaseLoader
    from langchain_community.vectorstores import FAISS
    from langchain_openai import OpenAIEmbeddings
    loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
    docs = loader.load()
    documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)
    vector = FAISS.from_documents(documents, OpenAIEmbeddings())
    retriever = vector.as_retriever()
    
    from langchain.tools.retriever import create_retriever_tool
    retriever_tool = create_retriever_tool(
        retriever,
        "langsmith_search",
        "Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
    )
    
    • 2.创建 Agent
    from langchain_openai import ChatOpenAI
    tools = [search, retriever_tool]
    llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
    
    from langchain import hub
    prompt = hub.pull("hwchase17/openai-functions-agent") # Get the prompt to use
    
    from langchain.agents import create_openai_functions_agent
    agent = create_openai_functions_agent(llm, tools, prompt)
    
    from langchain.agents import AgentExecutor
    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
    
    • 3.运行 Agent

    运行 Agent,示例代码如下:

    agent_executor.invoke({"input": "hi!"})
    

    这样的交互不会记忆上下文,是无状态的。所以,如果考虑上下文,需要把以往的记录加入到 Memory,示例代码如下:

    from langchain_core.messages import AIMessage, HumanMessage
    agent_executor.invoke(
        {
            "chat_history": [
                HumanMessage(content="hi! my name is bob"),
                AIMessage(content="Hello Bob! How can I assist you today?"),
            ],
            "input": "what's my name?",
        }
    )
    

    也可以直接使用 LangChain 提供的 ChatMessageHistory、RunnableWithMessageHistory 来实现自动跟踪历史消息的功能,可以参考 LangChain 文档。

    LangChain 模块:Chains

    Chains 是一个调用/处理/计算任务序列的抽象,可以根据我们自己的需求来构建不同的调用链、处理链,等等。Chains 是通过 LangChain 提供的 LCEL 来构建的。下面是 LainChain 提供的一些 LCEL Constructor,可以方便构建一个 Chain,如下所示:

    • create_stuff_documents_chain
    • create_openai_fn_runnable
    • create_structured_output_runnable
    • load_query_constructor_runnable
    • create_sql_query_chain
    • create_history_aware_retriever
    • create_retrieval_chain

    通过名称,就可以大致了解每个 LCEL Constructor 实现的 Chain 的功能。

    LangChain 模块:Memory

    Memory 模块主要用来支持读(READ)和写(WRITE)两个基本的操作。在与 LLMM 交互过程中需要能够即时地获取、保存会话信息,具体交互流程如下图所示:
    langchain_memory
    下面以一个与 LLM 交互的过程为例,说明如何使用 LangChain 提供的 ConversationBufferMemory,示例代码如下:

    from langchain_openai import OpenAI
    from langchain.prompts import PromptTemplate
    from langchain.chains import LLMChain
    from langchain.memory import ConversationBufferMemory
    
    llm = OpenAI(temperature=0)
    prompt = PromptTemplate.from_template(template)
    memory = ConversationBufferMemory(memory_key="chat_history") # need to align the `memory_key`
    conversation = LLMChain(llm=llm, prompt=prompt, verbose=True, memory=memory)
    

    当然也可以直接使用 ChatModel 来实现整个会话过程,具体例子可以参考 LangChain 官网文档。
    Memory 模块提供了多个不同的实现,具体也可以根据自己实际应用需求来实现,如下所示是 LangChain 提供的一些实现:

    • ConversationBufferMemory
    • ConversationBufferWindowMemory
    • ConversationEntityMemory
    • ConversationKGMemory
    • ConversationSummaryMemory
    • ConversationSummaryBufferMemory
    • ConversationTokenBufferMemory
    • VectorStoreRetrieverMemory

    具体功能和使用方法,可以点击对应链接查看官网文档的详细说明。

    LangChain 模块:Callbacks

    LangChain 支持使用 Callbacks,通过 Hook 到基于 LLM 的应用的任何位置,来实现类似日志记录、监控、Streaming 处理以及其它任务等等。要想实现自定义的 Callback 能力,可以直接实现 CallbackHandler 接口,开发满足自己应用需求的 CallbackHandler。
    LangChain 实现的 StdOutCallbackHandler 是一个非常基础的 Callback,使用示例如下:

    from langchain.callbacks import StdOutCallbackHandler
    from langchain.chains import LLMChain
    from langchain_openai import OpenAI
    from langchain.prompts import PromptTemplate
    
    handler = StdOutCallbackHandler()
    llm = OpenAI()
    prompt = PromptTemplate.from_template("1 + {number} = ")
    
    chain = LLMChain(llm=llm, prompt=prompt, callbacks=[handler]) # Constructor callback
    chain.invoke({"number":2})
    
    chain = LLMChain(llm=llm, prompt=prompt, verbose=True) # Use verbose flag to achieve callbacks
    chain.invoke({"number":2})
    
    chain = LLMChain(llm=llm, prompt=prompt) # Request callbacks
    chain.invoke({"number":2}, {"callbacks":[handler]})
    

    LCEL(LangChain Expression Language)

    LCEL(LangChain Expression Language),是一种声明式表达式语言,可以非常容易地构建松耦合的执行链(Chain),主要优点如下所示:

    • 支持流式处理:可以通过 Streaming 的方式与 LLM 交互,比如根据自己构建的 LLM 应用的负载情况和处理能力,从 LLM 以 Streaming 的方式处理 LLM 输出的消息结果。
    • 支持异步:通过 LCEL 实现异步处理,也能方便地将同步方式改成异步方式,使 LLM 应用能够获得更好的性能。
    • 支持优化的并行执行:可以在 LangChain 底层对 LCEL chain 的每一步进行并行化,提高处理性能。
    • 支持重试与容错处理:可以对 LCEL chain 的每一步进行失败重试、容错处理,提高 LLM 应用的可靠性和健壮性。
    • 支持访问中间结果:通过访问 LCEL chain 中每一步的中间结果,能够对整个应用的处理过程进行透明化,并根据实际情况评估从而制定优化应用的方案。
    • 支持配置输入/输出 Schema:可以方便地用于验证输入/输出,同时更好地方便 LangServe 实现。
    • 支持无缝集成 LangSmith 跟踪监控:所有步骤执行的细节都可以记录到 LangSmith,从而最大限度地实现可观察性和可调试性。
    • 支持无缝集成 LangServe 部署:基于 LCEL 实现的 chain 可以非常方便地使用 LangServe 来完成部署。

    下面通过一个实现 prompt + model + output parser 的例子,用来说明使用 LCEL 带来的便利和直观,示例代码如下:

    from langchain_core.output_parsers import StrOutputParser
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI
    
    prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")
    model = ChatOpenAI(model="gpt-4")
    output_parser = StrOutputParser()
    
    chain = prompt | model | output_parser
    
    chain.invoke({"topic": "ice cream"})
    

    首先定义每一步中的逻辑,然后使用 UNIX 管道符 “|” 顺序连接,就可以构建好一个 LCEL chain,并启动执行该 LCEL chain。
    上面例子,是最简单的使用串行方式构建 LCEL chain,LangChain 也支持更加复杂的 LCEL chain,可以根据自己的应用需求来记性编排。下面是官网文档中相对复杂的一个例子,如图所示:
    langchain_rag_example_graph
    具体代码实现,参考官网这里 RAG Search Example。

    LangServe 工具介绍

    LangServe 提供了将 LangChain 的 runnables 和 chains 部署为 REST API 的能力,它是使用 FastAPI 来实现的。使用 LangServe 部署基于 LLM 的应用,会包含两个部分:Server(提供 LLM ChatModel 服务)、Client(调用模型服务)。
    下面通过一个例子来说明如何构建。

    • 实现 Server 端

    Server 包括 OpenAI ChatModel 和 Anthropic ChatModel,它们可以提供会话服务能力,示例代码如下:

    #!/usr/bin/env python
    from fastapi import FastAPI
    from langchain.prompts import ChatPromptTemplate
    from langchain.chat_models import ChatAnthropic, ChatOpenAI
    from langserve import add_routes
    import uvicorn
    
    app = FastAPI(
        title="LangChain Server",
        version="1.0",
        description="A simple api server using Langchain's Runnable interfaces",
    )
    
    add_routes(
        app, ChatOpenAI(), path="/openai",
    )
    
    add_routes(
        app, ChatAnthropic(), path="/anthropic",
    )
    
    model = ChatAnthropic()
    prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
    add_routes(
        app, prompt | model, path="/joke",
    )
    
    if __name__ == "__main__":
        uvicorn.run(app, host="localhost", port=8000)
    

    如果没有报错,打开浏览器浏览 localhost:8080/docs 可以看到对应的页面,说明 Server 端部署成功。

    • 实现 Client 端

    Client 端用来与 Server 端进行交互:调用 Server 端提供的 REST API 就可以实现。Client 端示例的 Python 代码如下:

    from langchain.schema import SystemMessage, HumanMessage
    from langchain.prompts import ChatPromptTemplate
    from langchain.schema.runnable import RunnableMap
    from langserve import RemoteRunnable
    
    openai = RemoteRunnable("http://localhost:8000/openai/")
    anthropic = RemoteRunnable("http://localhost:8000/anthropic/")
    joke_chain = RemoteRunnable("http://localhost:8000/joke/")
    
    joke_chain.invoke({"topic": "parrots"})
    
    # or async
    await joke_chain.ainvoke({"topic": "parrots"})
    
    prompt = [
        SystemMessage(content='Act like either a cat or a parrot.'),
        HumanMessage(content='Hello!')
    ]
    
    # Supports astream
    async for msg in anthropic.astream(prompt):
        print(msg, end="", flush=True)
    
    prompt = ChatPromptTemplate.from_messages(
        [("system", "Tell me a long story about {topic}")]
    )
    
    # Can define custom chains
    chain = prompt | RunnableMap({
        "openai": openai,
        "anthropic": anthropic,
    })
    
    chain.batch([{"topic": "parrots"}, {"topic": "cats"}])
    

    上面代码给出了不同的调用方式:同步调用、异步调用、流式调用,甚至可以根据 Server 端提供的服务接口实现自定义的 Chain 调用。

    LangSmith 工具介绍

    使用 LangChain 可以方便、快速构建我们的 LLM 应用或 Agent 的原型,但是并不能方便地实现从原型到生产环境的部署。所以就有了 LangSmith,LangSmith 可以非常方便地对原型进行调试、测试,持续地迭代直至满足生产环境要求。
    使用 LangSmith 平台,需要通过 https://smith.langchain.com/ 创建一个 LangSmith 账号并注册一个 API Key 才能使用。具体如何使用,可以参考官方文档 https://docs.smith.langchain.com/user_guide,这里不再过多说明。

    LangGraph 介绍

    LangGraph 是一个用来构建有状态的、多 Actor 的 LLM 应用的库,它是在 LangChain 框架的基础之上构建实现的。LangGraph 的灵感来自 Pregel 和 Apache Beam,支持基于 LCEL 的能力构建一个带有循环的(Cyclic)的应用模式,从而实现协调多个 Chain 跨多个计算步骤(Computation Steps)的能力。可见,LangGraph 是支持带环的计算图模式的,并不是简单的 DAG(有向无环图)。
    下面以官网的入门例子为例,说明如何使用 LangGraph,如下所示:

    • 1.构建一个 Online 搜索工具 Tavily
    from langchain_community.tools.tavily_search import TavilySearchResults
    tools = [TavilySearchResults(max_results=1)]
    
    from langgraph.prebuilt import ToolExecutor
    tool_executor = ToolExecutor(tools) # wrap Tavily tool using LangGraph ToolExecutor
    
    • 2.构建 LLM 对象
    from langchain_openai import ChatOpenAI
    # We will set streaming=True so that we can stream tokens
    model = ChatOpenAI(temperature=0, streaming=True)
    
    from langchain.tools.render import format_tool_to_openai_function
    functions = [format_tool_to_openai_function(t) for t in tools]
    model = model.bind_functions(functions) # Bind tools to the model
    
    • 3.定义 Agent 状态

    langgraph 提供的图主要是指 StatefulGraph,它可以被一个状态对象参数化(Parameterized)。状态对象在图中的每个节点之间传递,每一个节点会返回更新状态对象的操作,从而实现状态的更新。
    这里的状态会跟踪会话过程中产生的消息的列表,构建的图中的每个节点需要把消息添加到列表中,所以使用 TypedDict 来实现 Agent 状态,如下所示:

    from typing import TypedDict, Annotated, Sequence
    import operator
    from langchain_core.messages import BaseMessage
    
    class AgentState(TypedDict):
        messages: Annotated[Sequence[BaseMessage], operator.add]
    
    • 4.定义图节点(Node)和边(Edge)

    在 langgraph 中,图节点是一个 function 对象或 runnable 对象;然后通过边来连接各个节点,在 langgraph 中有两种类型的边:Conditional Edge 和 Normal Edge,可以通过定义函数来实现,确定从一个节点到另一个节点的路径。示例代码如下:

    from langgraph.prebuilt import ToolInvocation
    import json
    from langchain_core.messages import FunctionMessage
    
    # Define the function that determines whether to continue or not
    def should_continue(state):
        messages = state['messages']
        last_message = messages[-1]
        if "function_call" not in last_message.additional_kwargs:
            return "end"
        else:
            return "continue"
    
    # Define the function that calls the model
    def call_model(state):
        messages = state['messages']
        response = model.invoke(messages)
        return {"messages": [response]} # Return a list to add to the existing list
    
    # Define the function to execute tools
    def call_tool(state):
        messages = state['messages']
        # Based on the continue condition, we know the last message involves a function call
        last_message = messages[-1]
        # We construct an ToolInvocation from the function_call
        action = ToolInvocation(
            tool=last_message.additional_kwargs["function_call"]["name"],
            tool_input=json.loads(last_message.additional_kwargs["function_call"]["arguments"]),
        )
        response = tool_executor.invoke(action) # Call the tool_executor and get back a response
        function_message = FunctionMessage(content=str(response), name=action.tool) # use the response to create a FunctionMessage
        return {"messages": [function_message]}
    

    上面代码定义了一个 LangGraph 中需要个节点(call_model 和 call_tool 达标图中的两个计算节点)和边(通过 should_continue 函数决定计算下一个节点),节点和边的连接构建组合成图的逻辑,会在下一步进行。

    • 5.定义 StateGraph
    from langgraph.graph import StateGraph, END
    
    workflow = StateGraph(AgentState)
    
    # Define the two nodes we will cycle between
    workflow.add_node("agent", call_model)
    workflow.add_node("action", call_tool)
    
    workflow.set_entry_point("agent") # Set the entrypoint as `agent`
    
    # Add a conditional edge
    workflow.add_conditional_edges(
        "agent", # First, we define the start node. We use `agent`.
        should_continue, # Next, the function will determine which node is called next.
        # Finally we pass in a mapping, and based on which one it matches, that node will then be called.
        {
            "continue": "action", # If `tools`, then we call the tool node.
            "end": END # Otherwise we finish.
        }
    )
    
    # Add a normal edge from `tools` to `agent`: after `tools` is called, `agent` node is called next.
    workflow.add_edge('action', 'agent')
    
    app = workflow.compile() # # Compiles it into a LangChain Runnable,
    
    • 6.执行定义好的 LangGraph
    from langchain_core.messages import HumanMessage
    
    inputs = {"messages": [HumanMessage(content="what is the weather in sf")]}
    app.invoke(inputs)
    

    通过上面 1~6 步骤,就可以构建并执行一个基于 LangGraph 的简单 LLM 应用。

    参考资源

    • https://python.langchain.com/docs/get_started/introduction
    • https://blog.langchain.dev/langchain-v0-1-0/
    • https://blog.langchain.dev/the-new-langchain-architecture-langchain-core-v0-1-langchain-community-and-a-path-to-langchain-v0-1/
    • https://blog.bytebytego.com/p/how-to-build-a-smart-chatbot-in-10
    • https://python.langchain.com/docs/langsmith/
    • https://python.langchain.com/docs/langsmith/walkthrough
    • https://docs.smith.langchain.com/
    • https://python.langchain.com/docs/langgraph


沪ICP备19023445号-2号
友情链接