作者:Peter_Monster
关键组件说明
- 文档加载器:
PyPDFLoader:处理 PDF 文件TextLoader:处理纯文本文件WebBaseLoader:抓取网页内容
- 文本处理:
RecursiveCharacterTextSplitter:按 1000 字符分块,保留 200 字符重叠
- 向量存储:
FAISS:高性能本地向量数据库,支持余弦相似度搜索
- 核心链:
ConversationalRetrievalChain:结合检索器和 LLM,支持多轮对话
- 工具调用:
PythonAstREPLTool:执行 Python 代码,用于数据分析或计算
- 记忆模块:
ConversationBufferMemory:保存最近 5 轮对话历史
技术选型依据
向量数据库:FAISS 在本地部署场景下具有较高的检索效率,适合中小型知识库文本分割:RecursiveCharacterTextSplitter 能在保持语义完整的前提下优化上下文窗口工具集成:Python 解释器工具可以扩展系统能力,处理需要动态计算的复杂问题模型选择:ChatOpenAI 提供稳定的生成能力,OllamaEmbeddings 支持本地低成本部署
1. 框架的本质:标准化的开发范式
LangChain 的核心价值在于提供了一套结构化的开发框架,帮助开发者高效构建基于大语言模型(LLM)的复杂应用。它通过以下方式实现这一目标:
模块化设计:将应用拆分为可复用的组件(如模型接口、提示模板、工具调用、内存管理等),开发者可像搭积木一样组合这些模块。
标准化接口:定义统一的交互协议(如 Runnable 接口),允许不同模型(如 OpenAI、Anthropic)、工具(如 SQL 数据库、搜索引擎)和数据源(如 PDF、Notion)无缝集成。
控制流管理:通过 链(Chains) 和 代理(Agents) 实现多步骤任务编排。例如,链可以串联 “文档检索→信息提取→结果生成” 的流程,而代理能让 LLM 动态决定调用哪些工具(如调用 Wolfram Alpha 进行数学计算)。
全生命周期支持:从开发阶段的快速原型构建(如用 create_agent 模板生成聊天机器人),到生产阶段的部署(通过 LangServe 转为 API)和监控(通过 LangSmith 追踪模型行为),LangChain 覆盖了应用的完整生命周期。
2. 工具和库的集合:丰富的功能组件
LangChain 提供了大量开箱即用的工具和库,作为框架的 “血肉”:
模型集成:支持 1000+ 模型和工具,包括 GPT-4、Gemini、Wolfram Alpha、Pinecone 向量数据库等18。
核心库:
langchain-core:定义基础抽象(如消息类型、工具接口)和表达式语言(LCEL),用于声明式构建复杂逻辑25。
langchain-community:社区贡献的扩展库,涵盖文件加载器、自定义工具等小众需求8。
langgraph:用于构建有状态的多智能体应用,支持持久化对话历史和复杂控制流25。
实用工具:如提示优化、输出解析、流式处理等,帮助开发者解决 LLM 应用中的常见痛点35。
3. 开源生态的核心价值
LangChain 采用 MIT 开源协议,允许自由使用和修改,其生态优势体现在:
快速迭代:社区持续贡献新组件(如支持最新向量数据库或 API),开发者无需等待官方更新即可接入前沿技术。
跨平台支持:同时提供 Python 和 JavaScript 版本,适配后端服务、前端应用、边缘计算等多种场景。
企业级落地:通过 LangSmith 进行模型监控和评估,LangServe 实现微服务化部署,降低了从实验到生产的门槛。
4. 与工具库的本质区别
LangChain 与普通工具库的关键差异在于框架的系统性:
工具库(如 Requests、Pandas)解决单一问题(如 HTTP 请求、数据处理),而 LangChain 整合这些工具,形成完整的解决方案。例如,结合 langchain-openai(模型调用)、langchain-sqlite(数据库查询)和 langchain-chains(流程编排),可快速构建一个能回答数据库问题的智能代理。
动态决策能力:LangChain 的代理机制允许 LLM 根据实时反馈调整工具调用策略,而工具库通常需要开发者预先编写固定逻辑
二、LangChain的核心模块
LLM和提示(Prompt):统一大模型访问API,同时提供了Prompt提示模板管理机制。
输出解析器(Output Parsers):LangChain接受大模型返回的文本内容之后,可以使用专门的输出解析器对文本内容进行格式化,例如解析json,或者将llm输出的内容转化为Python对象。
链(Chain):对一些常见的场景封装了一些现成的模块,如:基于上下文信息的问答系统,自然语言生成SQL查询等等,因为实现这些任务的过程就像工作流一样,一步步的执行,所以叫链。
表达式语言:LangChain Expression Language(LCEL),新版本特性,用于解决工作流编排问题,通过LCEL表达式,我们可以灵活的自定义AI任务处理流程,也就是灵活自定义链(Chain)。
数据增强生成(RAG):大模型不了解新的信息,为了避免它一本正经的瞎讲,我们将新的信息导入到LLM,用于增强LLM生成的内容质量,这种模式叫做RAG模式
Agents:是一种基于大模型的应用设计模式,利用LLM的自然语言理解和推理能力,更具用户的需求自动调用外部系统、设备共同去完成任务,例如:用户输入“明天请假一天”,大模型(LLM)自动调用请假系统,发起一个请假申请。
模型记忆(memory):让大模型记住之前的对话内容,这种能力成为模型记忆。
使用LangChain进行大模型开发,需要安装相关包依赖,安装命令:- # LangChain框架安装
- pip install langchain
- # 版本查看
- pip show langchain
复制代码 LangChain V1.0 vs LangGraph V1.0:分工与定位
LangChain :构建 AI 智能体的 最快方式 。提供标准的工具调用架构、供应商无关设计和可插拔的中间件系统,让开发者高效构建通用 Agent。
LangGraph :一个 底层运行时框架 ,专为需要长期运行、可控且高定制化的生产级智能体设计。
三、使用LangChain框架,调用AI大模型
构建的工作流用途不同,调用的方式也会稍有区别:
1. 专注于生成单段文本(如回答问题或写一段文字)
- from config import Config
- conf=Config()
- #1、openai方式连接大模型
- from openai import OpenAI
- # 初始化Deepseek的API客户端
- client = OpenAI(api_key = conf.API_KEY, base_url="https://api.deepseek.com")
- # 调用Deepseek的API,生成回答
- response =client.chat.completions.create(model="deepseek-chat'
- messages=[
- {"role": "system","content":"你是传智教育的助手传智小智,,请根据用户的问题给出回答"},
- {"role": "user","content":"你好,请你介绍一下你自己。"}
- ]
- # 打印模型最终的响应结果
- print(response.choices[0].message.content)
复制代码 2. 能聊天的应用(如客服机器人或问答系统)
2.1 OpenAI标准库调用:
- from langchain_openai import ChatOpenAI
- from langchain.schema import HumanMessage, SystemMessage
- # 初始化 ChatOpenAI,配置 DeepSeek API
- llm = ChatOpenAI(
- model=conf.MODEL,
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150
- )
- messages = [SystemMessage(content = "你是一个从事大模型开发多年的工程师,请根据用户的问题给出回答。"),
- HumanMessage(content = "请你用一句话介绍一下LangChain")
- ]
- result = llm.invoke(message)
- print(result.content)
复制代码 2.2 LangChain-模型厂商调用:
- model = ChatDeepSeek(
- model="...",
- api_key="...",
- api_base="https://DeepSeek.ai/api/v1",
- temperature = 0.5
- )
复制代码 区别:在调用DeepSeek等有独立接口的厂商的深度思考模型的时候,使用厂商独立接口可以拿到深度思考的思考过程。
2.3 自定义调用
应用场景:OpenAI标准库不会把深度思考模型的思考过程给你,但你又想使用OpenAI标准库方式来调用大模型,又想获取这个思考过程(俗称犟种)。
问:这几种调用方式都用到了 invoke() 这个函数:这个函数可以传入几种数据类型?
答:三种:字符串、消息对象列表、字典列表。
问:消息对象有几种,都是什么?
答:四种message:AI_message、tool_message、human_message、system_message
四、提示词管理工具类
LangChain封装了一组专门用于提示词(Prompts)管理的工具类,方便我们格式化提示词(prompts)内容,目的是将提示的逻辑(固定结构)与提示的数据(动态变量)彻底分离,从而实现代码的简洁、复用、安全和可维护性。
| 模板类型 | 核心作用 | 输出格式 | 使用场景 | | ChatPromptTemplate | 现代应用的主力。用于构建包含多个角色(系统、用户、AI)的对话式“剧本” | ChatPromptValue(本质是List[BaseMessage]) | 所有需要与聊天模型进行结构化、角色化交互的应用 | | PromptTemplate | 基础的字符串模板,将变量填充到单个字符串中。 | StringPromptValue(本质是字符串) | 构建需要简单文本输入的工具提示。 | FewShotChatMessage
PromptTemplate | 在提示中动态插入多个对话示例,引导模型进行“举一反三”,极大提升复杂任务的准确性 | ChatPromptValue | 需要特定输出格式的复杂任务,如代码生成、简历解析、合同条款提取等等 | 五、Message
聊天模型(Chat Model)以聊天消息列表作为输入,这个聊天消息列表的消息内容也可以通过提示词模板进行管理。这些聊天消息与原始字符串不同,因为每个消息都与“角色(role)”相关联。
例如,在OpenAI的Chat Completion API中,Openai的聊天模型,给不同的聊天消息定义了三种角色类型分别是助手(assistant)、人类(human)或系统(system)角色:
AIMessagePromptTemplate:助手(Assistant)消息指的是当前消息是AI回答的内容。
HumanMessagePromptTemplate:人类(user)消息指的是你发给AI的内容。
SystemMessagePromptTemplate:系统(system)消息通常是用来给AI身份进行描述。
六、Chain
能被称作LangChain,可想而知,Chain的含金量有多高了吧。Chain,也就是链的意思,指的是按照某一种逻辑,按顺序组合成一个流水线的方式。- from langchain_openai import ChatOpenAI
- from langchain.schema import HumanMessage, SystemMessage
- # 初始化 ChatOpenAI,配置 DeepSeek API
- llm = ChatOpenAI(
- model=conf.MODEL,
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150
- )
- # 直接使用模型+ 输出解析器搭建一个链
- basic_chain = llm | StrOutputParaser()
- '''
- 这是一个最简单的链式结构,模型+结果解析器
- '''
- # 查看输出结果
- question = "你是哪个?"
- result = basic_chain.invoke(question)
- print(result)
复制代码 问:前面有提到LECL,Chain与其有何区别?
答:用最接地气的话讲,核心区别就一个:Chain 是 “固定好的流程模板”,LCEL 是 “能自由拼搭的积木语法” —— 前者帮你把 “做事步骤” 固定死,后者让你随便组合步骤,想怎么搭就怎么搭。
1. Chain:像“现成的组装玩具说明书”
比如你买了个 “乐高小车”,Chain 就相当于说明书上写死的步骤:
先拼车轮(对应 “调用工具查数据”);
再拼车身(对应 “把数据传给 AI 处理”);
最后装车灯(对应 “AI 输出结果”)。
它的特点是:
步骤固定死,不能乱改:想在 “拼车身” 后加个 “贴贴纸”(比如加个数据过滤步骤),要么找个带 “贴贴纸” 的新说明书(对应换个现成的 Chain),要么自己拆了原步骤改(代码麻烦);
不用想 “怎么组合”:拿来就能用,但只能做说明书上写的事;
种类多但不通用:比如有 “拼小车的 Chain”“拼房子的 Chain”(对应 LLMChain、SequentialChain、RouterChain),不同场景要换不同的 Chain,记起来麻烦。
2. LCEL(LangChain Expression Language):像“乐高积木的通用拼接规则”
还是乐高,但 LCEL 不管你想拼车、拼房子还是拼飞船,只给你一套 “怎么拼” 的通用逻辑(比如 “凸点对凹点”“长积木可以接短积木”),然后让你随便拿零件组合:
零件就是 “功能模块”:比如 “车轮模块”“车身模块”“贴贴纸模块”“查天气模块”“AI 生成模块”;
拼接用 “简单符号”:比如用 “|”(管道符)像 “传接力棒” 一样串步骤,比如:
查天气模块 | 过滤无效数据模块 | AI 推荐活动模块 | 输出结果模块
(意思是:先查天气→再筛掉没用的信息→再让 AI 推荐活动→最后告诉用户);
想改就改,想加就加:比如突然想加 “如果下雨就提醒带伞”(条件判断),直接插个 “条件模块” 进去就行;想让 “查天气” 和 “查温度” 同时做(并行),也能直接拼,不用换整套流程。
核心区别对比(一句话总结)
| 维度 | Chain(老方法) | LCEL(新方法) | | 本质 | 固定的 “流程模板” | 灵活的 “积木拼接语法” | | 用法 | 找现成模板,按模板做事 | 拿模块自由组合,自己定流程 | | 灵活性 | 低:改步骤要换模板 / 改代码 | 高:随便加、减、换模块,支持条件 / 并行 | | 学习成本 | 高:要记各种不同类型的 Chain | 低:一套语法通吃所有模块 | | 适用场景 | 简单、固定的流程(比如 “输入→AI 输出”) | 复杂、多变的流程(比如 “查数据→过滤→条件判断→AI 生成→多轮交互”) | 最后补个大白话结论:
Chain 是 LangChain 早期的 “老工具”,适合新手快速上手简单需求(比如直接让 AI 回答问题);
LCEL 是现在 LangChain 推荐的 “新玩法”,相当于把原来 “固定的模板” 拆成了 “可自由组合的零件 + 通用拼接规则”,能搞定更复杂的需求,而且用起来更简单(不用记一堆 Chain 类型,靠 “|” 就能串流程)。
那么好,讲完这个区别之后,我们应该来看看:
都有什么链、有什么特点···
1. 串行链
- # 初始化 ChatOpenAI,配置 DeepSeek APIconf=Config()
- llm = ChatOpenAI(
- model=conf.MODEL,
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150
- )
-
- print("--- 1. 串行链 (Sequential Chain) 示例 ---")
-
- # 定义流水线的三个“工位”
- prompt = ChatPromptTemplate.from_template("写一句关于“{topic}”的七言绝句。")
- # llm 在通用环境中已定义
- parser = StrOutputParser()
- # 使用 LCEL `|` 管道符,将三个工位连接成一条串行流水线
- serial_chain = prompt | llm | parser
- print(type(serial_chain))
-
- # 启动流水线,投入原材料
- input_data = {"topic": "月色"}
- result = serial_chain.invoke(input_data)
-
- print(f"【输入】: {input_data}")
- print(f"【最终输出】: {result}")
复制代码 2. 并行链
- # langchain_4_2_chain.py (Corrected)
- from langchain.prompts import PromptTemplate
- from config import Config
- import json
- from langchain_openai import ChatOpenAI
- from langchain_core.prompts import ChatPromptTemplate
- from langchain_core.output_parsers import StrOutputParser
- # Import the necessary class
- from langchain_core.runnables import RunnableParallel
-
- # 初始化 ChatOpenAI,配置 DeepSeek APIconf=Config()
- llm = ChatOpenAI(
- model=conf.MODEL,
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150
- )
-
- print("\n--- 2. 并行链 (Parallel Chain) 示例 ---")
-
- # 定义两条独立的子流水线
- poem_chain = ChatPromptTemplate.from_template("写一首关于“{topic}”的诗。") | llm | StrOutputParser()
- joke_chain = ChatPromptTemplate.from_template("讲一个关于“{topic}”的俏皮话。") | llm | StrOutputParser()
-
- # 使用 RunnableParallel 将字典结构转换为一个可执行的并行链
- parallel_chain = RunnableParallel({
- "poem": poem_chain,
- "joke": joke_chain
- })
- # 启动并行流水线
- input_data = {"topic": "程序员"}
- # Now .invoke() works because parallel_chain is a Runnable object, not a dict
- result = parallel_chain.invoke(input_data)
-
- print(f"【输入】: {input_data}")
- print("【最终输出】:")
- # 结果是一个字典,包含了所有子流水线的输出
- print(json.dumps(result, indent=2, ensure_ascii=False))
复制代码 3. 分支与RunnablePassthrough()
该函数可以保留一份原本的数据文件- # 初始化 ChatOpenAI,配置 DeepSeek APIconf=Config()
- llm = ChatOpenAI(
- model=conf.MODEL,
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150
- )
-
- print("\n--- 3. 分支与 RunnablePassthrough 示例 ---")
-
- # 1. 模拟一个检索器
- def fake_retriever(query: str) -> str:
- """一个模拟的检索器,根据查询返回固定的上下文。"""
- return f"关于“{query}”的背景知识是:这是一个非常重要的概念。"
-
- # 2. 定义需要同时接收 context 和 question 的 Promptrag_prompt = ChatPromptTemplate.from_template(
- "根据以下上下文回答问题。\n上下文: {context}\n问题: {question}"
- )
-
- # 3. 构建包含 Passthrough 的并行链
- # 这条链接收一个字符串(问题)作为输入
- # rag_prompt
- chain = {
- # "context" 分支:对输入运行检索器
- "context": fake_retriever,
- # "question" 分支:直接“透传”原始输入
- "question": RunnablePassthrough()
- } | rag_prompt | llm | StrOutputParser()
-
- print(type(chain))
- # 4. 执行链
- user_question = "LCEL"
- result = chain.invoke(user_question)
复制代码 4. RunnableLambda自定义
RunnableLambda是一个“适配器”,它可以将任何普通的python函数包装成一个标准的LangChain组件,让它无缝接入到LCEL的“|”流水线中。
实现方式:- # 初始化 ChatOpenAI,配置 DeepSeek APIconf=Config()
- llm = ChatOpenAI(
- model=conf.MODEL,
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150
- )
-
- print("\n--- 4. RunnableLambda 示例 ---")
-
- # 1. 定义一个普通的 Python 函数,它不是标准的 LangChain 组件
- def add_comment(text: str) -> str:
- """百度官方网站"""
- return text.strip() + "\n 关于更多信息,欢迎访问:https://www.baidu.com/"
-
- # 2. 使用 RunnableLambda 将其包装成一个“标准工位”
- custom_processor = RunnableLambda(add_comment)
-
- # 3. 构建一条包含自定义工位的串行链
- chain = (
- ChatPromptTemplate.from_template("请解释一下“{concept}”是什么。最多100字")
- | llm
- | StrOutputParser()
- | custom_processor # 在这里接入我们的自定义函数
- )
-
- # 4. 执行链
- result = chain.invoke({"concept": "大模型"})
-
- print("【最终输出】:")
- print(result)
复制代码 七、输出解析器(output Parsers)
输出解析器是LangChain“模型I/O”模块中的关键组件。他的核心职责是扮演一个翻译官。
应用程序需要精确、结构化的数据,OutputParser接收LLM返回的文本以后,根据我们预先设定好的规则,将其解析转换为程序可以使用的、干净的结构化数据
| 解析器名称 | 核心功能 | 输出的 Python 类型 | 工业级应用场景 | | StrOutputParser | 默认解析器。将 LLM 的输出直接解析为字符串。 | str | 简单的文本生成、内容续写、摘要、翻译等。 | | JsonOutputParser | 极其常用。将 LLM 输出的 JSON 字符串解析为 Python 字典。 | dict | API 调用:生成符合 API 规范的 JSON 请求体。
数据提取:从非结构化文本(如邮件、报告)中提取实体信息。 | | PydanticOutputParser | 极其常用。将 LLM 输出解析为预先定义的 Pydantic 对象,提供类型安全和数据验证。 | 自定义的 pydantic.BaseModel 对象 | 高可靠性系统:从简历中提取信息并存入数据库,确保字段类型正确(如年龄是整数)。
配置生成:根据自然语言生成严格的 JSON 配置文件。 | | CommaSeparatedListParser | 将 LLM 输出的、用逗号分隔的文本解析为列表。 | list[str] | 标签生成:为文章、产品生成关键词标签。
头脑风暴:生成一系列相关的想法或选项。 | | DatetimeOutputParser | 从文本中智能地解析出日期和时间信息。 | datetime.datetime | 任务调度:从 “明天下午三点提醒我开会” 中提取精确时间。
信息归档:从新闻或日志中提取事件发生的时间。 | 来个实现示例吧,这里我们使用的是JsonOutputParser:- # 确保您已安装必要的库,并已在环境中设置好 API 密钥 from langchain_openai import ChatOpenAI
- from langchain_core.prompts import ChatPromptTemplate
- from langchain_core.output_parsers import JsonOutputParser, PydanticOutputParser,StrOutputParser
- from pydantic.v1 import BaseModel, Field
- from typing import List
- from config import Config
- conf=Config()
- llm = ChatOpenAI(
- model=conf.MODEL,
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150)print("--- 1. JsonOutputParser 示例 ---")# 1. 创建一个 JsonOutputParser 实例
- json_parser = JsonOutputParser()# 2. 创建一个 Prompt 模板,并注入格式化指令 # .get_format_instructions() 会自动生成告诉 LLM 如何输出 JSON 的指令
- prompt = ChatPromptTemplate.from_template("""从以下文本中提取用户的姓名和城市。
-
- {format_instructions}
-
- 文本: {text}""")# 3. 构建 LCEL 链
- chain = prompt | llm | json_parser
-
- # 4. 准备输入数据
- unstructured_text ="张伟是一位居住在北京的软件工程师,他今年30岁。"# 5. 调用链并传入格式化指令和文本
- result = chain.invoke({"format_instructions": json_parser.get_format_instructions(),"text": unstructured_text
- })# 6. 验证输出 print(f"【输入文本】: {unstructured_text}")print(f"【输出类型】: {type(result)}")print(f"【解析结果】: {result}")# 预期输出: # 【输出类型】: <class 'dict'> # 【解析结果】: {'name': '张伟', 'city': '北京'}
复制代码 八、文档加载器(document_loaders)
用于从各种数据源加载文档的组件,旨在将不同格式的文件(文本、PDF、Markdown、Word、PowerPoint等)或数据源(网页、数据库)转换为统一的Document对象。每个Document对象包含page_content(文档内容)和 metadata(元数据,如文件路径、页码等)
| 加载器名称 | 支持文件类型 | 功能描述 | 模块路径 | 工业场景应用 | | TextLoader | .txt | 加载纯文本文件,支持指定编码(如 UTF-8) | langchain_community.
document_loaders | 解析日志文件、用户指令、配置文件(如设备日志、控制指令) | | PyPDFLoader | .pdf | 加载 PDF 文件,按页面提取文本 | langchain_community.
document_loaders | 提取技术手册、合同、报告内容,生成结构化数据(如设备说明书、质量报告) | Unstructured
MarkdownLoader | .md | 加载 Markdown 文件,保留标题、列表等结构 | langchain_community.
document_loaders | 解析技术文档、API 文档,构建知识库或 RAG 系统(如开发手册、操作指南) | | Docx2txtLoader | .docx | 加载 Word 文档,提取纯文本 | langchain_community.
document_loaders | 处理企业文档、报告、会议记录(如项目计划书、操作规程) | UnstructuredPower
PointLoader | .ppt, .pptx | 加载 PowerPoint 文件,提取幻灯片文本 | langchain_community.
document_loaders | 解析培训材料、演示文档(如技术培训 PPT、产品介绍) | | WebBaseLoader | 网页 | 加载网页内容,支持 URL 或 HTML | langchain_community.
document_loaders | 抓取在线技术文档、新闻、产品说明,补充知识库 | | CSVLoader | .csv | 加载 CSV 文件,支持按行或列提取数据 | langchain_community.
document_loaders | 解析工业数据表格、传感器数据(如生产数据、设备状态记录) | | JSONLoader | .json | 加载 JSON 文件,提取指定字段或全部内容 | langchain_community.
document_loaders | 解析结构化数据、API 响应(如设备状态 JSON、日志文件) | | DirectoryLoader | 目录 | 批量加载目录中的多种文件类型 | langchain_community.
document_loaders | 批量处理企业文档库、混合格式文件(如技术文档、日志文件夹) | 来个实现的代码示例吧,这里示例使用的是textloader:- from langchain_community.document_loaders import TextLoader # 使用新模块路径 from config import Config
- from langchain_openai import ChatOpenAI
- from datetime import datetime
-
- # 初始化配置和模型
- conf = Config()
- llm = ChatOpenAI(
- model=conf.MODEL,# 直接指定模型名称
- api_key=conf.API_KEY,
- base_url=conf.API_URL,
- temperature=0.7,
- max_tokens=150)# Document Loaders 示例:加载文档并接入大模型总结
- loader = TextLoader(r"D:\LLM_Codes\Chapter3_RAG\rag_base_frame\data\林青霞.txt", encoding="utf-8")
-
- documents = loader.load()
- doc=documents[0]print("\n--- 1. 加载后的原始元信息 ---")print(doc.metadata)# 1.2 像操作字典一样,为 Document 对象添加自定义元信息 print("\n--- 2. 添加自定义元信息 ---")
- doc.metadata['author']='DT.L'
- doc.metadata['version']='1.1'
- doc.metadata['processed_at']= datetime.now().isoformat()
- doc.metadata['tags']=['test','loader','metadata']print("更新后的元信息:")print(doc.metadata)print("\n--- 3. 访问特定的元信息 ---")print(f"Author: {doc.metadata.get('author','Unknown')}")print(f"Tags: {doc.metadata.get('tags')}")print("\n--- 4. 删除元信息 ---")del doc.metadata['version']print("更新后的元信息:")print(doc.metadata)print("\n--- 5. 获取文本信息 ---")print(doc.page_content)# --- 6. 清空文本信息 -不能采用del --- print("\n--- 6. 清空文本信息 ---")
- doc.page_content =""# 正确做法:赋值为空字符串,而不是删除属性 print("更新后的信息:")print(doc)
复制代码 九、文本分割器
文本分割器是依据字符、结构、语义等规则,将各类格式的文本拆分为适配 LLM 输入、知识库检索等场景的合适片段,辅助后续文本处理的工具。
| 分割器名称 | 功能描述 | 类型 | 工业场景应用 | | RecursiveCharacterTextSplitter | 递归按字符分割,先尝试自然边界(如段落、句子),太大则继续细分。 | 通用字符解析 | 通用文本处理,如日志、报告、PDF 文档分割,便于 RAG 检索。 | | CharacterTextSplitter | 简单按指定分隔符(如换行、逗号)直接分割。 | 基础字符解析 | 简单字符串或 CSV 数据处理,如传感器数据日志。 | | TokenTextSplitter | 按 token(词元)分割,支持 LLM token 计数。 | Token 基于解析 | LLM 输入优化,如处理 API 响应或长查询,控制 token 限制。 | | MarkdownTextSplitter | 按 Markdown 结构(如标题、列表)智能分割。 | 结构化解析 | Markdown 文档分割,保留语义结构,用于知识库构建。 | | HTMLSplitter | 按 HTML 标签(如 、 )分割网页内容。 | 结构化解析 | 网页数据爬取,如在线技术文档或新闻提取。 | | SentenceTextSplitter | 按句子边界分割,使用 NLP 识别句子(包括标点)。 | 语义解析 | 自然语言文本,如文章或对话分析,保持句子完整。 | | PythonCodeTextSplitter | 按 Python 代码结构(如函数、类)分割。 | 代码解析 | 源代码文件分析,如脚本调试或代码库管理。 | | LatexTextSplitter | 按 LaTeX 结构(如章节、公式)分割。 | 结构化解析 | 学术论文或数学文档处理。 | | SpacyTextSplitter | 使用 SpaCy NLP 库按句子或实体分割(需安装 SpaCy)。 | 语义解析 | 高级 NLP 场景,如实体提取或生物医学文本。 | | NLTKTextSplitter | 使用 NLTK 库按句子或词分割(需安装 NLTK)。 | 语义解析 | 文本研究或分析,如时间序列数据描述。 | 来个示例吧,这里使用的是RecursiveCharacterTextSplitter:- # ---------------------------------------------------- # 1. 准备环境和依赖库 # ---------------------------------------------------- # 确保你已经安装了 langchain# pip install langchain langchain-core import logging
- from langchain_core.documents import Document
- from langchain.text_splitter import RecursiveCharacterTextSplitter
-
- # ---------------------------------------------------- # 2. 配置日志 # ---------------------------------------------------- # 配置一个简单的日志记录器,方便在控制台清晰地看到输出
- logging.basicConfig(
- level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s')
- log = logging.getLogger(__name__)# ---------------------------------------------------- # 3. 准备示例文本和Document对象 # ---------------------------------------------------- # 模拟一段工业监控报告,这段文本包含了由空行分隔的段落、 # 由换行符分隔的列表项,以及一个没有内部换行的长句子。
- sample_text ="""
- LangChain框架是构建大语言模型应用的核心工具。它提供了模块化的组件和标准化的接口,极大地简化了从数据处理到模型调用和输出解析的整个流程。
-
- 在工业监控领域,LangChain展现了巨大潜力。例如,工程师可以利用它快速构建一个RAG系统,该系统能够:
- 1. 实时读取设备传感器日志。
- 2. 将非结构化的日志文本分割、向量化并存入知识库。
- 3. 当检测到异常指标时,自动从知识库中检索相关维护手册和历史故障案例。
- 4. 利用LLM分析检索到的信息,并生成一份详细的故障诊断报告和操作建议。
-
- 这个过程不仅响应迅速,而且极大地提升了故障排查的准确性和效率。传统的监控系统通常依赖固定的规则和阈值,而基于LangChain的智能系统则能理解日志的深层语义,从而发现更复杂的潜在问题。
- """# 将原始文本封装成 LangChain 的 Document 对象 # metadata 字段可以用来存储文档的来源、ID等元信息
- doc = Document(
- page_content=sample_text,
- metadata={"source":"industrial_monitor_log.txt","id":"doc_001"})
-
- log.info(f"原始文档创建成功,总长度: {len(doc.page_content)} 字符。")# print("-" * 80) # print(f"原始文档内容:\n{doc.page_content}") # print("-" * 80) # ---------------------------------------------------- # 4. 初始化并使用 RecursiveCharacterTextSplitter# ---------------------------------------------------- # 初始化递归字符分割器 # chunk_size: 定义每个文本块(chunk)的最大字符数。这是硬性限制。 # chunk_overlap: 定义相邻块之间重叠的字符数,用于保持上下文的连续性。 # separators: 定义了分割文本时尝试使用的分隔符列表,按从左到右的优先级顺序。
- text_splitter = RecursiveCharacterTextSplitter(
- chunk_size=100,
- chunk_overlap=50,
- length_function=len,
- is_separator_regex=False,
- separators=["\n\n","\n"," ",""]# 默认分隔符,这里显式写出以便理解 )# 执行分割操作 # 注意:split_documents 方法接收一个 Document 列表,返回也是一个 Document 列表
- chunks = text_splitter.split_documents([doc])# ---------------------------------------------------- # 5. 打印和验证分割结果 # ----------------------------------------------------
- log.info(f"文本分割完成,共生成 {len(chunks)} 个块(chunks)。")print("\n"+"="*80)print(" 分割结果展示")print("="*80+"\n")for i, chunk inenumerate(chunks):print(f">>> --- Chunk {i+1} / {len(chunks)} ---")print(f" 源数据 (Metadata): {chunk.metadata}")print(f" 块长度 (Length): {len(chunk.page_content)} characters")print(f" 块内容 (Content):\n\n'{chunk.page_content}'")print("\n"+"-"*80+"\n")
复制代码 十、工具
工具是赋予大型语言模型(LLM)与外部世界交互能力的接口。
LangChain 工具调用的实质是让大语言模型(LLM)通过标准化接口动态调用外部工具,扩展自身能力。其核心流程包括:1、意图识别与参数提取,2、工具适配与执行,3、结果格式化反馈16。
示例:
用户问 “12345×67890 是多少”,LLM 调用内置的CalculatorTool,传入参数 “12345*67890”,工具计算后返回结果,LLM 再整理成自然语言回答16。整个过程无需人工干预,工具调用逻辑完全自动化。
代码落地:- # --- 步骤2: 定义我们的工具 ---@tool defmultiply(a:int, b:int)->int:"""用于计算两个整数的乘积。"""print(f"正在执行乘法: {a} * {b}")return a * b
- @tooldefsearch_weather(city:str)->str:"""用于查询指定城市的实时天气。"""print(f"正在查询天气: {city}")if"北京"in city:return"北京今天是晴天,气温25摄氏度。"elif"上海"in city:return"上海今天是阴天,有小雨,气温22摄氏度。"else:returnf"抱歉,我没有'{city}'的天气信息。"
复制代码 完整代码:- # -*- coding: utf-8 -*- from langchain_openai import ChatOpenAI
- from langchain_core.tools import tool
- from langchain_core.messages import HumanMessage, AIMessage
-
- # --- DeepSeek API 配置 ---# 请替换为你的 DeepSeek API 密钥
- API_KEY ="sk-52****************************"
- API_URL ="https://api.deepseek.com/v1"
- MODEL ="deepseek-chat"# --- 步骤1: 初始化 ChatOpenAI ---# 虽然我们用的是DeepSeek,但它兼容OpenAI的API格式,所以可以使用ChatOpenAI类
- llm = ChatOpenAI(
- model=MODEL,
- api_key=API_KEY,
- base_url=API_URL,
- temperature=0.8,
- max_tokens=300)# --- 步骤2: 定义我们的工具 ---@tooldefmultiply(a:int, b:int)->int:"""用于计算两个整数的乘积。"""print(f"正在执行乘法: {a} * {b}")return a * b
-
- '''
- 下面的工具中,返回的东西是写死的,真实的业务场景中,会去调用其他数据提供商的api接口来调用实时数据
- '''@tooldefsearch_weather(city:str)->str:"""用于查询指定城市的实时天气。"""print(f"正在查询天气: {city}")if"北京"in city:return"北京今天是晴天,气温25摄氏度。"elif"上海"in city:return"上海今天是阴天,有小雨,气温22摄氏度。"else:returnf"抱歉,我没有'{city}'的天气信息。"# 将工具列表放入一个变量
- tools =[multiply, search_weather]# --- 步骤3: 将工具绑定到LLM --- # .bind_tools() 方法会将工具的结构信息(名称、描述、参数)传递给模型 # 这样模型在推理时就知道自己有哪些"超能力"了
- llm_with_tools = llm.bind_tools(tools)# --- 步骤4: 发起调用 ---# 第一次调用:让模型决定是否以及如何调用工具 print("--- 第一次调用:模型生成工具调用指令 ---")
- query ="北京今天天气怎么样?另外请帮我计算一下 12乘以8 等于多少?"# invoke 方法会返回一个 AIMessage 对象 # 如果模型决定调用工具,相关信息会储存在 .tool_calls 属性中
- ai_msg = llm_with_tools.invoke(query)print("模型返回的AIMessage:")print(ai_msg)print("\n解析出的工具调用请求:")print(ai_msg.tool_calls)# --- 步骤5: 执行工具调用 ---print("\n--- 执行工具调用 ---")
- tool_results =[]for tool_call in ai_msg.tool_calls:# 根据工具名称找到对应的工具函数
- tool_name = tool_call["name"]
- tool_args = tool_call["args"]# 查找对应的工具
- selected_tool =Nonefor t in tools:if t.name == tool_name:
- selected_tool = t
- breakif selected_tool:print(f"执行工具: {tool_name}, 参数: {tool_args}")# 执行工具并获取结果 """
- 使用 @tool 装饰器定义函数时,LangChain 会自动将这个普通函数包装成一个 Tool 对象。
- 这个 Tool 类继承了 Runnable 基类,因此具有 invoke() 方法。
- """ result = selected_tool.invoke(tool_args)
- tool_results.append({"tool_call_id": tool_call["id"],"name": tool_name,"result": result
- })print(f"工具执行结果: {result}")else:print(f"未找到工具: {tool_name}")
复制代码 十一、智能代理(Agent)
Agent内部包含一个LLM和一套工具,他能像一个“思考者”一样,自主规划、决定是否需要调用工具、调用哪个工具、以及如何组织调用顺序,直到最终完成任务。
适用于处理复杂、多步骤的任务,当我们武大确定完成任务需要调用多少次,调用哪些工具时,Agent就是最佳选择~
直接来个代码示例吧:- # -*- coding: utf-8 -*- from langchain_openai import ChatOpenAI
- from langchain_core.tools import tool
- from langchain.agents import create_tool_calling_agent, AgentExecutor
- from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder # 修正:需要导入 MessagesPlaceholder # --- 步骤1: DeepSeek API 配置和初始化 ---# 1.1 配置API参数
- API_KEY ="sk-52e226ac3cac46838cb282b45b1a648e"# 替换为你的实际API密钥
- API_URL ="https://api.deepseek.com/v1"# DeepSeek API端点
- MODEL ="deepseek-chat"# 使用的模型名称 # 1.2 初始化ChatOpenAI客户端 # 虽然使用DeepSeek,但兼容OpenAI API格式
- llm = ChatOpenAI(
- model=MODEL,
- api_key=API_KEY,
- base_url=API_URL,
- temperature=0,# 温度参数设为0,使输出更确定性
- max_tokens=1000# 增加最大token数以处理更长的响应 )# --- 步骤2: 定义工具函数 ---# 2.1 定义乘法计算工具 @tooldefmultiply(a:int, b:int)->int:"""
- 用于计算两个整数的乘积。
-
- 参数:
- a: 第一个整数
- b: 第二个整数
-
- 返回:
- 两个整数的乘积
- """print(f"正在执行乘法: {a} * {b}")return a * b
-
-
- # 2.2 定义天气查询工具 @tooldefsearch_weather(city:str)->str:"""
- 用于查询指定城市的实时天气。
-
- 参数:
- city: 城市名称
-
- 返回:
- 该城市的天气信息
- """print(f" 正在查询天气: {city}")if"北京"in city:return"北京今天是晴天,气温25摄氏度。"elif"上海"in city:return"上海今天是阴天,有小雨,气温22摄氏度。"elif"广州"in city:return"广州今天是多云,气温28摄氏度。"elif"深圳"in city:return"深圳今天是晴天,气温30摄氏度。"else:returnf"抱歉,我没有'{city}'的天气信息。"# 2.3 创建工具列表
- tools =[multiply, search_weather]# --- 步骤3: 创建Agent提示模板 ---# 3.1 构建提示模板 # ChatPromptTemplate用于定义对话的结构和内容
- prompt = ChatPromptTemplate.from_messages([# 系统消息:定义AI助手的角色和行为准则 ("system","你是一个乐于助人的AI助手。请根据用户需求选择合适的工具来解决问题。"),# 用户输入:{input}是占位符,运行时会被实际用户输入替换 ("human","{input}"),# 关键修正:使用MessagesPlaceholder而不是("placeholder", "...") # agent_scratchpad用于存储Agent思考过程中的中间步骤(工具调用和结果) # agent_scratchpad 是 LangChain Agent 框架中一个预定义的、固定的变量名,用于存储Agent思考过程中的中间步骤。
- MessagesPlaceholder(variable_name="agent_scratchpad"),])# --- 步骤4: 创建工具调用Agent --- # 4.1 使用create_tool_calling_agent创建Agent # 这个函数将LLM、工具和提示模板组合成一个完整的Agent
- agent = create_tool_calling_agent(
- llm=llm,# 语言模型
- tools=tools,# 可用工具列表
- prompt=prompt # 提示模板 )# --- 步骤5: 创建Agent执行器 ---# 5.1 创建AgentExecutor # AgentExecutor负责管理Agent的执行循环:思考->调用工具->整合结果->继续思考
- agent_executor = AgentExecutor(
- agent=agent,# 要执行的Agent
- tools=tools,# 可用工具列表
- verbose=True,# 开启详细日志,显示Agent的思考过程
- handle_parsing_errors=True,# 自动处理解析错误
- max_iterations=5# 最大迭代次数,防止无限循环 )# --- 步骤6: 运行Agent处理任务 ---print("=" * 60) print("="*60)# 6.1 处理单步任务 print("\n--- 运行Agent处理单步任务 ---")print("用户问题: 上海今天天气怎么样?")
- response1 = agent_executor.invoke({"input":"上海今天天气怎么样?"})print("\n 最终回答:")print(response1["output"])# 6.2 处理多步任务 print("\n"+"="*60)print("--- 运行Agent处理多步任务 ---")print("用户问题: 先帮我查一下北京的天气,然后计算 35 乘以 3 的结果是多少?")
- response2 = agent_executor.invoke({"input":"先帮我查一下北京的天气,然后计算 35 乘以 3 的结果是多少?"})print("\n最终回答:")print(response2["output"])# 6.3 处理更复杂的任务 print("\n"+"="*60)print("--- 运行Agent处理复杂任务 ---")print("用户问题: 请告诉我广州的天气,然后计算23乘以4,最后告诉我深圳的天气")
- response3 = agent_executor.invoke({"input":"请告诉我广州的天气,然后计算23乘以4,最后告诉我深圳的天气"})print("\n最终回答:")print(response3["output"])print("\n"+"="*60)
复制代码 十二、记忆模块
默认情况下,大模型是无状态的,也就是它不会记住你之前的任何交互。所以每一次提问都是一次全新的、独立的对话。
记忆模块 的作用就是赋予LLM上下文感知能力。通过存储、管理和传递历史对话信息,让LLM能够“记住”之前的交流内容,从而实现连贯的、有逻辑的多轮对话。
| 对比维度 | ConversationBufferMemory | ConversationBufferWindowMemory | | 核心机制 | 完整保留全部对话历史(用户提问 + AI 回复),无自动清理逻辑 | 仅保留最近 N 轮对话(N 为窗口大小,可配置),自动丢弃早期历史 | | 优点 | 1. 上下文完整性最高,可回溯任意早期对话信息;
2. 配置简单(无需设置窗口大小);
3. 无信息丢失风险,适合依赖长历史的任务 | 1. 严格控制 Token 消耗,降低 API 成本;
2. 减少冗余信息,提升模型响应速度;
3. 灵活适配不同对话长度需求(调整 N 值);
4. 避免历史信息过载导致的模型逻辑偏移 | | 缺点 | 1. 对话越长,Token 消耗越大,成本越高;
2. 长历史可能包含冗余信息,拖慢响应速度;
3. 极端长对话可能触发模型 Token 上限(如 GPT-3.5 的 4k/8k 上下文);
4. 历史信息过载可能导致模型聚焦性下降 | 1. 无法回溯窗口外的早期对话,可能丢失关键历史信息;
2. 需手动调试窗口大小(N 过小可能缺失必要上下文,N 过大则失去窗口意义);
3. 短窗口场景下,模型无法感知远期对话逻辑 | | 适用场景 | 1. 短对话交互(如单轮 / 3-5 轮问答、简单咨询);
2. 需完整回溯历史的任务(如复杂问题拆解、多步骤协作、上下文依赖强的推理);
3. 对 Token 成本不敏感、追求上下文完整性的场景 | 1. 长对话场景(如客服对话、多轮聊天、持续任务协作);
2. 对 Token 成本和响应速度敏感的场景(如批量交互、高频调用);
3. 仅需基于近期上下文决策的任务(如日常聊天、即时咨询、短期任务跟进);
4. 模型上下文窗口有限的场景(如使用 4k 上下文的模型处理长对话) | | Token 消耗 | 随对话长度线性增长(无上限,除非手动清理) | Token 消耗稳定在窗口大小对应的范围(可控) | | 配置难度 | 低(仅需初始化,无需额外参数) | 中(需合理设置 k 参数,如 k=5 表示保留最近 5 轮) | 至此我们才算是大概的过了一遍LangChain都有什么组件,这些组件的作用和如何使用,还有其他的组件比如embedding Models、Retrievers等等我们不再详细介绍,自己补充一下吧~~
原文地址:https://blog.csdn.net/weixin_63182000/article/details/155467750 |