零基础学AI大模型之LangChain聊天模型多案例实战
大家好,我是工藤学编程 🦉 | 一个正在努力学习的小博主,期待你的关注 |
---|---|
实战代码系列最新文章😉 | C++实现图书管理系统(Qt C++ GUI界面版) |
SpringBoot实战系列🐷 | 【SpringBoot实战系列】SpringBoot3.X 整合 MinIO 存储原生方案 |
分库分表 | 分库分表之实战-sharding-JDBC分库分表执行流程原理剖析 |
消息队列 | 深入浅出 RabbitMQ-RabbitMQ消息确认机制(ACK) |
AI大模型 | 零基础学AI大模型之ChatModel聊天模型与ChatPromptTemplate实战 |
前情摘要:
1、零基础学AI大模型之读懂AI大模型
2、零基础学AI大模型之从0到1调用大模型API
3、零基础学AI大模型之SpringAI
4、零基础学AI大模型之AI大模型常见概念
5、零基础学AI大模型之大模型私有化部署全指南
6、零基础学AI大模型之AI大模型可视化界面
7、零基础学AI大模型之LangChain
8、零基础学AI大模型之LangChain六大核心模块与大模型IO交互链路
9、零基础学AI大模型之Prompt提示词工程
10、零基础学AI大模型之LangChain-PromptTemplate
11、零基础学AI大模型之ChatModel聊天模型与ChatPromptTemplate实战
本文章目录
- 零基础学AI大模型之LangChain聊天模型多案例实战
- 一、实战准备:基础依赖与LLM配置
- 1.1 安装依赖
- 1.2 通用LLM配置说明
- 二、案例1:基础领域专家——固定角色的LLM调用
- 核心思路
- 完整代码与拆解
- 执行结果与分析
- 三、案例2:带参数的领域专家——动态角色与个性化输出
- 核心思路
- 完整代码与拆解
- 执行结果与分析
- 四、案例3:合规客服系统——场景化约束与风险控制
- 核心思路
- 完整代码与拆解
- 执行结果与分析
- 五、实战总结:核心流程与关键技巧
- 1.3个实战技巧
- 2. 模型替换建议
零基础学AI大模型之LangChain聊天模型多案例实战
大家好,我是工藤学编程!在上一篇中,我们搞懂了ChatModel的核心特性和ChatPromptTemplate的基础用法,今天这篇咱们直接“落地实战”——围绕**「模板构建→参数注入→LLM调用」** 的完整流程,拆解3个高频业务场景(领域专家、带参数领域专家、合规客服),手把手教你用LangChain快速搭建可复用的聊天模型应用。
先明确一个核心执行顺序,这是所有案例的底层逻辑,务必记住:
from_template(定义单角色模板)
→ from_messages(组合多角色模板)
→ format_messages(注入动态参数)
→ model.invoke(调用LLM生成响应)
一、实战准备:基础依赖与LLM配置
所有案例均基于LangChain核心库和DeepSeek模型(也可替换为GPT-3.5/4、 Claude等),先完成环境搭建:
1.1 安装依赖
# 核心依赖:LangChain基础框架 + OpenAI兼容适配器(用于调用通义千问等模型)
pip install langchain-core langchain-openai
1.2 通用LLM配置说明
案例中都会用到ChatOpenAI
类(注意:虽叫“OpenAI”,但支持配置第三方模型的OpenAI兼容接口,如通义千问的dashscope.aliyuncs.com
),关键参数含义:
model_name
:模型名称(如通义千问的qwen-plus
,GPT-3.5的gpt-3.5-turbo
);base_url
:模型API的基础地址(通义千问专属地址如上,OpenAI为https://api.openai.com/v1
);api_key
:你的模型API密钥(需替换为自己的,通义千问密钥从阿里云DashScope获取);temperature
:随机性参数(0~1,0更严谨、1更灵活,案例中统一设为0.7)。
二、案例1:基础领域专家——固定角色的LLM调用
场景需求:构建一个“Java专家”聊天模型,接收用户关于Java知识点的提问,输出专业解释(无动态参数,角色和回复风格固定)。
核心思路
直接用SystemMessage
和HumanMessage
构建固定角色的消息列表,跳过模板定义步骤(适合简单、无参数的场景),直接调用LLM。
完整代码与拆解
# 1. 导入依赖:LLM客户端 + 消息类型
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
# 2. 初始化LLM模型
model = ChatOpenAI(model_name='deepseek-r1:7b', # 本地模型名称,根据实际情况填写base_url="http://127.0.0.1:11434/v1", # 本地模型API地址api_key="none", # 本地模型通常不需要真实API密钥temperature=0.7 # 可根据需要调整温度参数
)# 3. 构建消息列表(类似Java的List<Message>,固定角色与内容)
messages = [# System角色:定义“Java专家”身份和语言要求SystemMessage(content="你是一个Java专家,用中文回答,解释要通俗易懂,带核心要点"),# Human角色:用户的具体提问(固定为“解释volatile关键字的作用”)HumanMessage(content="解释volatile关键字的作用")
]# 4. 同步调用LLM(invoke()为LangChain标准同步调用方法,类似Java的execute())
response = model.invoke(messages)# 5. 输出结果(response.content为LLM的核心回复内容)
print("LLM响应结果:")
print(response.content)
执行结果与分析
LLM响应结果:
<think>
好的,我现在需要解释一下Java中的volatile关键字的作用。首先,我得回忆一下之前学过的相关知识。我知道,在Java中,变量分为局部变量、实例变量和静态变量三种类型。这些变量在不同场合下会有不同的行为,尤其是在多线程环境中处理时,就需要特别注意。volatile关键字主要是针对实例变量的,它的作用是告诉编译器这个变量不会被其他线程修改。这样编译器就可以做一些优化,比如不使用 locks(锁),直接读取和写入同一个内存块,避免了潜在的竞态条件问题。举个例子,在一个线程中更新变量x,另一个线程在等待的时候读取x,这时候由于变量是volatile,两个线程会在同一个内存块上操作,不会有 race condition的问题出现。这样不仅解决了竞态条件,还可能提升性能,因为不涉及锁的开销。但是需要注意的是,volatile并不完全等同于静态类型。虽然静态类型在单个线程内不可修改,但它是被其他线程可见的,可能会导致竞态条件或错误的行为。而volatile变量虽然是不可变的,但它对其他线程是可见的,所以最好使用final关键字来隐藏它的可见性。综合来说,volatile的关键字通过告诉编译器变量不会被修改,从而优化性能并解决竞态条件问题,但需要注意它的使用场景和限制。
</think>### 解释:`volatile` 关键字在 Java 中用于标识一个 instance variable(实例变量),声明该变量不会被其他线程修改。当变量被标记为 `volatile` 时,编译器知道这个变量的值是可见的,并且不会有其他线程对它进行更新。### 核心要点:1. **阻止并发读写**:- 当两个或多个线程在访问同一个 instance variable 时,默认情况下这些线程会竞争锁(mutex)来避免修改和读取操作冲突。`volatile` 关键字可以将这种情况下的竞态条件问题消除。2. **优化性能**:- 因为 `volatile` 变量不会被其他线程修改,编译器可以对访问该变量的代码进行更有效的优化,比如直接使用内存中的值而不需要等待锁的到来。3. **可见性**:- 虽然 `volatile` 关键字阻止了其他线程对变量的修改,但它并没有隐藏变量的可见性。其他线程可以读取和写入该变量,但不会进行任何修改。4. **结合 final**:- 如果在字段上同时使用 `final` 和 `volatile`,则表示这个字段是不可修改且不可见的(除了当前线程),这样的字段通常用于私有常量或者作为缓存的保护机制。### 示例:```java
public class MyClass {volatile int x = 0;public void updateX() {// 只能被同一个线程内部使用x++;}
}// 可见性问题:其他线程可以通过读取x,但无法修改它。
int y = new MyClass().x; // y的值也是 1// 不许其他线程修改:
public void otherThreadUpdateX() {// 这里会抛出一个 ConcurrentModificationException
}### 总结:`volatile` 关键字通过阻止其他线程对变量进行修改,使得变量在单个实例内是不可变的。这种特性允许编译器对访问该变量的操作进行优化,并且也隐含了该变量对所有线程都是可见的。
- 核心亮点:无需模板,直接通过
SystemMessage
固定角色,快速实现“领域专家”的基础能力; - 适用场景:角色和提问场景固定(如专属技术问答、固定知识科普)。
三、案例2:带参数的领域专家——动态角色与个性化输出
场景需求:构建一个“可切换领域”的专家模型——支持动态指定领域(如机器学习、Python、Java)、输出风格(如“用比喻说明”“带代码示例”),并解释用户指定的概念(如过拟合、装饰器)。
核心思路
用from_template
定义单角色的参数化模板(系统角色模板+用户角色模板),再用from_messages
组合成完整对话模板,最后用format_messages
注入动态参数(领域、风格、概念),实现“一次模板,多场景复用”。
完整代码与拆解
# 1. 导入依赖:LLM客户端 + 模板类
from langchain_openai import ChatOpenAI
from langchain_core.prompts import (ChatPromptTemplate, # 对话模板容器SystemMessagePromptTemplate, # 系统角色模板HumanMessagePromptTemplate # 用户角色模板
)# 2. 步骤1:用from_template定义单角色参数化模板(核心:{变量名}表示动态参数)
# 系统角色模板:动态指定领域({domain})和输出风格({style_guide})
system_template = SystemMessagePromptTemplate.from_template("你是一位专业的{domain}专家,回答需严格满足:{style_guide}"
)
# 用户角色模板:动态指定要解释的概念({concept})
human_template = HumanMessagePromptTemplate.from_template("请解释:{concept}"
)# 3. 步骤2:用from_messages组合多角色模板(将系统模板和用户模板整合为完整对话)
chat_prompt = ChatPromptTemplate.from_messages([system_template, # 加入系统角色模板human_template # 加入用户角色模板
])# 4. 步骤3:用format_messages注入动态参数(生成可直接传给LLM的消息列表)
# 这里指定:领域=机器学习,风格=用比喻+示例,概念=过拟合
messages = chat_prompt.format_messages(domain="机器学习",style_guide="使用生活比喻和具体示例说明,避免专业术语堆砌",concept="过拟合"
)# 5. 初始化LLM模型(同案例1)
model = ChatOpenAI(model_name='deepseek-r1:7b', # 本地模型名称,根据实际情况填写base_url="http://127.0.0.1:11434/v1", # 本地模型API地址api_key="none", # 本地模型通常不需要真实API密钥temperature=0.7 # 可根据需要调整温度参数
)# 6. 调用LLM并输出结果
response = model.invoke(messages)
print("带参数领域专家响应结果:")
print(response.content)
执行结果与分析
带参数领域专家响应结果:
<think>
嗯,用户问的是“过拟合”,我得先理解一下这个概念。过拟合在机器学习中是一个常见的问题,指的是模型在训练数据上表现很好,但是泛化能力差,在新的数据上效果不佳。要怎么用生活比喻来解释呢?比如,学生考试前过度复习,记住了所有题型,结果考试时遇到新题型就发挥不好。这个比喻挺直观的,大家都能明白。具体例子方面,可以想到分类问题,比如识别水果。如果模型只学了几个特定的水果的样子,碰到其他新的水果就会搞错,这就是过拟合的表现。这样用户就能明白为什么过拟合是个大问题。还要解释原因和解决方法。过拟合的原因通常是训练数据不够或者模型复杂度太高。解决办法包括正则化、交叉验证、简化模型等,这些在实际操作中都是常用的方法。最后,总结一下过拟合的影响,强调需要找到一个平衡点,让模型既能记住训练内容又具备泛化能力。
</think>过拟合就像一个学生在考试前过度复习,结果记住了所有练习题的解法,但遇到新的题目时却无法运用这些知识正确作答。这种现象可以用生活中的例子来更好地理解。### 生活比喻:
想象一下你正在学习如何识别水果。如果你花了很多时间仔细研究每种水果的样子,甚至记住它们的每一个细节(比如某个苹果的具体颜色、纹路等等),结果当你看到一个新的水果时,无法准确判断它是什么。这就是过拟合的表现:模型在训练数据中表现得太好,但不能很好地应对新的、未知的情况。### 具体示例:
1. **分类问题**:你有一个分类模型,用来识别图片中的物品(比如动物、植物等)。如果你的模型经过过度训练,它可能会非常熟悉已有的图片,甚至记住每个细节。然而,当它面对一张从未见过的新图片时,可能无法正确分类——这就是过拟合。2. **正则化问题**:在一个简单的线性回归模型中,如果模型过于复杂(即参数过多),它可能会完美地拟合训练数据集。然而,这样的模型在预测新的数据时往往表现不佳,因为它对训练数据的噪声和异常值过于敏感——这也是过拟合的一个典型例子。### 原因与解决方法:
- **原因**:过度复杂的模型(比如深度神经网络)或者缺乏足够的训练数据都可能导致过拟合。
- **解决方法**:- 使用正则化技术,限制模型的复杂度。- 增加训练数据量或使用数据增强(data augmentation)来提高模型的泛化能力。- 进行交叉验证以确保模型在不同数据集上表现良好。### 总结:
过拟合的本质是模型在学习过程中过于专注于训练数据,而忽略了数据中的普遍规律。为了避免这种情况,关键在于找到一个平衡点:让模型既能记住训练内容,又具备泛化的能力。
- 核心亮点:通过
from_template
定义参数化模板,from_messages
组合,实现“一套代码支持多领域、多风格”,复用性极强; - 关键逻辑:
from_template
负责“拆”(单角色参数化),from_messages
负责“合”(多角色整合),format_messages
负责“填”(注入参数)。
四、案例3:合规客服系统——场景化约束与风险控制
场景需求:构建一个企业客服助手,需满足严格的合规规则:不透露内部系统名、不提供医疗/金融建议、特定场景(如病情咨询、支付问题)转人工,同时支持动态指定企业名称、用户等级。
核心思路
用ChatPromptTemplate.from_messages
直接定义“系统+用户”的组合模板(跳过单独的from_template
,适合模板逻辑较简单的场景),在system
prompt中明确合规约束,通过参数注入业务信息(企业名、转人工条件、用户等级),确保LLM严格遵守规则。
完整代码与拆解
# 1. 导入依赖:LLM客户端 + 对话模板
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate# 2. 初始化LLM模型(同前)
# model = ChatOpenAI(
# model_name='deepseek-r1:7b', # 本地模型名称,根据实际情况填写
# base_url="http://127.0.0.1:11434/v1", # 本地模型API地址
# api_key="none", # 本地模型通常不需要真实API密钥
# temperature=0.7 # 可根据需要调整温度参数
# )# 3. 步骤1:用from_messages定义合规客服模板(系统角色含约束,用户角色含参数)
compliance_template = ChatPromptTemplate.from_messages([# 系统角色:明确企业、合规规则、转人工条件({company}、{transfer_cond}为参数)("system", """您是{company}的客服助手,必须严格遵守以下规则:
1. 不透露任何内部系统名称(如CRM、订单系统名);
2. 绝对不提供医疗建议、金融投资建议;
3. 遇到{transfer_cond}这类问题,必须回复“已为您转接人工客服,请耐心等待”,不做额外解释;
4. 回复需礼貌,开头需称呼用户等级(如“VIP用户您好”)。"""),# 用户角色:动态指定用户等级({user_level})和提问({query})("human", "[{user_level}用户]:{query}")
])# 4. 步骤2:注入参数生成消息列表(模拟“VIP用户问感冒吃什么药”)
messages = compliance_template.format_messages(company="老王医生", # 企业名称transfer_cond="病情咨询、支付问题", # 转人工条件user_level="VIP", # 用户等级query="感冒应该吃什么药?" # 用户提问(属于“病情咨询”,需转人工)
)# 5. 调用LLM并输出结果
response = model.invoke(messages)
print("合规客服系统响应结果:")
print(response.content)
执行结果与分析
合规客服系统响应结果:
<think>
好,我现在需要处理用户的咨询。用户是VIP级别的,询问感冒应该吃什么药。首先,我要确认用户的需求是否涉及医疗建议。根据规则,我不能提供医疗建议或医疗建议的回复,所以这可能是一个问题。接下来,查看是否有内部系统名称提到。这里并没有提到任何CRM或其他系统,所以我可以继续处理。然后,考虑是否需要转接人工客服。根据规则,遇到健康咨询时,应该转接,而不是给出药物建议。即使用户想知道吃什么药,但直接给出药物可能违反规定,所以可能需要转接。另外,检查是否有其他问题,比如支付问题或订单问题,这显然不在当前范围内。现在决定:按照规则,当涉及到健康咨询时,应转接人工客服,而不提供药物推荐。因此,我应该回复用户,让他们耐心等待,并转接他们到人工客服。最后,确保回复礼貌,开头称呼VIP用户,并符合所有规定。
</think>已为您转接人工客服,请耐心等待。
- 核心亮点:通过
system
prompt的“规则清单”实现强约束,LLM会自动识别用户提问是否命中“转人工条件”(如案例中“感冒吃药”属于“病情咨询”),严格执行合规逻辑; - 关键设计:将“可变业务信息”(企业名、转人工条件)设为参数,无需修改模板即可适配不同企业/场景,降低维护成本。
五、实战总结:核心流程与关键技巧
1.3个实战技巧
- 模板设计:简单场景用
from_messages
直接定义组合模板;复杂场景用from_template
拆单角色模板,再用from_messages
整合; - 合规控制:将规则(如禁止内容、转人工条件)写在
system
prompt中,用“清单式描述”(1.2.3.)让LLM更易理解; - 参数化:把“可变信息”(领域、企业名、用户等级)设为参数,避免硬编码,提升模板复用性。
2. 模型替换建议
案例中用的是deepseek,若想替换为其他模型,只需修改model_name
和base_url
:
- GPT-3.5/4:
model_name="gpt-3.5-turbo"
,base_url="https://api.openai.com/v1"
; - Claude 3:需用
langchain_anthropic
库的ChatAnthropic
类,而非ChatOpenAI
。
关注我,跟着实战学AI大模型不迷路~ 🚀