LangChain框架
本文最后更新于15 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

学习目标:

  1. 熟悉 LangChain核心组件
  2. 熟悉 LangChain的使用方式
  3. 熟悉 LangChain各个开发组件

一. 简介

LangChain 是⼀个⽤于开发由⼤型语⾔模型(LLMs)驱动的应⽤程序的框架。

官⽅⽂档:https://python.langchain.com/docs/introduction/

中文文档: https://langchain.ichuangpai.com/

说明: LangChain就是一个开发大模型应用开发框架,可以在原有模型的基础上加一些独属于我们自己的一些数据和配置(公司的内部数据 ),能让我们做开发的时候更加方便,类似java开发中springpython开发中的Django,爬虫开发中的scrapy.

LangChain简化了LLM应用程序生命周期的各个阶段:

开发阶段:使用LangChain的开源构建块和组件构建应用程序,利用第三方集成和模板快速启动。

生产化阶段:使用LangSmith检查、监控和评估您的链,从而可以自信地持续优化和部署。

部署阶段:使用LangServe将任何链转化为API。

1. Langchain的核心组件

  • 模型(Models):包含各大语言模型的LangChain接口和调用细节,以及输出解析机制。
  • 提示模板(Prompts):使提示工程流线化,进一步激发大语言模型的潜力。
  • 数据检索(Indexes):构建并操作文档的方法,接受用户的查询并返回最相关的文档,轻松搭建本地知识库。
  • 记忆(Memory):通过短时记忆和长时记忆,在对话过程中存储和检索数据,让ChatBot记住你。
  • 链(Chains):LangChain中的核心机制,以特定方式封装各种功能,并通过一系列的组合,自动而灵活地完成任务。
  • 代理(Agents):另一个LangChain中的核心机制,通过“代理”让大模型自主调用外部工具和内部工具,使智能Agent成为可能。

2. 模块封装的功能

2.1 模型 I/O 封装

  • LLMs:大语言模型
  • ChatModels:一般基于 LLMs,但按对话结构重新封装
  • Prompt:提示词模板
  • OutputParser:解析输出

2.2 Retrieval 数据连接与向量检索封装

  • Retriever: 向量的检索
  • Document Loader:各种格式文件的加载器
  • Embedding Model:文本向量化表示,用于检索等操作
  • Verctor Store: 向量的存储
  • Text Splitting:对文档的常用操作

2.3 Agents 代理封装

根据用户输入,自动规划执行步骤,自动选择每步需要的工具,最终完成用户指定的功能,包括:

  • Tools:调用外部功能的函数,例如:调 google 搜索、文件 I/O、Linux Shell 等等
  • Toolkits:操作某软件的一组工具集,例如:操作 DB、操作 Gmail 等等

3. 开源第三方库

  • langchain-core :基础抽象和LangChain表达式语言
  • langchain-community :第三方集成。合作伙伴包(如langchain-openai、langchain-anthropic等),一些集成已经进一步拆分为自己的轻量级包,只依赖于langchain-core
  • langchain :构成应用程序认知架构的链、代理和检索策略
  • langgraph:通过将步骤建模为图中的边和节点,使用 LLMs 构建健壮且有状态的多参与者应用程序
  • langserve:将 LangChain 链部署为 REST API
  • LangSmith:一个开发者平台,可让您调试、测试、评估和监控LLM应用程序,并与LangChain无缝集成

注意: Langchain开发我们一般说的是他的整个生态

4. LangChain基本使用

模块安装

# 安装LangChain 
pip install langchain
pip install -U langchain-ollama
pip install python-dotenv
https://pypi.tuna.tsinghua.edu.cn/simple

4.1模型调用

通过LangChain的接口来调用ollama对话

from dotenv import load_dotenv
from langchain_ollama import ChatOllama
import os

load_dotenv()

llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="llama3.1")

# 直接提供问题,并调用llm
response = llm.invoke("什么是大模型?")
print(response)
print("=" * 50)
print(response.content)
content='大模型是一种新型人工智能技术,用于处理和理解人类语言。这种模型的特点是能够处理大量的文本数据,并能够根据输入内容生成相关的回复或回答。大模型通常依赖于深度学习方法来训练,这类算法能让机器通过数据自我学习和改进自己的识别能力。' additional_kwargs={} response_metadata={'model': 'llama3.1', 'created_at': '2025-09-05T01:51:31.01231346Z', 'done': True, 'done_reason': 'stop', 'total_duration': 1961507887, 'load_duration': 48376451, 'prompt_eval_count': 15, 'prompt_eval_duration': 1883590, 'eval_count': 78, 'eval_duration': 1910623159, 'model_name': 'llama3.1'} id='run--6f582936-7bab-46f4-8476-bf2434d047fe-0' usage_metadata={'input_tokens': 15, 'output_tokens': 78, 'total_tokens': 93}
==================================================
大模型是一种新型人工智能技术,用于处理和理解人类语言。这种模型的特点是能够处理大量的文本数据,并能够根据输入内容生成相关的回复或回答。大模型通常依赖于深度学习方法来训练,这类算法能让机器通过数据自我学习和改进自己的识别能力。

多轮对话的封装

import os
from dotenv import load_dotenv
from langchain_ollama import ChatOllama
from langchain.schema import (
    AIMessage,  # 等价于OpenAI接口中的assistant role AI 模型的回复消息
    HumanMessage,  # 等价于OpenAI接口中的user role  表示用户输入的消息
    SystemMessage  # 等价于OpenAI接口中的system role  系统级指令或背景设定
)

load_dotenv()

llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="llama3.1")
                 
messages = [
    SystemMessage(content="你是各位老师的个人助理。你叫小戈"),
    HumanMessage(content="我的名字叫小张"),
    AIMessage(content="不好意思,暂时无法获取天气情况"),
    # HumanMessage(content="我是谁?")
    HumanMessage(content="今天天气怎么样")
]
response = llm.invoke(messages)
print(response.content)
我可以检查一下天气预报给你看看。目前显示今天是晴天,气温在15度左右。

4.2 使用提示模板

# 我们也可以创建prompt template, 并引入一些变量到prompt template中,这样在应用的时候更加灵活
from langchain_core.prompts import ChatPromptTemplate
from dotenv import load_dotenv
from langchain_ollama import ChatOllama
import os

load_dotenv()
llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="llama3.1")

# 需要注意的一点是,这里需要指明具体的role,在这里是system和用户
prompt = ChatPromptTemplate.from_messages([
    ("system", "您是世界级的技术文档编写者"),
    ("human", "{input}")  # {input}为变量
])
print(prompt)

# 我们可以把prompt和具体llm的调用和在一起(通过chain,chain可以理解为sequence of calls to take)  Linux  ps aux | grep redis
chain = prompt | llm
response = chain.invoke({"input": "大模型中的LangChain是什么?"})
print(response.content)
input_variables=['input'] input_types={} partial_variables={} messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], input_types={}, partial_variables={}, template='您是世界级的技术文档编写者'), additional_kwargs={}), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], input_types={}, partial_variables={}, template='{input}'), additional_kwargs={})]
LangChain 是一个用于构建大型语言模型 (LLMs) 应用的链式操作库。它提供了一系列预先构造好的函数和方法,以帮助开发人员在 LLMs 上执行特定的任务,例如问答、摘要、生成文本等。

LangChain 的主要功能包括:

1. **数据处理**:LangChain 提供了各种数据处理函数,如数据清洗、切片、拼接等。
2. **LLMs 操作**:LangChain 包含了一系列用于操作 LLMs 的函数,例如问答、摘要、生成文本等。
3. **链式操作**:LangChain 支持链式操作,这使得开发人员可以轻松地组合多个函数来实现复杂的任务。
4. **可扩展性**:LangChain 允许用户自定义函数和方法,以适应特定的应用场景。

使用 LangChain,开发人员可以快速构建大型语言模型应用程序,使得开发过程变得更加高效和容易。

4.3 使用输出解释器

from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from dotenv import load_dotenv
import os

load_dotenv()
# 初始化模型
llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="llama3.1")


# 创建提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "您是世界级的技术文档编写者。"),
    ("human", "{input}")
])

# 使用输出解析器
# output_parser = StrOutputParser()
output_parser = JsonOutputParser()

# 将其添加到上一个链中
chain = prompt | llm | output_parser
# chain = prompt | llm

# 调用它并提出同样的问题。答案是一个字符串,而不是ChatMessage
# 如果你没有让大模型使用json格式输出,会报错
# res = chain.invoke({"input": "LangChain是什么?"})
res = chain.invoke({"input": "LangChain是什么? 问题用question 回答用answer 用JSON格式回复"})

print(res)
{'name': 'LangChain', 'description': 'LangChain是一个开源的Python库,专注于语言链(Language Chain)编程模型,它使开发者能够轻松地构建复杂的语言任务和应用。', 'features': ['支持多种语言模型和任务类型', '提供强大的API和SDK', '易于使用和扩展'], 'use_cases': ['问答系统', '生成型任务', '对话系统', '自然语言处理等']}

4.4 向量存储

  • 使用一个简单的本地向量存储 FAISS,首先需要安装它
pip install faiss-cpu
pip install langchain_community
# 导入和使用 WebBaseLoader
import os

from langchain_community.document_loaders import WebBaseLoader
from dotenv import load_dotenv
import bs4
# 对于嵌入模型,这里使用ollamaAPI调用 
from langchain_ollama.embeddings import OllamaEmbeddings
# 使用此嵌入模型将文档摄取到矢量存储中
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import RecursiveCharacterTextSplitter

load_dotenv()


# 读取网页中的数据
loader = WebBaseLoader(
    web_path="https://www.gov.cn/xinwen/2020-06/01/content_5516649.htm",
    bs_kwargs=dict(parse_only=bs4.SoupStrainer(id="UCAP-CONTENT"))
)
# 读取数据
docs = loader.load()
print(docs)
SER_AGENT environment variable not set, consider setting it to identify your requests.
[Document(metadata={'source': 'https://www.gov.cn/xinwen/2020-06/01/content_5516649.htm'}, page_content='\n新华社北京6月1日电\n中华人民共和国民法典\n.........)]
# 创建向量模型
embeddings = OllamaEmbeddings(base_url=os.getenv("OLLAMA_BASE_URL"),
                model="bge-m3:latest")
print(embeddings)
# 使用分割器分割文档
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
documents = text_splitter.split_documents(docs)
print(documents)
# 向量存储  embeddings 会将 documents 中的每个文本片段转换为向量,并将这些向量存储在 FAISS 向量数据库中
vector = FAISS.from_documents(documents, embeddings)

4.5 RAG+Langchain

基于外部知识,增强大模型回复

from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_retrieval_chain
import os
from dotenv import load_dotenv

load_dotenv()

# {context}变量必须包含
prompt = ChatPromptTemplate.from_template("""仅根据提供的上下文回答以下问题:

<context>
{context}
</context>

问题: {input}""")
# 创建llm连接
llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="llama3.1")
# 创建文档组合链  将文档内容和用户问题组合成一个完整的提示,然后传递给语言模型生成回答
document_chain = create_stuff_documents_chain(llm, prompt)
# 生成检索器示例
retriever = vector.as_retriever()
retriever.search_kwargs = {"k": 3}  # 限制为最多检索3个文档
# 创建检索链   该链结合了检索器和文档组合链,实现了从向量数据库中检索相关文档,并将这些文档与用户问题组合成提示
retrieval_chain = create_retrieval_chain(retriever, document_chain)
# 调用检索链并获取回答
response = retrieval_chain.invoke({"input": "建设用地使用权是什么?"})
print(response["answer"])
建设用地使用权是指用于建設建筑物、構築物及其附屬設施用的土地上的權利。

4.6 代理使用

在LangChain框架中,Agents是一种利用大型语言模型(Large Language Models,简称LLMs)来执行任务和做出决策的系统

在 LangChain 的世界里,Agent 是一个智能代理,它的任务是听取你的需求(用户输入)和分析当前的情境(应用场景),然后从它的工具箱(一系列可用工具)中选择最合适的工具来执行操作

  • 使用工具(Tool):LangChain中的Agents可以使用一系列的工具(Tools)实现,这些工具可以是API调用、数据库查询、文件处理等,Agents通过这些工具来执行特定的功能。
  • 推理引擎(Reasoning Engine):Agents使用语言模型作为推理引擎,以确定在给定情境下应该采取哪些行动,以及这些行动的执行顺序。
  • 可追溯性(Traceability):LangChain的Agents操作是可追溯的,这意味着可以记录和审查Agents执行的所有步骤,这对于调试和理解代理的行为非常有用。
  • 自定义(Customizability):开发者可以根据需要自定义Agents的行为,包括创建新的工具、定义新的Agents类型或修改现有的Agents。
  • 交互式(Interactivity):Agents可以与用户进行交互,响应用户的查询,并根据用户的输入采取行动。
  • 记忆能力(Memory):LangChain的Agents可以被赋予记忆能力,这意味着它们可以记住先前的交互和状态,从而在后续的决策中使用这些信息。
  • 执行器(Agent Executor):LangChain提供了Agent Executor,这是一个用来运行代理并执行其决策的工具,负责协调代理的决策和实际的工具执行。
from langchain_ollama import ChatOllama
from langchain import hub
from langchain.agents import AgentExecutor
from langchain.tools.retriever import create_retriever_tool
from langchain.agents import create_react_agent, AgentExecutor
from langchain_ollama import ChatOllama
import os
from dotenv import load_dotenv

load_dotenv()
# 读取数据
retriever = vector.as_retriever()

# 检索器工具
retriever_tool = create_retriever_tool(
    retriever,
    "中华人民共和国民法典的一个检索器工具",
    "用于检索《中华人民共和国民法典》相关法条。仅当问题明确涉及法律条文时使用",
)

tools = [retriever_tool]
llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="qwen3:4b")
# 创建一个agent代理,tools:该代理可以访问的工具
agent = create_react_agent(
    llm=llm,
    tools=tools,
    # 可以不传 prompt,或使用 hub 的标准 prompt
    prompt=hub.pull("hwchase17/react")
)
# agent:要执行那个代理 tools:代理可以调用的工具,verbose:是否以详细模型运行,
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 运行代理
res = agent_executor.invoke({"input": "婚姻法是什么"})
print(res)
> Entering new AgentExecutor chain...
<think>
嗯,用户问“婚姻法是什么”。首先,我需要确定这个问题是否涉及到《中华人民共和国民法典》中的相关法条。因为在中国,原来的《婚姻法》已经被《民法典》取代了,所以现在可能没有单独的婚姻法了。用户可能不太清楚这个变动,所以需要解释清楚。

首先,我应该用提供的检索器工具来查一下《中华人民共和国民法典》中关于婚姻的部分。不过问题问的是“婚姻法”,而民法典中可能有婚姻家庭编。可能需要检索民法典的婚姻家庭编相关内容。

不过用户的问题是“婚姻法是什么”,可能他们以为现在还有单独的婚姻法,但实际上自2021年1月1日《民法典》实施后,婚姻法已经被废止,现在属于民法典的婚姻家庭编。所以需要先确认这一点。

所以,我应该使用检索器工具,查询民法典中与婚姻相关的内容,比如可能的条目。比如,可能的检索词是“婚姻家庭”或者“婚姻”。

不过,用户的问题可能需要先说明婚姻法已经被民法典取代,所以回答时要指出这一点,然后给出民法典中婚姻家庭编的相关内容。

先调用检索器,输入查询词“婚姻法”或者“婚姻家庭”。不过根据工具的描述,这个检索器是用于检索《中华人民共和国民法典》相关法条,所以可能需要输入“婚姻”或者“婚姻家庭”来找到对应的条款。

可能的Action Input是“婚姻法”,但根据实际情况,民法典中没有“婚姻法”这个条目,而是有婚姻家庭编。所以可能检索到的是民法典的婚姻家庭编的条文。

不过需要实际调用工具看看。假设调用后,会返回民法典中婚姻家庭编的条款,比如第一千零四十三条到第一千零七十八条左右的内容。

然后,根据检索结果,解释婚姻法已经被民法典取代,现在婚姻相关法律属于民法典婚姻家庭编。

所以,步骤应该是:

1. 使用检索器查询“婚姻法”或者“婚姻家庭”。
2. 观察返回结果,确认是否民法典中有关联条款。
3. 解释婚姻法已废止,现在由民法典婚姻家庭编管理。

现在调用工具,Action Input是“婚姻法”,但可能需要更准确的关键词,比如“婚姻家庭”或者“婚姻”。

不过根据工具的参数,query是字符串,所以输入“婚姻法”可能返回民法典中相关的条款。

假设检索结果返回了民法典的婚姻家庭编的条文,那么就可以说明现在没有单独的婚姻法,而是民法典中的婚姻家庭编。

比如,可能的观察结果是:《中华人民共和国民法典》自2021年1月1日起施行,原《婚姻法》被废止,相关婚姻法律规范已纳入民法典的婚姻家庭编(第一千零四十三条至第一千零七十八条)。

所以,最终答案需要说明这一点。
</think>

Thought: 我需要确定用户询问的“婚姻法”在现行法律体系中的定位。根据中国法律,原《婚姻法》已于2021年1月1日《中华人民共和国民法典》实施后被废止,相关条款已整合至民法典的婚姻家庭编。因此,应通过检索器查询民法典中婚姻家庭相关条款,同时说明法律变更情况。

Action: 中华人民共和国民法典的一个检索器工具
Action Input: "婚姻家庭"
Finished chain.
{'input': '婚姻法是什么', 'output': '《婚姻法》已于2021年1月1日《中华人民共和国民法典》实施后被废止,原《婚姻法》相关法律规范已整合至《中华人民共和国民法典》的**婚姻家庭编**(第六编)。目前中国婚姻法律体系由《民法典》婚姻家庭编(第一千零四十三条至第一千零七十八条)统一规范,涵盖结婚条件、家庭关系、财产分割等内容。例如:  \n- **第一千零四十三条**:夫妻应当互相忠实、互相尊重,家庭成员应维护平等、和睦、文明的婚姻家庭关系;  \n- **第一千零四十七条**:结婚年龄为男不得早于22周岁,女不得早于20周岁;  \n- **第一千零五十三条**:一方患有重大疾病,需在结婚登记前如实告知另一方。  \n\n因此,当前中国不存在单独的《婚姻法》,相关法律由《中华人民共和国民法典》婚姻家庭编统一管理。'}

二.LangChain的Model

可以把对模型的使用过程拆解成三块: 输入提示(Format)、调用模型(Predict)、输出解析(Parse)

  • 1.提示模板: LangChain的模板允许动态选择输入,根据实际需求调整输入内容,适用于各种特定任务和应用。
  • 2.语言模型: LangChain 提供通用接口调用不同类型的语言模型,提升了灵活性和使用便利性。
  • 3.输出解析: 利用 LangChain 的输出解析功能,精准提取模型输出中所需信息,避免处理冗余数据,同时将非结构化文本转换为可处理的结构化数据,提高信息处理效率。

这三块形成了一个整体,在LangChain中这个过程被统称为Model I/O。针对每块环节,LangChain都提供了模板和工具,可以帮助快捷的调用各种语言模型的接口

很多用户可能对大模型使用的不熟练,那么我们给了他模版他只需要填关键字就行

1. 提示模板

在LangChain的Model I/O中,提示模板是其组成之一,语言模型的提示是用户提供的一组指令或输入,用于指导模型的响应,帮助模型理解上下文并生成相关且连贯的基于语言的输出,例如回答问题、完成句子或参与某项活动、对话

PromptTemplates 是LangChain中的一个概念,通过接收原始用户输入,并返回一个准备好传递给语言模型的信息(即提示词 prompt)

通俗点说,prompt template 是一个模板化的字符串,可以用来生成特定的提示(prompts)。你可以将变量插入到模板中,从而创建出不同的提示。这对于重复生成相似格式的提示非常有用,尤其是在自动化任务中。

1.1 LangChain提示模板特点

  1. 清晰易懂的提示: 提高提示文本的可读性,使其更易于理解,尤其是在处理复杂或涉及多个变量的情况下。
  2. 增强可重用性: 使用模板,可以在多个地方重复使用,简化代码,无需重复构建提示字符串。
  3. 简化维护: 使用模板后,如果需要更改提示内容,只需修改模板,无需逐个查找所有用到该提示的地方。
  4. 智能处理变量: 模板可以自动处理变量的插入,无需手动拼接字符串。
  5. 参数化生成: 模板可以根据不同的参数生成不同的提示,有助于个性化文本生成。

1.2 类型

  1. LLM提示模板 PromptTemplate:常用的String提示模板
  2. 聊天提示模板 ChatPromptTemplate: 常用的Chat提示模板,用于组合各种角色的消息模板,传入聊天模型。消息模板包括:ChatMessagePromptTemplate、HumanMessagePromptTemplate、AIlMessagePromptTemplate、SystemMessagePromptTemplate等
  3. 样本提示模板 FewShotPromptTemplate:通过示例来教模型如何回答
  4. 部分格式化提示模板:提示模板传入所需值的子集,以创建仅期望剩余值子集的新提示模板。
  5. 管道提示模板 PipelinePrompt: 用于把几个提示组合在一起使用。
  6. 自定义模板:允许基于其他模板类来定制自己的提示模板。

使用的模块

from langchain.prompts.prompt import PromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.pipeline import PipelinePromptTemplate
from langchain.prompts import ChatPromptTemplate
from langchain.prompts import (
    ChatMessagePromptTemplate,
    SystemMessagePromptTemplate,
    AIMessagePromptTemplate,
    HumanMessagePromptTemplate,
)

1.3 String提示模板

# 导入LangChain中的ollama模型接口
from langchain_ollama import ChatOllama
# 导入LangChain中的提示模板
from langchain.prompts import PromptTemplate
import os
from dotenv import load_dotenv

load_dotenv()

# 创建模型实例
llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="qwen3:4b")

prompt = PromptTemplate(
    template="您是一位专业的程序员。\n对于信息 {text} 进行简短描述"
)

# 输入提示
input = prompt.format(text="大模型langchain")

# 得到模型的输出
output = llm.invoke(input)
# output = model.invoke("您是一位专业的程序员。对于信息 langchain 进行简短描述")

# 打印输出内容
print(output.content)

LangChain 是开源Python框架,专为大模型(LLM)信息处理和应用开发设计,简化任务自动化与推理。

1.4 聊天提示模板

  • PromptTemplate创建字符串提示的模板。默认情况下,使用Python的str.format语法进行模板化。而ChatPromptTemplate是创建聊天消息列表的提示模板。
  • 创建一个ChatPromptTemplate提示模板,模板的不同之处是它们有对应的角色。
from langchain.prompts.chat import ChatPromptTemplate
# 导入LangChain中的ollama模型接口
from langchain_ollama import ChatOllama
import os
from dotenv import load_dotenv

load_dotenv()

template = "你是一个数学家,你可以计算任何算式"
# template = "你是一个翻译专家,擅长将 {input_language} 语言翻译成 {output_language}语言."
human_template = "{text}"

chat_prompt = ChatPromptTemplate.from_messages([
    ("system", template),
    ("human", human_template),
])
# print(chat_prompt)


# 创建模型实例
llm = ChatOllama(base_url=os.getenv("OLLAMA_BASE_URL"),
                 model="qwen3:4b")
# 输入提示
messages = chat_prompt.format_messages(text="我今年18岁,我的舅舅今年38岁,我的爷爷今年72岁,我和舅舅一共多少岁了?")
# print(messages)
# messages = chat_prompt.format_messages(input_language="英文", output_language="中文", text="I love Large Language Model.")
print(messages)
# 得到模型的输出
output = llm.invoke(messages)
# 打印输出内容
print(output.content)
[SystemMessage(content='你是一个数学家,你可以计算任何算式', additional_kwargs={}, response_metadata={}), HumanMessage(content='我今年18岁,我的舅舅今年38岁,我的爷爷今年72岁,我和舅舅一共多少岁了?', additional_kwargs={}, response_metadata={})]
<think>
嗯,用户问的是,他今年18岁,舅舅38岁,爷爷72岁,问我和舅舅一共多少岁了。首先,我需要确认问题是不是问“我和舅舅一共多少岁”,也就是用户自己加上舅舅的年龄总和。

首先,用户自己是18岁,舅舅是38岁,那加起来应该是18加38。那计算的话,18加38等于56。爷爷的年龄72岁可能是个干扰项,因为问题只问我和舅舅的年龄和,所以爷爷的年龄不需要用到。

不过,我需要再仔细看一下问题有没有哪里理解错了。用户说“我和舅舅一共多少岁了”,所以是用户自己(18)加上舅舅(38),对吧?爷爷的年龄可能只是题目里提到的,但问题不涉及爷爷的年龄和,所以应该不用考虑。

那计算的话,18+31?不,是38。18加38,个位8+8=16,进位1,十位1+3=4,加进位1就是5,所以是56岁。所以答案应该是56岁。

不过,有没有可能用户的问题有其他意思?比如“我和舅舅一共多少岁”是不是指他们两人年龄的总和,是的,所以就是18+38=56。爷爷的年龄72可能只是题目里给出的其他信息,但问题不涉及,所以不用算。

再确认一下,用户的问题是否是中文的,这里翻译过来可能没问题。用户说“我今年18岁,我的舅舅今年38岁,我的爷爷今年72岁,我和舅舅一共多少岁了?”,所以问题明确是“我和舅舅一共多少岁”,所以就是18+38=56岁。

可能的错误点:有没有可能用户问的是“我和舅舅的年龄和比爷爷小多少”之类的,但题目里没有,所以应该只是直接加两个年龄。所以答案是56岁。
</think>

你今年18岁,舅舅今年38岁,两人年龄之和为:  
**18 + 38 = 56岁**  

爷爷的年龄(72岁)在本题中未被使用,因此无需考虑。  

**答案:56岁**

LangChain提供不同类型的MessagePromptTemplate.最常用的是AIMessagePromptTemplate、 SystemMessagePromptTemplate和HumanMessagePromptTemplate,分别创建人工智能消息、系统消息和人工消息。

# 导入聊天消息类模板
from langchain.prompts import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain_ollama import ChatOllama
import os
from dotenv import load_dotenv

load_dotenv()

# 系统模板的构建
system_template = "你是一个翻译专家,擅长将 {input_language} 语言翻译成 {output_language}语言."
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)

# 用户模版的构建
human_template = "{text}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)

# 组装成最终模版
prompt_template = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

# 格式化提示消息生成提示
prompt = prompt_template.format_prompt(input_language="英文", output_language="中文",
                                       text="I love Large Language Model.").to_messages()
# 打印模版
print("prompt:", prompt)

# 创建模型实例
model = ChatOllama(
                   base_url=os.getenv("OLLAMA_BASE_URL"),
                   model='qwen3:4b')
# 得到模型的输出
result = model.invoke(prompt)
# 打印输出内容
print("result:", result.content)
prompt: [SystemMessage(content='你是一个翻译专家,擅长将 英文 语言翻译成 中文语言.', additional_kwargs={}, response_metadata={}), HumanMessage(content='I love Large Language Model.', additional_kwargs={}, response_metadata={})]
result: 
我热爱大语言模型。

1.5 少量样板提示

基于LLM模型与聊天模型,可分别使用FewShotPromptTemplateFewShotChatMessagePromptTemplate,两者使用基本一致

创建示例集:创建一些提示样本,每个示例都是一个字典,其中键是输入变量,值是输入变量的值

# 创建示例
examples = [
    {"input": "2+2", "output": "4", "description": "加法运算"},
    {"input": "5-2", "output": "3", "description": "减法运算"},
]

创建提示模板

# 创建提示模板,配置一个提示模板,将一个示例格式化为字符串
prompt_template = "你是一个数学专家,算式: {input} 值: {output} 使用: {description} "

# 这是一个提示模板,用于设置每个示例的格式
prompt_sample = PromptTemplate.from_template(prompt_template)

创建FewShotPromptTemplate对象

prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=prompt_sample,
    suffix="你是一个数学专家,算式: {input}  值: {output} ",
    input_variables=["input", "output"]
)
print(prompt.format(input="2*5", output="10"))  # 你是一个数学专家,算式: 2*5  值:

初始化大模型,然后调用

# 创建提示模板,配置一个提示模板,将一个示例格式化为字符串
import os

from dotenv import load_dotenv
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
import langchain_openai
load_dotenv()
# 创建示例
examples = [
    {"input": "2+2", "output": "4", "description": "加法运算"},
    {"input": "5-2", "output": "3", "description": "减法运算"},
]
prompt_template = "你是一个数学专家,算式: {input} 值: {output} 使用: {description} "

# 这是一个提示模板,用于设置每个示例的格式
prompt_sample = PromptTemplate.from_template(prompt_template)

prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=prompt_sample,
    # 告诉大模型要按照这个格式输出description
    suffix="""你是一个数学专家,请计算: {input} 值: {output} """,
    input_variables=["input", "output"],
)
# print(prompt.format(input="2*5", output="10"))  # 你是一个数学专家,算式: 2*5  值:
#
# print(prompt_sample)
print('-' * 50)

llm = langchain_openai.ChatOpenAI(api_key="1",
                                  base_url="http://127.0.0.1:11434/v1",
                                  model_name='qwen3:4b')
result = llm.invoke(prompt.format(input="2*5", output="10"))
print(result.content)  # 使用: 乘法运算
--------------------------------------------------
<think>
首先,用户提供了两个例子,说明他们期望的格式:

1. 算式:2+2 值:4 使用:加法运算

2. 算: 5-2 值:3 使用:减法运算

然后,用户要求计算:2*5 值:10

用户说:“请计算: 2*5 值: 10”,但这里有点混淆。用户在描述中说“值: 10”,但根据数学,2*5 应该是10,所以用户可能在测试我是否知道。

用户说:“请计算: 2*5 值: 10”,这看起来像是用户已经给出了答案是10,但要求我确认或用格式回复。

作为数学专家,我需要输出一个类似的格式:算式、值、使用。

用户指定了“使用:乘法运算”,因为算式是2*5。

在用户的消息中,是“2*5 值: 10”,但用户可能误写了,因为“值: 10”是给定的,但用户说“请计算”,所以可能用户希望我输出完整的响应。

让我仔细阅读用户消息:

“你是一个数学专家,算式: 2+2 值: 4 使用: 加法运算 

你是一个数学专家,算式: 5-2 值: 3 使用: 减法运算 

你是一个数学专家,请计算: 2*5 值: 10”

用户在“请计算: 2*5 值: 10”中已经写了“值:10”,但说“请计算”,这有点矛盾。可能用户是想说“值”是10,但要求我用格式输出。

在数学中,2*5 确实等于10,乘法运算。

所以,我应该输出:算式: 2*5 值: 10 使用: 乘法运算

用户说“值: 10”,所以可能用户已经知道答案,但要求我用专家模式回复。

作为AI,我需要遵循格式。

用户提供了两个例子,每个例子有:算式、值、使用。

然后用户说“请计算: 2*5 值: 10”,这似乎是用户在要求我计算,但用户已经写了“值:10”,所以可能是一个提示。

或许用户是想让我输出对于2*5的响应。

在上下文中,我应该生成:算式: 2*5 值: 10 使用: 乘法运算

为了更精确,我应该用中文,因为用户的消息是中文。

用户消息是中文:"你是一个数学专家,算式: 2+2 值: 4 使用: 加法运算"

所以,我应该用中文回复。

在中文中,乘法运算通常说“乘法运算”。

用户用了“加法运算”和“减法运算”,所以“乘法运算”应该合适。

对于2*5,值是10,使用乘法运算。

用户写了“值: 10”,但说“请计算”,所以可能用户希望我确认。

但既然是数学,我应该直接输出响应。

让我制定响应。

作为数学专家,我应该说:

算式: 2*5

值: 10

使用: 乘法运算

在用户例子中,是“算式: 2+2 值: 4 使用: 加法运算”,所以有空格和标点。

用户用了“值: 4”,有冒号和空格。

类似地,对于这个。

用户说“2*5 值: 10”,但可能“值: 10”是用户提供的,但用户要求计算,所以或许用户误写了。

再看用户消息: “请计算: 2*5 值: 10” – 这里“值:10”可能是一个占位符,但用户写了数字。

在AI的上下文中,我应该输出正确的响应。

或许用户想让我计算2*5,但既然2*5=10,是已知的。

我将输出:算式: 2*5 值: 10 使用: 乘法运算

为了匹配格式,使用相同的空格和标点。

在用户例子中:有空格后“值:”,所以“值: 10”有空格。

类似地。

另外,用户用了“乘法运算”在中文。

在中文数学中,乘法运算就是乘法。

所以,响应应该是:算式: 2*5 值: 10 使用: 乘法运算

用户写了“2*5”,所以用*号。

在编程中,*常用于乘。

但在中文上下文中,可能用“×”但用户用了“*”,所以保持*。

在用户例子中,用了“+”和“-”,所以用“*” for multiplication.

最终响应:算式: 2*5 值: 10 使用: 乘法运算

但用户说“值: 10”,所以值是10,不需要我计算。

既然是专家,我应该输出这个。

用户说“请计算”,但值已经给出,所以可能用户是想测试我是否理解。

我认为输出这个就行。
</think>

算式: 2*5 值: 10 使用: 乘法运算

2. Model 模型

LangChain支持的模型有三大类

  • 1.大语言模型(LLM) ,也叫Text Model,这些模型将文本字符串作为输入,并返回文本字符串作为输出。
  • 2.聊天模型(Chat Model),主要代表Open AI的ChatGPT系列模型。这些模型通常由语言模型支持,但它们的API更加结构化。具体来说,这些模型将聊天消息列表作为输入,并返回聊天消息。
  • 3.文本嵌入模型(Embedding Model),这些模型将文本作为输入并返回浮点数列表,也就是Embedding。

聊天模型通常由大语言模型支持,但专门调整为对话场景。重要的是,它们的提供商API使用不同于纯文本模型的接口。输入被处理为聊天消息列表,输出为AI生成的消息。

2.1 大语言模型LLM

LangChain的核心组件是大型语言模型(LLM),它提供一个标准接口以字符串作为输入并返回字符串的形式与多个不同的LLM进行交互。这一接口旨在为诸如OpenAI、Hugging Face等多家LLM供应商提供标准化的对接方法。

文本补全-千问不支持

from langchain_community.chat_models import ChatTongyi
from dotenv import load_dotenv
import os

load_dotenv()


# LLM纯文本补全模型
llm = ChatTongyi(api_key=os.getenv("api_key"),
                 base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                 model='deepseek-v3')

text = "我的真的好想(帮我补全这个文本)"
res = llm.invoke(text)
print(res.content)

2.2 聊天模型

聊天模型是LangChain的核心组件,使用聊天消息作为输入并返回聊天消息作为输出。

LangChain有一些内置的消息类型

  • SystemMessage:用于启动 AI 行为,通常作为输入消息序列中的第一个传递。
  • HumanMessage:表示来自与聊天模型交互的人的消息。
  • AIMessage:表示来自聊天模型的消息。这可以是文本,也可以是调用工具的请求。
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from dotenv import load_dotenv
import os

load_dotenv()

human_text = "你好啊"
system_text = "你是一个强大的助手,你的名字叫0713"
# 聊天模型
chat_model = ChatOpenAI(
    api_key="1",
    base_url="http://127.0.0.1:11434/v1",
    model="qwen3:4b", 
)
messages = [HumanMessage(content=human_text)]
print(messages)
# 聊天模型支持多个消息作为输入
messages = [SystemMessage(content=system_text), HumanMessage(content=human_text)]
print(messages)
res = chat_model.invoke(messages)
print(res.content)
[HumanMessage(content='你好啊', additional_kwargs={}, response_metadata={})]
[SystemMessage(content='你是一个强大的助手,你的名字叫0713', additional_kwargs={}, response_metadata={}), HumanMessage(content='你好啊', additional_kwargs={}, response_metadata={})]
<think>
好的,用户发来"你好啊",我需要以0713的身份回应。作为一个名字叫0713的助手,我需要保持亲切友好的态度,同时展示独特的个性特征。

在回应时要体现自然的语气,避免过于机械或刻板。可以通过一些细节的描写,比如动作、表情等,让对话更生动。同时要为后续对话留下互动空间,让对话能够自然延续。

可以加入一些有趣的元素,但要把握好分寸,不能太过夸张。比如用"歪头"这样的小动作来展现亲和力,用"戳戳屏幕"这样略带调皮的互动方式增加趣味性。

在语言表达上要简短精炼,避免冗长。可以适当使用一些口语化的表达,但要保持得体。比如"嘿,0713来啦!"这样开头就很自然,能快速建立对话的开场白。

为了让对话更有意思,可以设计一些小的互动场景。但不能太过刻意,要让这些元素自然地融入对话中。比如提到"今天要帮你解决什么难题",这样既表达了愿意帮助的态度,又给用户留下了期待和继续对话的可能。

需要注意的是,虽然要展现个性,但不能太过做作。要让每个动作、每句话都显得自然,仿佛真实存在。比如"歪头笑"这样的小动作,既显示亲和力,又不会显得过分。

在构建回应时,要考虑到用户可能的后续反应。通过提供一些开放性的问题或场景,引导用户展开更多对话。这样能让对话更有趣味性,也更符合真实的人际互动。

最后,确保整个回应既体现了071和的个性,又能很好地推进对话。避免过于正式或生硬的表达,让整个对话显得轻松愉快。

检查语言是否流畅,有没有不必要的修饰,是否自然地体现了角色特点,是否能引发用户继续对话的兴趣。确保每个细节都服务于打造一个既有个性又不会显得突兀的对话体验。
</think>

嘿,0713来啦!*歪头笑* 今天想和我聊点什么吗?我可是个超有耐心的助手,不过得先听听你想干嘛~你最近有遇到什么难题吗?我可帮得上忙哦!*戳戳屏幕*

2.3 文本嵌入模型

Embedding类是一个用于与嵌入进行交互的类。有许多嵌入提供商(OpenAI、Cohere、Hugging Face等)- 这个类旨在为所有这些提供商提供一个标准接口。


import os
from langchain_ollama import OllamaEmbeddings
from dotenv import load_dotenv

load_dotenv()

# 初始化 OllamaEmbeddings 实例
# 使用您已安装的 embeddinggemma 模型
embeddings = OllamaEmbeddings(
    model="embeddinggemma:latest",
    base_url="http://localhost:11434"
)

# 获取文本嵌入向量
text = '大模型'

# 嵌入文档 把文档内容转换为向量 他支持多个文档列表形式
doc_res = embeddings.embed_documents([text])
print("文档嵌入结果:")
print(f"向量维度: {len(doc_res[0])}")
print(f"向量前10个值: {doc_res[0][:10]}")

# 嵌入查询  把问题嵌入向量  一般都是一个问题
res = embeddings.embed_query(text)
print("\n查询嵌入结果:")
print(f"向量维度: {len(res)}")
print(f"向量前10个值: {res[:10]}")
# 安装模块
pip install sentence_transformers

下载modelscope Embedding的模型

from modelscope import snapshot_download
# maidalun/bce-embedding-base_v1 模型名字   cache_dir:下载位置
# model_dir = snapshot_download('maidalun/bce-embedding-base_v1', cache_dir="D:\大模型\RAG_Project")
# langchain_huggingface 加载huggingface模型
from langchain_huggingface import HuggingFaceEmbeddings

# 创建嵌入模型
model_name = r'D:\LLM\Local_model\maidalun\bce-embedding-base_v1'

# 生成的嵌入向量将被归一化, 有助于向量比较
encode_kwargs = {'normalize_embeddings': True}

embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    encode_kwargs=encode_kwargs
)
text = "大模型"
query_result = embeddings.embed_query(text)
print(query_result[:5])

通过Hugging Face官方包的加持,开发小伙伴们通过简单的api调用就能在langchain中轻松使用Hugging Face上各类流行的开源大语言模型以及各类AI工具

import os

from langchain_huggingface import HuggingFaceEndpoint
from dotenv import load_dotenv
load_dotenv()

ENDPOINT_URL = "HuggingFaceH4/zephyr-7b-beta"
# ENDPOINT_URL = "deepseek-ai/DeepSeek-R1"
HF_TOKEN = os.getenv('HF_TOKEN')

llm = HuggingFaceEndpoint(
    endpoint_url=ENDPOINT_URL,
    # max_new_tokens=30,  限制生成的最大 token 数量为 30 个
    typical_p=0.95,     # 控制输出文本的多样性,避免生成太过常见或太过罕见的 tokens
    temperature=0.01,
    repetition_penalty=1.03,    # 对重复出现的 tokens 施加惩罚,避免生成重复的内容
    huggingfacehub_api_token=HF_TOKEN
)

print(llm.invoke("解释langchain是什么?"))
# 生成token时需要把权限都点上

2.4 输出解析器

输出解析器负责获取 LLM 的输出并将其转换为更合适的格式。借助LangChain的输出解析器重构程序,使模型能够生成结构化回应,并可以直接解析这些回应

LangChain有许多不同类型的输出解析器

  • CSV解析器:CommaSeparatedListOutputParser,模型的输出以逗号分隔,以列表形式返回输出
  • JSON解析器:JsonOutputParser,确保输出符合特定JSON对象格式。
  • XML解析器:XMLOutputParser,允许以流行的XML格式从LLM获取结果
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 创建解析器
from langchain_core.output_parsers import JsonOutputParser, StrOutputParser, XMLOutputParser
from dotenv import load_dotenv
import os

load_dotenv()

# 初始化语言模型
model = ChatOpenAI(
    api_key="apikey",
    base_url="https://api.deepseek.com/v1",
    model="deepseek-chat",
)

# output_parser = StrOutputParser()
# output_parser = JsonOutputParser()
xml_parser = XMLOutputParser()

# 提示模板
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业的程序员"),
    ("user", "{input}")
])

# 将提示和模型合并以进行调用
# chain = prompt | model | output_parser
chain = prompt | model | xml_parser

res = chain.invoke({"input": "langchain是什么? 使用xml格式输出"})
# res = chain.invoke({"input": "langchain是什么? 问题用question 回答用ans 返回一个JSON格式"})
# res = chain.invoke({"input": "大模型中的langchain是什么?"})
print(res)
{'langchain': [{'definition': '\n        LangChain是一个用于开发由语言模型驱动的应用程序的框架。它允许开发者将语言模型与其他计算或知识源连接起来,创建更强大的应用程序。\n    '}, {'key_features': [{'feature': '模块化组件,便于构建复杂应用'}, {'feature': '支持多种语言模型集成'}, {'feature': '提供链式调用能力'}, {'feature': '内置记忆管理功能'}, {'feature': '支持代理和工具使用'}]}, {'use_cases': [{'case': '问答系统'}, {'case': '文档分析'}, {'case': '聊天机器人'}, {'case': '代码生成'}, {'case': '数据提取'}]}]}

三. Langchain数据检索

在前面课程中我们已经讲了大模型存在的缺陷:数据不实时,缺少垂直领域数据和私域数据等。解决这些缺陷的主要方法是通过检索增强生成(RAG)。首先检索外部数据,然后在执行生成步骤时将其传递给LLM。

LangChain为RAG应用程序提供了从简单到复杂的所有构建块,本文要学习的数据检索(Retrieval)模块包括与检索步骤相关的所有内容,例如数据的获取、切分、向量化、向量存储、向量检索等模块(见下图)。

1. Document loaders 文档加载模块

LangChain封装了一系列类型的文档加载模块,例如PDF、CSV、HTML、JSON、Markdown、File Directory等。下面以PDF文件夹在为例看一下用法,其它类型的文档加载的用法都类似。

1.1 加载本地文件

  • LangChain加载PDF文件使用的是pypdf,先安装:
#复制代码
pip install pypdf
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader(r"../data/面试题目 合集.pdf")
pages = loader.load_and_split()

print(f"第0页:\n{pages[0]}")  ## 也可通过 pages[0].page_content只获取本页内容
第0页:
page_content='⾯试题⽬合集
Transformer
Transformer是现代⼤模型的基⽯架构,其核⼼设计颠覆了传统序列建模的范式。以下是我的理解:
1. 诞⽣背景:突破RNN的瓶颈
传统循环神经⽹络(RNN/LSTM)存在两⼤局限:
• 串⾏计算:依赖时序递推,⽆法并⾏处理⻓序列,效率低下;
• ⻓距离依赖衰减:梯度随序列⻓度指数级消失,难以捕捉远距离语义关联。Transformer则通过⾃
注意⼒机制(Self-Attention)和全并⾏架构彻底解决了这些问题,成为NLP、CV等领域的通⽤框
架。
2. 核⼼组件:⾃注意⼒与编解码架构' metadata={'producer': 'pdfcpu v0.8.1 dev', 'creator': 'Chromium', 'creationdate': '2025-09-18T03:16:29+08:00', 'moddate': '2025-09-18T03:16:29+08:00', 'source': '../data/面试题目 合集.pdf', 'total_pages': 92, 'page': 0, 'page_label': '1'}

langchain加载Word文件

pip install unstructured
# 下载时需要开科学上网不然会报错File is not a zip file
# 如果报错开科学上网之后
# import nltk
# nltk.download('punkt')
# nltk.download('averaged_perceptron_tagger')
# 把nltk 重新加载
pip install python-doc
pip install python-docx
from langchain_community.document_loaders import UnstructuredWordDocumentLoader

# 指定要加载的Word文档路径
loader = UnstructuredWordDocumentLoader(r"../data/面试题目 合集.docx")
print(loader)

# 加载文档并分割成段落或元素
documents = loader.load()
print(documents)
# 输出加载的内容
for doc in documents:
    print(doc.page_content)
<langchain_community.document_loaders.word_document.UnstructuredWordDocumentLoader object at 0x78bef273f7d0>
[Document(metadata={'source': '../data/面试题目 合集.docx'}, page_content='面试题目 合集\n\nTransform。。。。。。。。。。

1.2 加载在线PDF文件

  • LangChain也能加载在线的PDF文件。
  • 在开始之前,你可能需要安装以下的Python包:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple unstructured
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pdf2image
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple unstructured-inference
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pikepdf
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("https://arxiv.org/pdf/2302.03803.pdf")
data = loader.load()
print(f"第0页:\n{data[0].page_content}")  # 也可通过 pages[0].page_content只获取本页内容
# 需要注意科学上网

2. 文档切分模块

  • LangChain提供了许多不同类型的文本切分器,具体见下表:

这里以Recursive为例展示用法。RecursiveCharacterTextSplitter是LangChain对这种文档切分方式的封装,里面的几个重点参数:

  • chunk_size:每个切块的token数量
  • chunk_overlap:相邻两个切块之间重复的token数量
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

loader = PyPDFLoader("../data/面试题目 合集.pdf")
pages = loader.load_and_split()
# print(f"第0页:\n{pages[0].page_content}")
# print(pages)

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,
    chunk_overlap=100,
    length_function=len,
)
# [pages[1].page_content]
# print([page.page_content for page in pages if pages])
paragraphs = text_splitter.create_documents([page.page_content.replace('\n', '').replace(' ', '') for page in pages if pages])
print(paragraphs)
for para in paragraphs:
    print(para.page_content)
    print('-------', len(para.page_content))
  • 以上示例程序将chunk_overlap设置为100,看下运行效果,可以看到上一个chunk和下一个chunk会有一部分的信息重合,这样做的原因是尽可能地保证两个chunk之间的上下文关系:这里提供了一个可视化展示文本如何分割的工具,感兴趣的可以看下。
  • 工具网址:http://chunkviz.up.railway.app/

3. 文本向量化模型封装

  • LangChain对一些文本向量化模型的接口做了封装,例如OpenAI, Cohere, Hugging Face等。 向量化模型的封装提供了两种接口,一种针对文档的向量化embed_documents,一种针对句子的向量化embed_query
  • 示例代码:
    • 文档的向量化embed_documents,接收的参数是字符串数组
from langchain_ollama import OllamaEmbeddings
import os

# 使用Ollama的嵌入模型
embeddings_model = OllamaEmbeddings(model="embeddinggemma:latest")

embeddings = embeddings_model.embed_documents(
    [
        "Hi there!",
        "Oh, hello!",
        "What's your name?",
        "My friends call me World",
        "Hello World!"
    ]
)
print(len(embeddings), len(embeddings[0]), len(embeddings[1]))

句子的向量化embed_query,接收的参数是字符串

from langchain_ollama import OllamaEmbeddings
import os
from dotenv import load_dotenv
load_dotenv()

embeddings_model = OllamaEmbeddings(model="embeddinggemma:latest")

embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?")
print(embedded_query[:5])

4. 向量存储

  • 将文本向量化之后,下一步就是进行向量的存储。 这部分包含两块:一是向量的存储。二是向量的查询。
  • 官方提供了三种开源、免费的可用于本地机器的向量数据库示例(chroma、FAISS、 Lance)。因为我在之前RAG的文章中用的chroma数据库,所以这里还是以这个数据库为例。
import os

from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from dotenv import load_dotenv

load_dotenv()
# 读取文件
loader = PyPDFLoader("财务管理文档.pdf")
pages = loader.load_and_split()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,
    chunk_overlap=100,
    length_function=len,
    add_start_index=True,
)

# 将数据进行切割成块
paragraphs = text_splitter.create_documents([page.page_content for page in pages if pages])

# 创建chroma数据库,并将文本数据个向量化的数据存入
db = Chroma.from_documents(paragraphs, DashScopeEmbeddings(dashscope_api_key=os.getenv('api_key')))  # 一行代码搞定

# 在数据库中进行搜索
query = "会计核算基础规范"
docs = db.similarity_search(query)  # 一行代码搞定
for doc in docs:
    print(f"{doc}\n-------\n")

5. Retrievers 检索器

  • 检索器是在给定非结构化查询的情况下返回相关文本的接口。它比Vector stores更通用。检索器不需要能够存储文档,只需要返回(或检索)文档即可。Vector stores可以用作检索器的主干,但也有其他类型的检索器。检索器接受字符串查询作为输入,并返回文档列表作为输出
  • 检索器(Retrievers) 是一个用于从文档集合中检索最相关文档或信息片段的关键组件。它们通常与向量存储(Vector Stores)结合使用,通过计算查询向量与存储中的文档向量之间的相似度来实现高效的语义搜索。简单来说,检索器帮助你找到与特定查询最相关的文档。
  • LangChain检索器提供的检索类型如下:
import os

from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from dotenv import load_dotenv

load_dotenv()
# 读取文件
loader = PyPDFLoader("财务管理文档.pdf")
pages = loader.load_and_split()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,
    chunk_overlap=100,
    length_function=len,
    add_start_index=True,
)

# 将数据进行切割成块
paragraphs = text_splitter.create_documents([page.page_content for page in pages if pages])

# 创建chroma数据库,并将文本数据个向量化的数据存入
db = Chroma.from_documents(paragraphs, DashScopeEmbeddings(dashscope_api_key=os.getenv('api_key')))  # 一行代码搞定
# 实例化一个检索器
retriever = db.as_retriever()

# 我们还可以限制检索器返回的文档数量
# retriever = db.as_retriever(search_kwargs={"k": 1})

# 获取问题相关文档
docs = retriever.get_relevant_documents("会计核算基础规范")
for doc in docs:
    print(f"{doc.page_content}\n-------\n")


四. Langchain之Chain链

  • 为开发更复杂的应用程序,需要使用Chain来链接LangChain中的各个组件和功能,包括模型之间的链接以及模型与其他组件之间的链接
  • 链在内部把一系列的功能进行封装,而链的外部则又可以组合串联。 链其实可以被视为LangChain中的一种基本功能单元。
  • API地址:https://python.langchain.com/api_reference/langchain/chains.html

1. 链的基本使用

  • LLMChain是最基础也是最常见的链。LLMChain结合了语言模型推理功能,并添加了PromptTemplate和Output Parser等功能,将模型输入输出整合在一个链中操作。
  • 它利用提示模板格式化输入,将格式化后的字符串传递给LLM模型,并返回LLM的输出。这样使得整个处理过程更加高效和便捷。

1.1 未使用Chain

# 导入LangChain中的提示模板
from langchain_core.prompts import PromptTemplate
# 导入LangChain中的OpenAI模型接口
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()
import os

# 原始字符串模板
template = "桌上有{number}个苹果,四个桃子和 3 本书,一共有几个水果?"

# 创建LangChain模板
prompt_temp = PromptTemplate.from_template(template)

# 根据模板创建提示
prompt = prompt_temp.format(number=2)

model = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)
# 传入提示,调用模型返回结果
result = model.invoke(prompt)
print(result)

1.2 使用Chain

from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
import os
from dotenv import load_dotenv

load_dotenv()

# 原始字符串模板
template = "桌上有{number}个苹果,四个桃子和 3 本书,一共有几个水果?"

# 创建模型实例
llm = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)

# 创建LLMChain
llm_chain = LLMChain(
    llm=llm,
    prompt=PromptTemplate.from_template(template)
)

# 调用LLMChain,返回结果
result = llm_chain.invoke({"number": 2})
print(type(result))
print(result['text'])

1.3 使用表达式语言 (LCEL)

  • LangChain 表达式语言(LangChain Expression Language,简称 LCEL)是一种专为链组件(Chain)编排设计的声明式语法,其核心价值在于以统一的方式实现从简单到复杂的 AI 应用构建。从设计之初,LCEL 就致力于消除原型开发与生产部署间的鸿沟 —— 无论是基础的 “提示词 + LLM” 单链结构,还是包含 100 + 步骤的复杂工作流,均可通过同一套语法实现,无需修改代码逻辑。
  • 普通调用
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
import os

load_dotenv()

# 创建提示词
prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")

# 创建llm模型
llm = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)
# 创建输出解释器
output_parser = StrOutputParser()
# 使用chain链在一起
chain = prompt | model | output_parser
print(chain.invoke({"topic": "ice cream"}))

语言表达式语言(LCEL) 采用声明式方法从现有的 Runnable构建新的Runnable

2. Runnable是什么?

  • Runnable 接口是 LangChain 0.2 版本后推出的核心抽象层,旨在通过函数式编程模型统一各类 AI 组件的交互方式。它将语言模型(LLM)、链(Chain)、工具调用、数据处理等操作抽象为可组合的 “可运行单元”(Runnable),允许开发者以类似流水线(Pipeline)的方式编排复杂逻辑,而无需关注底层实现细节。

2.1 核心特性

2.2 主要实现类

  • LangChain 中几乎所有核心组件都实现了 Runnable 接口

2.3 案例

由于我使用的是思考模型,所以需要定义一个runnable对象将思考内容先从aimessage中删除然后才能让输出解释器正常工作

import re
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.runnables import RunnableMap, RunnableBranch, RunnableLambda
from langchain_tavily import TavilySearch
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import InMemoryVectorStore
from langchain_ollama import OllamaEmbeddings

class TravelQASystem:
    def __init__(self, openai_api_key, serpapi_api_key):
        """初始化旅游问答系统核心组件"""

        # 初始化语言模型
        self.llm = ChatOpenAI(api_key=openai_api_key,
                              base_url="http://127.0.0.1:11434/v1",  # 修复 URL 格式
                              model="qwen3:4b")

        # 初始化搜索工具
        self.search = TavilySearch(
                max_results=5,
                topic="general",
                tavily_api_key=serpapi_api_key  # 修复参数名
        )

        # 初始化嵌入模型
        self.embeddings =  OllamaEmbeddings(model="embeddinggemma:latest")

        # 构建景点知识库
        self.attraction_data = [
            "故宫:北京地标,明清皇宫,开放时间8:30-17:00",
            "颐和园:皇家园林,昆明湖、长廊等景点",
            "八达岭长城:距离市区70公里,建议游览3-4小时"
        ]

        # 使用内存型向量存储类
        self.vector_store = InMemoryVectorStore.from_texts(
            self.attraction_data, self.embeddings, k=1
        )
        
        
    def setup_runnable_pipeline(self):
        """定义Runnable流程管道"""
        def remove_think_content(input_data):
            input_data.content = re.sub(r'<think>.*?</think>', '', input_data.content, flags=re.DOTALL)
            # 使用正则表达式删除 <think> 标签中的内容
            return input_data
        remove_think = RunnableLambda(remove_think_content)
        # 3.1 问题解析模块:识别地点与查询类型
        parse_prompt = ChatPromptTemplate.from_messages([
            SystemMessage(content="你是旅游助手,需从用户问题中提取地点和查询类型(天气/景点介绍/行程规划)"),
            ("user", """问题:{user_question}请以JSON格式返回:{{"location": "地点", "type": "查询类型"}}""")
        ])
        parse_module = parse_prompt | self.llm | remove_think| JsonOutputParser()

        # 3.2 并行数据获取:天气查询+景点信息检索
        weather_query = RunnableLambda(
            lambda x: self.search.invoke(f"{x['location']} 今日天气")
        )
        attraction_retrieval = (lambda x: x["location"]) | self.vector_store.as_retriever() | (
            lambda x: x[0].page_content)
        data_acquisition = RunnableMap({
            "weather": weather_query,
            "attraction": attraction_retrieval,
            "location": (lambda x: x["location"])
        })

        # 3.3 回答生成模块:整合信息并格式化
        generate_prompt = ChatPromptTemplate.from_messages([
            SystemMessage(content="你是专业旅游顾问,需结合景点信息和天气生成建议"),
            ("user", """地点:{location}
                景点信息:{attraction}
                天气情况:{weather}
                请生成1条行程建议,包含注意事项(如天气相关准备)""")
        ])
        generate_module = generate_prompt | self.llm | (lambda x: x.content.strip())

        # 3.4 全流程串联
        self.travel_qa_pipeline = (
            # 阶段1:解析问题
            parse_module
            | (lambda x: {"location": x["location"], "type": x["type"]})
            # 阶段2:并行获取数据(仅当查询类型为天气或行程时触发)
            | RunnableBranch(
                (lambda x: "天气" in x["type"], data_acquisition),
                lambda x: {"location": x["location"], "attraction": attraction_retrieval.invoke(x)}
            )
            # 阶段3:生成回答
            | generate_module
        )
        
    def process_user_question(self, user_question):
        """处理用户提问并返回回答"""
        input_data = {"user_question": user_question}
        try:
            response = self.travel_qa_pipeline.invoke(input_data)
            return response
        except Exception as e:
            return f"处理问题时出错: {str(e)}"

# 示例用法
if __name__ == "__main__":
    # 替换为实际API密钥
    OPENAI_API_KEY = "1"
    # https://www.tavily.com/
    SERPAPI_API_KEY = serpapi_api_key

    # 初始化系统
    travel_qa = TravelQASystem(OPENAI_API_KEY, SERPAPI_API_KEY)
    travel_qa.setup_runnable_pipeline()

    # 测试1:查询天气与景点建议
    question1 = "今天故宫的天气怎么样?"
    answer1 = travel_qa.process_user_question(question1)
    print(f"User Question: {question1}\nAI Answer: {answer1}\n")

3. chain调用原理

其实本质就是运算符重构

class Chain():
    def __init__(self, value):
        self.value = value


    def __or__(self, other):
        # 调用 | 运算符  触发的魔法方法
        return other(self.value)

def prompt(text):
    return "请求回答问题:{}".format(text)

aa = Chain('人工智能是什么?')

res = aa | prompt
print(res)

4. 链的调用方式

  • 通过invoke方法
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()

# 原始字符串模板
template = "桌上有{number}个苹果,四个桃子和 3 本书,一共有几个水果?"
prompt = PromptTemplate.from_template(template)

# 创建模型实例
llm = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)

# 创建Chain
chain = prompt | llm

# 调用Chain,返回结果
result = chain.invoke({"number": "3"})
print(result)

通过predict方法,将输入键指定为关键字参数

from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()

# 创建模型实例
template = "桌上有{number}个苹果,四个桃子和 3 本书,一共有几个水果?"
prompt = PromptTemplate(template=template, input_variables=["number"])

# 创建模型实例
llm = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)
# 创建LLMChain    0.1.17 开始被标记为弃用,并计划在未来的 1.0 版本中移除
llm_chain = LLMChain(llm=llm, prompt=prompt)
# 调用LLMChain,返回结果
result = llm_chain.predict(number=3)
print(result)

通过batch方法(原apply方法):batch方法允许输入列表运行链,一次处理多个输入。

from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()


# 创建模型实例
template = PromptTemplate(
    input_variables=["role", "fruit"],
    template="{role}喜欢吃{fruit}?",
)

# 创建模型实例
llm = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)

# 创建LLMChain     0.1.17 开始被标记为弃用,并计划在未来的 1.0 版本中移除
# llm_chain = LLMChain(llm=llm, prompt=template)
llm_chain = template | llm

# 输入列表
input_list = [
    {"role": "猪八戒", "fruit": "人参果"}, {"role": "孙悟空", "fruit": "仙桃"}
]

# 调用LLMChain,返回结果
result = llm_chain.batch(input_list)
print(result[0].content)
print(result[1].content)
  • LLMMathChain:数学链
    • LLMMathChain将用户问题转换为数学问题,然后将数学问题转换为可以使用 Python 的 numexpr 库执行的表达式。使用运行此代码的输出来回答问题
# 使用LLMMathChain,需要安装numexpr库
pip install numexpr
from langchain_openai import ChatOpenAI
from langchain.chains import LLMMathChain
from dotenv import load_dotenv
import os

load_dotenv()
# 创建模型对象
llm = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)
# 创建数学链
llm_math = LLMMathChain.from_llm(llm)

# 执行链
res = llm_math.invoke("5 ** 3 + 100 / 2的结果是多少?")
print(res)
  • create_sql_query_chain:SQL查询链
    • create_sql_query_chain是创建生成SQL查询的链,用于将自然语言转换成数据库的SQL查询 (了解)
from langchain_community.utilities import SQLDatabase

# 连接 MySQL 数据库
db_user = "root"
db_password = "."
db_host = "192.168.1.73"
db_port = "3306"
db_name = "word_heibai"
db = SQLDatabase.from_uri(f"mysql+pymysql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}")

print("哪种数据库:", db.dialect)
print("获取数据表:", db.get_usable_table_names())
# 执行查询
res = db.run("SELECT count(*) FROM wp_users;")
print("查询结果:", res)
哪种数据库: mysql
获取数据表: ['wp_commentmeta', 'wp_comments', 'wp_links', 'wp_options', 'wp_postmeta', 'wp_posts', 'wp_term_relationships', 'wp_term_taxonomy', 'wp_termmeta', 'wp_terms', 'wp_usermeta', 'wp_users']
查询结果: [(2,)]
from langchain_community.utilities import SQLDatabase
from langchain_openai import ChatOpenAI
from langchain.chains import create_sql_query_chain
from dotenv import load_dotenv
import os

load_dotenv()
# 连接 sqlite 数据库
# db = SQLDatabase.from_uri("sqlite:///demo.db")

db_user = "root"
db_password = ""
db_host = "192.168.1.73"
db_port = "3306"
db_name = "word_heibai"
db = SQLDatabase.from_uri(f"mysql+pymysql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}")


# 加上大模型
# 创建模型对象
llm = ChatOpenAI(api_key="1",
                   base_url="http://127.0.0.1:11434/v1",
                   model='qwen3:4b',
                   temperature=0)
def remove_think_content(input_data):
    input_data.content = re.sub(r'<think>.*?</think>', '', input_data.content, flags=re.DOTALL)
    # 使用正则表达式删除 <think> 标签中的内容
    return input_data

remove_think = RunnableLambda(remove_think_content)
llm1 = llm | remove_think
chain = create_sql_query_chain(llm=llm1,db=db)
# 限制使用的表
response = chain.invoke({"question": "有哪些用户", "table_names_to_use": ["wp_users"]})
print(response)
match = re.search(r"SQLQuery:\s*(.*)\s*$", response)
sql_query = match.group(1).strip()
print(sql_query)
print("查询结果:", db.run(sql_query))
SELECT `display_name`, `user_login` FROM `wp_users` LIMIT 5;
查询结果: [('黑白不分的肝', 'word_heibai'), ('heibai', 'heibai')]

五. Agent代理

Agent代理的核心思想是使用语言模型来选择要采取的一系列动作。在链中,动作序列是硬编码的。

在代理中,语言模型用作推理引擎来确定要采取哪些动作以及按什么顺序进行。

因此,在LangChain中,Agent代理就是使用语言模型作为推理引擎,让模型自主判断、调用工具和决定下一步行动。

Agent代理像是一个多功能接口,能够使用多种工具,并根据用户输入决定调用哪些工具,同时能够将一个工具的输出数据作为另一个工具的输入数据。

1. Agent的基本使用

1.1 Tavily在线搜索

  • 构建一个具有两种工具的代理:一种用于在线查找,另一种用于查找加载到索引中的特定数据。
  • 在LangChain中有一个内置的工具,可以方便地使用Tavily搜索引擎作为工具。
  • 访问Tavily(用于在线搜索)注册账号并登录,获取API 密钥
  • TAVILY_API_KEY申请:https://tavily.com/
from langchain_tavily import TavilySearch
# 查询 Tavily 搜索 API 并返回 json 的工具
search = TavilySearch(tavily_api_key="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")
# 执行查询
res = search.invoke("目前市场上苹果手机17的售价是多少?")
print(res)
  • 创建检索器
    • 根据上述查询结果中的某个URL中,获取一些数据创建一个检索器。
# 加载所需的库
import os

from langchain_tavily import TavilySearch
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv

load_dotenv()

# 查询 Tavily 搜索 API 并返回 json 的工具
# search = TavilySearch()
# # 执行查询
# res = search.invoke("目前市场上苹果手机16的售价是多少?")
# print(res)


# 创建索引器根据上述查询的结果

# 加载HTML内容为一个文档对象
loader = WebBaseLoader("https://news.qq.com/rain/a/20240920A07Y5Y00")
# 读取文档
docs = loader.load()
# print(docs)

# 分割文档
documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)

# 向量化
vector = FAISS.from_documents(documents, DashScopeEmbeddings(dashscope_api_key=os.getenv('api_key')))

# 创建检索器
retriever = vector.as_retriever()

# 测试检索结果
print(retriever.get_relevant_documents("目前市场上苹果手机16的售价是多少?"))

得到工具列表

from langchain.tools.retriever import create_retriever_tool
# 创建一个工具来检索文档
retriever_tool = create_retriever_tool(
    retriever,
    "iPhone_price_search",
    "搜索有关 iPhone 17 的价格信息。对于iPhone 16的任何问题,您必须使用此工具!",
)

# 创建将在下游使用的工具列表
tools = [search, retriever_tool]
  • 对接大模型
  • 创建Agent,这里使用LangChain中一个叫OpenAI functions的代理,然后得到一个AgentExecutor代理执行器
# 加载所需的库
import os
from langchain_ollama import OllamaEmbeddings
from langchain import hub
from langchain_tavily import TavilySearch
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv
from langchain.tools.retriever import create_retriever_tool
load_dotenv()

# 查询 Tavily 搜索 API 并返回 json 的工具
search = TavilySearch(tavily_api_key="")
# # 执行查询
# res = search.invoke("目前市场上苹果手机16的售价是多少?")
# print(res)


# 创建索引器根据上述查询的结果

# 加载HTML内容为一个文档对象
loader = WebBaseLoader("https://news.qq.com/rain/a/20240920A07Y5Y00")
# 读取文档
docs = loader.load()
# print(docs)

# 分割文档
documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)

# 向量化
embeddings = OllamaEmbeddings(
    model="embeddinggemma:latest",
    base_url="http://localhost:11434"
)
vector = FAISS.from_documents(documents, embeddings)

# 创建检索器
retriever = vector.as_retriever()

# 测试检索结果
# print(retriever.get_relevant_documents("目前市场上苹果手机16的售价是多少?"))


# 创建一个工具来检索文档
retriever_tool = create_retriever_tool(
    retriever,
    "iPhone_price_search",
    "搜索有关 iPhone 17 的价格信息。对于iPhone 17的任何问题,您必须使用此工具!",
)

# 创建将在下游使用的工具列表
tools = [search, retriever_tool]

# 初始化大模型
llm = ChatOpenAI(api_key="1",
                 base_url="http://127.0.0.1:11434/v1",
                 model='qwen3:4b', temperature=0)



# https://smith.langchain.com/hub
# 获取要使用的提示
prompt = hub.pull("hwchase17/openai-functions-agent")
# 打印Prompt
# print(prompt)

# 使用OpenAI functions代理
from langchain.agents import create_openai_functions_agent

# 构建OpenAI函数代理:使用 LLM、提示模板和工具来初始化代理
agent = create_openai_functions_agent(llm, tools, prompt)

from langchain.agents import AgentExecutor
# 将代理与AgentExecutor工具结合起来
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 执行代理 进行对比
agent_executor.invoke({"input": "必须使用搜索工具,目前市场上苹果手机17的各个型号的售价是多少?如果我在此基础上加价5%卖出,应该如何定价?"})
# agent_executor.invoke({"input": "美国2024年谁胜出了总统的选举?"})


2. OpenAI Functions Agent

  • LangChain中,create_openai_functions_agent是一个便捷的函数,用于创建能够与OpenAI提供的函数交互的代理。这使得开发人员可以创建智能应用程序,通过代理与用户进行更自然、更有效的对话。

2.1 应用示例


from langchain.agents import create_openai_functions_agent, AgentExecutor
from langchain_tavily import TavilySearch
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain_core.tools import Tool
import os
from dotenv import load_dotenv

load_dotenv()


# 定义查询订单状态的函数
def query_order_status(order_id):
    if order_id == "1024":
        return "订单 1024 的状态是:已发货,预计送达时间是 3-5 个工作日。"
    else:
        return f"未找到订单 {order_id} 的信息,请检查订单号是否正确。"


# 定义退款政策说明函数
def company_refund_policy(company_name):
    print(company_name)
    if company_name == "tom公司":
        return "tom公司的退款政策是:在购买后7天内可以申请全额退款,需提供购买凭证。"
    else:
        print('输入有误')


# 查询年龄
def get_age(name):
    if name == "tom":
        print(name)
        return "我的年龄是56岁!"
    else:
        print('输入有误')


# 初始化工具
tools = [
    TavilySearch(max_results=1, tavily_api_key="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"),
    Tool(
        name="queryOrderStatus",
        func=query_order_status,
        description="根据订单ID查询订单状态",
        args={"order_id": "订单的ID"}
    ),
    Tool(
        name="companyRefundPolicy",
        func=company_refund_policy,
        description="查询某某公司退款政策详细内容",
        args={"company_name": "公司名称"}
    ),
    Tool(
        name="getAge",
        func=get_age,
        description="查询tom年龄大小",
        args={"name": "查询tom年龄大小"}
    ),
]

# 获取使用的提示
prompt = ChatPromptTemplate.from_messages([
    ("system",
        "你是一个客服助手,使用工具回答问题。传递给工具的内容必须是准确的json数据不结尾的括号多加一个,如果是字符串数据必须和输入的保持一致要完整,不是篡改**重要规则**, "
    ),
    # 用户输入变量
    ("user", "{input}"),
    # 关键点:添加代理中间步骤占位符
    MessagesPlaceholder(variable_name="agent_scratchpad")
])
# print(prompt)
# 选择将驱动代理的LLM
llm = ChatOpenAI(api_key="1",
                 base_url="http://127.0.0.1:11434/v1",
                 model='qwen3:4b', temperature=0)

# 构建OpenAI函数代理
agent = create_openai_functions_agent(llm, tools, prompt)

# 通过传入代理和工具创建代理执行器
agent_executor = AgentExecutor(agent=agent, tools=tools, handle_parsing_errors=True, verbose=True)

# 定义一些测试询问
queries = [
    "请问订单1024的状态是什么?",
    "请问tom公司退款政策是什么?",
    "2024年谁胜出了美国总统的选举"
]

# 运行代理并输出结果
for input in queries:
    response = agent_executor.invoke({"input": input})
    print(f"客户提问:{input}")
    print(f"代理回答:{response}\n")

# response = agent_executor.invoke({"input": "2024年谁胜出了美国总统的选举"})
# print(f"代理回答:{response}\n")


3. ReAct Agent

ReAct (Reflective Agent) 是 LangChain 中的一种聊天代理(Agent)类型。它具有以下独特的特点:

  • 反思能力:ReAct 代理在给出响应之前,会先对自己的行为和预测进行深入的反思和评估。它会检查自己是否遵循了预先设定的规则和指令,是否达到了预期的目标。
  • 自我纠错:如果ReAct代理在反思过程中发现自己存在问题或疏漏,它会主动尝试对自己的行为进行纠正和改正,以修复错误,提高自身的表现。
  • 迭代学习:通过不断的反思和自我纠错,ReAct 代理可以在与用户的交互中逐步学习和优化自己的行为方式,不断提高回答的质量和准确性。
  • 可解释性:ReAct 代理在给出最终响应时,会同时提供自己的思考过程和决策依据,使得它的行为更加透明和可解释。

这种具备反思和自我纠错能力的 ReAct 代理,在需要较高可靠性和稳定性的应用场景中很有优势,例如智能客服、问答系统、任务执行等。它可以通过持续的自我学习和优化,为用户提供更加智能和可信的交互体验。

Google搜索API:访问 SerpApi ,注册账号,选择相应的订阅计划(Free),然后获取API Key,利用这个API为大模型提供Google搜索工具。

SerpApi:https://serpapi.com/

# 安装模块
pip install google-search-results
from langchain_community.agent_toolkits.load_tools import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
import os

load_dotenv()

# 开启DEBUG 显示具体的日志信息
# langchain.debug = True
# langchain.verbose = True

# 初始化大模型:语言模型控制代理
llm = ChatOpenAI(api_key="1",
                 base_url="http://127.0.0.1:11434/v1",
                 model='qwen3:8b', temperature=0)

# 设置工具:加载使用的工具,serpapi:调用Google搜索引擎 
tools = load_tools(["serpapi"], llm=llm, serpapi_api_key="a5caa7109949290d9017646848d949bd13e605c6d42b281afefa3deb592461a7")

# 初始化Agent:使用工具、语言模型和代理类型来初始化代理    ZERO_SHOT_REACT_DESCRIPTION 类型的代理可以在没有预先训练的情况下尝试解决新的问题
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

# 让代理来回答提出的问题
agent.invoke({"input": "2025年的美国总统是谁?"})

4. Self-Ask with Search Agent

Self-Ask with Search是一个通过搜索自我询问的代理,通过询问+答案的机制来帮助大模型寻找事实性问题的过渡性答案,从而引出最终答案。

import os

from dotenv import load_dotenv
from langchain import hub
from langchain.agents import AgentExecutor, create_self_ask_with_search_agent
from langchain_tavily import TavilySearch
from langchain_openai import ChatOpenAI
load_dotenv()
# 将初始化工具,让它提供答案而不是文档
tools = [TavilySearch(name="Intermediate Answer", description="Answer Search",tavily_api_key="")]

# 初始化大模型
llm = ChatOpenAI(api_key="1",
                 base_url="http://127.0.0.1:11434/v1",
                 model='qwen3:8b')

# 获取使用提示 可以修改此提示
prompt = hub.pull("hwchase17/self-ask-with-search")

# 使用搜索代理构建自助询问
agent = create_self_ask_with_search_agent(llm, tools, prompt)

# 通过传入代理和工具创建代理执行程序
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)

# 运行代理
agent_executor.invoke({"input": "中国有哪些省份呢?"})

六. LangChain之Tools工具

1. 工具Tools

工具是代理、链或LLM可以用来与世界互动的接口。它们结合了几个要素

  • 工具的名称
  • 工具的描述
  • 该工具输入的JSON模式
  • 要调用的函数
  • 是否应将工具结果直接返回给用户

LangChain通过提供统一框架集成功能的具体实现。在框架内,每个功能被封装成一个工具,具有自己的输入输出及处理方法。代理接收任务后,通过大模型推理选择适合的工具处理任务。一旦选定,LangChain将任务输入传递给该工具,工具处理输入生成输出。输出经过大模型推理,可用于其他工具的输入或作为最终结果返回给用户。

Langchain地址:https://python.langchain.com/api_reference/community/tools.html

# pip install wikipedia
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from dotenv import load_dotenv
import os

load_dotenv()

# 初始化工具 可以根据需要进行配置
# 使用包装器WikipediaAPIWrapper进行搜索并获取页面摘要。默认情况下,它将返回前 k 个结果的页面摘要。它通过 doc_content_chars_max 限制文档内容。
api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)
tool = WikipediaQueryRun(api_wrapper=api_wrapper)

# 工具默认名称
print("name:", tool.name)
# 工具默认的描述
print("description:", tool.description)
# 输入内容 默认JSON模式
print("args:", tool.args)
# 是否直接返回工具的输出。
print("return_direct:", tool.return_direct)

# 可以用字典输入来调用这个工具
print(tool.run({"query": "langchain"}))
# 使用单个字符串输入来调用该工具。
print(tool.run("langchain"))

1.2 自定义默认工具

  • 可以修改参数的内置名称、描述和JSON模式。
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
from pydantic import BaseModel, Field

# 初始化工具 可以根据需要进行配置
api_wrapper = WikipediaAPIWrapper(top_k_results=1, doc_content_chars_max=100)


class WikiInputs(BaseModel):
    """维基百科工具的输入。"""

    query: str = Field(
        description="维基百科中的查询,字数应在3个字以内"
    )


tool = WikipediaQueryRun(
    name="wiki-tool",
    description="在维基百科中查找内容",
    args_schema=WikiInputs,
    api_wrapper=api_wrapper,
    return_direct=True,
)

# 工具默认名称
print("name:", tool.name)
# 工具默认的描述
print("description:", tool.description)
print(tool.run("langchain"))

1.3 自定义工具

  • 在LangChain中,自定义工具有多种方法
  • @tool装饰器
  • @tool装饰器是定义自定义工具的最简单方法。装饰器默认使用函数名称作为工具名称,但可以通过传递字符串作为第一个参数来覆盖此设置。此外,装饰器将使用函数的文档字符串作为工具的描述 – 因此必须提供文档字符串。

from langchain.tools import tool

@tool
def add_number(a: int, b: int) -> int:
    """add two numbers."""
    return a + b


print(add_number.name)
print(add_number.description)
print(add_number.args)

res = add_number.run({"a": 10, "b": 20})
print(res)

1.4 更多Tools

  • Tavily Search工具:Tavily的搜索API是一个专门为人工智能代理(llm)构建的搜索引擎,可以快速提供实时、准确和真实的结果。
  • 访问Tavily(https://tavily.com/)注册账号并登录,获取API 密钥
from langchain import hub
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_openai import ChatOpenAI
load_dotenv()
tool = TavilySearchResults(max_results=1)
#  使用Tavily搜索工具
tools = [TavilySearchResults(max_results=1,  tavily_api_key=os.getenv("TAVILY_API_KEY"))]
# print(tool.run("目前市场上黄金的售价是多少?"))
# https://smith.langchain.com/hub
# 获取要使用的提示
prompt = hub.pull("hwchase17/openai-tools-agent")

# 初始化大模型
llm = ChatOpenAI(
    api_key=os.getenv("api_key"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    model="qwen-plus"
)

# 构建 OpenAI 工具代理
agent = create_openai_tools_agent(llm, tools, prompt)

# 通过传入代理和工具创建代理执行程序
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# 运行代理
agent_executor.invoke({"input": "中国有多少个省份?"})

七. LangChain之Memory

  • 大多数的 LLM 应用程序都会有一个会话接口,允许我们和 LLM 进行多轮的对话,并有一定的上下文记忆能力。但实际上,模型本身是不会记忆任何上下文的,只能依靠用户本身的输入去产生输出。而实现这个记忆功能,就需要额外的模块去保存我们和模型对话的上下文信息,然后在下一次请求时,把所有的历史信息都输入给模型,让模型输出最终结果。
from langchain_openai import ChatOpenAI
import os

llm = ChatOpenAI(api_key="1",
                 base_url="http://127.0.0.1:11434/v1",
                 model='qwen3:8b')

# 直接提供问题,并调用llm
response = llm.invoke("你好我是坤坤")
# print(response)
# print("=" * 50)
print(response.content)

response = llm.invoke("我是谁?")

print(response.content)
  • 而在 LangChain 中,提供这个功能的模块就称为 Memory,用于存储用户和模型交互的历史信息。
  • 记忆系统需要支持两种基本操作:读取和写入。
  • 在接收到初始用户输入之后但在执行核心逻辑之前,链将从其内存系统中读取并增强用户输入。
  • 在执行核心逻辑之后但在返回答案之前,链会将当前运行的输入和输出写入内存,以便在将来的运行中引用它们。
  • 对该图的解释: 1、输入问题: ({“question”: …}) 2、读取历史消息: 从Memory中READ历史消息({“past_messages”: […]}) 3、构建提示(Prompt): 读取到的历史消息和当前问题会被合并,构建一个新的Prompt 4、模型处理: 构建好的提示会被传递给语言模型进行处理。语言模型根据提示生成一个输出。 5、解析输出: 输出解析器通过正则表达式 regex(“Answer: (.*)”)来解析,返回一个回答({“answer”: …})给用户 6、得到回复并写入Memory: 新生成的回答会与当前的问题一起写入Memory,更新对话历史。Memory会存储最新的对话内容,为后续的对话提供上下文支持。

1. Chat Messages

  • Chat Messages: 最基础的记忆管理方法,是用于管理和存储对话历史的具体实现。它们通常用于构建对话系统,帮助系统保持对话的连续性和上下文。这些消息通常包含了对话的每一轮,包括用户的输入和系统的响应。

2. RunnableWithMessageHistory

  • RunnableWithMessageHistory 包装另一个 Runnable 并为其管理聊天消息历史记录;它负责读取和更新聊天消息历史记录。

2.1 本地内存存储

  • 可以将聊天记录进行本地存储
import json
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langchain.schema import messages_from_dict, messages_to_dict
from dotenv import load_dotenv
import os

# 加载环境变量(需要包含API_KEY)
load_dotenv()

# 初始化大语言模型(通义千问)
llm = ChatOpenAI(api_key="1",
                 base_url="http://127.0.0.1:11434/v1",
                 model='qwen3:8b')

# 创建对话提示模板
prompt = ChatPromptTemplate.from_messages([
    # 系统角色设定
    ("system", "你是一个友好的助手"),
    # 历史消息占位符(变量名必须与链配置中的history_messages_key一致)
    MessagesPlaceholder(variable_name="history"),
    # 用户输入占位符
    ("user", "{input}")
])

# 构建基础对话链(组合提示模板和语言模型)
base_chain = prompt | llm

# 全局会话存储字典(Key: session_id, Value: ChatMessageHistory实例)
store = {}


def get_session_history(session_id):
    """获取或创建会话历史存储对象
    Args:
        session_id: 会话唯一标识(用于多会话隔离)
    Returns:
        对应会话的聊天历史记录对象
    """
    if session_id not in store:
        store[session_id] = ChatMessageHistory()  # 初始化空历史记录
    return store[session_id]


# 创建支持历史记录的对话链
conversation = RunnableWithMessageHistory(
    base_chain,  # 基础对话链
    get_session_history=get_session_history,  # 历史记录获取方法
    input_messages_key="input",  # 输入文本的键名
    history_messages_key="history"  # 历史记录的键名(需与提示模板中的变量名一致)
)


def save_memory(filepath, session_id):
    """保存指定会话的历史记录到文件
    Args:
        filepath: 文件保存路径(建议使用.json扩展名)
        session_id: 要保存的会话ID(默认"default")
    """
    history = get_session_history(session_id)
    # 将消息对象列表转换为字典格式
    dicts = messages_to_dict(history.messages)
    # 写入JSON文件(UTF-8编码)
    with open(filepath, "w", encoding='utf-8') as f:
        json.dump(dicts, f, ensure_ascii=False)


def load_memory(filepath, session_id):
    """从文件加载历史记录到指定会话
    Args:
        filepath: 历史记录文件路径
        session_id: 要加载到的会话ID(默认"default")
    """
    with open(filepath, "r", encoding='utf-8') as f:
        dicts = json.load(f)
    # 将字典转换回消息对象列表
    messages = messages_from_dict(dicts)
    # 更新全局存储中的会话历史
    store[session_id] = ChatMessageHistory(messages=messages)


def legacy_predict(input_text: str, session_id: str = "default") -> str:
    """模拟旧版predict方法的调用接口
    Args:
        input_text: 用户输入文本
        session_id: 会话ID(默认"default")
    Returns:
        AI生成的回复文本
    """
    return conversation.invoke(
        {"input": input_text},  # 输入参数
        # 配置参数(必须包含session_id来关联历史记录)
        config={"configurable": {"session_id": session_id}}
    ).content


if __name__ == "__main__":
    # 使用默认会话ID
    SESSION_ID = "default"

    # 模拟连续对话(4轮)
    legacy_predict("你好", SESSION_ID)  # 问候
    legacy_predict("你是谁,我是坤坤", SESSION_ID)  # 身份确认
    legacy_predict("你的背后实现原理是什么", SESSION_ID)  # 技术原理询问

    # 查询对话历史(第4轮)
    last_response = legacy_predict('截止到现在我们聊了什么?', SESSION_ID)
    print("最后一次回答:", last_response)

    # 持久化保存对话历史(JSON格式)
    save_memory("./memory_new.json", SESSION_ID)

    # 模拟重新加载历史记录(清空当前会话后重新加载)
    load_memory("./memory_new.json", SESSION_ID)

    # 验证历史恢复效果(第5轮)
    reload_response = legacy_predict("我回来了,我们之前都聊了一些什么?", SESSION_ID)
    print("\n恢复后的回答:", reload_response)

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇