零基础学AI大模型之LangChain聊天模型多案例实战
前情摘要:
零基础学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。
版权及免责申明:本文来源于#chandfy,由@人工智能研究所整理发布。如若内容造成侵权/违法违规/事实不符,请联系本站客服处理!该文章观点仅代表作者本人,不代表本站立场。本站不承担相关法律责任。
如若转载,请注明出处:https://www.aipuzi.cn/ai-tutorial/252.html