)
《LangChain RAG 全链路学习笔记》系列 · 第 2 篇共 13 篇作者AVA环境Python 3.11 LangChain 1.x⚠️看之前先注意一件事网上大量 LangChain 教程还是 0.3.xfrom langchain.schema import HumanMessage这种写法照抄会直接ModuleNotFoundError: No module named langchain。本系列代码全部在 1.x 上实测跑通新旧路径都标了。这一章解决什么问题第 1 章解决的是「能连上」。这一章解决的是**「输入输出到底该是什么形状」**。问题来自一个很具体的困惑LangChain 里有个OpenAI还有个ChatOpenAI名字只差一个词都能调模型 —— 那到底该用哪个有什么区别答案是它们代表两代不同的模型抽象输入输出的形状完全不一样。搞混了就是一串看起来毫无道理的报错。这一章的两个知识点LLM vs ChatModel、三种消息角色是后面所有章节的地基—— 第 3 章提示词模板、第 7 章记忆、第 13 章工具全都建立在这三个消息类上面。地基没打牢后面每一步都会晃。核心结论一LangChain 有两套模型抽象LLM老式ChatModel新式代表类OpenAIChatOpenAI父类BaseLLMBaseChatModel输入一个字符串消息列表list[BaseMessage]输出strAIMessage取文本直接就是文本要.content现状保留兼容新项目别用主流一律用这个一句话判断标准看它爹是不是BaseChatModel。实操建议真接模型一律用ChatOpenAI。老式的OpenAI走的是/completions老端点用它调新模型经常直接 404 —— 知道有这么回事就行别用。核心结论二三种消息角色类名准确拼写代表谁用途SystemMessage系统人设、规则、总体目标。习惯上放列表第一个HumanMessage用户用户说的话AIMessage模型模型说的话也可以由你「伪造」塞进历史这三个类从第 2 章一直用到第 13 章是整门课出现频率最高的东西。核心结论三模型本身没有记忆 ⭐这是这一章最容易误解的一点看这段代码messages[SystemMessage(content你是知识渊博的专家请简洁的用20个字回答问题),HumanMessage(content我的身份是学员名字叫小顾),AIMessage(content欢迎有什么需要咨询的?),# ← 人肉写死的HumanMessage(content三国志作者的是谁),AIMessage(content《三国志》的作者是三国时期的历史学家陈寿。),# ← 也是人肉写死的HumanMessage(content红楼梦呢),]responsemodel.invoke(messages)看起来模型「记得」前面聊过什么但那些历史全是我在代码里手写的。模型每次都是无状态的。你必须在每次invoke时把整段历史重新发一遍。这就是第 7 章要学 Memory 的原因 —— 手动拼历史太蠢了得有人帮你管。最小可运行代码fromlangchain_core.messagesimportAIMessage,HumanMessage,SystemMessage# ⭐ 注意路径fromlangchain_openaiimportChatOpenAIfromdotenvimportload_dotenv load_dotenv()modelChatOpenAI(modeldeepseek-chat,base_urlhttps://api.deepseek.com/v1,temperature0.1,)messages[SystemMessage(content你是一个知识渊博的助手请简洁回答20个字内),HumanMessage(content请用一句话解释量子力学),]responsemodel.invoke(messages)print(type(response))# class langchain_core.messages.ai.AIMessageprint(response.content)# 要 .content 才是正文⚠️ 我踩到的坑1.from langchain.messages import ...直接跑不了课程源码里02-多轮对话.py和04-chat.py都用了这个路径在你本机是当场ModuleNotFoundError。因为本机没装裸langchain包。正确路径是fromlangchain_core.messagesimportHumanMessage# ✅ 1.x 的正解2. LLM 和 ChatModel 的返回值不能混用OpenAI(...).invoke(你好)# → str写 .content 会 AttributeErrorChatOpenAI(...).invoke(...)# → AIMessage必须 .content方向相反的两个坑老式的加.content报错新式的不加.content打出一坨。3. 消息列表里不能混纯字符串messages[你好,HumanMessage(content再见)]# ❌会抛AttributeError: str object has no attribute content。要么整体传一个str要么整个列表都是消息对象。不能一半一半。4.SystemMessage别乱放位置惯例也是多数厂商的要求是放第一个。放中间行为不可控 —— 有的模型直接忽略有的会报错。5.base_url的变量名写错报错却指向别处课程源码里有一句base_urlos.getenv(OPENAI_BASE_URL)但.env里的变量名其实是OPENAI_API_BASE—— 名字对不上。更阴的是老式OpenAI和ChatOpenAI对base_urlNone的处理不一样一个会回落到默认地址一个会回落到环境变量。所以你看到的报错可能完全指不到真正的原因。要么不传要么把变量名写对。⚠️ 1.x 和 0.3.x 的导入路径差异这条是本篇相对网上教程最大的不同# 网上 0.3.x 教程fromlangchain.schemaimportHumanMessage# 本机 1.x 实际fromlangchain_core.messagesimportHumanMessage# ⭐照抄老教程 当场ModuleNotFoundError。第 1 章和这一章都栽在同一个坑上后面还会反复出现。补充 ①我为什么一开始分不清OpenAI和ChatOpenAI我第一反应是右边那个是在左边的基础上「升级」来的右边的应该包含了左边的能力。真正区分它们的是返回值—— 左边返回的就是一段字符串文本右边相当于把大模型内部的一堆东西都返回了正文、模型参数、用量统计什么的全在里面。补充 ②我在这章实际遇到的报错其实报错倒是不咋多主要就是打印的时候忘了加.content结果打出了一坨 —— 它不报错直接把一堆字段全打出来了。补充 ③关于「模型没有记忆」我原本以为……我当时以为模型会自己带着一块缓存里面装着上几轮的对话。现在看来它就是一个「干净」的模型 —— 每次调用都是全新的什么都不记得。✅ 自测三问OpenAI和ChatOpenAI的本质区别是什么各自invoke返回什么区别就是返回值第一个返回str第二个返回AIMessage。根子上是输入输出的形状不一样 —— 一个字符串进字符串出一个消息列表进消息对象出。返回值只是这个差别的外在表现。0.3.x 的from langchain.schema import HumanMessage在 1.x 有哪几种正确写法你本机哪种能跑from langchain_core.messages import HumanMessage——本机能跑的只有这一种。langchain.schema0.3.x 老写法和langchain.messages课程源码里的写法在本机都是ModuleNotFoundError见上面「坑 1」。02-多轮对话.py是怎么实现「记得上一句」的这种做法有什么局限就是把每一次的对话都重新喂给大模型一遍。局限是历史一长 token 成本就上去了而且手动拼历史容易出错、维护也麻烦。本系列共 13 篇下一篇03 提示词模板 —— 把 prompt 从字符串变成对象笔记同步更新在 GitHubgithub.com/MaDai66/langchain-rag-notes代码基于 LangChain 1.x 实测有问题欢迎评论区交流。