LangChain 学习笔记 01:从一次聊天调用看懂组件与组合
第一次写聊天程序时,代码很容易给人一种“已经完成了”的错觉:传入一句话,模型返回一段话,终端里也确实打印出了答案。
但只要继续加两个需求,比如“回答必须保持固定格式”和“把每次调用记录下来”,原来那几行代码就会迅速膨胀。第 2 章从第一个聊天机器人讲起,我觉得它真正想建立的不是某个 API 的记忆,而是一种最小心智模型:数据怎样进入组件,又怎样从一个组件流向下一个组件。
版本说明:本书采用较早期 LangChain 写法。下面的示例重点展示组合思路,安装包、模型初始化和具体方法请以当前官方文档为准。
先看最小流程
一个聊天应用至少包含四个动作:读取配置、创建模型、组织消息、处理返回值。
配置 -> 消息 -> 模型 -> 返回结果
看起来很短,但每个箭头后面都有工程问题:密钥放在哪里?系统指令和用户问题是否分开?请求超时怎么办?返回空内容怎么办?这次回答用了多少 token?
所以,第一个 Demo 的价值不是证明模型“会聊天”,而是让这些边界第一次显形。
为什么不直接拼一个大字符串
聊天模型接收的是带角色的消息,而不是一段没有来源的长文本。系统消息描述助手的行为边界,用户消息表达本轮需求,历史消息提供上下文,工具消息则记录外部调用结果。
如果把它们全部拼成一个字符串,我们很难回答两个问题:哪一段内容可以信任?哪一段指令拥有更高优先级?
例如做知识库问答时,检索到的文档里可能刚好出现“忽略之前要求”之类的文字。它应该只是资料内容,不能被当作系统命令。保留消息角色和数据来源,既方便调试,也是安全边界的一部分。
“组件”解决职责混在一起的问题
书中反复出现模型、提示词模板、输出解析器和检索器。它们可以先这样理解:
- Prompt 负责把业务输入整理成模型能读懂的消息;
- Model 负责生成或推理;
- Output Parser 负责把模型结果变成程序可使用的数据;
- Retriever 负责根据问题寻找外部资料。
每个组件只承担一段职责。更换模型时,提示词和业务输入不必全部推倒;输出从普通文本改成结构化对象时,检索逻辑也不需要跟着重写。
“组合”让流程可以读出来
LangChain 表达式常用管道方式描述流程。省略初始化细节后,一个最小示例大致是:
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名耐心的 Python 助教。"),
("human", "请用一个例子解释:{topic}"),
])
chain = prompt | model | StrOutputParser()
answer = chain.invoke({"topic": "生成器"})
这段代码好读的地方,不是竖线少写了几行,而是数据方向很明确:字典进入模板,模板生成消息,模型返回消息,解析器提取最终文本。
流程被组合以后,还可以统一使用调用、批处理、流式输出和追踪等能力。不过我不会把所有业务逻辑都塞进管道。权限检查、订单状态、数据库事务和人工审批,仍然适合写成显式代码。
我会给第一个应用补哪些东西
如果准备把 Demo 往真实项目推进,我会按下面的顺序补齐:
- 配置集中管理:密钥不写进源码,模型名、超时和输出长度也不要散落在各个函数里。
- 输入有边界:限制长度,区分系统指令、用户输入和外部资料。
- 失败可预期:分别处理网络错误、限流、超时、内容过滤和空结果。
- 输出可使用:需要 JSON 或固定字段时,必须解析并校验,不能只靠提示词保证格式。
- 调用可追踪:记录模型、提示词版本、耗时和 token,至少能复盘一次异常回答。
这章给我的启发
刚开始学 LangChain,很容易把注意力放在“这个类叫什么”“竖线怎样写”。但第一个应用更值得检查的是:输入是否清楚,组件职责是否单一,输出能否被下一步稳定接住。
聊天机器人只是一个很小的入口。这个入口搭稳以后,后面接检索、记忆和 Agent 时,复杂度才不会一下子失去控制。

浙公网安备 33010602011771号