吴恩达《LangChain LLM 应用开发精读笔记》4-Chains 链
今天我们学习了 LangChain 的Chains (链)。核心三要点LLMChain是原子:最基本的“Prompt + LLM”单元。是流水线:把简单的任务串起来,实现复杂逻辑。是大脑:根据问题类型,智能选择处理路径。一句话记住它Chain 让 AI 不再只能做单选题(一次问答),而是能做复杂的综合题(多步推理、分工合作)。下一讲,我们将进入一个非常实用的领域 ——Q&A over Docume

大家好,我是飞哥!👋
欢迎来到吴恩达《LangChain LLM 应用开发》系列课程的第四讲。这一讲,我们将学习 LangChain 最重要的构建块 —— Chain (链)。
如果说 Prompt 是给 AI 下达的“单个指令”,那么 Chain 就是把一连串指令串起来的“工作流”。
1. 为什么:从“积木”到“流水线”
Harrison 在视频中提到:
“The chain usually combines an LLM together with a prompt, and with this building block, you can also put a bunch of these building blocks together…”
翻译:这种链条通常会将一个语言模型与一个提示相结合,有了这个基本组件,你还可以将许多这样的基本组件组合在一起……
1.1 场景锚定 ⚓️
想象你在做一道复杂的菜(比如“红烧肉”)。你不能只对厨师喊一句“做红烧肉!”,然后期待成品直接出现。
你得把过程拆解:
- 切肉:把肉切成块。
- 焯水:去掉血水。
- 炒糖色:上色。
- 炖煮:小火慢炖。
之前的 LLM 调用就像是“单步操作”,而 Chain 就是把这些步骤连成一条自动化的“流水线”。
1.2 生动类比 🍊
- LLMChain:就像一个“全能工”,给他一个任务(Prompt),他给你一个结果。
- SequentialChain:就像“工厂流水线”。工人 A 处理完(比如翻译),直接传给工人 B(比如总结),中间不需要你插手。
- RouterChain:就像医院的“分诊台”。护士看你一眼(分析问题),如果是骨折就指引你去骨科,如果是感冒就指引你去内科。
1.3 核心骨架 🦴
所以,Chain (链) 的本质,就是“编排”:
它的工作就是把多个 LLM 调用或者其他操作(如搜索、计算)按顺序串联起来,形成一个完整的业务逻辑。
2. 是什么:LangChain 的核心链类型
视频中主要介绍了三种最常用的链:
-
LLMChain:最基础的原子链。
- 结构:
Prompt+LLM。 - 功能:输入 -> 提示模板 -> 模型 -> 输出。
- 结构:
-
SequentialChain (顺序链):
- 结构:
Chain A->Chain B-> … - 功能:把上一个链的输出,作为下一个链的输入。
- SimpleSequentialChain:单输入 -> 单输出(最简单,像传声筒)。
- SequentialChain:多输入 -> 多输出(更灵活,像复杂的电路图)。
- 结构:
-
RouterChain (路由链):
- 结构:
Input->Router->Chain A或Chain B。 - 功能:智能分流。根据用户的问题类型,动态选择最合适的 Prompt 或 Chain 来处理。
- 结构:
3. 怎么用:实战演练 (DeepSeek 版) 🛠️
我们要用代码来验证一下。为了方便大家通过 DeepSeek 学习,我们对代码做了适配,并将所有 Prompt 翻译成了中文。
3.1 环境准备
请确保你的项目目录中有一个 .env 文件,内容如下(把 Key 换成你自己的):
OPENAI_API_KEY=sk-你的DeepSeek密钥
OPENAI_API_BASE=https://api.deepseek.com
OPENAI_MODEL_NAME=deepseek-chat
3.2 代码实战
(1) LLMChain:最简单的链
给产品起名字。
# 核心代码片段
prompt = ChatPromptTemplate.from_template(
"给一家生产{product}的公司起个最好的名字是什么?"
)
chain = LLMChain(llm=llm, prompt=prompt)
product = "大号床单套装"
print(f"输入产品: {product}")
response = chain.run(product)
print(f"AI 回答: {response}")
🔮 运行结果:
输入产品: 大号床单套装
AI 回答: 为一家生产大号床单套装的公司起名,需要兼顾**舒适感、品质感、尺寸特色和记忆点**。以下是一些精选方向和建议,并附上简要解析,供您参考:
---
### **一、直接突出“大尺寸/奢华感”**
1. **云端漫眠**
(强调柔软如云、宽敞可漫游的睡眠体验)
2. **帝王织造**
(凸显奢华尊享,适合高端定位)
3. **穹庐卧室**
(“穹庐”比喻天空般的覆盖感,诗意且显宽敞)
---
... (省略部分输出) ...
**最后推荐**:
若追求**高端奢华**,可选 **“帝王织造”**;
若侧重**年轻化传播**,可选 **“懒人星系”**;
若想塑造**诗意品牌**,**“月光褶皱”** 会很有辨识度。
希望这些名字能激发您的灵感,祝品牌大卖! 🛏️✨
(2) SimpleSequentialChain:单线流水线
任务:先翻译评论,再总结评论。
# Chain 1: 翻译评论
prompt1 = ChatPromptTemplate.from_template(
"把下面的评论翻译成中文:\n\n{Review}"
)
chain_one = LLMChain(llm=llm, prompt=prompt1)
# Chain 2: 总结评论
prompt2 = ChatPromptTemplate.from_template(
"用一句话总结下面的评论:\n\n{Chinese_Review}"
)
chain_two = LLMChain(llm=llm, prompt=prompt2)
# 串联起来
overall_simple_chain = SimpleSequentialChain(
chains=[chain_one, chain_two],
verbose=True
)
review = "Je trouve le goût médiocre. La mousse ne tient pas, c'est bizarre."
print(f"原始评论 (法文): {review}")
response = overall_simple_chain.run(review)
print(f"最终总结: {response}")
🔮 运行结果:
原始评论 (法文): Je trouve le goût médiocre. La mousse ne tient pas, c'est bizarre.
> Entering new SimpleSequentialChain chain...
我觉得味道一般。泡沫不持久,有点奇怪。
这款啤酒泡沫消散快且口感略显怪异。
> Finished chain.
最终总结: 这款啤酒泡沫消散快且口感略显怪异。
飞哥点评:看到了吗?第二个链直接拿到了第一个链的翻译结果,完全自动!
(3) SequentialChain:多变量流水线
任务升级:翻译 -> 总结 -> 识别语言 -> 自动回复(回复时需要用到总结和语言两个变量)。
# Chain 1: 翻译评论
# input: Review
# output: Chinese_Review
prompt1 = ChatPromptTemplate.from_template(
"把下面的评论翻译成中文:\n\n{Review}"
)
chain_one = LLMChain(llm=llm, prompt=prompt1, output_key="Chinese_Review")
# Chain 2: 总结评论
# input: Chinese_Review
# output: summary
prompt2 = ChatPromptTemplate.from_template(
"用一句话总结下面的评论:\n\n{Chinese_Review}"
)
chain_two = LLMChain(llm=llm, prompt=prompt2, output_key="summary")
# Chain 3: 识别语言
# input: Review
# output: language
prompt3 = ChatPromptTemplate.from_template(
"下面的评论是用什么语言写的:\n\n{Review}"
)
chain_three = LLMChain(llm=llm, prompt=prompt3, output_key="language")
# Chain 4: 自动回复
# input: summary, language
# output: followup_message
prompt4 = ChatPromptTemplate.from_template(
"针对以下总结,用指定的语言写一条后续回复:\n\n总结: {summary}\n\n语言: {language}"
)
chain_four = LLMChain(llm=llm, prompt=prompt4, output_key="followup_message")
# 组合所有链
overall_chain = SequentialChain(
chains=[chain_one, chain_two, chain_three, chain_four],
input_variables=["Review"],
output_variables=["Chinese_Review", "summary", "followup_message"],
verbose=True
)
print(f"原始评论 (法文): {review}")
result = overall_chain(review)
print("\n--- 执行结果 ---")
print(f"中文翻译: {result['Chinese_Review']}")
print(f"总结: {result['summary']}")
print(f"自动回复: {result['followup_message']}")
🔮 运行结果:
原始评论 (法文): Je trouve le goût médiocre. La mousse ne tient pas, c'est bizarre.
> Entering new SequentialChain chain...
> Finished chain.
--- 执行结果 ---
中文翻译: 我觉得味道一般。泡沫不持久,有点奇怪。
总结: 这款啤酒泡沫消散快且口感略显怪异。
自动回复: Je note votre retour, merci. Nous transmettons ces remarques sur la mousse et le goût à notre équipe qualité pour amélioration.
(4) RouterChain:智能分诊台
这是最酷的!我们定义了物理、数学、历史、计算机四位“专家”。
当你问“什么是黑体辐射?”时,Router 会自动把你导向“物理专家”。
# 第一步:定义不同领域的“专家人设” (Prompt Templates)
# 物理专家
physics_template = """你是一位非常聪明的物理学教授。 \
你擅长以简洁易懂的方式解释复杂的物理概念。 \
当你不知道答案时,你会承认不知道。
这里有一个问题:
{input}"""
# 数学专家
math_template = """你是一位非常优秀的数学家。 \
你擅长回答数学问题。 \
之所以这么说,是因为你能够将难题分解成各个组成部分, \
先回答各个部分,然后将它们组合起来回答更广泛的问题。
这里有一个问题:
{input}"""
# 历史专家
history_template = """你是一位非常博学的历史学家。 \
你对从早期到现在的人类历史有着极好的了解。 \
你有能力思考、反思、辩论、讨论和评价过去。 \
你尊重历史证据,能够利用这些证据支持你的解释和判断。
这里有一个问题:
{input}"""
# 计算机专家
computerscience_template = """你是一位成功的计算机科学家。 \
你有以此为业的热情,特别是对应用原理解决问题充满热情。 \
你知道计算机科学是理论、实验和工程的统一体。
这里有一个问题:
{input}"""
# 第二步:将这些专家信息整理成列表
# 这里的 description 非常重要,RouterChain 会根据这个描述来判断问题该交给谁
prompt_infos = [
{
"name": "physics",
"description": "适合回答物理问题",
"prompt_template": physics_template,
},
{
"name": "math",
"description": "适合回答数学问题",
"prompt_template": math_template,
},
{
"name": "history",
"description": "适合回答历史问题",
"prompt_template": history_template,
},
{
"name": "computer_science",
"description": "适合回答计算机科学问题",
"prompt_template": computerscience_template,
},
]
# 第三步:为每个专家创建一个具体的 LLMChain (目标链)
destination_chains = {}
for p_info in prompt_infos:
name = p_info["name"]
prompt_template = p_info["prompt_template"]
# 创建 Prompt 模板对象
prompt = ChatPromptTemplate.from_template(template=prompt_template)
# 创建该专家的链
chain = LLMChain(llm=llm, prompt=prompt)
# 存入字典,key 是专家的名字
destination_chains[name] = chain
# 第四步:定义一个“默认链”
# 如果 AI 觉得问题不属于以上任何一类,就用这个链来回答
default_chain = LLMChain(llm=llm, prompt=ChatPromptTemplate.from_template("{input}"))
# 第五步:构建路由链 (Router Chain)
# 5.1 生成“候选人名单”字符串,格式为 "名字: 描述"
destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)
# 5.2 定义路由模板 (这是 LangChain 内置的一个高级模板)
# 它的作用是告诉 AI:“请分析输入,从下面的候选人名单中选一个最合适的,并以 JSON 格式返回选择结果”
MULTI_PROMPT_ROUTER_TEMPLATE = """ Given a raw text input to a language model select the model prompt best suited for the input. \
You will be given the names of the available prompts and a description of what the prompt is best suited for. \
You may also revise the original input if you think that revising it will ultimately lead to a better response from the language model.
<< FORMATTING >>
Return a markdown code snippet with a JSON object formatted to look like:
```json
{{{{
"destination": string \ name of the prompt to use or "DEFAULT"
"next_inputs": string \ a potentially modified version of the original input
}}}}
```
REMEMBER: "destination" MUST be one of the candidate prompt names specified below OR it can be "DEFAULT" if the input is not well suited for any of the candidate prompts.
REMEMBER: "next_inputs" can just be the original input if you don't think any modifications are needed.
<< CANDIDATE PROMPTS >>
{destinations}
<< INPUT >>
{{input}}
<< OUTPUT (remember to include the ```json) >>"""
# 5.3 填充模板
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
# 5.4 创建 Prompt 对象,并指定 output_parser
# RouterOutputParser 会自动把 AI 返回的 JSON 解析出来,提取出 "destination" (去哪里)
router_prompt = PromptTemplate(
template=router_template,
input_variables=["input"],
output_parser=RouterOutputParser(),
)
# 5.5 创建路由链本身
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
# 第六步:组合最终的 MultiPromptChain
# 它包含三部分:
# 1. router_chain: 大脑,负责分类
# 2. destination_chains: 专家组,负责干活
# 3. default_chain: 兜底方案
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain,
verbose=True, # 开启 verbose 可以看到路由选择的过程
)
# --- 测试环节 ---
# 测试 1: 物理问题 -> 应该路由到 physics
print("\n测试 1: 物理问题")
print(chain.run("什么是黑体辐射?"))
# 测试 2: 数学问题 -> 应该路由到 math
print("\n测试 2: 数学问题")
print(chain.run("2 + 2 等于几?"))
# 测试 3: 生物问题 -> 应该路由到 default (因为没有生物专家)
print("\n测试 3: 生物问题 (应该走默认链)")
print(chain.run("为什么每一种细胞里的 DNA 都是一样的?"))
🔮 运行结果 (物理题):
测试 1: 物理问题
> Entering new MultiPromptChain chain...
physics: {'input': '什么是黑体辐射?'}
> Finished chain.
好的,这是一个很好的问题,也是物理学史上一个极其重要的概念。
让我用简洁易懂的方式来解释:
### 一句话概括
**黑体辐射** 是指一个“**理想化的、不反射任何光**”的物体(称为“黑体”),在被加热时,**只因其自身温度而发出的电磁辐射**。
---
### 核心比喻
想象一个完美的、内部中空的金属球,上面只开了一个极小的孔。
* 任何光(辐射)进入这个小孔后,在球内壁上被无数次反射、吸收,几乎不可能再逃出来——所以这个小孔看起来是“**绝对黑**”的。这就是一个“**理想黑体**”的近似实现。
* 当我们加热这个金属球时,球壁会发出辐射,其中一部分会从小孔中泄漏出来。这时我们测量从小孔出来的辐射,它就是纯粹的 **“黑体辐射”** —— 它**只取决于球内部的温度**,而与球壁是什么材料无关。
---
### 关键特征与重要性
1. **普适性**:黑体辐射的能谱(即不同波长/颜色的光各有多少能量)**只取决于温度**,与物体的材质、形状等无关。温度越高,辐射总能量越大,且颜色从红 -> 黄 -> 白 -> 蓝变化(“**白炽**”现象)。
2. **历史难题与经典物理的失效**:
* 在19世纪末,物理学家用经典的电磁学和热力学理论(瑞利-金斯公式)去计算黑体辐射能谱时,发现在**短波长(紫外线区域)** 会得出荒谬的结论——能量趋于无穷大(即“**紫外灾难**”)。
* 这个理论与实验严重不符,暴露了经典物理学在微观领域的根本缺陷。
3. **量子力学的诞生**:
* 1900年,**马克斯·普朗克**提出了一个“**绝望的**”解决方案。他假设:黑体壁上的振子(发出辐射的源头)**不能以任意大小的能量辐射,而只能以一份份“能量包”的形式辐射**,每份能量的大小为 **E = hν**(其中ν是频率,h是一个新的常数,即**普朗克常数**)。
* 这个“**能量量子化**”的假设完美地拟合了实验数据,并直接催生了**量子理论**。普朗克也因此被称为“**量子力学之父**”。
---
### 总结要点
* **是什么**:理想黑体因热而发出的、仅取决于温度的电磁辐射。
* **为什么重要**:
* 它是**热辐射的基准和标准**。
* 它的研究导致了**量子革命**,是现代物理学的两大支柱之一(另一个是相对论)的起点。
* 它在现实中广泛应用,例如:通过测量恒星或宇宙背景辐射的能谱,我们可以推算出它们的温度(太阳表面约5800K,宇宙微波背景约2.7K)。
所以,黑体辐射远不止是一个热学概念,它是一把“**打开量子世界大门的钥匙**”。
🔮 运行结果 (数学题):
测试 2: 数学问题
> Entering new MultiPromptChain chain...
math: {'input': '2 + 2 等于几?'}
> Finished chain.
好的,我们一步步来推理。
**步骤 1:理解问题**
问题是:\( 2 + 2 \) 等于多少?
这是一个基本的整数加法运算。
... (省略推理过程) ...
**最终答案**:\(\boxed{4}\)
🔮 运行结果 (生物题 - 走默认):
测试 3: 生物问题 (应该走默认链)
> Entering new MultiPromptChain chain...
None: {'input': '为什么每一种细胞里的 DNA 都是一样的?'}
> Finished chain.
这是一个非常好的问题,也是一个常见的误解。实际上,**并不是每一种细胞里的DNA都是一样的**。
### 1. 核心原则:基因组的一致性(“一样”的部分)
在生物发育的早期,一个受精卵通过无数次细胞分裂,形成身体的所有细胞。在这个过程中,DNA会进行精确的复制和分配。因此,从皮肤细胞、肝细胞到神经元,它们的细胞核里都携带了构建整个生物体所需的全部遗传指令(约2万-2.5万个基因)。这就是“**一样**”说法的来源。
### 2. 关键差异:基因表达的不同(“不一样”的解读)
虽然DNA序列基本相同,但不同细胞**只使用其中一小部分基因**。这就是细胞分化的本质。
* **肝细胞**:开启制造消化酶和解毒蛋白的基因,关闭其他无关基因。
* **红细胞**:在成熟过程中甚至会将细胞核(包含DNA)完全排出,只保留血红蛋白来运输氧气。
* **神经元**:开启与电信号传导和神经递质相关的基因。
* **免疫细胞(B细胞和T细胞)**:为了识别无数种病原体,它们的DNA会发生**特异性的、程序性的重排**,以产生多样化的抗体和受体。这是最重要的例外之一。
**可以把DNA想象成一本完整的、厚厚的菜谱(基因组)。**
* 每个细胞都拥有同一本完整的菜谱。
* 但**肝细胞**只照着“**川菜**”部分做菜,并把其他章节(如“甜点”、“面点”)用书签合上或锁起来。
* **肌肉细胞**则只照着“**力量增肌餐**”部分做菜。
* 虽然书的内容一样,但每个细胞阅读和执行的章节完全不同,因此最终的产品(细胞形态和功能)也截然不同。
### 3. 重要的例外和修饰(确实“不一样”的部分)
除了基因选择性表达,DNA本身在特定情况下也会发生实质性的改变:
1. **免疫细胞的基因重排**:如前所述,这是为了产生免疫多样性,是DNA序列本身的改变。
2. **生殖细胞(精子和卵子)的减数分裂**:它们的DNA经过重组和减半,只包含一半的基因组,与体细胞完全不同。
3. **细胞衰老、癌变或受辐射时**:会发生DNA突变、损伤或染色体变异,导致序列改变。
4. **表观遗传修饰**:这是在不改变DNA序列的前提下,通过给DNA“加标签”(如甲基化)或改变包裹DNA的组蛋白,来长期、稳定地控制基因的“开启”或“关闭”。这些修饰在细胞分裂时有时可以遗传给子细胞,是维持细胞身份记忆的关键。例如,一个皮肤细胞分裂后,两个子细胞都知道自己是皮肤细胞,这主要靠表观遗传记忆。
### 总结
| 层面 | 是否“一样”? | 说明 |
| :--- | :--- | :--- |
| **DNA序列(基因组)** | **基本一样** | 除免疫细胞、生殖细胞等特例外,体细胞拥有相同的全套DNA蓝图。 |
| **基因表达(使用哪些基因)** | **完全不一样** | 细胞分化的核心。不同细胞开启不同的基因组合,执行特定功能。 |
| **表观遗传修饰(基因开关标记)** | **很不一样** | 决定并维持细胞类型的关键“记忆”系统。 |
| **DNA的物理状态** | **不一样** | 有些细胞(如成熟红细胞)没有细胞核;染色体在分裂期和间期的折叠方式也不同。 |
所以,更科学的结论是:**多细胞生物通过一套精密的调控系统,让拥有相同DNA蓝图的细胞,通过差异化的基因表达和表观遗传修饰,分化成功能各异的细胞类型,从而构建出复杂的生命体。** “DNA一样”是基础,但“**如何使用DNA**”才是生命多样性和复杂性的精髓所在。
4. 飞哥总结 📝
今天我们学习了 LangChain 的 Chains (链)。
核心三要点:
- LLMChain 是原子:最基本的“Prompt + LLM”单元。
- SequentialChain 是流水线:把简单的任务串起来,实现复杂逻辑。
- RouterChain 是大脑:根据问题类型,智能选择处理路径。
一句话记住它:
Chain 让 AI 不再只能做单选题(一次问答),而是能做复杂的综合题(多步推理、分工合作)。
下一讲,我们将进入一个非常实用的领域 —— Q&A over Documents (文档问答/RAG),看怎么让 AI 学会查阅你的私有文档!🚀
更多推荐

所有评论(0)