第3章:链(Chains)基础
本章将带你深入了解 LangChain 最核心的概念——链(Chain)。学会使用链,你就能像搭积木一样组合各种组件,构建出强大的 AI 应用。
3.1 什么是 Chain
Chain(链)是 LangChain 框架的核心概念,它代表了一系列组件的有序组合,形成一个处理数据的管道(Pipeline)。
形象理解:想象 Chain 就像工厂的流水线,原材料(输入)经过一道道工序(组件处理),最终变成成品(输出)。
Chain 的基本结构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 输入数据 │───▶│ 组件处理1 │───▶│ 组件处理2 │───▶│ 输出结果 │
│ (Input) │ │ (Component) │ │ (Component) │ │ (Output) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
一个最简单的 Chain 通常包含:
- 输入(Input):用户提供的原始数据或问题
- Prompt 模板:格式化输入,构建给 LLM 的指令
- LLM 模型:执行推理,生成回复
- 输出解析器(可选):将模型输出转换为结构化数据
- 输出(Output):最终返回给用户的结果
3.2 为什么使用 Chain
使用 Chain 有以下几个核心优势:
| 优势 | 说明 | 示例 |
|---|---|---|
| 模块化 | 每个组件职责单一,易于理解和维护 | Prompt 只负责格式化,LLM 只负责推理 |
| 可复用 | 封装好的 Chain 可以在多个场景复用 | 翻译链可用于文档、对话、邮件等场景 |
| 易于调试 | 可以单独测试每个环节,快速定位问题 | 检查 Prompt 输出、观察 LLM 响应 |
| 可扩展 | 方便添加新组件或替换现有组件 | 将 GPT-3.5 替换为 GPT-4 只需一行代码 |
最佳实践:把常用的功能封装成 Chain,可以大大提升开发效率。比如"翻译链"、"总结链"、"问答链"等。
3.3 LLMChain(基础链)
LLMChain是最基础的链类型,它将 Prompt + LLM + OutputParser 组合在一起,是最常用的链模式。
LLMChain 的组成
┌─────────────────────────────────────────────────────────────┐
│ LLMChain │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ PromptTemplate│───▶│ LLM │───▶│OutputParser │ │
│ │ (格式化输入) │ │ (推理) │ │ (解析输出) │ │
│ └──────────────┘ └──────────┘ └──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ {问题} ─────────────────────────────────────▶ {答案} │
│ │
└─────────────────────────────────────────────────────────────┘
完整代码示例
# 导入必要的库
from langchain import OpenAI, PromptTemplate, LLMChain
from langchain.output_parsers import CommaSeparatedListOutputParser
import os
# 设置 OpenAI API 密钥
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
# ==================== 基础 LLMChain 示例 ====================
# 步骤1:创建 Prompt 模板
# 使用 {product} 作为占位符,运行时会被替换为实际值
template = """
你是一位专业的产品经理。请为以下产品起一个吸引人的中文名字,并写一段简短的宣传语。
产品描述:{product}
请按以下格式输出:
产品名:xxx
宣传语:xxx
"""
prompt = PromptTemplate(
input_variables=["product"], # 定义输入变量
template=template # 定义模板内容
)
# 步骤2:创建 LLM 实例
# temperature=0.7 控制创造性,值越高输出越多样
llm = OpenAI(temperature=0.7)
# 步骤3:创建 LLMChain
# 将 prompt 和 llm 组合成链
llm_chain = LLMChain(
llm=llm,
prompt=prompt
)
# 步骤4:运行链
# 传入实际的产品描述
product_description = "一款可以自动记录宠物健康状况和饮食情况的智能项圈"
result = llm_chain.predict(product=product_description)
print("=== 基础 LLMChain 结果 ===")
print(result)
print()
# ==================== 带输出解析器的 LLMChain ====================
# 步骤1:创建输出解析器
# 这个解析器会将逗号分隔的文本转换为 Python 列表
output_parser = CommaSeparatedListOutputParser()
# 步骤2:获取格式说明
# 输出解析器会自动生成格式说明,告诉 LLM 如何输出
format_instructions = output_parser.get_format_instructions()
# 步骤3:创建新的 Prompt 模板,包含格式说明
template_with_parser = """
请列出{topic}的5个主要优点。
{format_instructions}
"""
prompt_with_parser = PromptTemplate(
input_variables=["topic"],
partial_variables={"format_instructions": format_instructions}, # 预设格式说明
template=template_with_parser
)
# 步骤4:创建带解析器的 LLMChain
chain_with_parser = LLMChain(
llm=llm,
prompt=prompt_with_parser,
output_parser=output_parser # 添加输出解析器
)
# 步骤5:运行并获取结构化输出
result_list = chain_with_parser.predict(topic="远程办公")
print("=== 带输出解析器的 LLMChain 结果 ===")
print(f"结果类型: {type(result_list)}")
print(f"结果内容: {result_list}")
# 现在 result_list 是一个 Python 列表,可以方便地处理
for i, item in enumerate(result_list, 1):
print(f"{i}. {item}")
提示:使用 predict() 方法时,参数名必须与 PromptTemplate 中定义的 input_variables 一致。
3.4 Sequential Chain(顺序链)
当需要多个步骤依次处理时,可以使用顺序链(Sequential Chain)。它允许你将多个链串联起来,前一个链的输出作为后一个链的输入。
SimpleSequentialChain:简单串联
SimpleSequentialChain 是最简单的顺序链,它按顺序执行多个链,每个链只有一个输入和一个输出,前一个输出自动作为后一个输入。
SimpleSequentialChain 执行流程:
输入 ──▶ Chain1 ──▶ 中间结果1 ──▶ Chain2 ──▶ 中间结果2 ──▶ Chain3 ──▶ 最终结果
每个链只有一个输入和一个输出,自动串联
from langchain import OpenAI, PromptTemplate, LLMChain, SimpleSequentialChain
import os
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
llm = OpenAI(temperature=0.7)
# ==================== Chain 1: 生成文章标题 ====================
template1 = """你是一位科技博主。请根据以下主题,创作一个吸引人的文章标题。
主题:{topic}
只输出标题,不要其他内容。"""
prompt1 = PromptTemplate(input_variables=["topic"], template=template1)
chain1 = LLMChain(llm=llm, prompt=prompt1, output_key="title")
# ==================== Chain 2: 根据标题写文章大纲 ====================
template2 = """你是一位资深编辑。请为以下标题创作一个详细的文章大纲。
标题:{title}
大纲格式:
一、引言
二、主要内容(分3-4点)
三、结论
只输出大纲。"""
prompt2 = PromptTemplate(input_variables=["title"], template=template2)
chain2 = LLMChain(llm=llm, prompt=prompt2, output_key="outline")
# ==================== Chain 3: 根据大纲写完整文章 ====================
template3 = """你是一位专业作家。请根据以下大纲,撰写一篇完整的文章。
大纲:
{outline}
要求:
1. 文章长度约800字
2. 语言流畅,逻辑清晰
3. 适合普通读者阅读
开始写作:"""
prompt3 = PromptTemplate(input_variables=["outline"], template=template3)
chain3 = LLMChain(llm=llm, prompt=prompt3, output_key="article")
# ==================== 组合成 SimpleSequentialChain ====================
# 注意:SimpleSequentialChain 要求每个链只有一个输入和一个输出
# 输出会自动传递给下一个链的输入
overall_chain = SimpleSequentialChain(
chains=[chain1, chain2, chain3], # 按顺序执行
verbose=True # 显示执行过程,方便调试
)
# 运行链
result = overall_chain.run("人工智能对未来教育的影响")
print("\n=== 最终结果 ===")
print(result)
注意:SimpleSequentialChain 要求每个子链只有一个输入变量和一个输出变量,且输出会自动作为下一个链的输入。
SequentialChain:多输入多输出
当需要处理多个输入和输出时,使用 SequentialChain。它可以明确指定每个链的输入和输出关系。
SequentialChain 执行流程:
输入A ──┐
├──▶ Chain1 ──┬──▶ 输出X ──┐
输入B ──┘ └──▶ 输出Y ──┼──▶ Chain2 ──▶ 最终结果
│
输入C ──┘
支持多输入多输出,可以灵活组合
from langchain import OpenAI, PromptTemplate, LLMChain, SequentialChain
import os
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
llm = OpenAI(temperature=0.7)
# ==================== 实战示例:写文章 → 翻译 → 评价 ====================
# Chain 1: 根据主题和风格写文章
# 输入:topic, style
# 输出:article
template1 = """你是一位专业作家。请根据以下要求写一篇文章:
主题:{topic}
风格:{style}
字数:约500字
文章内容:"""
prompt1 = PromptTemplate(
input_variables=["topic", "style"],
template=template1
)
chain1 = LLMChain(
llm=llm,
prompt=prompt1,
output_key="article" # 明确指定输出名称
)
# Chain 2: 翻译文章
# 输入:article
# 输出:translation
template2 = """请将以下文章翻译成英文:
{article}
英文翻译:"""
prompt2 = PromptTemplate(
input_variables=["article"],
template=template2
)
chain2 = LLMChain(
llm=llm,
prompt=prompt2,
output_key="translation"
)
# Chain 3: 评价文章质量
# 输入:article(同时使用原文)
# 输出:review
template3 = """请对以下文章进行评价:
原文:
{article}
请从以下几个方面评价:
1. 内容质量(1-10分)
2. 语言表达(1-10分)
3. 结构逻辑(1-10分)
4. 总体建议
评价:"""
prompt3 = PromptTemplate(
input_variables=["article"],
template=template3
)
chain3 = LLMChain(
llm=llm,
prompt=prompt3,
output_key="review"
)
# Chain 4: 生成摘要
# 输入:article, translation
# 输出:summary
template4 = """请根据原文和英文翻译,生成一个简短的双语摘要。
原文摘要(50字以内):
英文翻译摘要(30词以内):
原文:
{article}
英文翻译:
{translation}
摘要:"""
prompt4 = PromptTemplate(
input_variables=["article", "translation"],
template=template4
)
chain4 = LLMChain(
llm=llm,
prompt=prompt4,
output_key="summary"
)
# ==================== 组合成 SequentialChain ====================
overall_chain = SequentialChain(
chains=[chain1, chain2, chain3, chain4],
input_variables=["topic", "style"], # 初始输入
output_variables=["article", "translation", "review", "summary"], # 最终输出
verbose=True # 显示执行过程
)
# 运行链
result = overall_chain({
"topic": "远程工作的利与弊",
"style": "客观理性"
})
# 输出各个结果
print("\n" + "="*50)
print("生成的文章:")
print("="*50)
print(result["article"])
print("\n" + "="*50)
print("英文翻译:")
print("="*50)
print(result["translation"])
print("\n" + "="*50)
print("文章评价:")
print("="*50)
print(result["review"])
print("\n" + "="*50)
print("双语摘要:")
print("="*50)
print(result["summary"])
关键点:使用 output_key 明确命名每个链的输出,后续链可以通过这些名称引用前面链的输出。
3.5 Router Chain(路由链)
Router Chain 是 LangChain 中非常强大的功能,它可以根据输入内容自动路由到不同的子链进行处理。这非常适合构建智能客服、多意图识别等场景。
路由链的工作原理
Router Chain 执行流程:
用户输入
│
▼
┌─────────────┐
│ 路由链 │───▶ 分析输入,确定意图/类别
│ (Router) │
└─────────────┘
│
▼
选择对应的处理链
│
├─▶ 技术问题 ──▶ 技术客服链
├─▶ 销售咨询 ──▶ 销售支持链
├─▶ 售后服务 ──▶ 售后处理链
└─▶ 其他问题 ──▶ 通用客服链
根据路由结果,执行对应的子链
from langchain import OpenAI, PromptTemplate, LLMChain
from langchain.chains.router import MultiPromptChain
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains import ConversationChain
import os
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
llm = OpenAI(temperature=0)
# ==================== 定义多个专业领域的 Prompt ====================
# 技术支持模板
tech_template = """你是某软件公司的技术支持专家。你擅长解决各种技术问题,
包括软件安装、配置、故障排查等。
请用专业但易懂的语言回答用户的技术问题。如果问题不够明确,请询问更多细节。
用户问题:{input}"""
# 销售咨询模板
sales_template = """你是某公司的销售顾问。你熟悉公司的各种产品和服务,
能够根据客户需求推荐合适的解决方案。
请热情、专业地回答客户的销售咨询,突出产品优势,同时诚实说明产品限制。
客户咨询:{input}"""
# 售后服务模板
service_template = """你是某公司的售后服务专员。你负责处理客户的退换货、
投诉、使用指导等售后问题。
请耐心、友善地处理客户问题,尽力让客户满意。对于投诉,要认真倾听并道歉。
客户反馈:{input}"""
# 通用咨询模板
general_template = """你是某公司的客服代表。你友好、专业,能够回答各种一般性问题。
如果用户问题需要转接给专业部门,请礼貌地告知用户并简要说明原因。
用户咨询:{input}"""
# 创建 Prompt 信息列表
prompt_infos = [
{
"name": "技术支持",
"description": "回答软件安装、配置、故障排查等技术问题",
"prompt_template": tech_template
},
{
"name": "销售咨询",
"description": "回答产品价格、功能、购买方式等销售相关问题",
"prompt_template": sales_template
},
{
"name": "售后服务",
"description": "处理退换货、投诉、使用指导等售后问题",
"prompt_template": service_template
},
{
"name": "通用咨询",
"description": "回答其他一般性问题",
"prompt_template": general_template
}
]
# ==================== 构建目标链 ====================
# 为每个 Prompt 创建一个 LLMChain
destination_chains = {}
for p_info in prompt_infos:
name = p_info["name"]
prompt = PromptTemplate(
template=p_info["prompt_template"],
input_variables=["input"]
)
chain = LLMChain(llm=llm, prompt=prompt)
destination_chains[name] = chain
# 默认链:当路由无法确定时使用
default_chain = ConversationChain(llm=llm, output_key="text")
# ==================== 构建路由链 ====================
# 路由模板:告诉 LLM 如何选择
destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)
router_template = """根据用户输入,选择最适合处理该问题的客服类型。
可选类型:
{destinations}
用户输入:{{input}}
请分析用户意图,输出一个 JSON 对象:
{{{{
"destination": "选择的类型名称",
"next_inputs": "处理后的输入"
}}}}
如果不确定,请输出 "DEFAULT" 作为 destination。"""
router_prompt = PromptTemplate(
template=router_template.format(destinations=destinations_str),
input_variables=["input"],
output_parser=RouterOutputParser(), # 解析路由决策
)
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
# ==================== 组合成 MultiPromptChain ====================
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain,
verbose=True # 显示路由决策过程
)
# ==================== 测试不同场景 ====================
test_questions = [
"我的软件安装到一半报错,错误代码 0x80070005,怎么办?", # 技术问题
"你们的企业版多少钱?有什么功能?", # 销售问题
"我想退货,订单号是 12345,可以吗?", # 售后问题
"你们公司成立多久了?", # 通用问题
]
for question in test_questions:
print("\n" + "="*60)
print(f"用户问题:{question}")
print("="*60)
result = chain.run(question)
print(f"\n回复:\n{result}")
注意:Router Chain 需要正确配置 RouterOutputParser 来解析 LLM 的路由决策。建议在生产环境中添加错误处理机制。
3.6 LCEL(LangChain Expression Language)
LCEL 是 LangChain 表达式语言,它提供了一种更简洁、更直观的方式来组合链。使用管道操作符 |,你可以像写 Unix 管道命令一样构建链。
LCEL 的优势
- 简洁直观:代码更易读,像流水线一样清晰
- 自动类型推断:更好的 IDE 支持和类型检查
- 流式支持:更容易实现流式输出
- 并行执行:支持自动并行化
- 可观测性:更好的调试和日志支持
基础语法
# LCEL 基础语法
chain = component1 | component2 | component3
# 等价于传统写法
chain = Chain(components=[component1, component2, component3])
用 LCEL 重写链
from langchain import OpenAI, PromptTemplate
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.schema import StrOutputParser
import os
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
# ==================== 传统写法 vs LCEL 写法 ====================
# ---------------- 传统写法 ----------------
from langchain import LLMChain
template = "请列出{topic}的5个主要优点。"
prompt = PromptTemplate.from_template(template)
llm = OpenAI(temperature=0.7)
# 传统方式
chain_traditional = LLMChain(llm=llm, prompt=prompt)
result = chain_traditional.predict(topic="远程办公")
# ---------------- LCEL 写法 ----------------
from langchain.chat_models import ChatOpenAI
# 使用 ChatOpenAI 替代 OpenAI(推荐用于聊天场景)
chat_llm = ChatOpenAI(temperature=0.7)
# LCEL 写法:使用 | 管道符连接
chain_lcel = prompt | chat_llm
# 运行链
result = chain_lcel.invoke({"topic": "远程办公"})
print(result.content) # ChatOpenAI 返回的是消息对象
# ==================== 完整的 LCEL 链 ====================
# 步骤1:定义 Prompt
prompt = PromptTemplate.from_template("""
请分析以下主题,列出5个关键点。
主题:{topic}
要求:简洁明了,每点不超过20字。
""")
# 步骤2:定义输出解析器
output_parser = StrOutputParser() # 字符串输出解析器
# 步骤3:使用 LCEL 组合链
# prompt | llm | output_parser
chain = (
prompt
| chat_llm
| output_parser # 从消息对象中提取文本内容
)
# 步骤4:运行链
result = chain.invoke({"topic": "人工智能的应用"})
print("=== LCEL 链结果 ===")
print(result)
# ==================== 更复杂的 LCEL 示例 ====================
from langchain.schema.runnable import RunnableLambda
# 定义一个自定义处理函数
def add_header(text: str) -> str:
"""为结果添加标题"""
return f"【分析结果】\n\n{text}"
def count_lines(text: str) -> str:
"""统计行数"""
lines = text.strip().split('\n')
return f"{text}\n\n共 {len(lines)} 行"
# 将普通函数转换为 Runnable
add_header_runnable = RunnableLambda(add_header)
count_lines_runnable = RunnableLambda(count_lines)
# 组合更复杂的链
complex_chain = (
prompt
| chat_llm
| output_parser
| add_header_runnable # 添加自定义处理
| count_lines_runnable # 再添加一个处理
)
result = complex_chain.invoke({"topic": "机器学习"})
print("\n=== 复杂 LCEL 链结果 ===")
print(result)
提示:LCEL 是 LangChain 未来的主推方式,建议新项目中优先使用 LCEL 语法。
LCEL 中的实用技巧
from langchain import PromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.runnable import RunnableParallel, RunnablePassthrough
from langchain.schema import StrOutputParser
import os
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
llm = ChatOpenAI(temperature=0.7)
# ==================== 技巧1: 传递原始输入 ====================
# 使用 RunnablePassthrough 保留原始输入
template = """根据用户问题生成回答。
用户问题:{question}
上下文:{question}
回答:"""
prompt = PromptTemplate.from_template(template)
# RunnablePassthrough 可以让输入原样通过
chain = (
{"question": RunnablePassthrough()} # 保留原始输入
| prompt
| llm
| StrOutputParser()
)
result = chain.invoke("什么是深度学习?")
print(result)
# ==================== 技巧2: 并行执行 ====================
# 同时执行多个任务
# 定义两个并行的分析任务
sentiment_template = """分析以下文本的情感倾向(积极/消极/中性):
{text}
情感:"""
summary_template = """总结以下文本的主要内容(50字以内):
{text}
总结:"""
sentiment_prompt = PromptTemplate.from_template(sentiment_template)
summary_prompt = PromptTemplate.from_template(summary_template)
# 创建两个独立的链
sentiment_chain = sentiment_prompt | llm | StrOutputParser()
summary_chain = summary_prompt | llm | StrOutputParser()
# 使用 RunnableParallel 并行执行
parallel_chain = RunnableParallel(
sentiment=sentiment_chain, # 情感分析
summary=summary_chain # 内容总结
)
# 注意:这里需要为两个链提供相同的输入
text = """
人工智能正在深刻改变我们的生活方式。从智能手机的语音助手到自动驾驶汽车,
从医疗诊断到金融风控,AI技术无处不在。这些创新带来了巨大的便利,
但也引发了人们对隐私和就业的担忧。
"""
result = parallel_chain.invoke({"text": text})
print("\n=== 并行执行结果 ===")
print(f"情感分析:{result['sentiment']}")
print(f"内容总结:{result['summary']}")
# ==================== 技巧3: 条件分支(简单版) ====================
def route_by_length(data: dict) -> dict:
"""根据文本长度选择不同的处理方式"""
text = data["text"]
if len(text) < 100:
return {"route": "short", "text": text}
else:
return {"route": "long", "text": text}
# 这种路由需要结合其他组件使用
# 实际项目中可以使用 RouterChain 或自定义 Runnable
3.7 自定义 Chain
当内置的链无法满足需求时,你可以创建自定义 Chain。这在需要特殊处理逻辑或与外部系统集成时非常有用。
from langchain.chains.base import Chain
from langchain import OpenAI, PromptTemplate
from typing import Dict, List
import os
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
# ==================== 创建自定义 Chain ====================
class ValidationChain(Chain):
"""
自定义验证链:先验证输入,再决定是否调用 LLM
"""
# 定义链使用的组件
llm: OpenAI = None
prompt: PromptTemplate = None
# 验证规则
min_length: int = 5
max_length: int = 500
forbidden_words: List[str] = None
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.forbidden_words = self.forbidden_words or ["敏感词1", "敏感词2"]
if self.llm is None:
self.llm = OpenAI(temperature=0.7)
@property
def input_keys(self) -> List[str]:
"""定义输入变量"""
return ["query"]
@property
def output_keys(self) -> List[str]:
"""定义输出变量"""
return ["response", "validation_passed", "validation_message"]
def _validate(self, query: str) -> tuple:
"""
验证输入
返回: (是否通过, 错误信息)
"""
# 检查长度
if len(query) < self.min_length:
return False, f"输入太短,至少需要 {self.min_length} 个字符"
if len(query) > self.max_length:
return False, f"输入太长,最多 {self.max_length} 个字符"
# 检查敏感词
for word in self.forbidden_words:
if word in query:
return False, f"输入包含敏感词: {word}"
return True, "验证通过"
def _call(self, inputs: Dict[str, str]) -> Dict[str, str]:
"""
执行链的核心逻辑
"""
query = inputs["query"]
# 步骤1: 验证输入
is_valid, message = self._validate(query)
if not is_valid:
# 验证失败,返回错误信息
return {
"response": "",
"validation_passed": "False",
"validation_message": message
}
# 步骤2: 调用 LLM(如果没有设置 prompt,使用默认处理)
if self.prompt:
formatted_prompt = self.prompt.format(query=query)
response = self.llm.predict(formatted_prompt)
else:
response = self.llm.predict(f"请回答以下问题:{query}")
return {
"response": response,
"validation_passed": "True",
"validation_message": message
}
# ==================== 使用自定义 Chain ====================
# 创建验证链实例
validation_chain = ValidationChain(
min_length=3,
max_length=200,
forbidden_words=["垃圾", "诈骗"]
)
# 测试验证通过的情况
print("=== 测试1: 正常输入 ===")
result = validation_chain.run({"query": "请介绍一下Python语言"})
print(f"验证结果: {result}")
# 测试验证失败的情况(太短)
print("\n=== 测试2: 输入太短 ===")
result = validation_chain.run({"query": "Hi"})
print(f"验证结果: {result}")
# 测试验证失败的情况(包含敏感词)
print("\n=== 测试3: 包含敏感词 ===")
result = validation_chain.run({"query": "这是一个垃圾信息"})
print(f"验证结果: {result}")
# ==================== 更复杂的自定义 Chain ====================
class MultiStepChain(Chain):
"""
多步骤处理链:先分析问题,再生成回答,最后格式化输出
"""
llm: OpenAI = None
def __init__(self, **kwargs):
super().__init__(**kwargs)
if self.llm is None:
self.llm = OpenAI(temperature=0.7)
@property
def input_keys(self) -> List[str]:
return ["question"]
@property
def output_keys(self) -> List[str]:
return ["analysis", "answer", "formatted_response"]
def _call(self, inputs: Dict[str, str]) -> Dict[str, str]:
question = inputs["question"]
# 步骤1: 分析问题
analysis_prompt = f"""请分析以下问题的关键点和用户的潜在需求:
问题:{question}
分析:"""
analysis = self.llm.predict(analysis_prompt)
# 步骤2: 基于分析生成回答
answer_prompt = f"""基于以下分析,详细回答用户的问题。
问题:{question}
分析:{analysis}
请提供详细、准确的回答:"""
answer = self.llm.predict(answer_prompt)
# 步骤3: 格式化输出
formatted = f"""【问题分析】
{analysis}
【详细回答】
{answer}
---
希望以上回答对您有帮助!"""
return {
"analysis": analysis,
"answer": answer,
"formatted_response": formatted
}
# 使用多步骤链
multi_chain = MultiStepChain()
print("\n=== 多步骤链测试 ===")
result = multi_chain({"question": "如何提高编程能力?"})
print(result["formatted_response"])
自定义 Chain 的关键点:
- 继承
Chain基类 - 实现
input_keys和output_keys属性 - 实现
_call方法定义执行逻辑 - 可以使用 Pydantic 进行参数验证
3.8 完整实战:构建智能客服链
现在我们将综合运用本章所学知识,构建一个完整的智能客服链。
需求分析
- 用户输入问题 → 系统自动分类(技术/销售/售后/其他)
- 根据分类 → 路由到对应的处理链
- 生成专业回答 → 格式化输出
- 记录处理日志 → 便于后续分析
"""
智能客服系统 - 完整实战
功能:
1. 问题分类
2. 路由到专业客服
3. 生成回答
4. 格式化输出
5. 记录日志
"""
from langchain import OpenAI, PromptTemplate
from langchain.chains import LLMChain, SequentialChain, TransformChain
from langchain.output_parsers import CommaSeparatedListOutputParser
import os
from datetime import datetime
import json
os.environ["OPENAI_API_KEY"] = "your-api-key-here"
# ==================== 配置 ====================
llm = OpenAI(temperature=0.5)
# ==================== Chain 1: 问题分类器 ====================
# 输入:user_question
# 输出:category(技术/销售/售后/其他)
classify_template = """你是智能客服系统的分类器。请分析用户问题,将其分类为以下类别之一:
可选类别:
- 技术问题:软件安装、配置、故障排查、功能使用等技术相关问题
- 销售咨询:产品价格、功能对比、购买方式、授权许可等销售相关问题
- 售后服务:退换货、退款、投诉、账户问题等售后相关问题
- 其他问题:不属于以上类别的一般性问题
用户问题:{user_question}
请只输出类别名称(技术问题/销售咨询/售后服务/其他问题),不要其他内容。
类别:"""
classify_prompt = PromptTemplate(
input_variables=["user_question"],
template=classify_template
)
classify_chain = LLMChain(
llm=llm,
prompt=classify_prompt,
output_key="category"
)
# ==================== Chain 2: 生成专业回答 ====================
# 输入:user_question, category
# 输出:raw_response
def get_specialist_prompt(category):
"""根据类别获取专业 Prompt"""
prompts = {
"技术问题": """你是一位资深技术支持工程师。请专业、耐心地回答用户的技术问题。
如果问题信息不足,请礼貌地询问更多细节。
用户问题:{user_question}
请提供详细的解决方案:""",
"销售咨询": """你是一位热情的销售顾问。请专业地介绍产品优势,同时诚实说明产品限制。
突出产品如何满足客户需求。
客户咨询:{user_question}
请提供详细的回复:""",
"售后服务": """你是一位耐心的售后服务专员。请认真倾听客户问题,尽力解决。
对于投诉,要真诚道歉并提供补救方案。
客户反馈:{user_question}
请提供处理方案:""",
"其他问题": """你是一位友好的客服代表。请热情、专业地回答用户问题。
如果问题需要转接其他部门,请说明原因。
用户咨询:{user_question}
请提供回复:"""
}
return prompts.get(category, prompts["其他问题"])
# 使用动态 Prompt 的 Chain
response_template = """根据以下类别处理用户问题:
类别:{category}
用户问题:{user_question}
{specialist_instructions}"""
# 这里我们使用一个转换链来根据类别选择提示词
def generate_response(inputs):
"""根据分类生成专业回答"""
category = inputs["category"].strip()
question = inputs["user_question"]
# 获取对应类别的 Prompt
specialist_template = get_specialist_prompt(category)
specialist_prompt = PromptTemplate(
input_variables=["user_question"],
template=specialist_template
)
# 创建临时 Chain 生成回答
response_chain = LLMChain(llm=llm, prompt=specialist_prompt)
response = response_chain.predict(user_question=question)
return {"raw_response": response}
response_transform_chain = TransformChain(
input_variables=["user_question", "category"],
output_variables=["raw_response"],
transform=generate_response
)
# ==================== Chain 3: 格式化输出 ====================
# 输入:user_question, category, raw_response
# 输出:formatted_response
format_template = """请将以下客服回复整理成标准格式:
原始回复:
{raw_response}
请按以下格式输出(不要包含思考过程):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【客服回复】
{raw_response}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
问题类型:{category}
回复时间:{timestamp}
如有其他问题,欢迎继续咨询!
"""
format_prompt = PromptTemplate(
input_variables=["raw_response", "category"],
template=format_template
)
# 使用当前时间
from langchain.chains import TransformChain
def add_timestamp(inputs):
"""添加时间戳"""
inputs["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return inputs
timestamp_chain = TransformChain(
input_variables=["raw_response", "category"],
output_variables=["raw_response", "category", "timestamp"],
transform=add_timestamp
)
format_chain = LLMChain(
llm=llm,
prompt=format_prompt,
output_key="formatted_response"
)
# ==================== 组合成完整客服链 ====================
customer_service_chain = SequentialChain(
chains=[classify_chain, response_transform_chain, timestamp_chain, format_chain],
input_variables=["user_question"],
output_variables=["category", "raw_response", "timestamp", "formatted_response"],
verbose=True
)
# ==================== 日志记录功能 ====================
def log_interaction(question, result):
"""记录客服交互日志"""
log_entry = {
"timestamp": result["timestamp"],
"question": question,
"category": result["category"],
"response": result["raw_response"]
}
# 写入日志文件
with open("customer_service_log.jsonl", "a", encoding="utf-8") as f:
f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
print(f"\n[日志已记录] 类别: {result['category']}")
# ==================== 运行测试 ====================
test_questions = [
"软件安装时出现错误代码 0x80070005,如何解决?",
"企业版和个人版有什么区别?价格分别是多少?",
"我购买的软件激活失败,订单号是 12345,能退款吗?",
"你们公司的地址在哪里?"
]
print("="*70)
print("智能客服系统 - 开始测试")
print("="*70)
for question in test_questions:
print(f"\n{'='*70}")
print(f"用户问题:{question}")
print(f"{'='*70}")
# 运行客服链
result = customer_service_chain({"user_question": question})
# 显示格式化输出
print(f"\n{result['formatted_response']}")
# 记录日志
log_interaction(question, result)
print("\n" + "="*70)
print("测试完成!日志已保存到 customer_service_log.jsonl")
print("="*70)
# ==================== 统计功能 ====================
def show_statistics():
"""显示客服统计信息"""
try:
with open("customer_service_log.jsonl", "r", encoding="utf-8") as f:
logs = [json.loads(line) for line in f if line.strip()]
# 统计各类问题数量
from collections import Counter
categories = [log["category"] for log in logs]
category_count = Counter(categories)
print("\n" + "="*70)
print("客服数据统计")
print("="*70)
print(f"总咨询量:{len(logs)}")
print("\n问题分类统计:")
for category, count in category_count.most_common():
print(f" - {category}: {count}次")
print("="*70)
except FileNotFoundError:
print("暂无日志记录")
# 显示统计
show_statistics()
实战总结:这个智能客服系统展示了如何:
- 使用 SequentialChain 串联多个处理步骤
- 根据分类动态选择处理逻辑
- 使用 TransformChain 添加自定义处理(如时间戳)
- 实现日志记录和统计分析功能
本章小结
| 链类型 | 适用场景 | 核心特点 |
|---|---|---|
| LLMChain | 单一任务处理 | Prompt + LLM + OutputParser 的基础组合 |
| SimpleSequentialChain | 简单线性流程 | 单输入单输出,自动传递 |
| SequentialChain | 复杂多步骤流程 | 支持多输入多输出,灵活组合 |
| Router Chain | 多意图识别、智能路由 | 根据输入自动选择处理链 |
| LCEL | 现代链式编程 | 简洁直观,支持流式和并行 |
| 自定义 Chain | 特殊业务逻辑 | 完全可控,灵活扩展 |
下一章我们将学习文档加载与处理,这是构建知识库和 RAG 应用的基础。你将学会如何加载各种格式的文档、分割长文本、生成向量嵌入。