第 28 期 | 复杂流程编排:深入理解底层数据结构

⏱ 预计阅读 20 分钟 更新于 2026/5/7
💡 进群学习加 wx: agentupdate
(申请发送: agentupdate)

🎯 本期学习目标

嘿,各位未来的 AI 架构师们!我是你们的导师,一个在 AI 领域摸爬滚打了十年的老兵。前面几期,我们让智能客服小助手学会了“听”(接收输入)、“说”(生成回复),甚至能“理解”(通过提示词工程和输出解析器)。但仅仅这样,它还只是个“单细胞生物”,距离一个真正能独立解决问题的“思考者”还有距离。

本期,我们将深入 LangChain 的核心概念之一——Chain(链),让你的智能客服小助手学会像人类一样,分步骤、有逻辑地“思考”。学完本期,你将:

  1. 理解 LangChain Chain 的核心概念与作用:告别单步 LLM 调用,迈向复杂任务的分解与协作。
  2. 掌握 LLMChain 的使用:这是所有链的基础,学会如何将提示词模板与大型语言模型(LLM)有机结合。
  3. 掌握 SimpleSequentialChainSequentialChain:构建多步骤的智能客服思考流程,让小助手学会意图识别、信息检索、回复生成等复杂任务。
  4. 学会如何通过链式编程提升智能客服的逻辑推理和问题解决能力:让小助手不再是简单的“复读机”,而是能主动分析、处理信息的“大脑”。

准备好了吗?让我们一起解锁 AI 的“思考”能力!

📖 原理解析

为什么我们需要“链”?

想象一下,你作为一名资深客服,接到一个客户的咨询:“我的订单都三天了还没发货!你们到底怎么回事?!”。你会怎么处理?

  1. 识别情绪和意图:客户很着急,语气不满,核心诉求是“查询订单发货状态”并希望“尽快解决”。
  2. 获取信息:根据订单号(如果客户提供)或客户信息,查询订单系统的发货记录。
  3. 分析信息:订单确实未发货,原因可能是缺货、物流延迟等。
  4. 生成回复:根据查询结果和客户情绪,生成一个安抚情绪、解释原因并提供解决方案的专业回复。

这个过程,绝不是一个简单的“问答”就能完成的,它是一个多步骤、有逻辑依赖的思考流程

传统的 LLM 调用,就像你问一个问题,它直接给一个答案。如果任务复杂,我们就需要不断地构造新的提示词,把上一步的输出手动作为下一步的输入,这不仅繁琐,而且难以维护。

LangChain 的 Chain 概念,正是为了解决这个问题而生。 它允许我们将复杂的任务分解成一系列更小、更专注的步骤,每个步骤由一个或多个组件(如 LLM、PromptTemplate、OutputParser、Tool 等)构成,并将它们按特定顺序连接起来。上一个步骤的输出,可以作为下一个步骤的输入,从而形成一个有方向性的数据流和逻辑流

LLMChain:链的基础单元

在 LangChain 中,LLMChain 是最基础、也是最常用的链。它封装了一个提示词模板(PromptTemplate一个大型语言模型(LLM。它的作用是:接收输入 -> 格式化为提示词 -> 发送给 LLM -> 获取 LLM 的原始输出。

可以把它看作是智能客服的“一个思考单元”:比如“识别客户情绪”就是一个 LLMChain,它接收客户咨询,然后输出情绪标签。

顺序链:SimpleSequentialChainSequentialChain

当我们需要将多个 LLMChain 或其他类型的链串联起来,形成一个完整的思考流程时,就需要用到顺序链(Sequential Chains)

  1. SimpleSequentialChain (简单顺序链)

    • 特点:最简单粗暴的串联方式。它要求前一个链的单个输出作为后一个链的单个输入
    • 适用场景:当你有一个清晰的、线性的任务流,且每个步骤的输出都直接且完整地用于下一个步骤时。
    • 局限性:不够灵活,无法处理多个输入/输出变量的情况。
  2. SequentialChain (通用顺序链)

    • 特点:更强大、更灵活。它允许你定义整个链的多个输入变量多个输出变量,并且可以指定每个子链的输入和输出。这意味着中间步骤的输出可以被保留,或者作为后续多个链的输入。
    • 适用场景:当你需要一个复杂的思考流程,其中包含分支、多个中间结果需要传递,或者最终需要输出多个信息时。
    • 工作原理:你需要明确指定 input_variables (整个链的输入)、output_variables (整个链的最终输出),以及每个子链的 input_variablesoutput_keyoutput_key 会将子链的输出保存到一个字典中,供后续链使用。

通过 SequentialChain,我们可以为智能客服构建出类似人类的“思考路径”:

graph TD
    A[用户咨询] --> B{第一步: 客户情绪识别 Chain}
    B -- 客户语气 --> C{第二步: 回复草稿生成 Chain}
    C -- 回复草稿 --> D{第三步: 专业回复润色 Chain}
    D -- 最终回复 --> E[智能客服回复]

    style B fill:#f9f,stroke:#333,stroke-width:2px
    style C fill:#ccf,stroke:#333,stroke-width:2px
    style D fill:#cfc,stroke:#333,stroke-width:2px

这张图清晰地展示了,一个用户咨询是如何像流水线一样,经过不同的“思考”环节,最终生成一个连贯且高质量的回复的。每个环节都由一个独立的 LLMChain 负责,它们通过 SequentialChain 串联起来,实现了智能客服的复杂逻辑。

💻 实战代码演练 (客服项目中的具体应用)

好了,原理讲完了,是时候撸起袖子干活了!我们将用 SequentialChain 为我们的智能客服小助手实现一个更高级的回复流程:

场景模拟:当客户咨询时,我们的智能客服需要:

  1. 识别客户语气:判断客户是“紧急”、“中立”还是“不满”。
  2. 生成初步回复草稿:根据客户咨询内容和语气,生成一个礼貌的初步回复。
  3. 润色最终回复:结合客户语气和初步草稿,生成一个更专业、更人性化、更具安抚性的最终回复。

这个流程完美地展示了 SequentialChain 的强大之处,因为它需要将中间结果 (customer_tone, draft_response) 传递给后续的链。

import os
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain, SequentialChain
from langchain.globals import set_debug # 方便调试,可以看到每个链的输入输出

# 设置 OpenAI API Key
# 实际项目中,请从环境变量或安全配置中读取,不要硬编码
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY" # 请替换为你的实际 API Key

# 开启 LangChain 调试模式,可以打印出每个链的详细输入输出,非常有用!
set_debug(True)

print("--- 准备初始化智能客服链 ---")

# 1. 初始化 LLM
# 我们使用 gpt-4o,因为它在理解复杂指令和生成高质量文本方面表现出色。
# temperature 控制生成文本的随机性,0.7 意味着有一定的创造性但不会太离谱。
llm = ChatOpenAI(temperature=0.7, model_name="gpt-4o")

# --- 定义第一个链:客户语气识别 ---
# 目标:从客户的咨询中识别出其语气(紧急、中立、不满)。
# output_key="customer_tone":将此链的输出保存到字典中,键为 "customer_tone"。
prompt_template_tone = PromptTemplate(
    input_variables=["customer_query"],
    template="""你是一个专业的客服语气分析器。请分析以下客户的咨询内容,判断其语气是“紧急”、“中立”还是“不满”。
    仅输出语气标签,不要解释。

    客户咨询: "{customer_query}"
    语气: """
)
tone_chain = LLMChain(llm=llm, prompt=prompt_template_tone, output_key="customer_tone", verbose=True)
print("\n--- '客户语气识别' 链已定义 ---")

# --- 定义第二个链:初步回复草稿生成 ---
# 目标:根据客户咨询和已识别的语气,生成一个初步的、礼貌的回复草稿。
# input_variables=["customer_query", "customer_tone"]:需要前一个链的输出作为输入。
# output_key="draft_response":将此链的输出保存到字典中,键为 "draft_response"。
prompt_template_draft = PromptTemplate(
    input_variables=["customer_query", "customer_tone"],
    template="""你是一个智能客服助手。根据客户的咨询内容和语气,生成一个初步的、礼貌的回复草稿。
    请注意,这只是草稿,后续会进行润色。

    客户咨询: "{customer_query}"
    客户语气: "{customer_tone}"
    回复草稿: """
)
draft_chain = LLMChain(llm=llm, prompt=prompt_template_draft, output_key="draft_response", verbose=True)
print("\n--- '初步回复草稿生成' 链已定义 ---")

# --- 定义第三个链:专业回复润色 ---
# 目标:结合客户语气和初步草稿,生成最专业、最人性化的最终回复。
# input_variables=["customer_query", "customer_tone", "draft_response"]:需要前两个链的输出。
# output_key="final_response":将此链的输出保存到字典中,键为 "final_response"。
prompt_template_refine = PromptTemplate(
    input_variables=["customer_query", "customer_tone", "draft_response"],
    template="""你是一个顶级的客服专家,擅长根据客户情绪和原始回复草稿,生成最专业、最人性化、最能安抚客户的最终回复。

    客户咨询: "{customer_query}"
    客户语气: "{customer_tone}"
    回复草稿: "{draft_response}"

    请你润色上述回复草稿,生成最终的客服回复。如果客户语气是“紧急”或“不满”,请在回复中体现出额外的关注和解决问题的意愿。
    最终回复: """
)
refine_chain = LLMChain(llm=llm, prompt=prompt_template_refine, output_key="final_response", verbose=True)
print("\n--- '专业回复润色' 链已定义 ---")

# --- 组合成 SequentialChain ---
# input_variables: 整个 SequentialChain 接收的初始输入。
# output_variables: 整个 SequentialChain 最终输出的变量。
# (注意:这里可以包含中间结果,方便查看整个思考过程)
# chains: 按照顺序执行的子链列表。
overall_copilot_chain = SequentialChain(
    chains=[tone_chain, draft_chain, refine_chain],
    input_variables=["customer_query"],
    output_variables=["customer_tone", "draft_response", "final_response"], # 可以在这里输出中间结果,方便调试和分析
    verbose=True # 开启整个链的 verbose 模式,可以看到每个子链的执行情况
)
print("\n--- '智能客服总链' 已组合完成 ---")
print("-----------------------------------")

# --- 运行链条进行测试 ---

# 示例 1:客户语气紧急/不满
user_query_1 = "我的订单都三天了还没发货!你们到底怎么回事?!"
print(f"\n--- 正在处理客户咨询: '{user_query_1}' ---")
try:
    result_1 = overall_copilot_chain.invoke({"customer_query": user_query_1})
    print("\n--- 链式处理结果 ---")
    print(f"客户语气: {result_1['customer_tone']}")
    print(f"回复草稿: {result_1['draft_response']}")
    print(f"最终回复: {result_1['final_response']}")
except Exception as e:
    print(f"处理过程中发生错误: {e}")

print("\n-----------------------------------")

# 示例 2:客户语气中立
user_query_2 = "请问我的订单号 XYZ123 的发货状态?"
print(f"\n--- 正在处理客户咨询: '{user_query_2}' ---")
try:
    result_2 = overall_copilot_chain.invoke({"customer_query": user_query_2})
    print("\n--- 链式处理结果 ---")
    print(f"客户语气: {result_2['customer_tone']}")
    print(f"回复草稿: {result_2['draft_response']}")
    print(f"最终回复: {result_2['final_response']}")
except Exception as e:
    print(f"处理过程中发生错误: {e}")

print("\n-----------------------------------")

# --- TypeScript / JavaScript 开发者请看这里 ---
# LangChain.js (TypeScript/JavaScript) 中也有类似的链式概念。
# 你可以使用 @langchain/core 中的 `RunnableSequence` 来构建顺序执行的逻辑。
# 概念上是完全一致的,只是语法略有不同。
# 例如:
# import { ChatOpenAI } from "@langchain/openai";
# import { PromptTemplate } from "@langchain/core/prompts";
# import { RunnableSequence } from "@langchain/core/runnables";
#
# const llm = new ChatOpenAI({ temperature: 0.7, modelName: "gpt-4o" });
#
# const promptTemplateTone = PromptTemplate.fromTemplate(
#   `你是一个专业的客服语气分析器...客户咨询: {customer_query}\n语气:`
# );
#
# const toneChain = RunnableSequence.from([
#   promptTemplateTone,
#   llm,
#   // 可以添加输出解析器等
# ]);
#
# // 多个 RunnableSequence 可以通过 .pipe() 或 RunnableSequence.from([chain1, chain2]) 串联
# // 对于更复杂的输入/输出,可以使用 .assign() 或 RunnableMap
# // 核心思想都是将不同的组件串联起来,形成一个处理流程。
# console.log("--- TypeScript / JavaScript 开发者请参考 RunnableSequence ---");

运行效果预期:

当你运行这段 Python 代码时,set_debug(True)verbose=True 会让你看到大量的日志输出,详细展示了每个链的输入、输出以及 LLM 调用的细节。

对于第一个紧急/不满的咨询,你可能会看到类似这样的输出(具体内容由 LLM 生成):

--- 处理客户咨询: '我的订单都三天了还没发货!你们到底怎么回事?!' ---
[chain/start] [1:chain:overall_copilot_chain] Entering Chain run with input: {
  "customer_query": "我的订单都三天了还没发货!你们到底怎么回事?!"
}
[chain/start] [1:chain:overall_copilot_chain > 2:chain:tone_chain] Entering Chain run with input: {
  "customer_query": "我的订单都三天了还没发货!你们到底怎么回事?!"
}
[llm/start] [1:chain:overall_copilot_chain > 2:chain:tone_chain > 3:llm:llm] Entering LLM run with input: {
  "prompts": [
    "Human: 你是一个专业的客服语气分析器。请分析以下客户的咨询内容,判断其语气是“紧急”、“中立”还是“不满”。\n    仅输出语气标签,不要解释。\n\n    客户咨询: \"我的订单都三天了还没发货!你们到底怎么回事?!\"\n    语气: "
  ]
}
[llm/end] [1:chain:overall_copilot_chain > 2:chain:tone_chain > 3:llm:llm] Exiting LLM run with output: {
  "generations": [
    [
      {
        "text": "不满",
        "generation_info": null,
        "message": {
          "lc_kwargs": {
            "content": "不满",
            "additional_kwargs": {}
          },
          "lc_type": "AIMessage"
        }
      }
    ]
  ],
  "llm_output": {
    "token_usage": {
      "completion_tokens": 2,
      "prompt_tokens": 63,
      "total_tokens": 65
    },
    "model_name": "gpt-4o"
  }
}
[chain/end] [1:chain:overall_copilot_chain > 2:chain:tone_chain] Exiting Chain run with output: {
  "customer_tone": "不满"
}
[chain/start] [1:chain:overall_copilot_chain > 4:chain:draft_chain] Entering Chain run with input: {
  "customer_query": "我的订单都三天了还没发货!你们到底怎么回事?!",
  "customer_tone": "不满"
}
[llm/start] [1:chain:overall_copilot_chain > 4:chain:draft_chain > 5:llm:llm] Entering LLM run with input: {
  "prompts": [
    "Human: 你是一个智能客服助手。根据客户的咨询内容和语气,生成一个初步的、礼貌的回复草稿。\n    请注意,这只是草稿,后续会进行润色。\n\n    客户咨询: \"我的订单都三天了还没发货!你们到底怎么回事?!\"\n    客户语气: \"不满\"\n    回复草稿: "
  ]
}
[llm/end] [1:chain:overall_copilot_chain > 4:chain:draft_chain > 5:llm:llm] Exiting LLM run with output: {
  "generations": [
    [
      {
        "text": "非常抱歉给您带来不便,我们理解您对订单发货延迟感到不满。请您提供订单号,我们立即为您查询具体