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 往真实项目推进,我会按下面的顺序补齐:

  1. 配置集中管理:密钥不写进源码,模型名、超时和输出长度也不要散落在各个函数里。
  2. 输入有边界:限制长度,区分系统指令、用户输入和外部资料。
  3. 失败可预期:分别处理网络错误、限流、超时、内容过滤和空结果。
  4. 输出可使用:需要 JSON 或固定字段时,必须解析并校验,不能只靠提示词保证格式。
  5. 调用可追踪:记录模型、提示词版本、耗时和 token,至少能复盘一次异常回答。

这章给我的启发

刚开始学 LangChain,很容易把注意力放在“这个类叫什么”“竖线怎样写”。但第一个应用更值得检查的是:输入是否清楚,组件职责是否单一,输出能否被下一步稳定接住。

聊天机器人只是一个很小的入口。这个入口搭稳以后,后面接检索、记忆和 Agent 时,复杂度才不会一下子失去控制。

posted @ 2026-07-29 17:47  Hazy_star  阅读(4)  评论(0)    收藏  举报