langchain相关介绍

基于LangChain框架的RAG(Retrieval-Augmented Generation)过程,以及它如何集成提示词、应用到RAG、其Memory机制,以及基于ReAct的Agent相关内容。

LangChain框架下的RAG(Retrieval-Augmented Generation)详细介绍

RAG是一种强大的范式,它结合了检索(Retrieval)和生成(Generation)模型,以解决大型语言模型(LLM)在面对特定领域知识或最新信息时的局限性。LangChain为构建RAG系统提供了丰富的模块和工具。

RAG的核心过程

RAG的核心思想是:当用户提出问题时,系统首先从一个或多个外部知识源中检索相关信息,然后将这些检索到的信息作为上下文,连同用户问题一起输入到LLM中,由LLM生成最终的回答。

在LangChain中,RAG过程通常包含以下几个关键步骤:

  1. 数据加载 (Data Loading):

    • 目的: 从各种来源加载原始数据。

    • LangChain组件: DocumentLoader

    • 示例: 可以加载PDF、网页、文本文件、数据库等。LangChain提供了多种加载器,例如PyPDFLoaderWebBaseLoaderPandasCSVLoader等。

    • 过程:

      • 选择合适的DocumentLoader
      • 调用加载器的load()方法,将数据加载为Document对象列表。Document对象通常包含page_content(文本内容)和metadata(元数据,如文件路径、页码等)。
  2. 文本分割 (Text Splitting):

    • 目的: 将加载的原始文档分割成更小、更易于处理的块(chunks)。这是为了适应LLM的输入长度限制,并确保检索到的块具有足够的相关性。

    • LangChain组件: TextSplitter

    • 示例: RecursiveCharacterTextSplitterCharacterTextSplitter

    • 过程:

      • 选择合适的TextSplitterRecursiveCharacterTextSplitter通常是首选,因为它会尝试按段落、句子等语义单元进行分割,然后才回退到字符分割。
      • 设置chunk_size(每个块的最大长度)和chunk_overlap(块之间的重叠部分,有助于保留上下文)。
      • 调用分割器的split_documents()方法,生成一系列文本块。
  3. 向量化 (Vectorization) / 嵌入 (Embedding):

    • 目的: 将文本块转换为数值向量(嵌入),以便在向量数据库中进行存储和检索。相似的文本块在向量空间中距离更近。

    • LangChain组件: Embeddings

    • 示例: OpenAIEmbeddingsHuggingFaceEmbeddingsGoogleGenerativeAIEmbeddings

    • 过程:

      • 选择一个嵌入模型。
      • 将文本块传递给嵌入模型,生成对应的向量表示。
  4. 向量存储 (Vector Store):

    • 目的: 存储文本块及其对应的向量嵌入,并提供高效的相似性搜索能力。

    • LangChain组件: VectorStore

    • 示例: ChromaFAISSPineconeWeaviateQdrant

    • 过程:

      • 初始化一个向量存储实例。
      • 使用向量存储的add_documents()方法将文本块和它们的嵌入添加到数据库中。
      • 在需要检索时,使用similarity_search()或其他检索方法根据查询向量查找最相关的文本块。
  5. 检索 (Retrieval):

    • 目的: 根据用户查询,从向量数据库中检索最相关的文本块。

    • LangChain组件: Retriever

    • 示例: VectorStoreRetrieverMultiQueryRetrieverContextualCompressionRetriever

    • 过程:

      • 用户输入查询。
      • 查询首先被嵌入。
      • 使用嵌入后的查询在向量数据库中执行相似性搜索,获取与查询最相似的K个文本块。
      • 这些文本块就是检索到的上下文。
  6. 生成 (Generation) / 问答 (Question Answering):

    • 目的: 将检索到的上下文和用户查询一起传递给LLM,由LLM生成最终的回答。

    • LangChain组件: LLM(或ChatModel)、PromptTemplateRunnable

    • 示例: ChatOpenAIChatGoogleGenerativeAI

    • 过程:

      • 构建Prompt: 使用PromptTemplate将检索到的上下文和用户问题组合成一个清晰的提示,指导LLM生成高质量的回答。

        • 通常会有一个系统指令,告诉LLM扮演什么角色,以及如何使用提供的上下文。
        • 然后是上下文的占位符。
        • 最后是用户问题的占位符。
      • 调用LLM: 将构建好的提示发送给LLM。

      • 获取回答: LLM根据提示生成回答。

RAG工作流示意图(LangChain视角):

scss 复制代码
用户查询 -> Retriever (检索相关文档块) -> LLM (结合查询和文档块生成回答) -> 最终回答
               ^                                   ^
               |                                   |
           Vector Store (存储文档嵌入) <--- Embeddings (文档块向量化) <--- Text Splitter (文档分割) <--- Document Loader (加载原始数据)

RAG如何集成提示词 (Prompt Engineering)

提示词工程在RAG中至关重要,它直接影响LLM生成回答的质量和相关性。

  1. 明确指示: 在提示中明确告诉LLM它的任务是什么,例如"根据提供的上下文回答问题","如果上下文没有提供信息,请说明"。

  2. 提供上下文: 这是RAG的核心。将检索到的文档块作为上下文,明确地包含在提示中。

    • 示例:

      css 复制代码
      "请根据以下上下文信息回答问题。
      上下文:
      {context}
      
      问题: {question}
      回答:"
  3. 约束回答格式: 如果需要特定格式的回答(例如,列表、总结、简短答案),可以在提示中指定。

    • 示例: "请以三点总结的方式回答。"
  4. 角色扮演: 让LLM扮演特定角色,例如"你是一个专业的法律顾问",这有助于引导其生成更符合预期的回答。

  5. 避免幻觉: 在提示中添加指令,鼓励LLM在缺乏信息时承认,而不是编造答案。

    • 示例: "如果上下文没有提供足够的信息来回答问题,请回复'抱歉,我无法根据现有信息回答这个问题。'"
  6. 少样本学习 (Few-shot Learning): 在提示中提供少量输入-输出对的示例,帮助LLM理解任务。这在RAG中不常用作核心部分,但在需要特定回答风格时可能有用。

在LangChain中,通常使用PromptTemplateChatPromptTemplate来构建这些提示词。

Python

python 复制代码
from langchain_core.prompts import ChatPromptTemplate

# 假设 context_docs 是检索到的文档列表
# prompt 结构包含了上下文占位符和问题占位符
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个有用的助手,请根据提供的上下文回答问题。如果上下文没有提供相关信息,请说明。"),
        ("user", "上下文: {context}\n\n问题: {question}"),
    ]
)

# 在实际应用中,context 会由检索器提供
# chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser()

RAG中的Memory (记忆)

在RAG应用中,Memory通常指的是对话历史的记忆,这对于构建多轮对话的RAG系统至关重要。如果没有记忆,LLM每次都会将当前问题视为一个全新的问题,无法理解上下文或先前的对话轮次。

LangChain中的Memory机制:

LangChain提供了多种Memory类,用于存储和管理对话历史:

  1. ConversationBufferMemory:

    • 功能: 存储所有对话回合(包括用户输入和AI输出)的原始形式。
    • 特点: 简单直接,但随着对话轮次增加,内存会线性增长,可能超出LLM的上下文窗口限制。
    • 使用场景: 短对话或LLM上下文窗口较大的情况。
  2. ConversationBufferWindowMemory:

    • 功能: 只存储最近的N个对话回合。
    • 特点: 控制内存大小,避免上下文溢出。
    • 使用场景: 需要限制内存大小以适应LLM上下文窗口的场景。
  3. ConversationSummaryMemory:

    • 功能: 使用LLM自身来总结之前的对话回合,而不是存储原始对话。
    • 特点: 压缩内存,只保留关键信息,更节省token。
    • 使用场景: 长对话或需要高效利用token的场景。需要额外的LLM调用来生成摘要。
  4. ConversationTokenBufferMemory:

    • 功能: 存储对话回合,但会根据token数量进行截断,确保总token数不超过设定的阈值。
    • 特点: 精确控制token使用量。
    • 使用场景: 对token使用有严格限制的场景。
  5. ConversationSummaryBufferMemory:

    • 功能: 结合了ConversationBufferWindowMemoryConversationSummaryMemory的优点。它会保留最近的几个回合的原始形式,并对更早的回合进行总结。
    • 特点: 在保留近期详细信息的同时,压缩了历史对话。
    • 使用场景: 兼顾近期细节和长期上下文的通用场景。

RAG中集成Memory:

在RAG系统中,Memory通常与一个ConversationalRetrievalChain或自定义的chain结合使用。

  • ConversationalRetrievalChain 这是LangChain提供的一个高级Chain,专门用于处理带有对话历史的RAG任务。它内部会处理以下逻辑:

    1. 结合当前用户问题和对话历史,生成一个新的"独立"问题(question_generator)。这个独立问题移除了对历史对话的指代,使得检索器可以更有效地检索。

    2. 使用这个独立问题进行检索。

    3. 将检索到的上下文、原始用户问题和对话历史(或摘要)一起传递给LLM,生成最终回答。

    • 示例:

    Python

    ini 复制代码
    from langchain.chains import ConversationalRetrievalChain
    from langchain.memory import ConversationBufferWindowMemory
    from langchain_core.prompts import PromptTemplate
    from langchain_openai import ChatOpenAI, OpenAIEmbeddings
    from langchain_community.vectorstores import Chroma
    
    # 假设你已经有了向量存储 db 和 LLM
    llm = ChatOpenAI(model="gpt-4o", temperature=0)
    embeddings = OpenAIEmbeddings()
    
    # 假设 document_chunks 是你的文本块
    # db = Chroma.from_documents(document_chunks, embeddings)
    # retriever = db.as_retriever()
    
    # 示例向量存储和检索器 (实际中应加载你的数据)
    from langchain_core.documents import Document
    docs = [
        Document(page_content="苹果公司由史蒂夫·乔布斯、史蒂夫·沃兹尼亚克和罗纳德·韦恩于1976年4月1日创立。"),
        Document(page_content="iPad是苹果公司设计、开发和销售的平板电脑系列产品。"),
        Document(page_content="iPhone是苹果公司发布的一系列智能手机。"),
        Document(page_content="MacBook是苹果公司生产的笔记本电脑。"),
    ]
    db = Chroma.from_documents(docs, embeddings)
    retriever = db.as_retriever()
    
    
    memory = ConversationBufferWindowMemory(memory_key="chat_history", return_messages=True, k=5)
    
    # 可以自定义问题生成和QA的prompt
    # question_generator_prompt = PromptTemplate.from_template("""根据对话历史和当前问题,生成一个独立的问题,该问题可以独立于历史对话被回答。
    # 对话历史:
    # {chat_history}
    # 原始问题: {question}
    # 独立问题:"""
    # )
    
    qa_chain_prompt = PromptTemplate.from_template("""请根据以下对话历史和检索到的上下文回答问题。
    对话历史:
    {chat_history}
    上下文:
    {context}
    问题: {question}
    回答:"""
    )
    
    
    qa_chain = ConversationalRetrievalChain.from_llm(
        llm=llm,
        retriever=retriever,
        memory=memory,
        # combine_docs_chain_kwargs={"prompt": qa_chain_prompt} # 如果需要自定义QA Chain的prompt
    )
    
    # 第一次提问
    result1 = qa_chain.invoke({"question": "苹果公司是谁创立的?"})
    print(f"User: 苹果公司是谁创立的?\nAI: {result1['answer']}")
    
    # 第二次提问,利用记忆
    result2 = qa_chain.invoke({"question": "他们还生产什么?"})
    print(f"User: 他们还生产什么?\nAI: {result2['answer']}")
    
    # 检查记忆中的内容
    print("\nMemory Content:")
    for msg in memory.chat_memory.messages:
        print(f"{msg.type}: {msg.content}")

在这个例子中,ConversationalRetrievalChain 会自动处理将对话历史和当前问题合并以生成新的检索查询,并将检索结果、历史和当前问题提供给LLM以生成最终答案。

基于ReAct的Agent的相关内容

RAG主要关注从现有知识库中检索信息来增强LLM的回答,而Agent则更进一步,赋予LLM执行复杂任务的能力,通常通过使用工具(Tools)和进行多步推理来实现。

ReAct (Reasoning and Acting) 是一个强大的Agent范式。 它的核心思想是:LLM在执行任务时,不仅生成行动(Action) ,还生成推理(Thought) 。这种结合使得Agent能够:

  1. 计划(Plan): 通过生成推理步骤来规划如何解决问题。
  2. 执行(Execute): 根据推理结果选择并使用合适的工具。
  3. 观察(Observe): 从工具的输出中获取观察结果。
  4. 迭代(Iterate): 根据观察结果调整后续的推理和行动,直到任务完成。

LangChain中的ReAct Agent:

LangChain为构建ReAct Agent提供了强大的支持。

核心组件:

  1. LLM (Language Model): 作为Agent的"大脑",负责生成推理和行动。

  2. Tools (工具): Agent可以调用的外部功能,例如搜索引擎、计算器、API调用、RAG检索器等。

    • 定义: 每个工具都有一个名称、描述和输入模式。描述对于LLM理解何时以及如何使用工具至关重要。

    • 示例:

      • Google Search (搜索网络)
      • calculator (执行数学运算)
      • RetrievalQA.from_chain(retriever_chain) (基于RAG检索内部知识)
  3. Agent Executor (Agent 执行器): 负责驱动Agent的循环过程,即解析LLM的输出(推理、行动),执行工具,并将工具的观察结果反馈给LLM。

    • ReAct Agent的Prompt: 通常包含明确的指令,要求LLM遵循ReAct模式(Thought, Action, Action Input, Observation, ...)。

ReAct Agent的工作流程:

  1. 用户输入问题。

  2. Agent Executor 将问题和历史传递给LLM。

  3. LLM思考(Thought): LLM生成一个推理步骤,解释它正在尝试做什么以及为什么。

    • 示例: Thought: 我需要搜索关于...的信息。
  4. LLM决定行动(Action)和行动输入(Action Input): LLM根据思考的结果,选择一个合适的工具,并生成调用该工具所需的输入。

    • 示例: Action: Google Search
    • 示例: Action Input: "最新手机型号"
  5. Agent Executor 执行行动: 调用指定的工具,并传入相应的输入。

  6. 工具返回观察结果(Observation): 工具执行后返回结果。

    • 示例: Observation: [搜索结果]
  7. Agent Executor 将观察结果反馈给LLM。

  8. LLM再次思考(Thought)和行动(Action): LLM根据新的观察结果,继续推理,决定下一步的行动。这个循环持续进行,直到LLM认为问题已解决,并生成最终答案。

    • 示例: Thought: 根据搜索结果,我可以回答这个问题了。
    • 示例: Action: Final Answer
    • 示例: Action Input: [最终答案]

ReAct Agent如何与RAG集成:

RAG可以作为ReAct Agent的一个强大工具

  • 场景: 当Agent需要回答特定领域或私有知识库中的问题时,它可以调用一个RAG工具。

  • 实现:

    1. 首先构建一个独立的RAG链(例如,一个RetrievalQAChain或自定义的检索问答链)。
    2. 将这个RAG链封装成一个LangChain Tool
    3. 将这个RAG工具添加到ReAct Agent的可用工具列表中。

示例代码结构 (LangChain ReAct Agent with RAG Tool):

Python

ini 复制代码
from langchain.agents import AgentExecutor, create_react_agent
from langchain_community.tools.tavily_search import TavilySearchResults # 搜索引擎工具
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain_core.prompts import PromptTemplate
from langchain_core.messages import AIMessage, HumanMessage

# 1. 初始化LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)
embeddings = OpenAIEmbeddings()

# 2. 准备 RAG 数据和链 (作为 Agent 的一个工具)
# 假设你已经加载和分割了文档,并创建了向量存储
# 这里我们创建一个简单的示例
docs = [
    Document(page_content="关于公司内部政策,请查阅员工手册。"),
    Document(page_content="2024年的公司年会将在三亚举行。"),
    Document(page_content="技术部门的联系方式是[email protected]。"),
]
db = Chroma.from_documents(docs, embeddings)
retriever = db.as_retriever()

# 构建 RAG 链
qa_prompt = PromptTemplate.from_template("""根据以下上下文回答问题。
上下文: {context}
问题: {question}
回答:""")

rag_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff", # 简单地将所有检索到的文档塞进LLM
    retriever=retriever,
    # prompt=qa_prompt, # 可以自定义RAG的prompt
    return_source_documents=True
)

# 3. 将 RAG 链封装成一个 Tool
from langchain.tools import Tool

rag_tool = Tool(
    name="internal_knowledge_base",
    func=rag_chain.run, # 注意这里是 .run
    description="当问题涉及到公司内部政策、年会、部门联系方式等内部信息时,使用此工具。输入是用户问题。"
)

# 4. 定义其他工具 (例如,搜索引擎)
search_tool = TavilySearchResults() # 确保你设置了TAVILY_API_KEY环境变量

tools = [rag_tool, search_tool]

# 5. 定义 ReAct Agent 的 Prompt (通常包含 Thought, Action, Action Input, Observation 结构)
# LangChain的 create_react_agent 已经内置了 ReAct 范式的prompt
# 我们需要提供一个基本的prompt,它会和agent的system prompt结合
base_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个有用的AI助手,可以根据用户的问题使用工具。"),
        ("placeholder", "{agent_scratchpad}"), # 这个是ReAct Agent的关键,用于放置中间的Thought/Action/Observation
        ("human", "{input}"),
    ]
)

# 6. 创建 ReAct Agent
agent = create_react_agent(llm, tools, base_prompt)

# 7. 创建 Agent Executor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

# 8. 运行 Agent
print("Running Agent with RAG and Search Tools...")

# 问内部知识库的问题
print("\n--- Question 1: Internal Knowledge ---")
result1 = agent_executor.invoke({"input": "2024年的公司年会将在哪里举行?"})
print(f"Agent Answer: {result1['output']}")

# 问外部知识的问题
print("\n--- Question 2: External Knowledge ---")
result2 = agent_executor.invoke({"input": "月球是什么时候形成的?"})
print(f"Agent Answer: {result2['output']}")

# 问一个混合问题 (可能先使用内部,再使用外部,或者选择一个)
print("\n--- Question 3: Mixed Question ---")
result3 = agent_executor.invoke({"input": "请告诉我技术部门的联系方式,以及最新的AI技术发展是什么?"})
print(f"Agent Answer: {result3['output']}")

ReAct Agent的优点:

  • 可解释性: Thought 步骤提供了Agent决策过程的透明度,方便调试和理解。
  • 鲁棒性: 能够处理复杂的多步推理任务。
  • 灵活性: 可以通过添加不同类型的工具来扩展Agent的能力。
  • 避免幻觉: 通过调用外部工具获取真实信息,显著减少LLM的幻觉问题。

总结:

LangChain为构建复杂的RAG和Agent系统提供了模块化且强大的框架。RAG专注于通过检索增强LLM的知识,而ReAct Agent则通过多步推理和工具使用,使LLM能够执行更复杂的任务。RAG本身可以作为ReAct Agent的一个强大工具,使得Agent在需要内部或特定领域知识时,能够准确有效地获取信息。这种结合在构建智能、知识驱动的AI应用中具有巨大潜力。

相关推荐
90后的晨仔5 小时前
使用Trae这个AI工具实现一个个性的技术博客
ai编程
MrSkye8 小时前
从零到一:我用AI对话写出了人生第一个弹幕游戏 | Prompt编程实战心得
前端·ai编程·trae
用户18881478528819 小时前
谷歌Gemini编程吊打全场,国产AI全歇菜。
ai编程
俞乾9 小时前
这三个 MCP 组合,让 Cursor 指哪打哪
ai编程·cursor·vibecoding
量子位11 小时前
Scaling Law 首次在自动驾驶赛道被验证!小鹏汽车 CVPR 演讲详解:AI「吃」下 6 亿秒视频后,智能涌现
chatgpt·ai编程
量子位11 小时前
全方位实测首个 AI 原生浏览器!618 比价、写高考作文... 网友:再见 Chrome
人工智能·ai编程
加111 小时前
95%代码AI生成,是的你没听错...…
前端·ai编程
我姚学AI13 小时前
大厂程序员自研Flomo AI插件,1秒总结笔记,官方都没做到!
ai编程
Jaising66614 小时前
JetBrains AI 打零工(四)——维护 Junie Guidelines 与代码可追溯
ai编程·intellij idea