大家好,我是飞哥!👋

欢迎来到吴恩达《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 场景锚定 ⚓️

想象你在做一道复杂的菜(比如“红烧肉”)。你不能只对厨师喊一句“做红烧肉!”,然后期待成品直接出现。
你得把过程拆解:

  1. 切肉:把肉切成块。
  2. 焯水:去掉血水。
  3. 炒糖色:上色。
  4. 炖煮:小火慢炖。

之前的 LLM 调用就像是“单步操作”,而 Chain 就是把这些步骤连成一条自动化的“流水线”。

1.2 生动类比 🍊

  • LLMChain:就像一个“全能工”,给他一个任务(Prompt),他给你一个结果。
  • SequentialChain:就像“工厂流水线”。工人 A 处理完(比如翻译),直接传给工人 B(比如总结),中间不需要你插手。
  • RouterChain:就像医院的“分诊台”。护士看你一眼(分析问题),如果是骨折就指引你去骨科,如果是感冒就指引你去内科。

1.3 核心骨架 🦴

所以,Chain (链) 的本质,就是“编排”:

它的工作就是把多个 LLM 调用或者其他操作(如搜索、计算)按顺序串联起来,形成一个完整的业务逻辑。

用户输入

Chain 步骤1: 翻译

Chain 步骤2: 总结

Chain 步骤3: 自动回复

最终输出


2. 是什么:LangChain 的核心链类型

视频中主要介绍了三种最常用的链:

  1. LLMChain:最基础的原子链。

    • 结构:Prompt + LLM
    • 功能:输入 -> 提示模板 -> 模型 -> 输出。
  2. SequentialChain (顺序链)

    • 结构:Chain A -> Chain B -> …
    • 功能:把上一个链的输出,作为下一个链的输入。
    • SimpleSequentialChain:单输入 -> 单输出(最简单,像传声筒)。
    • SequentialChain:多输入 -> 多输出(更灵活,像复杂的电路图)。

    Output1

    Output2

    Input

    Chain1

    Chain2

    Output

  3. RouterChain (路由链)

    • 结构:Input -> Router -> Chain AChain B
    • 功能:智能分流。根据用户的问题类型,动态选择最合适的 Prompt 或 Chain 来处理。

    物理题

    数学题

    其他

    用户问题

    Router 大脑

    物理专家

    数学专家

    默认助手


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 (链)

核心三要点

  1. LLMChain 是原子:最基本的“Prompt + LLM”单元。
  2. SequentialChain 是流水线:把简单的任务串起来,实现复杂逻辑。
  3. RouterChain 是大脑:根据问题类型,智能选择处理路径。

一句话记住它

Chain 让 AI 不再只能做单选题(一次问答),而是能做复杂的综合题(多步推理、分工合作)。

下一讲,我们将进入一个非常实用的领域 —— Q&A over Documents (文档问答/RAG),看怎么让 AI 学会查阅你的私有文档!🚀

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐