当前位置: 首页 > news >正文

LangChain 实战第 2 章:搞懂消息结构,写出会“记住对话“的客服助手

LangChain 实战第 2 章:搞懂消息结构,写出会"记住对话"的客服助手

📌 这是系列第 2 篇。上一篇我们已经搭好 Python 环境、配好 DeepSeek、跑通了第一次模型调用。这一篇往上走一步:学会用消息结构和模型聊天,并做出一个能连续对话的命令行客服。

一、本章目标

学完这一章,你应该能够:

  • 理解模型调用里role(角色)是什么
  • system消息控制"助手是谁"
  • human消息把用户问题传给模型
  • ai回复存起来,形成多轮对话
  • 设置最常用的模型参数temperature
  • 亲手实现一个带上下文的命令行客服助手

二、为什么不能只传字符串?

上一章我们是这样调用的:

response = model.invoke("请用一句话介绍 LangChain 的作用")

这样能跑,但在真实项目里不够灵活。因为实际场景我们经常要同时告诉模型:

  • 你是什么角色(客服?讲师?翻译?)
  • 回答要遵守什么规则(语气、长度、格式)
  • 用户现在问了什么
  • 前面已经聊过什么

把这些信息全塞进一个字符串里,既难维护又容易出错。更好的办法,是把它们拆成结构化的消息传给模型。

三、三种消息角色:system / human / ai

大模型对话里有三类最常出现的消息:

角色作用
system设置模型的角色、规则和回答风格
human用户的输入
ai模型的回复
  • SystemMessage(系统消息):开发者设定——AI 是谁、能干啥、不能干啥、用什么语气、输出什么格式。
  • HumanMessage(用户消息):普通人的提问、需求、问题。
  • AIMessage(AI 回复):大模型返回的内容。

在 LangChain 里,这三个类是这样导入的:

from langchain_core.messages import AIMessage, HumanMessage, SystemMessage

拼一组消息也很直观:

messages = [ SystemMessage(content="你是一名客服助手,回答要礼貌、简洁。"), HumanMessage(content="我的订单什么时候发货?"), ]

💡 记住这个组合:system 定规则,human 提问题,ai 记回答。后面所有对话都建立在它之上。

四、SystemMessage:给 AI 定身份和规矩

SystemMessage相当于在对话最开头,给 AI 立好"永久人设":

  • 在一轮对话最前面发给模型
  • 优先级高于用户的所有提问HumanMessage
  • 整轮对话全程生效

它对应 OpenAI 接口里的role: "system"。常见用途有:

  • 限定角色(你是电商客服 / Python 讲师)
  • 限定语气(礼貌、专业、口语化)
  • 限定回答长度
  • 限定输出格式(JSON、分点、不超过 N 字)
  • 限定不能回答的范围

示例:

SystemMessage(content="你是一名 Python 讲师,回答要适合初学者。")

⚠️ 新手建议:第一阶段的 system 规则别写太长太复杂。规则一多,模型反而可能执行不稳定。先把"角色 + 一句要求"写清楚就够用了。

五、HumanMessage:把用户的话传进去

HumanMessage表示用户输入,也就是"用户说了什么"。

HumanMessage(content="我想查询订单物流")

真实项目里,用户输入通常来自这些地方:

  • 命令行输入
  • Web 页面的输入框
  • App 的聊天框
  • 后端 API 的请求参数

本章先用命令行输入来演示,最容易看懂原理。

六、AIMessage:模型的回复,也是下一轮的"记忆"

AIMessage表示模型回复。它不只是"拿到答案就完事"——在多轮对话里,必须把模型回复也加进历史消息,否则模型根本不知道之前聊过什么。

messages.append(AIMessage(content=response.content))

加上这一行后,下一次调用模型时,它就能"看到"自己上一次的回答,对话才连得起来。

七、案例一:一句话定好客服人设

system消息控制模型角色。创建01_customer_service.py

import os from dotenv import load_dotenv from langchain.chat_models import init_chat_model from langchain_core.messages import HumanMessage, SystemMessage load_dotenv() model = init_chat_model( model="deepseek-v4-flash", model_provider="openai", api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL"), temperature=0.5, ) messages = [ SystemMessage( content=( "你是一名电商客服助手。" "回答要礼貌、简洁。" "如果用户没有提供订单号,就提醒用户提供订单号。" ) ), HumanMessage(content="我的快递怎么还没到?"), ] response = model.invoke(messages) print(response.content)

运行:

python 01_customer_service.py

重点观察两点:模型是否用了客服语气?是否提醒你提供订单号?

八、模型参数 temperature:控制"稳重"还是"脑洞"

temperature是最常用、也最该先搞懂的模型参数。它控制输出的随机性、创造性、幻觉程度,取值范围0 ~ 2

  • 越接近0:输出固定、严谨、重复度高,几乎不瞎编;
  • 越接近2:脑洞大、自由发挥,容易编造不存在的内容。
取值区间特点适合场景
= 0完全确定性,同输入同输出代码生成、RAG 知识库问答、数据提取、订单查询、数学计算
0.1 ~ 0.4轻微随机,逻辑稳、极少幻觉客服问答、RAG、文档解析、结构化提取、规范代码
0.5 ~ 0.8创意与逻辑平衡文案、总结、普通聊天
≥ 1.0高创造性,易虚构写故事、诗歌、创意文案

设置方式就是把temperature传给init_chat_model

model = init_chat_model( model="deepseek-v4-flash", model_provider="openai", api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL"), temperature=0.3, )

💡 经验值:问答 / 客服 / 知识库temperature调低(0.1~0.4);文案 / 创意 / 改写可以调高(0.7~1.0)。第一阶段先把temperature用熟就够了,别一上来追一堆参数。

九、案例二:能连续对话的命令行客服

上面案例每次都只问一句话。真实客服要能"接着聊"。核心思路:把每一轮的用户提问和模型回复都存进messages列表

创建02_chat_with_history.py

import os from dotenv import load_dotenv from langchain.chat_models import init_chat_model from langchain_core.messages import SystemMessage, HumanMessage, AIMessage load_dotenv() model = init_chat_model( base_url=os.getenv("DEEPSEEK_BASE_URL"), api_key=os.getenv("DEEPSEEK_API_KEY"), model="deepseek-v4-flash", temperature=0.5, model_provider="openai" ) messages = [ SystemMessage(content="你是一个快递客服,回答问题要有礼貌,假如对方没有提供单号,请让其先提供快递单号") ] while True: question = input("用户:") if question == "exit": break messages.append(HumanMessage(content=question)) response = model.invoke(messages) print("客服的回答:" + response.content) messages.append(AIMessage(content=response.content))

运行:

python 02_chat_with_history.py

可以这样测试:

用户:我要查物流 用户:订单号是 A10086 用户:那我可以申请退款吗?

因为程序把历史消息都保留了,模型能看到前面的对话,所以第三问它才知道你在说"刚才那个订单"。

十、多轮对话的坑:历史不能无限堆

保存历史消息很有用,但不能无限制地一直存。原因很现实:

  • 消息越多,每次请求花的钱越多
  • 上下文太长,模型处理速度变慢
  • 超过模型上下文上限会直接报错
  • 太早的消息可能干扰当前回答

本章先用"保存全部历史"这种最简单的方式。后面会专门讲怎么控制上下文长度。

十一、案例三:只保留最近几轮

为了避免历史无限增长,可以只保留最近几条消息。创建03_chat_with_limited_history.py

import os from dotenv import load_dotenv from langchain.chat_models import init_chat_model from langchain_core.messages import SystemMessage, HumanMessage, AIMessage load_dotenv() model = init_chat_model( base_url=os.getenv("DEEPSEEK_BASE_URL"), api_key=os.getenv("DEEPSEEK_API_KEY"), model="deepseek-v4-flash", temperature=0.5, model_provider="openai" ) system = SystemMessage(content="你是一个快递客服,回答问题要有礼貌,假如对方没有提供单号,请让其先提供快递单号") history = [] while True: question = input("用户:") if question.lower() == "exit": break history.append(HumanMessage(content=question)) print("history 的长度是:" + str(len(history))) messages = [system] + history[-6:] response = model.invoke(messages) print("客服的回答:" + response.content) history.append(AIMessage(content=response.content))

关键在这一行:

messages = [system] + history[-6:]

history[-6:]表示只取最后 6 条历史消息。因为一轮对话通常包含"用户消息 + AI 回复"两条,所以 6 条 ≈ 最近 3 轮对话。

📘 顺手复习一下 list 切片(后面会经常用):

# lst[start:end:step],索引从 0 开始 a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(a[:4]) # [0, 1, 2, 3] print(a[3:]) # [3, 4, 5, 6, 7, 8, 9] print(a[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 倒序 print(a[::2]) # [0, 2, 4, 6, 8] 隔一个取 print(a[-1]) # 9 最后一位 print(a[-6:]) # [4, 5, 6, 7, 8, 9] 最后六个

十二、进阶预告:用模板更优雅地插历史

本章我们是手动拼消息的:

messages = [system_message] + history[-6:]

这种写法直观,适合先理解原理。等学到ChatPromptTemplate,可以用MessagesPlaceholder在模板里预留历史消息的位置,更省心:

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder prompt_template = ChatPromptTemplate.from_messages( [ ("system", "你是一名电商客服助手,回答要礼貌、简洁。"), MessagesPlaceholder(variable_name="history"), ("human", "{question}"), ] )

其中:

  • history用来接收以前的HumanMessageAIMessage
  • question表示用户当前的问题

本章只要知道"有这种更方便的写法"就行,完整用法下一章讲 Prompt 模板时展开。

十三、本章重点(速记)

  • 模型调用既可以传字符串,也可以传消息列表
  • SystemMessage设置角色和规则
  • HumanMessage表示用户输入
  • AIMessage表示模型回复
  • 多轮对话必须保存历史消息
  • 历史消息不能无限增长(用history[-6:]截断)
  • temperature影响回答的稳定性和创造性

十四、常见问题

Q1:为什么模型没严格按 system 规则回答?大模型不是传统if/else程序。system 规则能提高"按要求回答"的概率,但不保证 100% 执行。如果要求很重要,后续可以配合:更清晰的 Prompt、结构化输出、程序校验、后处理逻辑来兜底。

Q2:为什么多轮对话越来越慢?因为每次请求都把历史消息一起发给了模型。历史越长,请求内容越多、越慢。解决办法就是限制历史数量,例如history[-6:]

http://www.cnnetsun.cn/news/3594227.html

相关文章:

  • 没有完美的系统:辩证法视角下的计算机架构演进与实践论
  • 粉笔刷题App 与华图在线题库对比:客观评测与选型参考
  • Grok AI助手:从代码审查到架构设计的全方位开发效率提升指南
  • Qwen3.8模型代码生成与长文档处理技术解析
  • 计算机毕业设计之基于SpringBoot的社区论坛系统的设计与实现
  • Spring Boot 3 + Vue 3 + MySQL 动漫推荐管理系统源码前后端分离实战
  • 编程小计之准备入门
  • AI工具接入项目管理流程的3道生死线,86%团队在第2步就触发合规熔断机制
  • RISC-V系统调用机制与MenuOS移植实践
  • 我为什么做了一个邮件群发软件?17年开发经验总结(附Windows客户端)
  • 机器人项目最怕的不是改动,是改了以后没人知道
  • C语言基础篇(7):数组进阶——排序、查找与字符数组
  • Stellaris UART ROM API实战:从基础配置到DMA与9位通信优化
  • 国产轮胎性能实测:静音、耐磨与安全全解析
  • Markdown与Mermaid实现技术项目计划文档的版本控制与可视化
  • muduo网络库(六):Poller类与IO复用
  • PCB贴片打样服务解析:快速打样如何缩短电子产品研发周期?
  • Codex 遇到 CI 构建失败怎么办?从日志定位到最小修复的完整流程
  • 现代C++:内存模型和atomic:理解并发的复杂性
  • C#委托、事件和lambda表达式
  • 真激动,千问新人优惠券大放送!激活码:千问新人福利yPBm3m
  • 如果f(3x+2)是奇函数,求f(x)的对称中心
  • ESP32网络电台DIY:低成本构建物联网音频系统
  • 2026年AI大模型岗位趋势与核心技术解析
  • Loop Engineering 学习笔记:从手写 Prompt 到设计循环
  • 多语言句子嵌入与可靠性审计:技术原理与部署实践
  • 深入解析TI MibSPI并行模式与多缓冲机制:高速嵌入式通信实战
  • 从双雄到三强:Kimi K3暂停注册、DeepSeek V4满血回归,中国AI正在改写全球游戏规则
  • Qwen 3.8大模型本地部署与交互应用开发实战指南
  • 【深度】别再神话 Skill 了——一个完整 Skill 到底由什么组成,为什么多数 Skill 跑不起来